From 4cd131cf292bf4f5e06c7db89e9224fe36894330 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Sun, 28 Sep 2025 13:53:40 -0300 Subject: [PATCH 01/31] Update miniaudio to v0.11.23 (#5208) --- src/external/miniaudio.h | 6087 ++++++++++++++++++++++++++------------ 1 file changed, 4134 insertions(+), 1953 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index c74bebeb3..1e4e2c655 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.22 - 2025-02-24 +miniaudio - v0.11.23 - 2025-09-11 David Reid - mackron@gmail.com @@ -12,18 +12,10 @@ GitHub: https://github.com/mackron/miniaudio /* 1. Introduction =============== -To use miniaudio, include "miniaudio.h": - - ```c - #include "miniaudio.h" - ``` - -The implementation is contained in "miniaudio.c". Just compile this like any other source file. You -can include miniaudio.c if you want to compile your project as a single translation unit: - - ```c - #include "miniaudio.c" - ``` +To use miniaudio, just include "miniaudio.h" like any other header and add "miniaudio.c" to your +source tree. If you don't want to add it to your source tree you can compile and link to it like +any other library. Note that ABI compatibility is not guaranteed between versions, even with bug +fix releases, so take care if compiling as a shared object. miniaudio includes both low level and high level APIs. The low level API is good for those who want to do all of their mixing themselves and only require a light weight interface to the underlying @@ -303,7 +295,7 @@ The engine encapsulates both the resource manager and the node graph to create a use high level API. The resource manager and node graph APIs are covered in more later sections of this manual. -The code below shows how you can initialize an engine using it's default configuration. +The code below shows how you can initialize an engine using its default configuration. ```c ma_result result; @@ -391,7 +383,7 @@ Sounds are not started by default. Start a sound with `ma_sound_start()` and sto `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use `ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -the be started and/or stopped at a specific time. This can be done with the following functions: +to be started and/or stopped at a specific time. This can be done with the following functions: ```c ma_sound_set_start_time_in_pcm_frames() @@ -463,6 +455,11 @@ is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled wit miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details. +This library has been designed to be added directly to your source tree which is the preferred way +of using it, but you can compile it as a normal library if that's your preference. Be careful if +compiling as a shared object because miniaudio is not ABI compatible between any release, including +bug fix releases. It's recommended you link statically. + Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, @@ -532,7 +529,7 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use `-std=c*` compiler flags, nor `-ansi`. -You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling +You can enable the use of AudioWorklets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling with the following options: -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY @@ -881,7 +878,7 @@ read data within a certain range of the underlying data. To do this you can use This is useful if you have a sound bank where many sounds are stored in the same file and you want the data source to only play one of those sub-sounds. Note that once the range is set, everything -that takes a position, such as cursors and loop points, should always be relatvie to the start of +that takes a position, such as cursors and loop points, should always be relative to the start of the range. When the range is set, any previously defined loop point will be reset. Custom loop points can also be used with data sources. By default, data sources will loop after @@ -889,7 +886,7 @@ they reach the end of the data source, but if you need to loop at a specific loc the following: ```c - result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); + result = ma_data_source_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); if (result != MA_SUCCESS) { return result; // Failed to set the loop point. } @@ -3750,7 +3747,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 22 +#define MA_VERSION_REVISION 23 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3857,6 +3854,8 @@ typedef ma_uint16 wchar_t; #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ #endif +#define MA_UINT64_MAX (((ma_uint64)0xFFFFFFFF << 32) | (ma_uint64)0xFFFFFFFF) /* Weird shifting syntax is for VC6 compatibility. */ + /* Platform/backend detection. */ #if defined(_WIN32) || defined(__COSMOPOLITAN__) @@ -3865,29 +3864,55 @@ typedef ma_uint16 wchar_t; #define MA_WIN32_UWP #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) #define MA_WIN32_GDK + #elif defined(NXDK) + #define MA_WIN32_NXDK #else #define MA_WIN32_DESKTOP #endif + + /* The original Xbox. */ + #if defined(NXDK) /* <-- Add other Xbox compiler toolchains here, and then add a toolchain-specific define in case we need to discriminate between them later. */ + #define MA_XBOX + + #if defined(NXDK) + #define MA_XBOX_NXDK + #endif + #endif #endif -#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ +#if defined(__MSDOS__) || defined(MSDOS) || defined(_MSDOS) || defined(__DOS__) + #define MA_DOS + + /* No threading allowed on DOS. */ + #ifndef MA_NO_THREADING + #define MA_NO_THREADING + #endif + + /* No runtime linking allowed on DOS. */ + #ifndef MA_NO_RUNTIME_LINKING + #define MA_NO_RUNTIME_LINKING + #endif +#endif +#if !defined(MA_WIN32) && !defined(MA_DOS) /* If it's not Win32, assume POSIX. */ #define MA_POSIX - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; + #if !defined(MA_NO_THREADING) + /* + Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. + You can use this to avoid including pthread.h in the header section. The downside is that it + results in some fixed sized structures being declared for the various types that are used in + miniaudio. The risk here is that these types might be too small for a given platform. This + risk is yours to take and no support will be offered if you enable this option. + */ + #ifndef MA_NO_PTHREAD_IN_HEADER + #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ + typedef pthread_t ma_pthread_t; + typedef pthread_mutex_t ma_pthread_mutex_t; + typedef pthread_cond_t ma_pthread_cond_t; + #else + typedef ma_uintptr ma_pthread_t; + typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; + typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; + #endif #endif #if defined(__unix__) @@ -3914,8 +3939,11 @@ typedef ma_uint16 wchar_t; #if defined(__PROSPERO__) #define MA_PROSPERO #endif - #if defined(__NX__) - #define MA_NX + #if defined(__3DS__) + #define MA_3DS + #endif + #if defined(__SWITCH__) || defined(__NX__) + #define MA_SWITCH #endif #if defined(__BEOS__) || defined(__HAIKU__) #define MA_BEOS @@ -3925,12 +3953,13 @@ typedef ma_uint16 wchar_t; #endif #endif -#if defined(__has_c_attribute) - #if __has_c_attribute(fallthrough) - #define MA_FALLTHROUGH [[fallthrough]] - #endif +#if !defined(MA_FALLTHROUGH) && defined(__cplusplus) && __cplusplus >= 201703L + #define MA_FALLTHROUGH [[fallthrough]] #endif -#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) +#if !defined(MA_FALLTHROUGH) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L + #define MA_FALLTHROUGH [[fallthrough]] +#endif +#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) #if __has_attribute(fallthrough) #define MA_FALLTHROUGH __attribute__((fallthrough)) #endif @@ -3967,7 +3996,7 @@ typedef ma_uint16 wchar_t; #define MA_NO_INLINE __attribute__((noinline)) #else #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE __attribute__((noinline)) + #define MA_NO_INLINE #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline @@ -4350,7 +4379,7 @@ typedef struct typedef struct { - ma_int32 state; + ma_uint32 state; } ma_lcg; @@ -6569,7 +6598,7 @@ This section contains the APIs for device playback and capture. Here is where yo ************************************************************************************************************************************************************/ #ifndef MA_NO_DEVICE_IO /* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) +#if defined(MA_WIN32) && !defined(MA_XBOX) #define MA_SUPPORT_WASAPI #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ @@ -7426,6 +7455,7 @@ struct ma_context ma_proc snd_pcm_hw_params_set_rate_resample; ma_proc snd_pcm_hw_params_set_rate; ma_proc snd_pcm_hw_params_set_rate_near; + ma_proc snd_pcm_hw_params_set_rate_minmax; ma_proc snd_pcm_hw_params_set_buffer_size_near; ma_proc snd_pcm_hw_params_set_periods_near; ma_proc snd_pcm_hw_params_set_access; @@ -7986,6 +8016,7 @@ struct ma_device /*AAudioStream**/ ma_ptr pStreamPlayback; /*AAudioStream**/ ma_ptr pStreamCapture; ma_mutex rerouteLock; + ma_atomic_bool32 isTearingDown; ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; @@ -11255,7 +11286,7 @@ typedef struct ma_log* pLog; /* When set to NULL, will use the context's log. */ ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native sample rate of the device. */ ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ @@ -11419,11 +11450,11 @@ MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); +MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor); +MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength); +MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor); +MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength); MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); @@ -11544,17 +11575,23 @@ IMPLEMENTATION #endif #if !defined(MA_WIN32) -#include -#include /* select() (used for ma_sleep()). */ -#include + #if !defined(MA_NO_THREADING) + #include + #include /* For pthreads. */ + #endif + + #include /* select() (used for ma_sleep()). */ + #include /* For nanosleep() */ + #include #endif -#ifdef MA_NX -#include /* For nanosleep() */ +/* For fstat(), etc. */ +#if defined(MA_XBOX_NXDK) + #include /* Suggestion for NXDK: Add a sys/stat.h wrapper for compatibility. */ +#else + #include #endif -#include /* For fstat(), etc. */ - #ifdef MA_EMSCRIPTEN #include #endif @@ -11861,7 +11898,7 @@ static MA_INLINE ma_bool32 ma_has_neon(void) #endif #ifndef MA_RESTRICT - #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) + #if defined(__clang__) || defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))) #define MA_RESTRICT __restrict #else #define MA_RESTRICT @@ -11955,7 +11992,7 @@ static void ma_sleep__posix(ma_uint32 milliseconds) (void)milliseconds; MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ #else - #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) + #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_SWITCH) struct timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = milliseconds % 1000 * 1000000; @@ -11997,7 +12034,7 @@ static MA_INLINE void ma_yield(void) #endif #endif #else - __asm__ __volatile__ ("pause"); + __asm__ __volatile__ ("rep; nop"); #endif #elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) /* ARM */ @@ -12020,7 +12057,7 @@ static MA_INLINE unsigned int ma_disable_denormals(void) { unsigned int prevState; - #if defined(_MSC_VER) + #if defined(_MSC_VER) && !defined(MA_XBOX_NXDK) { /* Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't @@ -12043,7 +12080,7 @@ static MA_INLINE unsigned int ma_disable_denormals(void) } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(MA_SUPPORT_SSE2) && defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { prevState = _mm_getcsr(); _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); @@ -12067,7 +12104,7 @@ static MA_INLINE unsigned int ma_disable_denormals(void) static MA_INLINE void ma_restore_denormals(unsigned int prevState) { - #if defined(_MSC_VER) + #if defined(_MSC_VER) && !defined(MA_XBOX_NXDK) { /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ #if _MSC_VER <= 1200 @@ -12083,7 +12120,7 @@ static MA_INLINE void ma_restore_denormals(unsigned int prevState) } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(MA_SUPPORT_SSE2) && defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { _mm_setcsr(prevState); } @@ -12719,6 +12756,29 @@ MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } +MA_API MA_NO_INLINE int ma_wcscmp(const wchar_t* str1, const wchar_t* str2) +{ + if (str1 == str2) return 0; + + /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ + if (str1 == NULL) return -1; + if (str2 == NULL) return 1; + + for (;;) { + if (str1[0] == L'\0') { + break; + } + if (str1[0] != str2[0]) { + break; + } + + str1 += 1; + str2 += 1; + } + + return ((unsigned short*)str1)[0] - ((unsigned short*)str2)[0]; +} + MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) { int result; @@ -12736,6 +12796,22 @@ MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA return result; } +MA_API MA_NO_INLINE size_t ma_wcslen(const wchar_t* str) +{ + const wchar_t* end; + + if (str == NULL) { + return 0; + } + + end = str; + while (end[0] != '\0') { + end += 1; + } + + return end - str; +} + MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz; @@ -12758,7 +12834,7 @@ MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_ca MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) { - size_t sz = wcslen(src)+1; + size_t sz = ma_wcslen(src)+1; wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); if (dst == NULL) { return NULL; @@ -13189,7 +13265,7 @@ MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpe return MA_INVALID_ARGS; } -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if (defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(MA_XBOX_NXDK) err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return ma_result_from_errno(err); @@ -13231,7 +13307,7 @@ _wfopen() isn't always available in all compilation environments. This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. */ -#if defined(_WIN32) +#if defined(_WIN32) && !defined(MA_XBOX_NXDK) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define MA_HAS_WFOPEN #endif @@ -13247,29 +13323,34 @@ MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_ return MA_INVALID_ARGS; } -#if defined(MA_HAS_WFOPEN) + #if defined(MA_HAS_WFOPEN) { /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); + #if defined(_MSC_VER) && _MSC_VER >= 1400 + { + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return ma_result_from_errno(err); + } } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); + #else + { + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return ma_result_from_errno(errno); + } } - #endif + #endif + (void)pAllocationCallbacks; } -#else - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ + #elif !defined(MA_XBOX_NXDK) && !defined(MA_DOS) /* If your compiler does not support wcsrtombs(), add it here. */ { + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; @@ -13310,11 +13391,16 @@ MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_ ma_free(pFilePathMB, pAllocationCallbacks); } + #else + { + /* Getting here means there is no way to open the file with a wide character string. */ + *ppFile = NULL; + } + #endif if (*ppFile == NULL) { return MA_ERROR; } -#endif return MA_SUCCESS; } @@ -13323,7 +13409,7 @@ MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_ static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) { -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX +#if MA_SIZE_MAX > 0xFFFFFFFF MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { @@ -13343,7 +13429,7 @@ static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 si static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) { -#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX +#if MA_SIZE_MAX > 0xFFFFFFFF MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { @@ -13472,6 +13558,18 @@ static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst Logging **************************************************************************************************************************************************************/ +#ifndef ma_va_copy + #if !defined(_MSC_VER) || _MSC_VER >= 1800 + #if (defined(__GNUC__) && __GNUC__ < 3) + #define ma_va_copy(dst, src) ((dst) = (src)) /* This is untested. Not sure if this is correct for old GCC. */ + #else + #define ma_va_copy(dst, src) va_copy((dst), (src)) + #endif + #else + #define ma_va_copy(dst, src) ((dst) = (src)) + #endif +#endif + MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) { switch (logLevel) @@ -13712,9 +13810,15 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat int length; char pFormattedMessageStack[1024]; char* pFormattedMessageHeap = NULL; + va_list args2; /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); + ma_va_copy(args2, args); + { + length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args2); + } + va_end(args2); + if (length < 0) { return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ } @@ -13755,17 +13859,10 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat char* pFormattedMessage = NULL; va_list args2; - #if _MSC_VER >= 1800 + ma_va_copy(args2, args); { - va_copy(args2, args); + formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); } - #else - { - args2 = args; - } - #endif - - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); va_end(args2); if (formattedLen <= 0) { @@ -13964,7 +14061,7 @@ miniaudio's purposes. #define MA_LCG_A 48271 #define MA_LCG_C 0 -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ +static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_lcg_seed() to use an explicit seed. */ static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) { @@ -14013,7 +14110,7 @@ static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_in } - +#if 0 /* Currently unused. */ static MA_INLINE void ma_seed(ma_int32 seed) { ma_lcg_seed(&g_maLCG, seed); @@ -14038,6 +14135,7 @@ static MA_INLINE float ma_rand_f32(void) { return ma_lcg_rand_f32(&g_maLCG); } +#endif static MA_INLINE float ma_rand_range_f32(float lo, float hi) { @@ -14097,6 +14195,7 @@ Atomics **************************************************************************************************************************************************************/ /* c89atomic.h begin */ #ifndef ma_atomic_h +#define ma_atomic_h #if defined(__cplusplus) extern "C" { #endif @@ -14108,11 +14207,40 @@ extern "C" { #endif #endif typedef int ma_atomic_memory_order; -#define MA_ATOMIC_HAS_8 -#define MA_ATOMIC_HAS_16 -#define MA_ATOMIC_HAS_32 -#define MA_ATOMIC_HAS_64 -#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) +#if !defined(MA_ATOMIC_MODERN_MSVC) && \ + !defined(MA_ATOMIC_LEGACY_MSVC) && \ + !defined(MA_ATOMIC_LEGACY_MSVC_ASM) && \ + !defined(MA_ATOMIC_MODERN_GCC) && \ + !defined(MA_ATOMIC_LEGACY_GCC) && \ + !defined(MA_ATOMIC_LEGACY_GCC_ASM) + #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) || defined(__BORLANDC__) + #if (defined(_MSC_VER) && _MSC_VER > 1600) + #define MA_ATOMIC_MODERN_MSVC + #else + #if defined(MA_X64) + #define MA_ATOMIC_LEGACY_MSVC + #else + #define MA_ATOMIC_LEGACY_MSVC_ASM + #endif + #endif + #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || defined(__clang__) + #define MA_ATOMIC_MODERN_GCC + #else + #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) + #define MA_ATOMIC_LEGACY_GCC + #else + #define MA_ATOMIC_LEGACY_GCC_ASM + #endif + #endif +#endif +#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) + #include + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ ma_atomicType result; \ switch (order) \ @@ -14138,720 +14266,1501 @@ typedef int ma_atomic_memory_order; } break; \ } \ return result; - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ + typedef ma_uint32 ma_atomic_flag; + static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 1, order, _InterlockedExchange, ma_atomic_flag, long); + } + #else + { + (void)order; + return (ma_atomic_flag)_InterlockedExchange((volatile long*)dst, (long)1); + } + #endif + } + static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 0, order, _InterlockedExchange, ma_atomic_flag, long); + } + #else + { + (void)order; + _InterlockedExchange((volatile long*)dst, (long)0); + } + #endif + } + static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) + { + (void)order; + return (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, 0, 0); + } +#endif +#if defined(MA_ATOMIC_LEGACY_MSVC_ASM) + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + typedef ma_uint32 ma_atomic_flag; + static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + ma_atomic_flag result = 0; + (void)order; + __asm { + mov ecx, dst + mov eax, 1 + xchg [ecx], eax + mov result, eax + } + return result; + } + static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov dword ptr [esi], 0 + } + } else { + __asm { + mov esi, dst + mov eax, 0 + xchg [esi], eax + } + } + } + static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) + { + ma_atomic_flag result = 0; + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov eax, [esi] + mov result, eax + } + } else if (order <= ma_atomic_memory_order_release) { + __asm { + mov esi, dst + mov eax, [esi] + lock add dword ptr [esp], 0 + mov result, eax + } + } else { + __asm { + lock add dword ptr [esp], 0 + mov esi, dst + mov eax, [esi] + mov result, eax + lock add dword ptr [esp], 0 + } + } + return result; + } +#endif +#if defined(MA_ATOMIC_MODERN_GCC) + #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED + #define ma_atomic_memory_order_consume __ATOMIC_CONSUME + #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define ma_atomic_memory_order_release __ATOMIC_RELEASE + #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + typedef ma_uint32 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_flag_clear_explicit(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_flag_load_explicit(dst, order) __atomic_load_n(dst, order) +#endif +#if defined(MA_ATOMIC_LEGACY_GCC) + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + typedef ma_uint32 ma_atomic_flag; + static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, 1); + } + static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + if (order > ma_atomic_memory_order_release) { + __sync_synchronize(); + } + __sync_lock_release(dst); + } + static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) + { + (void)order; + return __sync_val_compare_and_swap((ma_atomic_flag*)dst, 0, 0); + } +#endif +#if defined(MA_ATOMIC_LEGACY_GCC_ASM) + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + #if defined(MA_X86) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory") + #elif defined(MA_X64) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory") + #else + #error Unsupported architecture. + #endif + #define MA_ATOMIC_XCHG_GCC_X86(instructionSizeSuffix, result, dst, src) \ + __asm__ __volatile__( \ + "xchg"instructionSizeSuffix" %0, %1" \ + : "=r"(result), \ + "=m"(*dst) \ + : "0"(src), \ + "m"(*dst) \ + : "memory" \ + ) + #define MA_ATOMIC_LOAD_RELAXED_GCC_X86(instructionSizeSuffix, result, dst) \ + __asm__ __volatile__( \ + "mov"instructionSizeSuffix" %1, %0" \ + : "=r"(result) \ + : "m"(*dst) \ + ) + #define MA_ATOMIC_LOAD_RELEASE_GCC_X86(instructionSizeSuffix, result, dst) \ + ma_atomic_thread_fence(ma_atomic_memory_order_release); \ + __asm__ __volatile__( \ + "mov"instructionSizeSuffix" %1, %0" \ + : "=r"(result) \ + : "m"(*dst) \ + : "memory" \ + ) + #define MA_ATOMIC_LOAD_SEQ_CST_GCC_X86(instructionSizeSuffix, result, dst) \ + ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst); \ + __asm__ __volatile__( \ + "mov"instructionSizeSuffix" %1, %0" \ + : "=r"(result) \ + : "m"(*dst) \ + : "memory" \ + ); \ + ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) + typedef ma_uint32 ma_atomic_flag; + static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + ma_atomic_flag result; + #if defined(MA_X86) || defined(MA_X64) + { + (void)order; + MA_ATOMIC_XCHG_GCC_X86("l", result, dst, 1); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) + { + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm__ __volatile__( + "movl $0, %0" + : "=m"(*dst) + ); + } else if (order == ma_atomic_memory_order_release) { + __asm__ __volatile__( + "movl $0, %0" + : "=m"(*dst) + : + : "memory" + ); + } else { + ma_atomic_flag tmp = 0; + __asm__ __volatile__( + "xchgl %0, %1" + : "=r"(tmp), + "=m"(*dst) + : "0"(tmp), + "m"(*dst) + : "memory" + ); + } + } + #else + { + #error Unsupported architecture. + } + #endif + } + static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) + { + #if defined(MA_X86) || defined(MA_X64) + { + ma_atomic_flag result; + if (order == ma_atomic_memory_order_relaxed) { + MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst); + } else if (order <= ma_atomic_memory_order_release) { + MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst); + } else { + MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst); + } + return result; + } + #else + { + #error Unsupported architecture. + } + #endif + } +#endif +#define ma_atomic_flag_test_and_set(dst) ma_atomic_flag_test_and_set_explicit(dst, ma_atomic_memory_order_acquire) +#define ma_atomic_flag_clear(dst) ma_atomic_flag_clear_explicit(dst, ma_atomic_memory_order_release) +typedef ma_atomic_flag ma_atomic_spinlock; +static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) +{ + for (;;) { + if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { + break; + } + while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + } + } +} +static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) +{ + ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); +} +ma_atomic_spinlock ma_atomic_global_lock; +#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC_ASM) || defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM) + #if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i486__)) || (defined(_M_IX86) && _M_IX86 >= 400))) + #if defined(MA_ATOMIC_LEGACY_MSVC) && defined(MA_X64) + #else + #define MA_ATOMIC_IS_LOCK_FREE_8 1 + #define MA_ATOMIC_IS_LOCK_FREE_16 1 + #endif + #define MA_ATOMIC_IS_LOCK_FREE_32 1 + #if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i586__)) || (defined(_M_IX86) && _M_IX86 >= 500))) + #define MA_ATOMIC_IS_LOCK_FREE_64 1 + #else + #endif + #else + #endif + #if defined(MA_ARM32) || defined(MA_ARM64) + #define MA_ATOMIC_IS_LOCK_FREE_8 1 + #define MA_ATOMIC_IS_LOCK_FREE_16 1 + #define MA_ATOMIC_IS_LOCK_FREE_32 1 + #if defined(MA_ARM64) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) + #define MA_ATOMIC_IS_LOCK_FREE_64 1 + #endif + #endif + #if defined(MA_ATOMIC_PPC32) || defined(MA_ATOMIC_PPC64) + #if (defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7))) && !defined(__clang__) + #else + #define MA_ATOMIC_IS_LOCK_FREE_8 1 + #define MA_ATOMIC_IS_LOCK_FREE_16 1 + #endif + #define MA_ATOMIC_IS_LOCK_FREE_32 1 + #if defined(MA_ATOMIC_PPC64) + #define MA_ATOMIC_IS_LOCK_FREE_64 1 + #endif + #endif + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) + { + (void)ptr; + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + return 1; + #else + return 0; + #endif + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) + { + (void)ptr; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + return 1; + #else + return 0; + #endif + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) + { + (void)ptr; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + return 1; + #else + return 0; + #endif + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) + { + (void)ptr; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + return 1; + #else + return 0; + #endif + } +#endif +#define MA_ATOMIC_COMPARE_AND_SWAP_LOCK(sizeInBits, dst, expected, replacement) \ + ma_uint##sizeInBits result; \ + ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ + { \ + result = *dst; \ + if (result == expected) { \ + *dst = replacement; \ + } \ + } \ + ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ + return result +#define MA_ATOMIC_LOAD_EXPLICIT_LOCK(sizeInBits, ptr, order) \ + ma_uint##sizeInBits result; \ + ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ + { \ + result = *ptr; \ + (void)order; \ + } \ + ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ + return result +#define MA_ATOMIC_STORE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \ + ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ + { \ + *dst = src; \ + (void)order; \ + } \ + ma_atomic_spinlock_unlock(&ma_atomic_global_lock) +#define MA_ATOMIC_STORE_EXPLICIT_CAS(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits oldValue; \ + do { \ + oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ + } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \ + (void)order +#define MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits result; \ + ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ + { \ + result = *dst; \ + *dst = src; \ + (void)order; \ + } \ + ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ + return result +#define MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits oldValue; \ + do { \ + oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ + } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \ + (void)order; \ + return oldValue +#define MA_ATOMIC_FETCH_ADD_LOCK(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits result; \ + ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ + { \ + result = *dst; \ + *dst += src; \ + (void)order; \ + } \ + ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ + return result +#define MA_ATOMIC_FETCH_ADD_CAS(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits oldValue; \ + ma_uint##sizeInBits newValue; \ + do { \ + oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ + newValue = oldValue + src; \ + } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ + (void)order; \ + return oldValue +#define MA_ATOMIC_FETCH_AND_CAS(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits oldValue; \ + ma_uint##sizeInBits newValue; \ + do { \ + oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ + newValue = (ma_uint##sizeInBits)(oldValue & src); \ + } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ + (void)order; \ + return oldValue +#define MA_ATOMIC_FETCH_OR_CAS(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits oldValue; \ + ma_uint##sizeInBits newValue; \ + do { \ + oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ + newValue = (ma_uint##sizeInBits)(oldValue | src); \ + } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ + (void)order; \ + return oldValue +#define MA_ATOMIC_FETCH_XOR_CAS(sizeInBits, dst, src, order) \ + ma_uint##sizeInBits oldValue; \ + ma_uint##sizeInBits newValue; \ + do { \ + oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ + newValue = (ma_uint##sizeInBits)(oldValue ^ src); \ + } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ + (void)order; \ + return oldValue +#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) + #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, replacement, order, intrin, ma_atomicType, msvcType) \ ma_atomicType result; \ switch (order) \ { \ case ma_atomic_memory_order_relaxed: \ { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ } break; \ case ma_atomic_memory_order_consume: \ case ma_atomic_memory_order_acquire: \ { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ } break; \ case ma_atomic_memory_order_release: \ { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ } break; \ case ma_atomic_memory_order_acq_rel: \ case ma_atomic_memory_order_seq_cst: \ default: \ { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ } break; \ } \ return result; - #define ma_atomic_memory_order_relaxed 0 - #define ma_atomic_memory_order_consume 1 - #define ma_atomic_memory_order_acquire 2 - #define ma_atomic_memory_order_release 3 - #define ma_atomic_memory_order_acq_rel 4 - #define ma_atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(MA_X86) - #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY - #endif - #if _MSC_VER < 1600 - #undef MA_ATOMIC_HAS_8 - #undef MA_ATOMIC_HAS_16 - #endif - #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #include - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, desired - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, desired - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, desired - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr desired - mov ecx, dword ptr desired + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((ma_uint64)resultEDX << 32) | resultEAX; - } - #endif + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + #define ma_atomic_compare_and_swap_8( dst, expected, replacement) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)replacement, (char)expected) #else - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) - #endif + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); + } #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xchg [ecx], eax - mov result, eax - } - return result; - } - #endif + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + #define ma_atomic_compare_and_swap_16(dst, expected, replacement) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)replacement, (short)expected) #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); + } + #endif + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + #define ma_atomic_compare_and_swap_32(dst, expected, replacement) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)replacement, (long)expected) + #else + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); + } + #endif + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + #define ma_atomic_compare_and_swap_64(dst, expected, replacement) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)replacement, (ma_int64)expected) + #else + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); + } + #endif + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); + { + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); + } #else + { + (void)order; + return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); + } + #endif + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); + } + #else + { + (void)order; + return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); + } + #endif + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); + } + #else + { + (void)order; + return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); + } + #endif + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); + } + #else + { + (void)order; + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); + } + #endif + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); + } + #else + { (void)order; return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - #endif } + #endif + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); + } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { #if defined(MA_ARM) + { MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); + } #else + { (void)order; return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - #endif } + #endif + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); + } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { #if defined(MA_ARM) + { MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); + } #else + { (void)order; return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - #endif } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); #endif - } - #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; } - #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 result = 0; - (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); + } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + #if defined(MA_32BIT) { - ma_uint16 result = 0; - (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; + MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order); } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; - } - #endif - #else - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); #else + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); + } + #else + { + (void)order; + return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + } + #endif + } + #endif + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); + } + #else + { (void)order; return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - #endif } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); + } #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { #if defined(MA_ARM) + { MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); + } #else + { (void)order; return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - #endif } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); + } #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { #if defined(MA_ARM) + { MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); + } #else + { (void)order; return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - #endif } - #endif - #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); - #else - (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); #endif - } + } #else - #endif - #endif - #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + #if defined(MA_32BIT) + { + MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order); + } + #else + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); + } + #else + { + (void)order; + return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + } + #endif + } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order); + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order); + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order); + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order); + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + { + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); + } + #endif + } + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #if defined(MA_X64) + #define ma_atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(MA_ARM64) + #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order + #else + static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) + { + volatile ma_uint32 barrier = 0; + ma_atomic_fetch_add_explicit_32(&barrier, 0, order); } #endif - #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) + #define ma_atomic_signal_fence(order) _ReadWriteBarrier(), (void)order +#endif +#if defined(MA_ATOMIC_LEGACY_MSVC_ASM) + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) { + ma_uint8 result = 0; + __asm { + mov ecx, dst + mov al, expected + mov dl, replacement + lock cmpxchg [ecx], dl + mov result, al + } + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + ma_uint16 result = 0; + __asm { + mov ecx, dst + mov ax, expected + mov dx, replacement + lock cmpxchg [ecx], dx + mov result, ax + } + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + ma_uint32 result = 0; + __asm { + mov ecx, dst + mov eax, expected + mov edx, replacement + lock cmpxchg [ecx], edx + mov result, eax + } + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + ma_uint32 resultEAX = 0; + ma_uint32 resultEDX = 0; + __asm { + mov esi, dst + mov eax, dword ptr expected + mov edx, dword ptr expected + 4 + mov ebx, dword ptr replacement + mov ecx, dword ptr replacement + 4 + lock cmpxchg8b qword ptr [esi] + mov resultEAX, eax + mov resultEDX, edx + } + return ((ma_uint64)resultEDX << 32) | resultEAX; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + ma_uint8 result = 0; + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov al, [esi] + mov result, al + } + } else if (order <= ma_atomic_memory_order_release) { + __asm { + mov esi, dst + mov al, [esi] + lock add dword ptr [esp], 0 + mov result, al + } + } else { + __asm { + lock add dword ptr [esp], 0 + mov esi, dst + mov al, [esi] + mov result, al + lock add dword ptr [esp], 0 + } + } + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + ma_uint16 result = 0; + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov ax, [esi] + mov result, ax + } + } else if (order <= ma_atomic_memory_order_release) { + __asm { + mov esi, dst + mov ax, [esi] + lock add dword ptr [esp], 0 + mov result, ax + } + } else { + __asm { + lock add dword ptr [esp], 0 + mov esi, dst + mov ax, [esi] + mov result, ax + lock add dword ptr [esp], 0 + } + } + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + ma_uint32 result = 0; + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov eax, [esi] + mov result, eax + } + } else if (order <= ma_atomic_memory_order_release) { + __asm { + mov esi, dst + mov eax, [esi] + lock add dword ptr [esp], 0 + mov result, eax + } + } else { + __asm { + lock add dword ptr [esp], 0 + mov esi, dst + mov eax, [esi] + mov result, eax + lock add dword ptr [esp], 0 + } + } + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0); + } + static MA_INLINE void __stdcall ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov al, src + mov [esi], al + } + } else { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + __asm { + mov esi, dst + mov al, src + xchg [esi], al + } + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order); + } + #endif + } + } + static MA_INLINE void __stdcall ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov ax, src + mov [esi], ax + } + } else { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + __asm { + mov esi, dst + mov ax, src + xchg [esi], ax + } + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order); + } + #endif + } + } + static MA_INLINE void __stdcall ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm { + mov esi, dst + mov eax, src + mov [esi], eax + } + } else { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + __asm { + mov esi, dst + mov eax, src + xchg [esi], eax + } + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order); + } + #endif + } + } + static MA_INLINE void __stdcall ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order); + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + ma_uint8 result = 0; (void)order; __asm { - lock add [esp], 0 + mov ecx, dst + mov al, src + lock xchg [ecx], al + mov result, al } + return result; } - #else - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) - { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + ma_uint16 result = 0; + (void)order; + __asm { + mov ecx, dst + mov ax, src + lock xchg [ecx], ax + mov result, ax } - #endif - #endif - #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); + return result; + } #else - (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + ma_uint32 result = 0; + (void)order; + __asm { + mov ecx, dst + mov eax, src + xchg [ecx], eax + mov result, eax + } + return result; + } #else - (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order); + } #else - (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + ma_uint8 result = 0; + (void)order; + __asm { + mov ecx, dst + mov al, src + lock xadd [ecx], al + mov result, al + } + return result; + } #else - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); + { + MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); + } #endif - } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + ma_uint16 result = 0; (void)order; - return oldValue; + __asm { + mov ecx, dst + mov ax, src + lock xadd [ecx], ax + mov result, ax + } + return result; } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + ma_uint32 result = 0; (void)order; - return oldValue; - #endif + __asm { + mov ecx, dst + mov eax, src + lock xadd [ecx], eax + mov result, eax + } + return result; } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #else { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order); } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #else { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #endif + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + ma_uint8 result = 0; (void)order; - return oldValue; - #endif + __asm { + mov ecx, dst + mov al, src + neg al + lock xadd [ecx], al + mov result, al + } + return result; } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #else { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order); } - #endif - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + #endif + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); - #else - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + ma_uint16 result = 0; (void)order; - return oldValue; - #endif + __asm { + mov ecx, dst + mov ax, src + neg ax + lock xadd [ecx], ax + mov result, ax + } + return result; } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + #else { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); - #else - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order); } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + #endif + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); - #else - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + ma_uint32 result = 0; (void)order; - return oldValue; - #endif + __asm { + mov ecx, dst + mov eax, src + neg eax + lock xadd [ecx], eax + mov result, eax + } + return result; } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + #else { - #if defined(MA_ARM) - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); - #else - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - #endif + MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order); } - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_16) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_32) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_64) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - #endif - #if defined(MA_ATOMIC_HAS_8) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) - #else - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) - #endif -#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #endif + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_ADD_CAS(64, dst, (ma_uint64)(-(ma_int64)src), order); + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); + } + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); + } + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); + } + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); + } + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); + } + static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) + { + (void)order; + __asm { + lock add dword ptr [esp], 0 + } + } + #define ma_atomic_signal_fence(order) __asm {}; (void)order +#endif +#if defined(MA_ATOMIC_MODERN_GCC) #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) @@ -14864,14 +15773,14 @@ typedef int ma_atomic_memory_order; #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) @@ -14892,19 +15801,19 @@ typedef int ma_atomic_memory_order; #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } #if defined(__clang__) @@ -14913,636 +15822,1134 @@ typedef int ma_atomic_memory_order; #pragma clang diagnostic ignored "-Watomic-alignment" #endif #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) { - __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } #if defined(__clang__) #pragma clang diagnostic pop #endif - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) -#else - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #if defined(__GNUC__) +#endif +#if defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM) + #define ma_atomic_signal_fence(order) __asm__ __volatile__("":::"memory") + #if defined(MA_ATOMIC_LEGACY_GCC) #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + return __sync_val_compare_and_swap(dst, expected, replacement); } - return __sync_lock_test_and_set(dst, src); + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); + } + #endif } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) { - ma_uint16 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + return __sync_val_compare_and_swap(dst, expected, replacement); + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); + } + #endif } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) { - ma_uint32 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + return __sync_val_compare_and_swap(dst, expected, replacement); + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); + } + #endif } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) { - ma_uint64 oldValue; - do { - oldValue = *dst; - } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + return __sync_val_compare_and_swap(dst, expected, replacement); + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); + } + #endif } - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) { - (void)order; - return __sync_fetch_and_add(dst, src); + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + (void)order; + return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order); + } + #endif } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { - (void)order; - return __sync_fetch_and_add(dst, src); + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + (void)order; + return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order); + } + #endif } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) { - (void)order; - return __sync_fetch_and_add(dst, src); + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + (void)order; + return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order); + } + #endif } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { - (void)order; - return __sync_fetch_and_add(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #else - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") - #else - #error Unsupported architecture. Please submit a feature request. - #endif - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) - { - volatile ma_uint64 result; - #if defined(MA_X86) - ma_uint32 resultEAX; - ma_uint32 resultEDX; - __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - #elif defined(MA_X64) - __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + (void)order; + return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order); + } + #endif } static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - ma_uint8 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); + } + #endif } static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - ma_uint16 result = 0; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); + } + #endif } static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); + } + #endif } static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - ma_uint64 result; - (void)order; - #if defined(MA_X86) - do { - result = *dst; - } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(MA_X64) - __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); + } + #endif } + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - ma_uint8 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); + } + #endif } static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - ma_uint16 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); + } + #endif } static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - #else - #error Unsupported architecture. Please submit a feature request. - #endif - return result; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); + } + #endif } static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(MA_X86) - ma_uint64 oldValue; - ma_uint64 newValue; - (void)order; - do { - oldValue = *dst; - newValue = oldValue + src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - return oldValue; - #elif defined(MA_X64) - ma_uint64 result; - (void)order; - __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); - return result; - #endif + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); + } + #endif } static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue - src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order); + } + #endif } static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue - src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order); + } + #endif } static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order); + } + #endif } static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue - src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(64, dst, (ma_uint64)(-(ma_int64)src), order); + } + #endif } static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue & src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); + } + #endif } static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue & src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); + } + #endif } static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); + } + #endif } static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue & src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue ^ src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue ^ src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #else + { + MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); + } + #endif } static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - ma_uint8 oldValue; - ma_uint8 newValue; - do { - oldValue = *dst; - newValue = (ma_uint8)(oldValue | src); - } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); + } + #endif } static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - ma_uint16 oldValue; - ma_uint16 newValue; - do { - oldValue = *dst; - newValue = (ma_uint16)(oldValue | src); - } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); + } + #endif } static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - ma_uint32 oldValue; - ma_uint32 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); + } + #endif } static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - ma_uint64 oldValue; - ma_uint64 newValue; - do { - oldValue = *dst; - newValue = oldValue | src; - } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); - (void)order; - return oldValue; + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + #else + { + MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); + } + #endif } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + #else + { + MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); + } + #endif + } + #elif defined(MA_ATOMIC_LEGACY_GCC_ASM) + #define MA_ATOMIC_CMPXCHG_GCC_X86(instructionSizeSuffix, result, dst, expected, replacement) \ + __asm__ __volatile__( \ + "lock; cmpxchg"instructionSizeSuffix" %2, %1" \ + : "=a"(result), \ + "=m"(*dst) \ + : "r"(replacement), \ + "0"(expected), \ + "m"(*dst) \ + : "cc", "memory") + #define MA_ATOMIC_XADD_GCC_X86(instructionSizeSuffix, result, dst, src) \ + __asm__ __volatile__( \ + "lock; xadd"instructionSizeSuffix" %0, %1" \ + : "=a"(result), \ + "=m"(*dst) \ + : "0"(src), \ + "m"(*dst) \ + : "cc", "memory") + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) + { + MA_ATOMIC_CMPXCHG_GCC_X86("b", result, dst, expected, replacement); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) + { + MA_ATOMIC_CMPXCHG_GCC_X86("w", result, dst, expected, replacement); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) + { + MA_ATOMIC_CMPXCHG_GCC_X86("l", result, dst, expected, replacement); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint64 result; + #if defined(MA_X86) + { + ma_uint32 resultEAX; + ma_uint32 resultEDX; + __asm__ __volatile__( + "pushl %%ebx\n" + "movl %4, %%ebx\n" + "lock cmpxchg8b (%%edi)\n" + "popl %%ebx\n" + : "=a"(resultEAX), + "=d"(resultEDX) + : "a"((ma_uint32)(expected & 0xFFFFFFFF)), + "d"((ma_uint32)(expected >> 32)), + "r"((ma_uint32)(replacement & 0xFFFFFFFF)), + "c"((ma_uint32)(replacement >> 32)), + "D"(dst) + : "memory", "cc"); + result = ((ma_uint64)resultEDX << 32) | resultEAX; + } + #elif defined(MA_X64) + { + MA_ATOMIC_CMPXCHG_GCC_X86("q", result, dst, expected, replacement); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); + } + #endif + } + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + MA_ATOMIC_LOAD_RELAXED_GCC_X86("b", result, dst); + } else if (order <= ma_atomic_memory_order_release) { + MA_ATOMIC_LOAD_RELEASE_GCC_X86("b", result, dst); + } else { + MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("b", result, dst); + } + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + MA_ATOMIC_LOAD_RELAXED_GCC_X86("w", result, dst); + } else if (order <= ma_atomic_memory_order_release) { + MA_ATOMIC_LOAD_RELEASE_GCC_X86("w", result, dst); + } else { + MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("w", result, dst); + } + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst); + } else if (order <= ma_atomic_memory_order_release) { + MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst); + } else { + MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst); + } + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint64 result; + #if defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + MA_ATOMIC_LOAD_RELAXED_GCC_X86("q", result, dst); + } else if (order <= ma_atomic_memory_order_release) { + MA_ATOMIC_LOAD_RELEASE_GCC_X86("q", result, dst); + } else { + MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("q", result, dst); + } + } + #elif defined(MA_X86) + { + (void)order; + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, dst, order); + } + #endif + } + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint8 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + { + MA_ATOMIC_XCHG_GCC_X86("b", result, dst, src); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint16 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + { + MA_ATOMIC_XCHG_GCC_X86("w", result, dst, src); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + { + MA_ATOMIC_XCHG_GCC_X86("l", result, dst, src); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) + { + ma_uint64 result; + (void)order; + #if defined(MA_X86) + { + MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order); + } + #elif defined(MA_X64) + { + MA_ATOMIC_XCHG_GCC_X86("q", result, dst, src); + } + #else + { + #error Unsupported architecture. + } + #endif + return result; + } + #else + { + MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE void ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm__ __volatile__ ( + "movb %1, %0" + : "=m"(*dst) + : "r"(src) + ); + } else { + __asm__ __volatile__ ( + "xchgb %1, %0" + : "=m"(*dst) + : "r"(src) + : "memory" + ); + } + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order); + } + #endif + } + static MA_INLINE void ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm__ __volatile__ ( + "movw %1, %0" + : "=m"(*dst) + : "r"(src) + ); + } else { + __asm__ __volatile__ ( + "xchgw %1, %0" + : "=m"(*dst) + : "r"(src) + : "memory" + ); + } + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order); + } + #endif + } + static MA_INLINE void ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) || defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm__ __volatile__ ( + "movl %1, %0" + : "=m"(*dst) + : "r"(src) + ); + } else { + __asm__ __volatile__ ( + "xchgl %1, %0" + : "=m"(*dst) + : "r"(src) + : "memory" + ); + } + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order); + } + #endif + } + static MA_INLINE void ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X64) + { + if (order == ma_atomic_memory_order_relaxed) { + __asm__ __volatile__ ( + "movq %1, %0" + : "=m"(*dst) + : "r"(src) + ); + } else { + __asm__ __volatile__ ( + "xchgq %1, %0" + : "=m"(*dst) + : "r"(src) + : "memory" + ); + } + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order); + } + #endif + } + #else + { + MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) || defined(MA_X64) + { + ma_uint8 result; + (void)order; + MA_ATOMIC_XADD_GCC_X86("b", result, dst, src); + return result; + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) || defined(MA_X64) + { + ma_uint16 result; + (void)order; + MA_ATOMIC_XADD_GCC_X86("w", result, dst, src); + return result; + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) || defined(MA_X64) + { + ma_uint32 result; + (void)order; + MA_ATOMIC_XADD_GCC_X86("l", result, dst, src); + return result; + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) + { + #if defined(MA_X86) + { + MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order); + } + #elif defined(MA_X64) + { + ma_uint64 result; + MA_ATOMIC_XADD_GCC_X86("q", result, dst, src); + (void)order; + return result; + } + #else + { + #error Unsupported architecture. + } + #endif + } + #else + { + MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); + } + #endif + } + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); + } + #else + #error Unsupported compiler. #endif - #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) - #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) - #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) - #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) - #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) - #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) - #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) - #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) - typedef ma_uint8 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) - #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) - #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #endif #if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(MA_ATOMIC_HAS_8) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint8 expectedValue; - ma_uint8 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_8(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_16) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint16 expectedValue; - ma_uint16 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_16(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_32) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint32 expectedValue; - ma_uint32 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_32(expected, result, failureOrder); - return 0; - } - } - #endif - #if defined(MA_ATOMIC_HAS_64) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint64 expectedValue; - ma_uint64 result; - (void)successOrder; - (void)failureOrder; - expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); - result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); - if (result == expectedValue) { - return 1; - } else { - ma_atomic_store_explicit_64(expected, result, failureOrder); - return 0; - } - } - #endif - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) -#endif -#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - return 1; - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(MA_64BIT) - return 1; - #else - #if defined(MA_X86) || defined(MA_X64) + ma_uint8 result; + (void)successOrder; + (void)failureOrder; + result = ma_atomic_compare_and_swap_8(dst, *expected, replacement); + if (result == *expected) { return 1; - #else + } else { + *expected = result; return 0; - #endif - #endif + } } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint16 result; + (void)successOrder; + (void)failureOrder; + result = ma_atomic_compare_and_swap_16(dst, *expected, replacement); + if (result == *expected) { + return 1; + } else { + *expected = result; + return 0; + } + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint32 result; + (void)successOrder; + (void)failureOrder; + result = ma_atomic_compare_and_swap_32(dst, *expected, replacement); + if (result == *expected) { + return 1; + } else { + *expected = result; + return 0; + } + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint64 result; + (void)successOrder; + (void)failureOrder; + result = ma_atomic_compare_and_swap_64(dst, *expected, replacement); + if (result == *expected) { + return 1; + } else { + *expected = result; + return 0; + } + } + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, replacement, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder) #endif #if defined(MA_64BIT) static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) @@ -15561,17 +16968,17 @@ typedef int ma_atomic_memory_order; { return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder); } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement) { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); + return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)replacement); } #elif defined(MA_32BIT) static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) @@ -15590,36 +16997,26 @@ typedef int ma_atomic_memory_order; { return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder); } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement) { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); + return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)replacement); } #else #error Unsupported architecture. #endif -#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_ptr(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_ptr(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) @@ -15632,14 +17029,14 @@ typedef int ma_atomic_memory_order; #define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_16( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_32( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_64( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) @@ -15660,14 +17057,6 @@ typedef int ma_atomic_memory_order; #define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) -#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) -#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) -#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) -#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) #define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) #define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) @@ -15680,14 +17069,14 @@ typedef int ma_atomic_memory_order; #define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) #define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder) #define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) #define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) @@ -15708,14 +17097,6 @@ typedef int ma_atomic_memory_order; #define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) #define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) @@ -15728,14 +17109,14 @@ typedef int ma_atomic_memory_order; #define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i16(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) @@ -15812,28 +17193,28 @@ static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, do r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if32 d; - d.f = desired; + d.f = replacement; return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if64 d; - d.f = desired; + d.f = replacement; return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if32 d; - d.f = desired; + d.f = replacement; return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if64 d; - d.f = desired; + d.f = replacement; return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) @@ -15924,10 +17305,10 @@ static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, d #define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) @@ -15938,39 +17319,24 @@ static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, d #define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float replacement) { ma_atomic_if32 r; ma_atomic_if32 e, d; e.f = expected; - d.f = desired; + d.f = replacement; r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); return r.f; } -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double replacement) { ma_atomic_if64 r; ma_atomic_if64 e, d; e.f = expected; - d.f = desired; + d.f = replacement; r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); return r.f; } -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) -{ - for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { - break; - } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - } - } -} -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) -{ - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); -} #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif @@ -16176,7 +17542,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority int result; pthread_attr_t* pAttr = NULL; -#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) +#if !defined(MA_EMSCRIPTEN) && !defined(MA_3DS) && !defined(MA_SWITCH) /* Try setting the thread priority. It's not critical if anything fails here. */ pthread_attr_t attr; if (pthread_attr_init(&attr) == 0) { @@ -16208,9 +17574,18 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority } #endif - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); + #if defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE >= 0 + { + if (stackSize > 0) { + pthread_attr_setstacksize(&attr, stackSize); + } } + #else + { + (void)stackSize; /* Suppress unused parameter warning. */ + } + #endif + if (scheduler != -1) { int priorityMin = sched_get_priority_min(scheduler); @@ -16267,6 +17642,21 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority } if (result != 0) { + /* + There have been reports that attempting to create a realtime thread can sometimes fail. In this case, + fall back to a normal priority thread. + + I'm including a compile-time option here to disable this functionality for those who have a hard + requirement on realtime threads and would rather an explicit failure. + */ + #ifndef MA_NO_PTHREAD_REALTIME_PRIORITY_FALLBACK + { + if(result == EPERM && priority == ma_thread_priority_realtime) { + return ma_thread_create__posix(pThread, ma_thread_priority_normal, stackSize, entryProc, pData); + } + } + #endif + return ma_result_from_errno(result); } @@ -16538,7 +17928,7 @@ static ma_result ma_event_signal__win32(ma_event* pEvent) static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) { - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); + *pSemaphore = CreateSemaphore(NULL, (LONG)initialValue, LONG_MAX, NULL); if (*pSemaphore == NULL) { return ma_result_from_GetLastError(GetLastError()); } @@ -17432,10 +18822,12 @@ static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) return (ma_uint16)(toc & 0x0000FFFF); } +#if 0 /* Currently unused, but might make use of this later. */ static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) { return (ma_uint16)((toc & 0xFFFF0000) >> 16); } +#endif static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) { @@ -17900,6 +19292,13 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) Dynamic Linking *******************************************************************************/ +/* Disable run-time linking on certain backends and platforms. */ +#ifndef MA_NO_RUNTIME_LINKING + #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) || defined(MA_SWITCH) || defined(MA_DOS) + #define MA_NO_RUNTIME_LINKING + #endif +#endif + #ifdef MA_POSIX /* No need for dlfcn.h if we're not using runtime linking. */ #ifndef MA_NO_RUNTIME_LINKING @@ -17909,104 +19308,124 @@ Dynamic Linking MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) { -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; + #ifndef MA_NO_RUNTIME_LINKING + { + ma_handle handle; - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } + handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + return handle; } - - return handle; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; -#endif + #else + { + /* Runtime linking is disabled. */ + (void)pLog; + (void)filename; + return NULL; + } + #endif } MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) { -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) + #ifndef MA_NO_RUNTIME_LINKING + { + #ifdef MA_WIN32 { - dlclose((void*)handle); + FreeLibrary((HMODULE)handle); } #else { - (void)handle; + /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ + #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) + { + dlclose((void*)handle); + } + #else + { + (void)handle; + } + #endif } #endif - #endif - (void)pLog; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; -#endif + (void)pLog; + } + #else + { + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + } + #endif } MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) { -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; + #ifndef MA_NO_RUNTIME_LINKING + { + ma_proc proc; - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop -#endif -#endif + #ifdef _WIN32 + { + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); + } + #else + { + #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" + #endif + proc = (ma_proc)dlsym((void*)handle, symbol); + #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) + #pragma GCC diagnostic pop + #endif + } + #endif - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + if (proc == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + } + + (void)pLog; /* It's possible for pContext to be unused. */ + return proc; } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; -#endif + #else + { + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + (void)symbol; + return NULL; + } + #endif } @@ -18020,13 +19439,6 @@ DEVICE I/O ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ -/* Disable run-time linking on certain backends and platforms. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - #ifdef MA_APPLE #include #endif @@ -18039,12 +19451,6 @@ DEVICE I/O #ifdef MA_POSIX #include - #include - - /* No need for dlfcn.h if we're not using runtime linking. */ - #ifndef MA_NO_RUNTIME_LINKING - #include - #endif #endif /* This must be set to at least 26. */ @@ -18299,7 +19705,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -#if defined(MA_WIN32) +#if defined(MA_WIN32) && !defined(MA_XBOX) /* WASAPI error codes. */ #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) @@ -18514,6 +19920,11 @@ typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); #endif /* MA_WIN32_DESKTOP */ +static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; +static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; +/*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ +/*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + MA_API size_t ma_strlen_WCHAR(const WCHAR* str) { size_t len = 0; @@ -18577,7 +19988,7 @@ Timing *******************************************************************************/ #if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static void ma_timer_init(ma_timer* pTimer) + static MA_INLINE void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -18589,7 +20000,7 @@ Timing pTimer->counter = counter.QuadPart; } - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -18600,7 +20011,7 @@ Timing } #elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) static ma_uint64 g_ma_TimerFrequency = 0; - static void ma_timer_init(ma_timer* pTimer) + static MA_INLINE void ma_timer_init(ma_timer* pTimer) { mach_timebase_info_data_t baseTime; mach_timebase_info(&baseTime); @@ -18609,7 +20020,7 @@ Timing pTimer->counter = mach_absolute_time(); } - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter = mach_absolute_time(); ma_uint64 oldTimeCounter = pTimer->counter; @@ -18634,7 +20045,7 @@ Timing #define MA_CLOCK_ID CLOCK_REALTIME #endif - static void ma_timer_init(ma_timer* pTimer) + static MA_INLINE void ma_timer_init(ma_timer* pTimer) { struct timespec newTime; clock_gettime(MA_CLOCK_ID, &newTime); @@ -18642,7 +20053,7 @@ Timing pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; } - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter; ma_uint64 oldTimeCounter; @@ -18656,7 +20067,7 @@ Timing return (newTimeCounter - oldTimeCounter) / 1000000000.0; } #else - static void ma_timer_init(ma_timer* pTimer) + static MA_INLINE void ma_timer_init(ma_timer* pTimer) { struct timeval newTime; gettimeofday(&newTime, NULL); @@ -18664,7 +20075,7 @@ Timing pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; } - static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter; ma_uint64 oldTimeCounter; @@ -19248,14 +20659,6 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n } -#if defined(MA_WIN32) - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -#endif - - MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ { @@ -19967,7 +21370,7 @@ static ma_result ma_context_init__null(ma_context* pContext, const ma_context_co WIN32 COMMON *******************************************************************************/ -#if defined(MA_WIN32) +#if defined(MA_WIN32) && !defined(MA_XBOX) #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() @@ -19982,7 +21385,7 @@ WIN32 COMMON #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) #endif -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) +#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) && !defined(MA_XBOX_NXDK) typedef size_t DWORD_PTR; #endif @@ -20409,11 +21812,21 @@ typedef enum MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ } MA_AUDIO_STREAM_CATEGORY; +typedef enum +{ + MA_AUDCLNT_STREAMOPTIONS_NONE, + MA_AUDCLNT_STREAMOPTIONS_RAW, + MA_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT, + MA_AUDCLNT_STREAMOPTIONS_AMBISONICS, + MA_AUDCLNT_STREAMOPTIONS_POST_VOLUME_LOOPBACK +} MA_AUDCLNT_STREAMOPTIONS; + typedef struct { ma_uint32 cbSize; BOOL bIsOffload; MA_AUDIO_STREAM_CATEGORY eCategory; + MA_AUDCLNT_STREAMOPTIONS Options; } ma_AudioClientProperties; /* IUnknown */ @@ -21588,6 +23001,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device { ma_IMMDeviceEnumerator* pDeviceEnumerator; HRESULT hr; + HRESULT CoInitializeResult; MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); @@ -21601,12 +23015,17 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm happy enough to use this hack instead. + + CoUninitialize should only be called if we successfully initialized. S_OK and S_FALSE both mean that we need to + call CoUninitialize since the internal ref count was increased. RPC_E_CHANGED_MODE means that CoInitializeEx was + called with a different COINIT value, and we don't call CoUninitialize in that case. Other errors are possible, + so we check for S_OK and S_FALSE specifically. */ - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); { hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - ma_CoUninitialize(pContext); + } + if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) { ma_CoUninitialize(pContext); } if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); @@ -21950,7 +23369,7 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de pActivationParams = &activationParams; /* When requesting a specific device ID we need to use a special device ID. */ - MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ + MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (ma_wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ pDeviceID = &virtualDeviceID; } else { pActivationParams = NULL; /* No activation parameters required. */ @@ -26679,6 +28098,9 @@ typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; typedef snd_pcm_state_t ma_snd_pcm_state_t; +/* snd_pcm_state_t */ +#define MA_SND_PCM_STATE_XRUN SND_PCM_STATE_XRUN + /* snd_pcm_stream_t */ #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE @@ -26874,6 +28296,7 @@ typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) ( typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_set_rate_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); @@ -28640,8 +30063,9 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; + ma_snd_pcm_hw_params_set_channels_minmax_proc _snd_pcm_hw_params_set_channels_minmax = snd_pcm_hw_params_set_channels_minmax; ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; + ma_snd_pcm_hw_params_set_rate_proc _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; @@ -28693,9 +30117,9 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; + ma_snd_pcm_poll_descriptors_proc _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; + ma_snd_pcm_poll_descriptors_count_proc _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; + ma_snd_pcm_poll_descriptors_revents_proc _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; @@ -28711,6 +30135,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; + pContext->alsa.snd_pcm_hw_params_set_rate_minmax = (ma_proc)_snd_pcm_hw_params_set_rate_minmax; pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; @@ -29436,7 +30861,7 @@ typedef void (* ma_pa_threaded_mainloop_unlock_proc) ( typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (const ma_pa_threaded_mainloop* m); typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); @@ -29445,13 +30870,13 @@ typedef void (* ma_pa_context_unref_proc) ( typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); +typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (const ma_pa_context* c); typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); +typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (const ma_pa_operation* o); typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); @@ -29460,12 +30885,12 @@ typedef void (* ma_pa_stream_unref_proc) ( typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); +typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (const ma_pa_stream* s); typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); +typedef const char* (* ma_pa_stream_get_device_name_proc) (const ma_pa_stream* s); typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); @@ -29473,15 +30898,15 @@ typedef void (* ma_pa_stream_set_moved_callback_proc) ( typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); +typedef int (* ma_pa_stream_is_corked_proc) (const ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); +typedef size_t (* ma_pa_stream_writable_size_proc) (const ma_pa_stream* s); +typedef size_t (* ma_pa_stream_readable_size_proc) (const ma_pa_stream* s); typedef struct { @@ -29777,7 +31202,7 @@ static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, } /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? MA_PA_CONTEXT_NOFLAGS : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); @@ -30510,7 +31935,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi const ma_pa_buffer_attr* pActualAttr = NULL; const ma_pa_channel_map* pActualChannelMap = NULL; ma_uint32 iChannel; - ma_pa_stream_flags_t streamFlags; + int streamFlags; MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->pulse); @@ -30568,8 +31993,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.channels = pDescriptorCapture->channels; } + /* PulseAudio has a maximum channel count of 32. We'll get a crash if this is exceeded. */ + if (ss.channels > 32) { + ss.channels = 32; + } + /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap); /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { @@ -30626,7 +32056,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi streamFlags |= MA_PA_STREAM_DONT_MOVE; } - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); + error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, (ma_pa_stream_flags_t)streamFlags); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); result = ma_result_from_pulse(error); @@ -30720,8 +32150,13 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.channels = pDescriptorPlayback->channels; } + /* PulseAudio has a maximum channel count of 32. We'll get a crash if this is exceeded. */ + if (ss.channels > 32) { + ss.channels = 32; + } + /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap); /* Use the requested sample rate if one was specified. */ @@ -30783,7 +32218,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi streamFlags |= MA_PA_STREAM_DONT_MOVE; } - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); + error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, (ma_pa_stream_flags_t)streamFlags, NULL, NULL); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); result = ma_result_from_pulse(error); @@ -31338,6 +32773,7 @@ typedef JackProcessCallback ma_JackProcessCallback; typedef JackBufferSizeCallback ma_JackBufferSizeCallback; typedef JackShutdownCallback ma_JackShutdownCallback; #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE +#define ma_JackNullOption JackNullOption #define ma_JackNoStartServer JackNoStartServer #define ma_JackPortIsInput JackPortIsInput #define ma_JackPortIsOutput JackPortIsOutput @@ -31352,6 +32788,7 @@ typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg) typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); typedef void (* ma_JackShutdownCallback) (void* arg); #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" +#define ma_JackNullOption 0 #define ma_JackNoStartServer 1 #define ma_JackPortIsInput 1 #define ma_JackPortIsOutput 2 @@ -31392,7 +32829,7 @@ static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_clie maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); + pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? ma_JackNullOption : ma_JackNoStartServer, &status, NULL); if (pClient == NULL) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -36994,7 +38431,7 @@ OSS Backend #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" -static int ma_open_temp_device__oss() +static int ma_open_temp_device__oss(void) { /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ int fd = open("/dev/mixer", O_RDONLY, 0); @@ -37834,25 +39271,30 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs (void)error; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); + /* When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. */ - job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; + if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Tearing down device.\n"); } else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; + job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); + job.data.device.aaudio.reroute.pDevice = pDevice; + + if (pStream == pDevice->aaudio.pStreamCapture) { + job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; + } else { + job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; + } + + result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); + return; + } } } @@ -38169,7 +39611,7 @@ static ma_result ma_close_streams__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ + /* When rerouting, streams may have been closed and never re-opened. Hence the extra checks below. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); pDevice->aaudio.pStreamCapture = NULL; @@ -38186,6 +39628,12 @@ static ma_result ma_device_uninit__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); + /* + Note: Closing the streams may cause a timeout error, which would then trigger rerouting in our error callback. + We must not schedule a reroute when device is getting destroyed. + */ + ma_atomic_bool32_set(&pDevice->aaudio.isTearingDown, MA_TRUE); + /* Wait for any rerouting to finish before attempting to close the streams. */ ma_mutex_lock(&pDevice->aaudio.rerouteLock); { @@ -38193,7 +39641,7 @@ static ma_result ma_device_uninit__aaudio(ma_device* pDevice) } ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - /* Destroy re-routing lock. */ + /* Destroy rerouting lock. */ ma_mutex_uninit(&pDevice->aaudio.rerouteLock); return MA_SUCCESS; @@ -38429,17 +39877,22 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) { + const ma_int32 maxAttempts = 4; /* Reasonable retry limit. */ + ma_result result; - int32_t retries = 0; + ma_int32 iAttempt; MA_ASSERT(pDevice != NULL); - /* - TODO: Stop retrying if main thread is about to uninit device. - */ - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { -error_disconnected: + /* We got disconnected! Retry a few times, until we find a connected device! */ + iAttempt = 0; + while (iAttempt++ < maxAttempts) { + /* Device tearing down? No need to reroute! */ + if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) { + result = MA_SUCCESS; /* Caller should continue as normal. */ + break; + } + /* The first thing to do is close the streams. */ ma_close_streams__aaudio(pDevice); @@ -38495,14 +39948,16 @@ error_disconnected: result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - goto done; + /* Reroute failed! */ + break; } result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); ma_close_streams__aaudio(pDevice); - goto done; + /* Reroute failed! */ + break; } /* We'll only ever do this in response to a reroute. */ @@ -38513,26 +39968,23 @@ error_disconnected: if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { result = ma_device_start__aaudio(pDevice); if (result != MA_SUCCESS) { - /* We got disconnected! Retry a few times, until we find a connected device! */ - retries += 1; - if (retries <= 3) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); - goto error_disconnected; + if (iAttempt < maxAttempts) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", iAttempt); + } else { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, giving up."); } - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); - goto done; } } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ + ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ } } - - result = MA_SUCCESS; - } -done: - /* Re-routing done */ - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); + if (result == MA_SUCCESS) { + /* Reroute successful! */ + break; + } + } + return result; } @@ -38698,7 +40150,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { - ma_result result; + ma_result result = MA_SUCCESS; ma_device* pDevice; MA_ASSERT(pJob != NULL); @@ -38706,19 +40158,22 @@ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; MA_ASSERT(pDevice != NULL); - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - return result; + ma_mutex_lock(&pDevice->aaudio.rerouteLock); + { + /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ + result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); + if (result != MA_SUCCESS) { + /* + Getting here means we failed to reroute the device. The best thing I can think of here is to + just stop the device. + */ + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); + ma_device_stop(pDevice); + } } + ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - return MA_SUCCESS; + return result; } #else /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ @@ -41236,13 +42691,13 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { ma_device* pDevice = (ma_device*)pData; -#ifdef MA_WIN32 +#if defined(MA_WIN32) && !defined(MA_XBOX) HRESULT CoInitializeResult; #endif MA_ASSERT(pDevice != NULL); -#ifdef MA_WIN32 +#if defined(MA_WIN32) && !defined(MA_XBOX) CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); #endif @@ -41333,8 +42788,8 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_event_signal(&pDevice->stopEvent); } -#ifdef MA_WIN32 - if (CoInitializeResult == S_OK) { +#if defined(MA_WIN32) && !defined(MA_XBOX) + if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) { ma_CoUninitialize(pDevice->pContext); } #endif @@ -41358,67 +42813,92 @@ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - if (pContext->win32.CoInitializeResult == S_OK) { - ma_CoUninitialize(pContext); + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + /* TODO: Remove this once the new single threaded backend system is in place in 0.12. */ + #if !defined(MA_XBOX) + { + if (pContext->win32.CoInitializeResult == S_OK || pContext->win32.CoInitializeResult == S_FALSE) { + ma_CoUninitialize(pContext); /* TODO: Remove this once the new single threaded backend system is in place in 0.12. */ + } + } + #endif + + #if defined(MA_WIN32_DESKTOP) + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); + #endif + + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); + } + #else + { + (void)pContext; } - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); #endif - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); -#else - (void)pContext; -#endif - return MA_SUCCESS; } static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { -#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - #if defined(MA_WIN32_DESKTOP) - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { + /* + TODO: Reassess all of this stuff and move everything to the relevant backends. For example, I think + GetForegroundWindow() and GetDesktopWindow() are only used by the DirectSound backend. + */ + #if (defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)) && !defined(MA_XBOX) + { + #if defined(MA_WIN32_DESKTOP) + { + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + } + #endif + + /* Ole32.dll */ + pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); + if (pContext->win32.hOle32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); + } + #else + { + (void)pContext; /* Unused. */ + } #endif - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; + /* TODO: Remove this once the new single threaded backend system is in place in 0.12. */ + #if !defined(MA_XBOX) + { + pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); } + #endif - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); -#else - (void)pContext; /* Unused. */ -#endif - - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); return MA_SUCCESS; } #else @@ -44016,7 +45496,7 @@ static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, m ma_uint64 i; for (i = 0; i < count; i += 1) { - dst_s32[i] = src_s16[i] << 16; + dst_s32[i] = (ma_int32)src_s16[i] << 16; } (void)ditherMode; @@ -56408,8 +57888,12 @@ MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 } /* Null terminate. Don't increment the length here. */ - if (pBufferOut != NULL && bufferCap > len + 1) { - pBufferOut[len] = '\0'; + if (pBufferOut != NULL) { + if (bufferCap > len) { + pBufferOut[len] = '\0'; + } else if (bufferCap > 0) { + pBufferOut[bufferCap - 1] = '\0'; + } } return len; @@ -56620,7 +58104,7 @@ MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCoun Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. */ - pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; + pRB->subbufferStrideInBytes = ma_align(pRB->subbufferSizeInBytes, MA_SIMD_ALIGNMENT); bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); @@ -59515,7 +60999,7 @@ MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo } -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && (defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_NXDK)) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) #define MA_USE_WIN32_FILEIO #endif @@ -59592,25 +61076,34 @@ static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; + #if !defined(MA_XBOX_NXDK) + { + HANDLE hFile; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; - (void)pVFS; + (void)pVFS; - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); + hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return ma_result_from_GetLastError(GetLastError()); + } + + *pFile = hFile; + return MA_SUCCESS; } - - *pFile = hFile; - return MA_SUCCESS; + #else + { + /* No CreateFileW() available. */ + return MA_NOT_IMPLEMENTED; + } + #endif } static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) @@ -59781,19 +61274,28 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - (void)pVFS; - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); + #if !defined(MA_XBOX_NXDK) + { + BY_HANDLE_FILE_INFORMATION fi; + BOOL result; + + result = GetFileInformationByHandle((HANDLE)file, &fi); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); + + return MA_SUCCESS; } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; + #else + { + /* GetFileInformationByHandle() is unavailable. */ + return MA_NOT_IMPLEMENTED; + } + #endif } #else static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) @@ -60131,6 +61633,8 @@ static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* p static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { + ma_result result; + if (pInfo == NULL) { return MA_INVALID_ARGS; } @@ -60142,10 +61646,43 @@ static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_inf } #if defined(MA_USE_WIN32_FILEIO) - return ma_default_vfs_info__win32(pVFS, file, pInfo); + result = ma_default_vfs_info__win32(pVFS, file, pInfo); #else - return ma_default_vfs_info__stdio(pVFS, file, pInfo); + result = ma_default_vfs_info__stdio(pVFS, file, pInfo); #endif + + if (result == MA_NOT_IMPLEMENTED) { + /* Not implemented. Fall back to seek/tell/seek. */ + ma_result result; + ma_int64 cursor; + ma_int64 sizeInBytes; + + result = ma_default_vfs_tell(pVFS, file, &cursor); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_default_vfs_seek(pVFS, file, 0, ma_seek_origin_end); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_default_vfs_tell(pVFS, file, &sizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pInfo->sizeInBytes = sizeInBytes; + + result = ma_default_vfs_seek(pVFS, file, cursor, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; + } + + MA_ASSERT(result == MA_SUCCESS); + } + + return result; } @@ -60333,8 +61870,8 @@ extern "C" { #define MA_DR_WAV_STRINGIFY(x) #x #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) #define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 18 +#define MA_DR_WAV_VERSION_MINOR 14 +#define MA_DR_WAV_VERSION_REVISION 1 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include #define MA_DR_WAVE_FORMAT_PCM 0x1 @@ -60350,8 +61887,9 @@ MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* p MA_API const char* ma_dr_wav_version_string(void); typedef enum { - ma_dr_wav_seek_origin_start, - ma_dr_wav_seek_origin_current + MA_DR_WAV_SEEK_SET, + MA_DR_WAV_SEEK_CUR, + MA_DR_WAV_SEEK_END } ma_dr_wav_seek_origin; typedef enum { @@ -60388,6 +61926,7 @@ MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); +typedef ma_bool32 (* ma_dr_wav_tell_proc)(void* pUserData, ma_int64* pCursor); typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); typedef struct { @@ -60432,6 +61971,11 @@ typedef enum ma_dr_wav_metadata_type_list_info_genre = 1 << 15, ma_dr_wav_metadata_type_list_info_album = 1 << 16, ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, + ma_dr_wav_metadata_type_list_info_location = 1 << 18, + ma_dr_wav_metadata_type_list_info_organization = 1 << 19, + ma_dr_wav_metadata_type_list_info_keywords = 1 << 20, + ma_dr_wav_metadata_type_list_info_medium = 1 << 21, + ma_dr_wav_metadata_type_list_info_description = 1 << 22, ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software | ma_dr_wav_metadata_type_list_info_copyright | ma_dr_wav_metadata_type_list_info_title @@ -60440,7 +61984,12 @@ typedef enum | ma_dr_wav_metadata_type_list_info_date | ma_dr_wav_metadata_type_list_info_genre | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber, + | ma_dr_wav_metadata_type_list_info_tracknumber + | ma_dr_wav_metadata_type_list_info_location + | ma_dr_wav_metadata_type_list_info_organization + | ma_dr_wav_metadata_type_list_info_keywords + | ma_dr_wav_metadata_type_list_info_medium + | ma_dr_wav_metadata_type_list_info_description, ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label | ma_dr_wav_metadata_type_list_note | ma_dr_wav_metadata_type_list_labelled_cue_region, @@ -60457,8 +62006,8 @@ typedef struct { ma_uint32 cuePointId; ma_uint32 type; - ma_uint32 firstSampleByteOffset; - ma_uint32 lastSampleByteOffset; + ma_uint32 firstSampleOffset; + ma_uint32 lastSampleOffset; ma_uint32 sampleFraction; ma_uint32 playCount; } ma_dr_wav_smpl_loop; @@ -60493,7 +62042,7 @@ typedef struct ma_uint8 dataChunkId[4]; ma_uint32 chunkStart; ma_uint32 blockStart; - ma_uint32 sampleByteOffset; + ma_uint32 sampleOffset; } ma_dr_wav_cue_point; typedef struct { @@ -60595,6 +62144,7 @@ typedef struct ma_dr_wav_read_proc onRead; ma_dr_wav_write_proc onWrite; ma_dr_wav_seek_proc onSeek; + ma_dr_wav_tell_proc onTell; void* pUserData; ma_allocation_callbacks allocationCallbacks; ma_dr_wav_container container; @@ -60637,9 +62187,9 @@ typedef struct ma_bool8 isUnsigned; } aiff; } ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); @@ -60711,9 +62261,9 @@ MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, siz MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_WAV_NO_STDIO MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); @@ -60753,8 +62303,8 @@ extern "C" { #define MA_DR_FLAC_STRINGIFY(x) #x #define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) #define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 12 -#define MA_DR_FLAC_VERSION_REVISION 43 +#define MA_DR_FLAC_VERSION_MINOR 13 +#define MA_DR_FLAC_VERSION_REVISION 1 #define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) #include #if defined(_MSC_VER) && _MSC_VER >= 1700 @@ -60817,8 +62367,9 @@ typedef enum } ma_dr_flac_container; typedef enum { - ma_dr_flac_seek_origin_start, - ma_dr_flac_seek_origin_current + MA_DR_FLAC_SEEK_SET, + MA_DR_FLAC_SEEK_CUR, + MA_DR_FLAC_SEEK_END } ma_dr_flac_seek_origin; typedef struct { @@ -60894,6 +62445,7 @@ typedef struct } ma_dr_flac_metadata; typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); +typedef ma_bool32 (* ma_dr_flac_tell_proc)(void* pUserData, ma_int64* pCursor); typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); typedef struct { @@ -60905,6 +62457,7 @@ typedef struct { ma_dr_flac_read_proc onRead; ma_dr_flac_seek_proc onSeek; + ma_dr_flac_tell_proc onTell; void* pUserData; size_t unalignedByteCount; ma_dr_flac_cache_t unalignedCache; @@ -60964,10 +62517,10 @@ typedef struct ma_dr_flac_bs bs; ma_uint8 pExtraData[1]; } ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); @@ -60981,9 +62534,9 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName #endif MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_FLAC_NO_STDIO MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); @@ -61031,6 +62584,12 @@ MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterat #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) +#ifndef MA_DR_MP3_NO_SIMD + #if (defined(MA_NO_NEON) && defined(MA_ARM)) || (defined(MA_NO_SSE2) && (defined(MA_X86) || defined(MA_X64))) + #define MA_DR_MP3_NO_SIMD + #endif +#endif + /* dr_mp3_h begin */ #ifndef ma_dr_mp3_h #define ma_dr_mp3_h @@ -61040,8 +62599,8 @@ extern "C" { #define MA_DR_MP3_STRINGIFY(x) #x #define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) #define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 6 -#define MA_DR_MP3_VERSION_REVISION 40 +#define MA_DR_MP3_VERSION_MINOR 7 +#define MA_DR_MP3_VERSION_REVISION 1 #define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) #include #define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 @@ -61050,7 +62609,7 @@ MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* p MA_API const char* ma_dr_mp3_version_string(void); typedef struct { - int frame_bytes, channels, hz, layer, bitrate_kbps; + int frame_bytes, channels, sample_rate, layer, bitrate_kbps; } ma_dr_mp3dec_frame_info; typedef struct { @@ -61063,8 +62622,9 @@ MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); typedef enum { - ma_dr_mp3_seek_origin_start, - ma_dr_mp3_seek_origin_current + MA_DR_MP3_SEEK_SET, + MA_DR_MP3_SEEK_CUR, + MA_DR_MP3_SEEK_END } ma_dr_mp3_seek_origin; typedef struct { @@ -61073,8 +62633,24 @@ typedef struct ma_uint16 mp3FramesToDiscard; ma_uint16 pcmFramesToDiscard; } ma_dr_mp3_seek_point; +typedef enum +{ + MA_DR_MP3_METADATA_TYPE_ID3V1, + MA_DR_MP3_METADATA_TYPE_ID3V2, + MA_DR_MP3_METADATA_TYPE_APE, + MA_DR_MP3_METADATA_TYPE_XING, + MA_DR_MP3_METADATA_TYPE_VBRI +} ma_dr_mp3_metadata_type; +typedef struct +{ + ma_dr_mp3_metadata_type type; + const void* pRawData; + size_t rawDataSize; +} ma_dr_mp3_metadata; typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); +typedef ma_bool32 (* ma_dr_mp3_tell_proc)(void* pUserData, ma_int64* pCursor); +typedef void (* ma_dr_mp3_meta_proc)(void* pUserData, const ma_dr_mp3_metadata* pMetadata); typedef struct { ma_uint32 channels; @@ -61087,7 +62663,9 @@ typedef struct ma_uint32 sampleRate; ma_dr_mp3_read_proc onRead; ma_dr_mp3_seek_proc onSeek; + ma_dr_mp3_meta_proc onMeta; void* pUserData; + void* pUserDataMeta; ma_allocation_callbacks allocationCallbacks; ma_uint32 mp3FrameChannels; ma_uint32 mp3FrameSampleRate; @@ -61096,13 +62674,20 @@ typedef struct ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; ma_uint64 currentPCMFrame; ma_uint64 streamCursor; + ma_uint64 streamLength; + ma_uint64 streamStartOffset; ma_dr_mp3_seek_point* pSeekPoints; ma_uint32 seekPointCount; + ma_uint32 delayInPCMFrames; + ma_uint32 paddingInPCMFrames; + ma_uint64 totalPCMFrameCount; + ma_bool32 isVBR; + ma_bool32 isCBR; size_t dataSize; size_t dataCapacity; size_t dataConsumed; ma_uint8* pData; - ma_bool32 atEnd : 1; + ma_bool32 atEnd; struct { const ma_uint8* pData; @@ -61110,9 +62695,12 @@ typedef struct size_t currentReadPos; } memory; } ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_MP3_NO_STDIO +MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); #endif @@ -61125,8 +62713,8 @@ MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_MP3_NO_STDIO @@ -61679,8 +63267,10 @@ static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav MA_ASSERT(pWav != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_wav_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; + if (origin == MA_DR_WAV_SEEK_CUR) { + maSeekOrigin = ma_seek_origin_current; + } else if (origin == MA_DR_WAV_SEEK_END) { + maSeekOrigin = ma_seek_origin_end; } result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); @@ -61690,6 +63280,26 @@ static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav return MA_TRUE; } + +static ma_bool32 ma_wav_dr_callback__tell(void* pUserData, ma_int64* pCursor) +{ + ma_wav* pWav = (ma_wav*)pUserData; + ma_result result; + + MA_ASSERT(pWav != NULL); + MA_ASSERT(pCursor != NULL); + + if (pWav->onTell == NULL) { + return MA_FALSE; /* Not implemented. */ + } + + result = pWav->onTell(pWav->pReadSeekTellUserData, pCursor); + if (result != MA_SUCCESS) { + return MA_FALSE; /* Failed to tell. */ + } + + return MA_TRUE; +} #endif static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) @@ -61784,7 +63394,7 @@ MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p { ma_bool32 wavResult; - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); + wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, ma_wav_dr_callback__tell, pWav, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } @@ -62363,8 +63973,10 @@ static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_fl MA_ASSERT(pFlac != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_flac_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; + if (origin == MA_DR_FLAC_SEEK_CUR) { + maSeekOrigin = ma_seek_origin_current; + } else if (origin == MA_DR_FLAC_SEEK_END) { + maSeekOrigin = ma_seek_origin_end; } result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); @@ -62374,6 +63986,26 @@ static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_fl return MA_TRUE; } + +static ma_bool32 ma_flac_dr_callback__tell(void* pUserData, ma_int64* pCursor) +{ + ma_flac* pFlac = (ma_flac*)pUserData; + ma_result result; + + MA_ASSERT(pFlac != NULL); + MA_ASSERT(pCursor != NULL); + + if (pFlac->onTell == NULL) { + return MA_FALSE; /* Not implemented. */ + } + + result = pFlac->onTell(pFlac->pReadSeekTellUserData, pCursor); + if (result != MA_SUCCESS) { + return MA_FALSE; /* Failed to tell. */ + } + + return MA_TRUE; +} #endif static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) @@ -62425,7 +64057,7 @@ MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_ #if !defined(MA_NO_FLAC) { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); + pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, ma_flac_dr_callback__tell, pFlac, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62986,9 +64618,12 @@ static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3 MA_ASSERT(pMP3 != NULL); - maSeekOrigin = ma_seek_origin_start; - if (origin == ma_dr_mp3_seek_origin_current) { - maSeekOrigin = ma_seek_origin_current; + if (origin == MA_DR_MP3_SEEK_SET) { + maSeekOrigin = ma_seek_origin_start; + } else if (origin == MA_DR_MP3_SEEK_END) { + maSeekOrigin = ma_seek_origin_end; + } else { + maSeekOrigin = ma_seek_origin_current; } result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); @@ -62998,6 +64633,21 @@ static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3 return MA_TRUE; } + +static ma_bool32 ma_mp3_dr_callback__tell(void* pUserData, ma_int64* pCursor) +{ + ma_mp3* pMP3 = (ma_mp3*)pUserData; + ma_result result; + + MA_ASSERT(pMP3 != NULL); + + result = pMP3->onTell(pMP3->pReadSeekTellUserData, pCursor); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} #endif static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) @@ -63098,7 +64748,7 @@ MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p { ma_bool32 mp3Result; - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); + mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, ma_mp3_dr_callback__tell, NULL, pMP3, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } @@ -64997,14 +66647,16 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e ext1 = extension; ext2 = ma_path_extension_w(path); -#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) - return _wcsicmp(ext1, ext2) == 0; -#else - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ + #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(MA_XBOX_NXDK) { + return _wcsicmp(ext1, ext2) == 0; + } + #elif !defined(MA_XBOX_NXDK) && !defined(MA_DOS) + { + /* + I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This + isn't the most efficient way to do it, but it should work OK. + */ char ext1MB[4096]; char ext2MB[4096]; const wchar_t* pext1 = ext1; @@ -65024,7 +66676,13 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e return strcasecmp(ext1MB, ext2MB) == 0; } -#endif + #else + { + /* Getting here means we don't have a way to do a case-sensitive comparison for wide strings. Fall back to a simple case-sensitive comparison. */ + /* TODO: Implement our own wchar_t-to-char conversion routine and then use the char* version for comparing. */ + return ma_wcscmp(ext1, ext2) == 0; + } + #endif } #endif /* MA_HAS_PATH_API */ @@ -66119,10 +67777,18 @@ static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, m { ma_encoder* pEncoder = (ma_encoder*)pUserData; ma_result result; + ma_seek_origin maSeekOrigin; MA_ASSERT(pEncoder != NULL); - result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + maSeekOrigin = ma_seek_origin_start; + if (origin == MA_DR_WAV_SEEK_CUR) { + maSeekOrigin = ma_seek_origin_current; + } else if (origin == MA_DR_WAV_SEEK_END) { + maSeekOrigin = ma_seek_origin_end; + } + + result = pEncoder->onSeek(pEncoder, offset, maSeekOrigin); if (result != MA_SUCCESS) { return MA_FALSE; } else { @@ -67644,7 +69310,7 @@ static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) ma_uint32 block; /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ - MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); + MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * (int) sizeof(block)), sizeof(block)); if (ma_is_little_endian()) { return block; @@ -67720,7 +69386,7 @@ static ma_uint32 ma_hash_string_32(const char* str) static ma_uint32 ma_hash_string_w_32(const wchar_t* str) { - return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); + return ma_hash_32(str, (int)ma_wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); } @@ -67880,6 +69546,7 @@ static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_ return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); } +#if 0 /* Currently unused, but might make use of this later. */ static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); @@ -67887,6 +69554,7 @@ static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_ return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); } +#endif static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) { @@ -69009,16 +70677,19 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m /* Failed to post job. Probably ran out of memory. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_uninit(pInitNotification); } else { + /* + Fences were acquired before posting the job, but since the job was not able to + be posted, we need to make sure we release them so nothing gets stuck waiting. + + In the WAIT_INIT case, these will have already been released in ma_job_process() + so we should only release fences in this branch. + */ + if (pInitFence != NULL) { ma_fence_release(pInitFence); } + if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } + /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); @@ -76674,7 +78345,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con } if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + ma_data_source_set_loop_point_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); } ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); @@ -76736,6 +78407,7 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); if (result != MA_SUCCESS) { + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); goto done; } @@ -77541,7 +79213,12 @@ MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) { - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + ma_uint32 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + if (sampleRate == 0) { + return 0; /* Prevent a division by zero. */ + } + + return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / sampleRate; } MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) @@ -77625,7 +79302,7 @@ MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeco return ma_sound_seek_to_pcm_frame(pSound, frameIndex); } -MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -77658,7 +79335,7 @@ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, } } -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor) { ma_uint64 seekTarget; @@ -77680,7 +79357,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* } } -MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) +MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -77694,7 +79371,7 @@ MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); } -MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) +MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor) { ma_result result; ma_uint64 cursorInPCMFrames; @@ -77720,7 +79397,7 @@ MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor return MA_SUCCESS; } -MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) +MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -78539,12 +80216,12 @@ MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint ma_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) { return MA_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, MA_DR_WAV_SEEK_CUR)) { return MA_FALSE; } bytesRemainingToSeek = 0; @@ -78555,17 +80232,17 @@ MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); + return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_SET); } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { + if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_SET)) { return MA_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); + return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_CUR); } - if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) { return MA_FALSE; } offset -= 0x7FFFFFFF; @@ -78588,7 +80265,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserD if (!onSeek(pUserData, offset, origin)) { return MA_FALSE; } - if (origin == ma_dr_wav_seek_origin_start) { + if (origin == MA_DR_WAV_SEEK_SET) { *pCursor = offset; } else { *pCursor += offset; @@ -78707,12 +80384,12 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_pa ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); } else { break; } @@ -78756,7 +80433,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_par pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); } else { break; } @@ -79096,7 +80773,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { ma_uint8 buffer[4]; size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, MA_DR_WAV_SEEK_CUR)) { return bytesRead; } bytesRead += 28; @@ -79191,7 +80868,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse return bytesRead; } allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES + 1; ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; } else { @@ -79274,6 +80951,16 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_location, "IARL")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_location); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_organization, "ICMS")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_organization); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_keywords, "IKEY")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_keywords); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_medium, "IMED")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_medium); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_description, "ISBJ")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_description); } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } @@ -79281,13 +80968,13 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); if (subchunkBytesRead < subchunkDataSize) { ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, MA_DR_WAV_SEEK_CUR)) { break; } bytesRead += 1; @@ -79324,7 +81011,7 @@ MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) return ma_dr_wav_bytes_to_u16(pFMT->subFormat); } } -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pReadSeekTellUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onRead == NULL || onSeek == NULL) { return MA_FALSE; @@ -79332,7 +81019,8 @@ MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRe MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; - pWav->pUserData = pReadSeekUserData; + pWav->onTell = onTell; + pWav->pUserData = pReadSeekTellUserData; pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { return MA_FALSE; @@ -79546,14 +81234,14 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, MA_DR_WAV_SEEK_CUR) == MA_FALSE) { return MA_FALSE; } } cursor += fmt.extendedSize; bytesReadSoFar += fmt.extendedSize; } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), MA_DR_WAV_SEEK_CUR) == MA_FALSE) { return MA_FALSE; } cursor += (header.sizeInBytes - bytesReadSoFar); @@ -79704,15 +81392,26 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p return MA_FALSE; } offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - pWav->dataChunkDataPos = cursor; + pWav->dataChunkDataPos = cursor + offset; dataChunkSize = chunkSize; - if (sequential || !isProcessingMetadata) { - break; + if (dataChunkSize > offset) { + dataChunkSize -= offset; } else { + dataChunkSize = 0; + } + if (sequential) { + if (foundChunk_fmt) { + if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += offset; + break; + } else { + return MA_FALSE; + } + } else { + chunkSize += header.paddingSize; + chunkSize -= sizeof(offsetAndBlockSizeData); if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { break; } @@ -79776,6 +81475,17 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p pWav->pMetadata = metadataParser.pMetadata; pWav->metadataCount = metadataParser.metadataCount; } + if (pWav->onTell != NULL && pWav->onSeek != NULL) { + if (pWav->onSeek(pWav->pUserData, 0, MA_DR_WAV_SEEK_END) == MA_TRUE) { + ma_int64 fileSize; + if (pWav->onTell(pWav->pUserData, &fileSize)) { + if (dataChunkSize + pWav->dataChunkDataPos > (ma_uint64)fileSize) { + dataChunkSize = (ma_uint64)fileSize - pWav->dataChunkDataPos; + } + } + } else { + } + } if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { dataChunkSize = 0; for (;;) { @@ -79795,8 +81505,14 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; pWav->bitsPerSample = fmt.bitsPerSample; - pWav->bytesRemaining = dataChunkSize; pWav->translatedFormatTag = translatedFormatTag; + if (!ma_dr_wav__is_compressed_format_tag(translatedFormatTag)) { + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame > 0) { + dataChunkSize -= (dataChunkSize % bytesPerFrame); + } + } + pWav->bytesRemaining = dataChunkSize; pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; @@ -79851,20 +81567,20 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p #endif return MA_TRUE; } -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) { return MA_FALSE; } return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); } -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return MA_FALSE; } return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); @@ -80026,8 +81742,8 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } @@ -80061,7 +81777,7 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset); } } break; case ma_dr_wav_metadata_type_acid: @@ -80147,15 +81863,20 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { const char* pID = NULL; switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; + case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; + case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; + case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; + case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; + case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; + case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; + case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + case ma_dr_wav_metadata_type_list_info_location: pID = "IARL"; break; + case ma_dr_wav_metadata_type_list_info_organization: pID = "ICMS"; break; + case ma_dr_wav_metadata_type_list_info_keywords: pID = "IKEY"; break; + case ma_dr_wav_metadata_type_list_info_medium: pID = "IMED"; break; + case ma_dr_wav_metadata_type_list_info_description: pID = "ISBJ"; break; default: break; } MA_DR_WAV_ASSERT(pID != NULL); @@ -80370,7 +82091,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr } pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; + ma_uint32 chunkSizeRIFF = 36 + (ma_uint32)initialDataChunkSize; runningPos += ma_dr_wav__write(pWav, "RIFF", 4); runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); runningPos += ma_dr_wav__write(pWav, "WAVE", 4); @@ -80493,7 +82214,31 @@ MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, } MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + int whence = SEEK_SET; + if (origin == MA_DR_WAV_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == MA_DR_WAV_SEEK_END) { + whence = SEEK_END; + } + return fseek((FILE*)pUserData, offset, whence) == 0; +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_stdio(void* pUserData, ma_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + ma_int64 result; + MA_DR_WAV_ASSERT(pFileStdio != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); +#if defined(_WIN32) && !defined(NXDK) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + *pCursor = result; + return MA_TRUE; } MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) { @@ -80502,7 +82247,7 @@ MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, cons MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, ma_dr_wav__on_tell_stdio, (void*)pFile, pAllocationCallbacks); if (result != MA_TRUE) { fclose(pFile); return result; @@ -80639,25 +82384,27 @@ MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, s MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + ma_int64 newCursor; MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return MA_FALSE; - } - } else { - if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return MA_FALSE; - } - } - pWav->memoryStream.currentReadPos += offset; + newCursor = pWav->memoryStream.currentReadPos; + if (origin == MA_DR_WAV_SEEK_SET) { + newCursor = 0; + } else if (origin == MA_DR_WAV_SEEK_CUR) { + newCursor = (ma_int64)pWav->memoryStream.currentReadPos; + } else if (origin == MA_DR_WAV_SEEK_END) { + newCursor = (ma_int64)pWav->memoryStream.dataSize; } else { - if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { - pWav->memoryStream.currentReadPos = offset; - } else { - return MA_FALSE; - } + MA_DR_WAV_ASSERT(!"Invalid seek origin"); + return MA_FALSE; } + newCursor += offset; + if (newCursor < 0) { + return MA_FALSE; + } + if ((size_t)newCursor > pWav->memoryStream.dataSize) { + return MA_FALSE; + } + pWav->memoryStream.currentReadPos = (size_t)newCursor; return MA_TRUE; } MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) @@ -80691,25 +82438,35 @@ MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataI MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + ma_int64 newCursor; MA_DR_WAV_ASSERT(pWav != NULL); - if (origin == ma_dr_wav_seek_origin_current) { - if (offset > 0) { - if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { - offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); - } - } else { - if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { - offset = -(int)pWav->memoryStreamWrite.currentWritePos; - } - } - pWav->memoryStreamWrite.currentWritePos += offset; + newCursor = pWav->memoryStreamWrite.currentWritePos; + if (origin == MA_DR_WAV_SEEK_SET) { + newCursor = 0; + } else if (origin == MA_DR_WAV_SEEK_CUR) { + newCursor = (ma_int64)pWav->memoryStreamWrite.currentWritePos; + } else if (origin == MA_DR_WAV_SEEK_END) { + newCursor = (ma_int64)pWav->memoryStreamWrite.dataSize; } else { - if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { - pWav->memoryStreamWrite.currentWritePos = offset; - } else { - pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; - } + MA_DR_WAV_ASSERT(!"Invalid seek origin"); + return MA_INVALID_ARGS; } + newCursor += offset; + if (newCursor < 0) { + return MA_FALSE; + } + if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) { + return MA_FALSE; + } + pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor; + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_memory(void* pUserData, ma_int64* pCursor) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); + *pCursor = (ma_int64)pWav->memoryStream.currentReadPos; return MA_TRUE; } MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) @@ -80721,7 +82478,7 @@ MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, siz if (data == NULL || dataSize == 0) { return MA_FALSE; } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) { return MA_FALSE; } pWav->memoryStream.data = (const ma_uint8*)data; @@ -80734,7 +82491,7 @@ MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void if (data == NULL || dataSize == 0) { return MA_FALSE; } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) { return MA_FALSE; } pWav->memoryStream.data = (const ma_uint8*)data; @@ -80793,30 +82550,30 @@ MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) } if (pWav->onSeek && !pWav->isSequentialWrite) { if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, 4, MA_DR_WAV_SEEK_SET)) { ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, MA_DR_WAV_SEEK_SET)) { ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, 16, MA_DR_WAV_SEEK_SET)) { ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, MA_DR_WAV_SEEK_SET)) { ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == ma_dr_wav_container_rf64) { int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, MA_DR_WAV_SEEK_SET)) { ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, MA_DR_WAV_SEEK_SET)) { ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } @@ -80863,7 +82620,7 @@ MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBuf if (bytesToSeek > 0x7FFFFFFF) { bytesToSeek = 0x7FFFFFFF; } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR) == MA_FALSE) { break; } bytesRead += bytesToSeek; @@ -80962,7 +82719,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) if (pWav->onWrite != NULL) { return MA_FALSE; } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, MA_DR_WAV_SEEK_SET)) { return MA_FALSE; } if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { @@ -81043,7 +82800,7 @@ MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFr } while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { + if (!pWav->onSeek(pWav->pUserData, offset32, MA_DR_WAV_SEEK_CUR)) { return MA_FALSE; } pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; @@ -81169,12 +82926,12 @@ MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToW MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { ma_uint64 totalFramesRead = 0; - static ma_int32 adaptationTable[] = { + static const ma_int32 adaptationTable[] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; - static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + static const ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static const ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; MA_DR_WAV_ASSERT(pWav != NULL); MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { @@ -81307,11 +83064,11 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint { ma_uint64 totalFramesRead = 0; ma_uint32 iChannel; - static ma_int32 indexTable[16] = { + static const ma_int32 indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; - static ma_int32 stepTable[89] = { + static const ma_int32 stepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, @@ -81334,7 +83091,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } @@ -81349,7 +83106,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } @@ -81424,7 +83181,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint return totalFramesRead; } #ifndef MA_DR_WAV_NO_CONVERSION_API -static unsigned short g_ma_dr_wavAlawTable[256] = { +static const unsigned short ma_dr_wav_gAlawTable[256] = { 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, @@ -81442,7 +83199,7 @@ static unsigned short g_ma_dr_wavAlawTable[256] = { 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; -static unsigned short g_ma_dr_wavMulawTable[256] = { +static const unsigned short ma_dr_wav_gMulawTable[256] = { 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, @@ -81462,11 +83219,11 @@ static unsigned short g_ma_dr_wavMulawTable[256] = { }; static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) { - return (short)g_ma_dr_wavAlawTable[sampleIn]; + return (short)ma_dr_wav_gAlawTable[sampleIn]; } static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) { - return (short)g_ma_dr_wavMulawTable[sampleIn]; + return (short)ma_dr_wav_gMulawTable[sampleIn]; } MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { @@ -82625,7 +84382,7 @@ MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, u } return pSampleData; } -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_wav wav; if (channelsOut) { @@ -82637,12 +84394,12 @@ MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRe if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_wav wav; if (channelsOut) { @@ -82654,12 +84411,12 @@ MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_wav wav; if (channelsOut) { @@ -82671,7 +84428,7 @@ MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRe if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); @@ -84106,23 +85863,23 @@ static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFro MA_DR_FLAC_ASSERT(offsetFromStart > 0); if (offsetFromStart > 0x7FFFFFFF) { ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) { return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } } } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, MA_DR_FLAC_SEEK_SET)) { return MA_FALSE; } } @@ -86600,6 +88357,7 @@ typedef struct { ma_dr_flac_read_proc onRead; ma_dr_flac_seek_proc onSeek; + ma_dr_flac_tell_proc onTell; ma_dr_flac_meta_proc onMeta; ma_dr_flac_container container; void* pUserData; @@ -86728,11 +88486,12 @@ static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) { ma_uint64 runningFilePos = 42; ma_uint64 seektablePos = 0; ma_uint32 seektableSize = 0; + (void)onTell; for (;;) { ma_dr_flac_metadata metadata; ma_uint8 isLastBlock = 0; @@ -86990,7 +88749,7 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea { if (onMeta) { metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) { isLastBlock = MA_TRUE; } else { onMeta(pUserDataMD, &metadata); @@ -87000,7 +88759,7 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: { if (onMeta) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) { isLastBlock = MA_TRUE; } } @@ -87024,7 +88783,7 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea } break; } if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) { isLastBlock = MA_TRUE; } } @@ -87288,6 +89047,7 @@ typedef struct { ma_dr_flac_read_proc onRead; ma_dr_flac_seek_proc onSeek; + ma_dr_flac_tell_proc onTell; void* pUserData; ma_uint64 currentBytePos; ma_uint64 firstBytePos; @@ -87306,29 +89066,29 @@ static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* buf } static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) { - if (origin == ma_dr_flac_seek_origin_start) { + if (origin == MA_DR_FLAC_SEEK_SET) { if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_SET)) { return MA_FALSE; } oggbs->currentBytePos = offset; return MA_TRUE; } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) { return MA_FALSE; } oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); + return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR); } } else { while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } oggbs->currentBytePos += offset; @@ -87354,7 +89114,7 @@ static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr continue; } if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { + if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } continue; @@ -87416,7 +89176,7 @@ static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) } bytesToEndOfPacketOrPage += segmentSize; } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); + ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, MA_DR_FLAC_SEEK_CUR); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { @@ -87469,36 +89229,44 @@ static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac int bytesSeeked = 0; MA_DR_FLAC_ASSERT(oggbs != NULL); MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == ma_dr_flac_seek_origin_start) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { + if (origin == MA_DR_FLAC_SEEK_SET) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, MA_DR_FLAC_SEEK_SET)) { return MA_FALSE; } if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { return MA_FALSE; } - return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); - } - MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; + return ma_dr_flac__on_seek_ogg(pUserData, offset, MA_DR_FLAC_SEEK_CUR); + } else if (origin == MA_DR_FLAC_SEEK_CUR) { + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; + } } + } else if (origin == MA_DR_FLAC_SEEK_END) { + return MA_FALSE; } return MA_TRUE; } +static ma_bool32 ma_dr_flac__on_tell_ogg(void* pUserData, ma_int64* pCursor) +{ + (void)pUserData; + (void)pCursor; + return MA_FALSE; +} static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; @@ -87515,7 +89283,7 @@ static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 runningGranulePosition = 0; for (;;) { if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); + ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, MA_DR_FLAC_SEEK_SET); return MA_FALSE; } runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; @@ -87534,7 +89302,7 @@ static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 } } } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, MA_DR_FLAC_SEEK_SET)) { return MA_FALSE; } if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { @@ -87629,7 +89397,7 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d if (mappingVersion[0] != 1) { return MA_FALSE; } - if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, 2, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } if (onRead(pUserData, sig, 4) != 4) { @@ -87674,17 +89442,17 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d return MA_FALSE; } } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } } } else { - if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } } } else { - if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, pageBodySize, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } } @@ -87698,7 +89466,7 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d return MA_TRUE; } #endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) +static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) { ma_bool32 relaxed; ma_uint8 id[4]; @@ -87708,12 +89476,14 @@ static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_fla MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; + pInit->onTell = onTell; pInit->onMeta = onMeta; pInit->container = container; pInit->pUserData = pUserData; pInit->pUserDataMD = pUserDataMD; pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; + pInit->bs.onTell = onTell; pInit->bs.pUserData = pUserData; ma_dr_flac__reset_cache(&pInit->bs); relaxed = container != ma_dr_flac_container_unknown; @@ -87736,7 +89506,7 @@ static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_fla if (flags & 0x10) { headerSize += 10; } - if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { + if (!onSeek(pUserData, headerSize, MA_DR_FLAC_SEEK_CUR)) { return MA_FALSE; } pInit->runningFilePos += headerSize; @@ -87779,7 +89549,7 @@ static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_ pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) +static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac_init_info init; ma_uint32 allocationSize; @@ -87794,7 +89564,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on ma_allocation_callbacks allocationCallbacks; ma_dr_flac* pFlac; ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + if (!ma_dr_flac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { @@ -87827,6 +89597,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; + pOggbs->onTell = onTell; pOggbs->pUserData = pUserData; pOggbs->currentBytePos = init.oggFirstBytePos; pOggbs->firstBytePos = init.oggFirstBytePos; @@ -87841,15 +89612,17 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on if (init.hasMetadataBlocks) { ma_dr_flac_read_proc onReadOverride = onRead; ma_dr_flac_seek_proc onSeekOverride = onSeek; + ma_dr_flac_tell_proc onTellOverride = onTell; void* pUserDataOverride = pUserData; #ifndef MA_DR_FLAC_NO_OGG if (init.container == ma_dr_flac_container_ogg) { onReadOverride = ma_dr_flac__on_read_ogg; onSeekOverride = ma_dr_flac__on_seek_ogg; + onTellOverride = ma_dr_flac__on_tell_ogg; pUserDataOverride = (void*)pOggbs; } #endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { #ifndef MA_DR_FLAC_NO_OGG ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif @@ -87875,6 +89648,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on pOggbs = NULL; pFlac->bs.onRead = ma_dr_flac__on_read_ogg; pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; + pFlac->bs.onTell = ma_dr_flac__on_tell_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } @@ -87894,7 +89668,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, MA_DR_FLAC_SEEK_SET)) { ma_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { @@ -87907,7 +89681,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on break; } } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, MA_DR_FLAC_SEEK_SET)) { ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } @@ -87950,8 +89724,31 @@ static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t } static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - MA_DR_FLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + int whence = SEEK_SET; + if (origin == MA_DR_FLAC_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == MA_DR_FLAC_SEEK_END) { + whence = SEEK_END; + } + return fseek((FILE*)pUserData, offset, whence) == 0; +} +static ma_bool32 ma_dr_flac__on_tell_stdio(void* pUserData, ma_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + ma_int64 result; + MA_DR_FLAC_ASSERT(pFileStdio != NULL); + MA_DR_FLAC_ASSERT(pCursor != NULL); +#if defined(_WIN32) && !defined(NXDK) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + *pCursor = result; + return MA_TRUE; } MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) { @@ -87960,7 +89757,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocati if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -87975,7 +89772,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_all if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -87990,7 +89787,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_ if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -88005,7 +89802,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -88033,24 +89830,35 @@ static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_ static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + ma_int64 newCursor; MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(offset >= 0); - if (offset > (ma_int64)memoryStream->dataSize) { + newCursor = memoryStream->currentReadPos; + if (origin == MA_DR_FLAC_SEEK_SET) { + newCursor = 0; + } else if (origin == MA_DR_FLAC_SEEK_CUR) { + newCursor = (ma_int64)memoryStream->currentReadPos; + } else if (origin == MA_DR_FLAC_SEEK_END) { + newCursor = (ma_int64)memoryStream->dataSize; + } else { + MA_DR_FLAC_ASSERT(!"Invalid seek origin"); return MA_FALSE; } - if (origin == ma_dr_flac_seek_origin_current) { - if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { - memoryStream->currentReadPos += offset; - } else { - return MA_FALSE; - } - } else { - if ((ma_uint32)offset <= memoryStream->dataSize) { - memoryStream->currentReadPos = offset; - } else { - return MA_FALSE; - } + newCursor += offset; + if (newCursor < 0) { + return MA_FALSE; } + if ((size_t)newCursor > memoryStream->dataSize) { + return MA_FALSE; + } + memoryStream->currentReadPos = (size_t)newCursor; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__on_tell_memory(void* pUserData, ma_int64* pCursor) +{ + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(pCursor != NULL); + *pCursor = (ma_int64)memoryStream->currentReadPos; return MA_TRUE; } MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) @@ -88060,7 +89868,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, co memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -88085,7 +89893,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_ memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -88103,21 +89911,21 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_ } return pFlac; } -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) { @@ -90410,7 +92218,7 @@ on_error: MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac* pFlac; if (channelsOut) { @@ -90422,13 +92230,13 @@ MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac* pFlac; if (channelsOut) { @@ -90440,13 +92248,13 @@ MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac* pFlac; if (channelsOut) { @@ -90458,7 +92266,7 @@ MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRea if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -91262,6 +93070,10 @@ static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) } while ((exp_q2 -= e) > 0); return y; } +#if (defined(__GNUC__) && (__GNUC__ >= 14)) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) { static const ma_uint8 g_scf_partitions[3][28] = { @@ -91320,7 +93132,10 @@ static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_ scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } -static const float g_ma_dr_mp3_pow43[129 + 16] = { +#if (defined(__GNUC__) && (__GNUC__ >= 14)) && !defined(__clang__) + #pragma GCC diagnostic pop +#endif +static const float ma_dr_mp3_g_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f }; @@ -91330,7 +93145,7 @@ static float ma_dr_mp3_L3_pow_43(int x) int sign, mult = 256; if (x < 129) { - return g_ma_dr_mp3_pow43[16 + x]; + return ma_dr_mp3_g_pow43[16 + x]; } if (x < 1024) { @@ -91339,7 +93154,7 @@ static float ma_dr_mp3_L3_pow_43(int x) } sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return ma_dr_mp3_g_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { @@ -91409,7 +93224,7 @@ static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); } else { - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } @@ -91437,7 +93252,7 @@ static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; - *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } MA_DR_MP3_CHECK_BITS; @@ -92268,7 +94083,7 @@ MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); info->frame_bytes = i + frame_size; info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); + info->sample_rate = ma_dr_mp3_hdr_sample_rate_hz(hdr); info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); @@ -92491,19 +94306,41 @@ static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(c } static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) { - size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + size_t bytesRead; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + if (bytesToRead == 0) { + return 0; + } + bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; return bytesRead; } +static size_t ma_dr_mp3__on_read_clamped(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + if (pMP3->streamLength == MA_UINT64_MAX) { + return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead); + } else { + ma_uint64 bytesRemaining; + bytesRemaining = (pMP3->streamLength - pMP3->streamCursor); + if (bytesToRead > bytesRemaining) { + bytesToRead = (size_t)bytesRemaining; + } + return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead); + } +} static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) { MA_DR_MP3_ASSERT(offset >= 0); + MA_DR_MP3_ASSERT(origin == MA_DR_MP3_SEEK_SET || origin == MA_DR_MP3_SEEK_CUR); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { return MA_FALSE; } - if (origin == ma_dr_mp3_seek_origin_start) { + if (origin == MA_DR_MP3_SEEK_SET) { pMP3->streamCursor = (ma_uint64)offset; - } else { + } else{ pMP3->streamCursor += offset; } return MA_TRUE; @@ -92513,18 +94350,18 @@ static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_ if (offset <= 0x7FFFFFFF) { return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_SET)) { return MA_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { + if (!ma_dr_mp3__on_seek(pMP3, (int)offset, MA_DR_MP3_SEEK_CUR)) { return MA_FALSE; } offset = 0; } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_CUR)) { return MA_FALSE; } offset -= 0x7FFFFFFF; @@ -92532,7 +94369,18 @@ static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_ } return MA_TRUE; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +static void ma_dr_mp3__on_meta(ma_dr_mp3* pMP3, ma_dr_mp3_metadata_type type, const void* pRawData, size_t rawDataSize) +{ + if (pMP3->onMeta) { + ma_dr_mp3_metadata metadata; + MA_DR_MP3_ZERO_OBJECT(&metadata); + metadata.type = type; + metadata.pRawData = pRawData; + metadata.rawDataSize = rawDataSize; + pMP3->onMeta(pMP3->pUserDataMeta, &metadata); + } +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData) { ma_uint32 pcmFramesRead = 0; MA_DR_MP3_ASSERT(pMP3 != NULL); @@ -92559,7 +94407,7 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { pMP3->atEnd = MA_TRUE; @@ -92578,16 +94426,20 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d return 0; } pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - if (info.frame_bytes > 0) { - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; - } + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; if (pcmFramesRead > 0) { pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; + pMP3->mp3FrameSampleRate = info.sample_rate; + if (pMP3FrameInfo != NULL) { + *pMP3FrameInfo = info; + } + if (ppMP3FrameData != NULL) { + *ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes; + } break; } else if (info.frame_bytes == 0) { size_t bytesRead; @@ -92604,7 +94456,7 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { pMP3->atEnd = MA_TRUE; return 0; @@ -92614,7 +94466,7 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d }; return pcmFramesRead; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData) { ma_uint32 pcmFramesRead = 0; ma_dr_mp3dec_frame_info info; @@ -92630,36 +94482,44 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_m pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.hz; + pMP3->mp3FrameSampleRate = info.sample_rate; + if (pMP3FrameInfo != NULL) { + *pMP3FrameInfo = info; + } + if (ppMP3FrameData != NULL) { + *ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos; + } break; } else if (info.frame_bytes > 0) { pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + pMP3->streamCursor += (size_t)info.frame_bytes; } else { break; } } pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + pMP3->streamCursor += (size_t)info.frame_bytes; return pcmFramesRead; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); } } static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) { MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); + return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, NULL, NULL); } #if 0 static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) { ma_uint32 pcmFrameCount; MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFrameCount == 0) { return 0; } @@ -92669,33 +94529,249 @@ static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) return pcmFrameCount; } #endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) { + ma_dr_mp3dec_frame_info firstFrameInfo; + const ma_uint8* pFirstFrameData; + ma_uint32 firstFramePCMFrameCount; + ma_uint32 detectedMP3FrameCount = 0xFFFFFFFF; MA_DR_MP3_ASSERT(pMP3 != NULL); MA_DR_MP3_ASSERT(onRead != NULL); ma_dr_mp3dec_init(&pMP3->decoder); pMP3->onRead = onRead; pMP3->onSeek = onSeek; + pMP3->onMeta = onMeta; pMP3->pUserData = pUserData; + pMP3->pUserDataMeta = pUserDataMeta; pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { return MA_FALSE; } - if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + pMP3->streamCursor = 0; + pMP3->streamLength = MA_UINT64_MAX; + pMP3->streamStartOffset = 0; + pMP3->delayInPCMFrames = 0; + pMP3->paddingInPCMFrames = 0; + pMP3->totalPCMFrameCount = MA_UINT64_MAX; + #if 1 + if (onSeek != NULL && onTell != NULL) { + if (onSeek(pUserData, 0, MA_DR_MP3_SEEK_END)) { + ma_int64 streamLen; + int streamEndOffset = 0; + if (onTell(pUserData, &streamLen)) { + if (streamLen > 128) { + char id3[3]; + if (onSeek(pUserData, streamEndOffset - 128, MA_DR_MP3_SEEK_END)) { + if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') { + streamEndOffset -= 128; + streamLen -= 128; + if (onMeta != NULL) { + ma_uint8 tag[128]; + tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G'; + if (onRead(pUserData, tag + 3, 125) == 125) { + ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V1, tag, 128); + } + } + } else { + } + } else { + } + } else { + } + if (streamLen > 32) { + char ape[32]; + if (onSeek(pUserData, streamEndOffset - 32, MA_DR_MP3_SEEK_END)) { + if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') { + ma_uint32 tagSize = + ((ma_uint32)ape[24] << 0) | + ((ma_uint32)ape[25] << 8) | + ((ma_uint32)ape[26] << 16) | + ((ma_uint32)ape[27] << 24); + streamEndOffset -= 32 + tagSize; + streamLen -= 32 + tagSize; + if (onMeta != NULL) { + if (onSeek(pUserData, streamEndOffset, MA_DR_MP3_SEEK_END)) { + size_t apeTagSize = (size_t)tagSize + 32; + ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(apeTagSize, pAllocationCallbacks); + if (pTagData != NULL) { + if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { + ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_APE, pTagData, apeTagSize); + } + ma_dr_mp3_free(pTagData, pAllocationCallbacks); + } + } + } + } + } + } else { + } + if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) { + return MA_FALSE; + } + pMP3->streamLength = (ma_uint64)streamLen; + if (pMP3->memory.pData != NULL) { + pMP3->memory.dataSize = (size_t)pMP3->streamLength; + } + } else { + if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) { + return MA_FALSE; + } + } + } else { + } + } else { + } + #endif + #if 1 + { + char header[10]; + if (onRead(pUserData, header, 10) == 10) { + if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') { + ma_uint32 tagSize = + (((ma_uint32)header[6] & 0x7F) << 21) | + (((ma_uint32)header[7] & 0x7F) << 14) | + (((ma_uint32)header[8] & 0x7F) << 7) | + (((ma_uint32)header[9] & 0x7F) << 0); + if (header[5] & 0x10) { + tagSize += 10; + } + if (onMeta != NULL) { + size_t tagSizeWithHeader = 10 + tagSize; + ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(tagSizeWithHeader, pAllocationCallbacks); + if (pTagData != NULL) { + MA_DR_MP3_COPY_MEMORY(pTagData, header, 10); + if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) { + ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader); + } + ma_dr_mp3_free(pTagData, pAllocationCallbacks); + } + } else { + if (onSeek != NULL) { + if (!onSeek(pUserData, tagSize, MA_DR_MP3_SEEK_CUR)) { + return MA_FALSE; + } + } else { + char discard[1024]; + while (tagSize > 0) { + size_t bytesToRead = tagSize; + if (bytesToRead > sizeof(discard)) { + bytesToRead = sizeof(discard); + } + if (onRead(pUserData, discard, bytesToRead) != bytesToRead) { + return MA_FALSE; + } + tagSize -= (ma_uint32)bytesToRead; + } + } + } + pMP3->streamStartOffset += 10 + tagSize; + pMP3->streamCursor = pMP3->streamStartOffset; + } else { + if (onSeek != NULL) { + if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) { + return MA_FALSE; + } + } else { + } + } + } else { + return MA_FALSE; + } + } + #endif + firstFramePCMFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData); + if (firstFramePCMFrameCount > 0) { + MA_DR_MP3_ASSERT(pFirstFrameData != NULL); + #if 1 + MA_DR_MP3_ASSERT(firstFrameInfo.frame_bytes > 0); + { + ma_dr_mp3_bs bs; + ma_dr_mp3_L3_gr_info grInfo[4]; + const ma_uint8* pTagData = pFirstFrameData; + ma_dr_mp3_bs_init(&bs, pFirstFrameData + MA_DR_MP3_HDR_SIZE, firstFrameInfo.frame_bytes - MA_DR_MP3_HDR_SIZE); + if (MA_DR_MP3_HDR_IS_CRC(pFirstFrameData)) { + ma_dr_mp3_bs_get_bits(&bs, 16); + } + if (ma_dr_mp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { + ma_bool32 isXing = MA_FALSE; + ma_bool32 isInfo = MA_FALSE; + const ma_uint8* pTagDataBeg; + pTagDataBeg = pFirstFrameData + MA_DR_MP3_HDR_SIZE + (bs.pos/8); + pTagData = pTagDataBeg; + isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g'); + isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o'); + if (isXing || isInfo) { + ma_uint32 bytes = 0; + ma_uint32 flags = pTagData[7]; + pTagData += 8; + if (flags & 0x01) { + detectedMP3FrameCount = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3]; + pTagData += 4; + } + if (flags & 0x02) { + bytes = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3]; + (void)bytes; + pTagData += 4; + } + if (flags & 0x04) { + pTagData += 100; + } + if (flags & 0x08) { + pTagData += 4; + } + if (pTagData[0]) { + pTagData += 21; + if (pTagData - pFirstFrameData + 14 < firstFrameInfo.frame_bytes) { + int delayInPCMFrames; + int paddingInPCMFrames; + delayInPCMFrames = (( (ma_uint32)pTagData[0] << 4) | ((ma_uint32)pTagData[1] >> 4)) + (528 + 1); + paddingInPCMFrames = ((((ma_uint32)pTagData[1] & 0xF) << 8) | ((ma_uint32)pTagData[2] )) - (528 + 1); + if (paddingInPCMFrames < 0) { + paddingInPCMFrames = 0; + } + pMP3->delayInPCMFrames = (ma_uint32)delayInPCMFrames; + pMP3->paddingInPCMFrames = (ma_uint32)paddingInPCMFrames; + } + } + if (isXing) { + pMP3->isVBR = MA_TRUE; + } else if (isInfo) { + pMP3->isCBR = MA_TRUE; + } + if (onMeta != NULL) { + ma_dr_mp3_metadata_type metadataType = isXing ? MA_DR_MP3_METADATA_TYPE_XING : MA_DR_MP3_METADATA_TYPE_VBRI; + size_t tagDataSize; + tagDataSize = (size_t)firstFrameInfo.frame_bytes; + tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData); + ma_dr_mp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize); + } + pMP3->pcmFramesRemainingInMP3Frame = 0; + pMP3->streamStartOffset += (ma_uint32)(firstFrameInfo.frame_bytes); + pMP3->streamCursor = pMP3->streamStartOffset; + ma_dr_mp3dec_init(&pMP3->decoder); + } + } else { + } + } + #endif + } else { ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); return MA_FALSE; } + if (detectedMP3FrameCount != 0xFFFFFFFF) { + pMP3->totalPCMFrameCount = detectedMP3FrameCount * firstFramePCMFrameCount; + } pMP3->channels = pMP3->mp3FrameChannels; pMP3->sampleRate = pMP3->mp3FrameSampleRate; return MA_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { return MA_FALSE; } MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); + return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks); } static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { @@ -92716,29 +94792,40 @@ static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_ static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) { ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + ma_int64 newCursor; MA_DR_MP3_ASSERT(pMP3 != NULL); - if (origin == ma_dr_mp3_seek_origin_current) { - if (byteOffset > 0) { - if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { - byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); - } - } else { - if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { - byteOffset = -(int)pMP3->memory.currentReadPos; - } - } - pMP3->memory.currentReadPos += byteOffset; + newCursor = pMP3->memory.currentReadPos; + if (origin == MA_DR_MP3_SEEK_SET) { + newCursor = 0; + } else if (origin == MA_DR_MP3_SEEK_CUR) { + newCursor = (ma_int64)pMP3->memory.currentReadPos; + } else if (origin == MA_DR_MP3_SEEK_END) { + newCursor = (ma_int64)pMP3->memory.dataSize; } else { - if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { - pMP3->memory.currentReadPos = byteOffset; - } else { - pMP3->memory.currentReadPos = pMP3->memory.dataSize; - } + MA_DR_MP3_ASSERT(!"Invalid seek origin"); + return MA_FALSE; } + newCursor += byteOffset; + if (newCursor < 0) { + return MA_FALSE; + } + if ((size_t)newCursor > pMP3->memory.dataSize) { + return MA_FALSE; + } + pMP3->memory.currentReadPos = (size_t)newCursor; return MA_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_mp3__on_tell_memory(void* pUserData, ma_int64* pCursor) { + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pCursor != NULL); + *pCursor = (ma_int64)pMP3->memory.currentReadPos; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; if (pMP3 == NULL) { return MA_FALSE; } @@ -92749,7 +94836,21 @@ MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_ pMP3->memory.pData = (const ma_uint8*)pData; pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; - return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); + result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, ma_dr_mp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks); + if (result == MA_FALSE) { + return MA_FALSE; + } + if (pMP3->streamLength <= (ma_uint64)MA_SIZE_MAX) { + pMP3->memory.dataSize = (size_t)pMP3->streamLength; + } + if (pMP3->streamStartOffset > (ma_uint64)MA_SIZE_MAX) { + return MA_FALSE; + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_mp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks); } #ifndef MA_DR_MP3_NO_STDIO #include @@ -92760,36 +94861,76 @@ static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t } static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + int whence = SEEK_SET; + if (origin == MA_DR_MP3_SEEK_CUR) { + whence = SEEK_CUR; + } else if (origin == MA_DR_MP3_SEEK_END) { + whence = SEEK_END; + } + return fseek((FILE*)pUserData, offset, whence) == 0; } -MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_mp3__on_tell_stdio(void* pUserData, ma_int64* pCursor) +{ + FILE* pFileStdio = (FILE*)pUserData; + ma_int64 result; + MA_DR_MP3_ASSERT(pFileStdio != NULL); + MA_DR_MP3_ASSERT(pCursor != NULL); +#if defined(_WIN32) && !defined(NXDK) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64(pFileStdio); + #else + result = ftell(pFileStdio); + #endif +#else + result = ftell(pFileStdio); +#endif + *pCursor = result; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) { ma_bool32 result; FILE* pFile; + if (pMP3 == NULL) { + return MA_FALSE; + } + MA_DR_MP3_ZERO_OBJECT(pMP3); if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { return MA_FALSE; } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); if (result != MA_TRUE) { fclose(pFile); return result; } return MA_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) { ma_bool32 result; FILE* pFile; + if (pMP3 == NULL) { + return MA_FALSE; + } + MA_DR_MP3_ZERO_OBJECT(pMP3); if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return MA_FALSE; } - result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); if (result != MA_TRUE) { fclose(pFile); return result; } return MA_TRUE; } +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_mp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_mp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); +} #endif MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) { @@ -92859,17 +95000,38 @@ static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 frames MA_DR_MP3_ASSERT(pMP3 != NULL); MA_DR_MP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { - ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + ma_uint32 framesToConsume; + if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) { + ma_uint32 framesToSkip = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame); + pMP3->currentPCMFrame += framesToSkip; + pMP3->pcmFramesConsumedInMP3Frame += framesToSkip; + pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip; + } + framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) { + if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { + ma_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame; + if (framesToConsume > framesRemainigToPadding) { + framesToConsume = (ma_uint32)framesRemainigToPadding; + } + } else { + break; + } + } if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - #else - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); - #endif + #if defined(MA_DR_MP3_FLOAT_OUTPUT) + { + float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + } + #else + { + ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); + ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); + } + #endif } pMP3->currentPCMFrame += framesToConsume; pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; @@ -92879,6 +95041,9 @@ static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 frames if (framesToRead == 0) { break; } + if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { + break; + } MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { break; @@ -92958,7 +95123,7 @@ static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) { MA_DR_MP3_ASSERT(pMP3 != NULL); MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { + if (!ma_dr_mp3__on_seek_64(pMP3, pMP3->streamStartOffset, MA_DR_MP3_SEEK_SET)) { return MA_FALSE; } ma_dr_mp3_reset(pMP3); @@ -93024,7 +95189,7 @@ static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uin seekPoint.mp3FramesToDiscard = 0; seekPoint.pcmFramesToDiscard = 0; } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { + if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, MA_DR_MP3_SEEK_SET)) { return MA_FALSE; } ma_dr_mp3_reset(pMP3); @@ -93035,7 +95200,7 @@ static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uin if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); + pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL); if (pcmFramesRead == 0) { return MA_FALSE; } @@ -93077,7 +95242,7 @@ MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint6 totalMP3FrameCount = 0; for (;;) { ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } @@ -93101,10 +95266,26 @@ MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint6 MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) { ma_uint64 totalPCMFrameCount; - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + if (pMP3 == NULL) { return 0; } - return totalPCMFrameCount; + if (pMP3->totalPCMFrameCount != MA_UINT64_MAX) { + totalPCMFrameCount = pMP3->totalPCMFrameCount; + if (totalPCMFrameCount >= pMP3->delayInPCMFrames) { + totalPCMFrameCount -= pMP3->delayInPCMFrames; + } else { + } + if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) { + totalPCMFrameCount -= pMP3->paddingInPCMFrames; + } else { + } + return totalPCMFrameCount; + } else { + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + return 0; + } + return totalPCMFrameCount; + } } MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) { @@ -93174,7 +95355,7 @@ MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSe MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { return MA_FALSE; } @@ -93198,7 +95379,7 @@ MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSe } mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; @@ -93336,18 +95517,18 @@ static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_c } return pFrames; } -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); From f36c8ddc56fa177b055e0fef44fe531fe59535d1 Mon Sep 17 00:00:00 2001 From: iann Date: Mon, 29 Sep 2025 02:34:38 +0900 Subject: [PATCH 02/31] [rlgl] Allow tint colors in GL_LINE (wires) and GL_POINT (points) draw modes on OpenGL 1.1 clean (#5207) * gate with OPENGL_11 prototype and definition for rlSetPointSize and rlGetPointSize * gate with OPENGL_11 prototype and definition for rlSetPointSize and rlGetPointSize * more coverage for fixing expected texcoords, normals, and colors * fix guard styling * fix guard issue, and remove fallback unneccessary else clause (default shader ill work) * opengl3.3 and es2 need the color array allocated in order to allow for updates later (unlike opengl11) --- src/rlgl.h | 29 +++++++++++++++++++++++++---- src/rmodels.c | 44 +++++++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 7db05251d..2a1a5d5f8 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -644,10 +644,8 @@ RLAPI void rlEnableVertexBufferElement(unsigned int id); // Enable vertex buffer RLAPI void rlDisableVertexBufferElement(void); // Disable vertex buffer element (VBO element) RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index RLAPI void rlDisableVertexAttribute(unsigned int index); // Disable vertex attribute index -#if defined(GRAPHICS_API_OPENGL_11) RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer -#endif // Textures state RLAPI void rlActiveTextureSlot(int slot); // Select and active a texture slot @@ -686,6 +684,8 @@ RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnablePointMode(void); // Enable point mode RLAPI void rlDisablePointMode(void); // Disable point mode +RLAPI void rlSetPointSize(float size); // Set the point drawing size +RLAPI float rlGetPointSize(void); // Get the point drawing size RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlDisableWireMode(void); // Disable wire mode RLAPI void rlSetLineWidth(float width); // Set the line drawing width @@ -2016,6 +2016,25 @@ float rlGetLineWidth(void) return width; } +// Set the point drawing size +void rlSetPointSize(float size) +{ +#if defined(GRAPHICS_API_OPENGL_11) + glPointSize(size); +#endif +} + +// Get the point drawing size +float rlGetPointSize(void) +{ + float size = 1; +#if defined(GRAPHICS_API_OPENGL_11) + glGetFloatv(GL_POINT_SIZE, &size); +#endif + return size; + +} + // Enable line aliasing void rlEnableSmoothLines(void) { @@ -4003,10 +4022,10 @@ void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffe #endif } -#if defined(GRAPHICS_API_OPENGL_11) // Enable vertex state pointer void rlEnableStatePointer(int vertexAttribType, void *buffer) { +#if defined(GRAPHICS_API_OPENGL_11) if (buffer != NULL) glEnableClientState(vertexAttribType); switch (vertexAttribType) { @@ -4017,14 +4036,16 @@ void rlEnableStatePointer(int vertexAttribType, void *buffer) //case GL_INDEX_ARRAY: if (buffer != NULL) glIndexPointer(GL_SHORT, 0, buffer); break; // Indexed colors default: break; } +#endif } // Disable vertex state pointer void rlDisableStatePointer(int vertexAttribType) { +#if defined(GRAPHICS_API_OPENGL_11) glDisableClientState(vertexAttribType); -} #endif +} // Load vertex array object (VAO) unsigned int rlLoadVertexArray(void) diff --git a/src/rmodels.c b/src/rmodels.c index 9aca37a24..01e85475c 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1293,10 +1293,19 @@ void UploadMesh(Mesh *mesh, bool dynamic) rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION); // Enable vertex attributes: texcoords (shader-location = 1) - mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic); - rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0); - rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + if (mesh->texcoords != NULL) + { + mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic); + rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + } + else + { + float value[2] = { 0.0f, 0.0f }; + rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, value, SHADER_ATTRIB_VEC2, 2); + rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD); + } // WARNING: When setting default vertex attribute values, the values for each generic vertex attribute // is part of current state, and it is maintained even if a different program object is used @@ -1431,12 +1440,12 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); - rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); + if (mesh.texcoords) rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); if (mesh.animNormals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals); - else rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + else if (mesh.normals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); - rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); + if (mesh.colors) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); rlPushMatrix(); rlMultMatrixf(MatrixToFloat(transform)); @@ -3836,6 +3845,7 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float // Draw a model points // WARNING: OpenGL ES 2.0 does not support point mode drawing +// TODO: gate these properly for non es 2.0 versions only void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) { rlEnablePointMode(); @@ -4420,7 +4430,11 @@ static Model LoadOBJ(const char *fileName) model.meshes[i].vertices = (float *)MemAlloc(sizeof(float)*vertexCount*3); model.meshes[i].normals = (float *)MemAlloc(sizeof(float)*vertexCount*3); model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4); + #else + model.meshes[i].colors = NULL; + #endif } MemFree(localMeshVertexCounts); @@ -4474,7 +4488,17 @@ static Model LoadOBJ(const char *fileName) for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i]; - for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; + if (objAttributes.texcoords != NULL && texcordIndex != TINYOBJ_INVALID_INDEX && texcordIndex >= 0) + { + for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; + } + else + { + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 0] = 0.0f; + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 0.0f; + } + if (objAttributes.normals != NULL && normalIndex != TINYOBJ_INVALID_INDEX && normalIndex >= 0) { for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i]; @@ -4485,11 +4509,9 @@ static Model LoadOBJ(const char *fileName) model.meshes[meshIndex].normals[localMeshVertexCount*3 + 1] = 1.0f; model.meshes[meshIndex].normals[localMeshVertexCount*3 + 2] = 0.0f; } - - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) for (int i = 0; i < 4; i++) model.meshes[meshIndex].colors[localMeshVertexCount*4 + i] = 255; - + #endif faceVertIndex++; localMeshVertexCount++; } From 4767841b7119396f129eceeab0c7e90e8198e48d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Sep 2025 19:45:52 +0200 Subject: [PATCH 03/31] Code format review --- src/rmodels.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 01e85475c..c54ef03fa 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4488,7 +4488,7 @@ static Model LoadOBJ(const char *fileName) for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i]; - if (objAttributes.texcoords != NULL && texcordIndex != TINYOBJ_INVALID_INDEX && texcordIndex >= 0) + if ((objAttributes.texcoords != NULL) && (texcordIndex != TINYOBJ_INVALID_INDEX) && (texcordIndex >= 0)) { for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; @@ -4499,7 +4499,7 @@ static Model LoadOBJ(const char *fileName) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 0.0f; } - if (objAttributes.normals != NULL && normalIndex != TINYOBJ_INVALID_INDEX && normalIndex >= 0) + if ((objAttributes.normals != NULL) && (normalIndex != TINYOBJ_INVALID_INDEX) && (normalIndex >= 0)) { for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i]; } From a2a22e5e4873111d45c0b9a09d482407d22a89ce Mon Sep 17 00:00:00 2001 From: Jett <30197659+JettMonstersGoBoom@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:02:53 -0400 Subject: [PATCH 04/31] ADDED: example: `core_input_actions` (#5211) * adding core_input_actions action based input API vs direct input, allows remapping of buttons or keys to an action. * adjusted formatting. * updates for struct name and fixed a typo --- examples/Makefile | 1 + examples/core/core_input_actions.c | 174 +++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 examples/core/core_input_actions.c diff --git a/examples/Makefile b/examples/Makefile index ee17bb350..6d409b511 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -524,6 +524,7 @@ CORE = \ core/core_input_mouse_wheel \ core/core_input_multitouch \ core/core_input_virtual_controls \ + core/core_input_actions \ core/core_random_sequence \ core/core_random_values \ core/core_render_texture \ diff --git a/examples/core/core_input_actions.c b/examples/core/core_input_actions.c new file mode 100644 index 000000000..b8266a8f4 --- /dev/null +++ b/examples/core/core_input_actions.c @@ -0,0 +1,174 @@ + +/******************************************************************************************* +* +* raylib [core_inputactionInputs] example - presents a simple API for remapping input to actions +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by MonstersGoBoom and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 MonstersGoBoom +* +********************************************************************************************/ + +/* + Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons. + for example instead of + IsKeyDown(KEY_LEFT) + you'd use + IsActionDown(ACTION_LEFT) + which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys + */ + + +#include +#include +#include +#include "raylib.h" + +// add your own action types here + +typedef enum ActionType +{ + NO_ACTION, + ACTION_UP, + ACTION_DOWN, + ACTION_LEFT, + ACTION_RIGHT, + ACTION_FIRE, + MAX_ACTION +} ActionType; + +// struct for key and button inputs +typedef struct ActionInput +{ + int key; + int button; +} ActionInput; + +// gamepad index, change this if you have multiple gamepads. +int gamepadIndex = 0; +static ActionInput actionInputs[MAX_ACTION] = {0}; + +// combines IsKeyPressed and IsGameButtonPressed to one action +bool isActionPressed(int action) +{ + if (action Date: Sun, 28 Sep 2025 23:31:07 +0200 Subject: [PATCH 05/31] REVIEWED: New example format and requirements: `core_input_actions` #5211 --- examples/Makefile | 2 +- examples/Makefile.Web | 4 + examples/README.md | 5 +- examples/core/core_input_actions.c | 243 ++++---- examples/core/core_input_actions.png | Bin 0 -> 15477 bytes examples/examples_list.txt | 1 + .../examples/core_input_actions.vcxproj | 569 ++++++++++++++++++ projects/VS2022/raylib.sln | 27 + 8 files changed, 738 insertions(+), 113 deletions(-) create mode 100644 examples/core/core_input_actions.png create mode 100644 projects/VS2022/examples/core_input_actions.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 6d409b511..8fb92ac2b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -516,6 +516,7 @@ CORE = \ core/core_custom_logging \ core/core_drop_files \ core/core_high_dpi \ + core/core_input_actions \ core/core_input_gamepad \ core/core_input_gestures \ core/core_input_gestures_testbed \ @@ -524,7 +525,6 @@ CORE = \ core/core_input_mouse_wheel \ core/core_input_multitouch \ core/core_input_virtual_controls \ - core/core_input_actions \ core/core_random_sequence \ core/core_random_values \ core/core_render_texture \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index a16d57853..87c2866c6 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -516,6 +516,7 @@ CORE = \ core/core_custom_logging \ core/core_drop_files \ core/core_high_dpi \ + core/core_input_actions \ core/core_input_gamepad \ core/core_input_gestures \ core/core_input_gestures_testbed \ @@ -732,6 +733,9 @@ core/core_drop_files: core/core_drop_files.c core/core_high_dpi: core/core_high_dpi.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_input_actions: core/core_input_actions.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_input_gamepad: core/core_input_gamepad.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file core/resources/ps3.png@resources/ps3.png \ diff --git a/examples/README.md b/examples/README.md index 1753a31bc..d7f56d8d4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,9 +17,9 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 163] +## EXAMPLES COLLECTION [TOTAL: 164] -### category: core [37] +### category: core [38] Examples using raylib[core](../src/rcore.c) platform functionality like window creation, inputs, drawing modes and system functionality. @@ -62,6 +62,7 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c | [core_high_dpi](core/core_high_dpi.c) | core_high_dpi | ⭐⭐☆☆ | 5.0 | 5.5 | [Jonathan Marler](https://github.com/marler8997) | | [core_render_texture](core/core_render_texture.c) | core_render_texture | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Ramon Santamaria](https://github.com/raysan5) | | [core_undo_redo](core/core_undo_redo.c) | core_undo_redo | ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_input_actions](core/core_input_actions.c) | core_input_actions | ⭐⭐☆☆ | 5.5 | 5.6 | [Jett](https://github.com/JettMonstersGoBoom) | ### category: shapes [20] diff --git a/examples/core/core_input_actions.c b/examples/core/core_input_actions.c index b8266a8f4..aff356087 100644 --- a/examples/core/core_input_actions.c +++ b/examples/core/core_input_actions.c @@ -1,41 +1,31 @@ - /******************************************************************************************* * -* raylib [core_inputactionInputs] example - presents a simple API for remapping input to actions +* raylib [core] example - input actions * -* Example complexity rating: [★☆☆☆] 1/4 +* Example complexity rating: [★★☆☆] 2/4 * * Example originally created with raylib 5.5, last time updated with raylib 5.6 * -* Example contributed by MonstersGoBoom and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Jett (@JettMonstersGoBoom) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2025 MonstersGoBoom +* Copyright (c) 2025 Jett (@JettMonstersGoBoom) * ********************************************************************************************/ -/* - Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons. - for example instead of - IsKeyDown(KEY_LEFT) - you'd use - IsActionDown(ACTION_LEFT) - which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys - */ +// Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons +// For example instead of using `IsKeyDown(KEY_LEFT)`, you can use `IsActionDown(ACTION_LEFT)` +// which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys - -#include -#include -#include #include "raylib.h" -// add your own action types here - -typedef enum ActionType -{ - NO_ACTION, +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum ActionType { + NO_ACTION = 0, ACTION_UP, ACTION_DOWN, ACTION_LEFT, @@ -44,42 +34,137 @@ typedef enum ActionType MAX_ACTION } ActionType; -// struct for key and button inputs -typedef struct ActionInput -{ +// Key and button inputs +typedef struct ActionInput { int key; int button; } ActionInput; -// gamepad index, change this if you have multiple gamepads. -int gamepadIndex = 0; -static ActionInput actionInputs[MAX_ACTION] = {0}; +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static int gamepadIndex = 0; // Gamepad default index +static ActionInput actionInputs[MAX_ACTION] = { 0 }; -// combines IsKeyPressed and IsGameButtonPressed to one action -bool isActionPressed(int action) +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +static bool IsActionPressed(int action); // Check action key/button pressed +static bool IsActionReleased(int action); // Check action key/button released +static bool IsActionDown(int action); // Check action key/button down + +static void SetActionsDefault(void); // Set the "default" keyset +static void SetActionsCursor(void); // Set the "alternate" keyset + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) { - if (actionGfQUFriv`U>e1VacN@3H##CB#u%cv2~yIG8ILhD?f1{@buYV` zzha?fw>*9zd!>9@qknjByKIJEmkqU*ZF$ba7H?k1s2H4o{B6U3+2&@)fxX_QJa_3!~IgDQjV;V(D|&bd8tIRpZjeh z)*WcxtH>vOlOf0BiaXbr`s0vnwqccQc19sXM25zP*~F#ghh{Z&WOdi&el>`D_}&G~z8} zT@X5iH%x=3^&%B z92k6Tk>A*rEffb#$>hLBaeX4WFO6q?HTTftqrTRpNYhhK@yF$OUK`r@O3mYt&=-1z z10e-JIhHsn#*Z^PPuz|Ny(kmka!_gCCE$8R{L2?=_q=uox zY%5(FuFYu7xg^8#Zo9BLqYGp4*gXDHGMhbUdC^6rb`1~dk05K>Q=K`y=U)O zR=+y*h1ux=zTZG@+L?`bheNO8rJD&ITNf8aUC3O*#cvk{C#nO_H&@1N7x6C05n6&{ zF2_OZ>(}Bp9$ryPHIIHfuJW}HrP67av`iX5+m-R+>u3^<=gN*XOu4g_?0-MIrEOmG z^JgrFqm25@zqu4duiv$)LIq_fp}4&mZ+$JuV-`9oz{6d#;fn(NucbyeYWAfv>N~~z zHors6KcK3^cQus%?qet*s18rN!Dq#qIM2yt#}@U{4%@}_RPmEJ_p`>~I?7rry01|6 z7Z>#(%&aQCJ~rdBO_c>T7G0 zCt@sdE*jHA#dVUrdHuqKN8GzsNmvV?NKt2q8Ya<0d8_nVs~x$4KokY9zIMD!m6y3lF=^C;aL7O1bU8e<^~zgh?dJ2rTjYP= z{{RH|{};dlU}4ytfEIujfELts6}Y3S%LaS__yX_+;0q&r?!Xs-F92TvzVK=Kg1u;s zMuWFqK&yLR6z_ab<{6Xf)vfNN5yvY#Y3t^JcJOdEzuyo0B-+7o@MJd7N?S)-IoBO+ zq^mmDHZ=5u`e5(J2cLu6R&d)L%tpk%8mWWq1VU|PFdO+4W+NY+4ECbE&PdB^7gXMf6J;vUo(_^Mm;Vk|Vd)IA3J1SU}c0$l9?3xI{;CwhPufEIuj)DH## zcT_za1AGDa0`LXk3nL%y1HJ%!0r&#&1>g(AkFNqP04)G5z*P~j09erVI{ + + + + Debug.DLL + ARM64 + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + ARM64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {6B1A933E-71B8-4C1F-9E79-02D98830E671} + Win32Proj + core_input_actions + 10.0 + core_input_actions + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;shcore.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index fe2241434..faca55fa0 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -345,6 +345,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_inline_styling", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_undo_redo", "examples\core_undo_redo.vcxproj", "{3B27F358-2679-4F38-B297-17B536F580BB}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_actions", "examples\core_input_actions.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|ARM64 = Debug.DLL|ARM64 @@ -4247,6 +4249,30 @@ Global {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.Build.0 = Release|x64 {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.ActiveCfg = Release|Win32 {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.Build.0 = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 + {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4421,6 +4447,7 @@ Global {49C67F03-1A56-4F96-B278-39B66EC93678} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {D496308F-3C3C-40B3-A3ED-EA327D244B3E} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From c551fb7118268193414f427da2b478cf2141fb2c Mon Sep 17 00:00:00 2001 From: Tim Little Date: Sun, 28 Sep 2025 22:40:09 +0100 Subject: [PATCH 06/31] Add models_basic_voxel example (#5212) --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/README.md | 3 +- examples/examples_list.txt | 1 + examples/models/models_basic_voxel.c | 152 +++++++++++++++++++++++++ examples/models/models_basic_voxel.png | Bin 0 -> 349583 bytes 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 examples/models/models_basic_voxel.c create mode 100644 examples/models/models_basic_voxel.png diff --git a/examples/Makefile b/examples/Makefile index 8fb92ac2b..36faed3b3 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -607,6 +607,7 @@ TEXT = \ MODELS = \ models/models_animation_gpu_skinning \ models/models_animation_playing \ + models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ models/models_box_collisions \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 87c2866c6..b1b1f0779 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -607,6 +607,7 @@ TEXT = \ MODELS = \ models/models_animation_gpu_skinning \ models/models_animation_playing \ + models/models_basic_voxel \ models/models_billboard_rendering \ models/models_bone_socket \ models/models_box_collisions \ @@ -1049,6 +1050,9 @@ models/models_animation_playing: models/models_animation_playing.c --preload-file models/resources/models/iqm/guytex.png@resources/models/iqm/guytex.png \ --preload-file models/resources/models/iqm/guyanim.iqm@resources/models/iqm/guyanim.iqm +models/models_basic_voxel: models/models_basic_voxel.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + models/models_billboard_rendering: models/models_billboard_rendering.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/billboard.png@resources/billboard.png diff --git a/examples/README.md b/examples/README.md index d7f56d8d4..48e83c852 100644 --- a/examples/README.md +++ b/examples/README.md @@ -145,7 +145,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | | [text_inline_styling](text/text_inline_styling.c) | text_inline_styling | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Wagner Barongello](https://github.com/SultansOfCode) | -### category: models [23] +### category: models [24] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. @@ -174,6 +174,7 @@ Examples using raylib models functionality, including models loading/generation | [models_animation_gpu_skinning](models/models_animation_gpu_skinning.c) | models_animation_gpu_skinning | ⭐⭐⭐☆ | 4.5 | 4.5 | [Daniel Holden](https://github.com/orangeduck) | | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | +| [models_basic_voxel](models/models_basic_voxel.c) | models_basic_voxel | ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) | ### category: shaders [29] diff --git a/examples/examples_list.txt b/examples/examples_list.txt index adaa12390..b5682123f 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -128,6 +128,7 @@ models;models_textured_cube;★★☆☆;4.5;4.5;2022;2025;"Ramon Santamaria";@r models;models_animation_gpu_skinning;★★★☆;4.5;4.5;2024;2025;"Daniel Holden";@orangeduck models;models_bone_socket;★★★★;4.5;4.5;2024;2025;"iP";@ipzaur models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van der Valk";@arceryz +models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 shaders;shaders_shapes_textures;★★☆☆;1.7;3.7;2015;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c new file mode 100644 index 000000000..8df15d73d --- /dev/null +++ b/examples/models/models_basic_voxel.c @@ -0,0 +1,152 @@ +/******************************************************************************************* +* +* raylib [models] example - basic voxel +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example contributed by Tim Little (@timlittle) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Tim Little (@timlittle) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +#define WORLD_SIZE 8 // Size of our voxel world (8x8x8 cubes) + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel"); + + DisableCursor(); // Lock mouse to window center + + // Define the camera to look into our 3d world (first person) + Camera3D camera = { 0 }; + camera.position = (Vector3){ -2.0f, 0.0f, -2.0f }; // Camera position at ground level + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Create a cube model + Mesh cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f); // Create a unit cube mesh + Model cubeModel = LoadModelFromMesh(cubeMesh); // Convert mesh to a model + cubeModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = BEIGE; + + // Initialize voxel world - fill with voxels + bool voxels[WORLD_SIZE][WORLD_SIZE][WORLD_SIZE] = { false }; + for (int x = 0; x < WORLD_SIZE; x++) + { + for (int y = 0; y < WORLD_SIZE; y++) + { + for (int z = 0; z < WORLD_SIZE; z++) + { + voxels[x][y][z] = true; + } + } + } + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_FIRST_PERSON); + + // Handle voxel removal with mouse click + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + // Cast a ray from the screen center (where crosshair would be) + Vector2 screenCenter = { screenWidth/2.0f, screenHeight/2.0f }; + Ray ray = GetMouseRay(screenCenter, camera); + + // Check ray collision with all voxels + bool voxelRemoved = false; + for (int x = 0; (x < WORLD_SIZE) && !voxelRemoved; x++) + { + for (int y = 0; (y < WORLD_SIZE) && !voxelRemoved; y++) + { + for (int z = 0; (z < WORLD_SIZE) && !voxelRemoved; z++) + { + if (!voxels[x][y][z]) continue; // Skip empty voxels + + // Build a bounding box for this voxel + Vector3 position = { x, y, z }; + BoundingBox box = { + (Vector3){ position.x - 0.5f, position.y - 0.5f, position.z - 0.5f }, + (Vector3){ position.x + 0.5f, position.y + 0.5f, position.z + 0.5f } + }; + + // Check ray-box collision + RayCollision collision = GetRayCollisionBox(ray, box); + if (collision.hit) + { + voxels[x][y][z] = false; // Remove this voxel + voxelRemoved = true; // Exit all loops + } + } + } + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawGrid(10, 1.0f); + + // Draw all voxels + for (int x = 0; x < WORLD_SIZE; x++) + { + for (int y = 0; y < WORLD_SIZE; y++) + { + for (int z = 0; z < WORLD_SIZE; z++) + { + if (!voxels[x][y][z]) continue; + + Vector3 position = { x, y, z }; + DrawModel(cubeModel, position, 1.0f, BEIGE); + DrawCubeWires(position, 1.0f, 1.0f, 1.0f, BLACK); + } + } + } + + EndMode3D(); + + DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY); + DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(cubeModel); + CloseWindow(); + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/models_basic_voxel.png b/examples/models/models_basic_voxel.png new file mode 100644 index 0000000000000000000000000000000000000000..8bbcaf8dae98aa01624392f07822e36f2f44a53d GIT binary patch literal 349583 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

D)bQt8=^%55 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¯=oYv~c#@#(mswRYT? z8*yd!3a*}7=oHHmqAZWz59zLYviI%V%I%m@hop9Wz(ZCsBtL-RRYU;xKK$mk3%x8Y zpKbkWX+$rF)9IrFMIyewHb@;yFThm#>+&BIRd>X;e=PivJ(?l>@XGLv zM$)nPVnZ*_+{1&r3l+Tr76VAlLk^NnLTlZRtW6YG!~EnhLc(j9LRv2X_ehVMrv8;|v#C!Iav%I%F!&Tyja@Bi^C z0Xu(@*MjNqUVa>4c7^N78c-~z;!pF1ccQ(3?-jph>MfK8Ql9#Qi2NGWwOWNC3E0in z{yvY$J=Jylq9v}!TI_gsVkqCquHyj5j-S1hiKSe2Qq8wD_dxU|M3(7@WI#iC*Sbm_ z)O-Cv6jYaH*9|8;nqX4bw@^r|TONNK2&lJsznS;$=|nm!&*X+FExpwIoOJgs$EZT3 z+P?B)dFZs)1Cl;Td!>U=xc|t;ub=sy-fHIafq1vI&>GBlyE&hga7~aUu5Ub$3c9T@ z_Bu*aUm&PP4~Rd5!O>%{hPpIkaapTm!1sr~d`5K>kSX?45mNY3g!X}0(x#>DzPq0C zzb}z9U}P1<4=^BXoo0?z`I>JmWdZ2#c*5X5@332V)Bz2=V4L?U2f4i2Y^)IPO^gLx z+ScB?j$O~i?zeql(@g&oazdfxHS@E+49LZM<-{P|tFUsuyU@d;W~BEH#=4`dvs2Hu zes%n|Oikrr1@&r)?93r=-Rx9V@scTZPJ_=?-3mYq(XL@QH*W&Z*u{W+g5(^qqTpU$6Tlgc&T(FE?|<-GYM6SDn?&J!BEwG`(1AVdr5w>0A8@MsQuxc2`_( z{q*=q|H=liF3J&#Ervsv-f_den4``HBpqO^)l0PniH`&Bb!x{mKtHX??yY}mTN!gI zp`;p%y?ux1g+3N_&k1Ct4>NonE&wp zaXPkJ|AAKHmILJMNrlF*Us}FQGZGBj-3DU)np!T~!pnuF)3JhdTRu(CTXx5@bJ(FFWOE873m&&$gdSzw#yH z_djN@=-0h$KKaK!Mg4Sq<%SzO0;1&5>}Nu1{8Mi%htZc{MiR~tibkJ$t@$kMvDK6W zDjSYcjLviww|r$@!2{8Zgalv00w(QlzT_E;XF?n3s(8A;0&n`m`sb%HUJ@ANhn_O> zY7d0L9_7u&a-`SL0-PUv(rlfgAa`G|GHz-`spgKCW8?<7n8tQW$bV&8n~3?8#8ph5G? zz4`L}sy;0X_X@!9nAj@*Y0U!bzHMMrI0-HgbU;>GH8qHU5jMDf-l*uOb_ei1T3-iQ zCb1%Y$5qLro<`%+YhedWBmQ|$G>)&8e4YZ9WS$a>Yy0nX^joz=;#4L8NCUAed!T05 zX)DIM;g6;U0Pquc?1uQ9-T7U2Oe*%5`a^n2U7AqQ1ykk~7spfP<|?Uq$d7?WJ4z0R z_UOO^qFt>oL=^N#Wyn19iVb@w7v+C;Qz^I5TjVnZ7>VHWYdJsu_ONm;pPV{0l25p~_#)%R4-92j?a>&2%CL{xZP@ywNRxq8K8zV<(dtyyVlm}(W^&Fq zM~G}M{GdS>r9{j$s-ZWftXDX_p7dPm@)aM^h+gHkmeY?ZW(p{PHbqT{ep;68l z(l=GgR0(H+-ES8HC2*nqz~`HSq^~AFzEn9rUnDN7dd`X4_h+`HQ<34<__qhVt@4o= zd`T^1B8EGkRJ<1Jh`B=nmt4bYosBV>FB)eTo%>Z0P~_D`{{bQU(d}9zEXLZq^y;f7 zzR`gMB-uZW{<}K=U^1}IpPmW&(l7yxaGFh@YlhYWOyANWlD9 z#h5#G+75^eHoz0A{SR?Si*>kKOUIlm(sMrNPeBS-8QAGLd&<=}aqo=tVkz&qkSJH@ zY=S6FbHZh;^6K^H^ew&LCEEk<=^YubRLi=Wi?yfO#i6Kam+iAN;Q^tUkbL9o?=Ve{ zz3h#1?S(|cZhUpi{v~!GQ+Uu|Tgjee zxg%w+;na3e$CTGbO?a{!Y4X|P0V%kcPX&voz-w&z6r?x*e-}6CQG?O06zb~ZuBsdg zt2e_~79?4mSO=9D=f_Sv1&h-?kQbii05CFRX(bEWV>pS$%C1>gE@H45lQRBdwmb+0T*;s~_eC6P3o5hGdED^|2D7ot)68IG+L5+V1^ZfrZGn z&@`Tbgh=K*P;-8)VCncr){T6d<9htl>w#hi_Yn_$QzAQ)oMoZrxGP=r%YDf_!Y%sf zLTmp?s#+mvq~BCsyrA7Wx1uE5izuMfbwqq4M$;&XZVdYh-t^p5QPA{C4rcb!=VLJp77{;{|!~%KA(5bxgjl! z#@}?dm-r!iUlpb%*!Nzf>XE2#z7aEozfcgT%!pSkW5nsv@7z3)v6kX!ny9 zvD4&noTAxr6g=7HjZl52wb+-McpG+}sC?iHgiN$zEg#bRhNq({;-* zhFnlj0JDwI{GCP_Aag=MoMvwatElByh{H?Yqw<1}*xq&aTJ$Fv=h51|Ydj5_-LqfR z#TsD*A1)7nSQ77oGj`?;mk@yDmM_qnTHmAI+1VSO+?;wgpL8kDo;G#p6LWJRf-K+T zP-E#Ko?fY4@aZkaR|?(m_3!{n`%EqM70cSSdh^HB;_odcKx-=I0>QXZb&E}{sndTe zHHdZoVZD^M-)ik8cn0~_o9KPOceHxEGKavncop`iY1#VdWiK}i`>JPsV&->v=BoM{ zFg7ZfT#9ReXs##I4gslrw_hWOt`h1pkB04o9TJRx4VpV^(ajuo?@j2uQFzV(04k$D z@GvU)TiU|6UY4aXUgG6hGyi5Trwk)FUUT(ho}Xo=eirs=LVN7-A`qKf^kyIYnL9xJ zdaVZZge`788E?;0rac0k8H|VREWep!qjc;>F~PV zqj{M=W5%5zxfH8p5szH*w>N7*9ayq*(IbakUl_0G&*gu1kOdHpA@%Jv9t8^tx6;Y$ z)|lNV^`FCvAFV_JgeKzgCGQm~=ifymuEV9%beu6^5W zPu9m1uGTu7f;L|_on-1!Us{2(YUeT0fYQ*pe&1Y75rN_b&|VB%WS_4UPJKeVbX%|a zjTAXmUlVZ{JlHp|eni_0oxe1MTw%VYiUupP8K6E>?lZma25IJd%Oe7I@0ZwY>aNAn zPJY9t$VaL+(%p};H#;F^e@)Kh2eI5RL|S3fImJ$GlJ^=r!n;HTSt`}QcFVI^Vo{q^ zyM`Ued3k%iIR|6Rv&=}8iJoWi`0IOzS5khO-T_yN^|{paVr{K5RrqOydB9ZO3vh#s zCW`V-k|*YIUmZ-OI8vT$;CPa6o&V%(qc*d1?SO{53?1Vsm;mOi>URyT8fO@!Go)+vcAQhrs8Zb*pTU)%)G#FI~M0`B*`A^%f<+@b4CkrWd?i z^9FmGG6ejK1W!nG{cANFiyQb)jk8ngvlrv{pwI-DI!*_Go`S{yE5wUMaIWXkBfziLu+Mm^({7 z6n}5PwY!lrz=|;_^QZTnP>M# zCm749|2cm4{;4KU-A%mjwIW;NANMBD!Gt^qa5>ZrWcfvu!CO+F_CKBaV%Y>t>!a>o zJ%0c?c}aZz4?^o2usc+w-J2TG&(lnSIO-6J6OBf~e|a)I+F`bsfW&|q#HWRKz?l< znH{+>YXVn@{X|Mn2rC~GR$r+_DU$@vsnItp z-Pd`HRFX#%48B?&cdLj=pakLNt)L+SOthgP7VKRZ^R^x8Y{Gq(nPjSWbteD*$v;%~ z0fgKMChGRG&pZWe*|70CdM`C9Uo!twARcKzqAj?2poQawTXB+9!)?gp{^w#0);y6_#B-;G_>Dj9l>f6B@ea>1Ez>e4Ms~fRt~A|tMp)QUn&c+s0=)YQJ~Sh%Kh`Ha9)>jTNSbBQC6cYO)h#9 zCqUxNGh$fj@}7Dh4&0y9%yc}RR63S)_lj0cHJh^4DBjV=PBKw= zZ52zpv#ch%_^Mf{o6A~oK9Du$d{^lGDss`|wPwW@PFGUb8~3ITCo=h*@p;(;r%|`h zFV44^!Ad!#n-#poLzcOWw zXP@S@^Jk;{la4(ut{#lyzX>paTm^tCWcKLeKh@faBFQ)3#aa{v zO`RkU|Dc&=112Bt=K499Yh7Vh=?9e1a#2+fJQ^TL&+w3Mvx0E<768x<|A=~ESspG+ zdgQn(UIOyq-9euO3n*kR6`+#$whdKb2cI3oo7va3k7`jG=qK$787`i@q`kJy@6v={ zP`VYBqZXzC{<|%@lU)!kC((V8M^D*%cB{75GoT;@7yL8}p%3>R_+u*p=3cWA_K$hW z=Mw1R<6}WwkGj3F*42q*$Dm~Ya|h227}@h_?IC(GrT)|W<|$vfDt2PKpRt>bkC-;W zXJKsn5)5Mu^RGPdgcRlg3qZOB6x`aeh6MT{UWdM~Sitgj`}+q5Kd=cH4+h9#l2ECw+_OON&?gOO#rONL;}!s*XLi1cWAEp&ozEFNebObcAR4HaMk)Ro*L z%Le(hknF?T-CiADgojF3*jt(7mMxR9eO}6ftveCd0It~*0{1#v4#r3ju~DhMiFX{K zAy9EDLfA>jJR?K93(p0Z6VJQ?qFQEe$=l;GG)!4Zfr+tVKgtTZEcnE-{7t3gEw>@u&tQkeWtFJhY)b|1?v2_d zo^vFh7FCvp?wm#Fv=3iC@A05}U<4XecjV1LD4V#HE6?wJ=2Gn6m-xcp6riLOaTvN5 zJvql5(euD|ZQ38EZ7MXO|FUW3nV$Z+CPZYFQQOePmJTf%Wy&WY#tPhYO3_0W#JJ$S z;Y(uXf`4zGlAu}Qjg?Q|lzWLPGzK68S|6S%o3N2CD=a1d^2SW=Sql~N2tLa%Vk9ol zyzw)9zSaaVslkFXKYWFLVr%?s2nk%Ry}cYs_FFGWdF$Zn&Q1CoH?Ea%=68)Tyw^@4 zRuMJQ8Kp6IcWyMr$AxL$<3$3@?0-dv1(l7Wf^910QSU~bIB*H{uNBK3H_)&%2j5`a z&`h)C_W*NAFlQlkrsgWD#K~~X<4xQ|qU1B}!!oz?`VH!sSrEOKD!Ek$|P#fNL_R0Ph2B$vw*KU%$%2sy?dPOs`O2B(FGHRK%UX5}b*MXoS&m~^gBsu;b z?vZcQFVE&8lf@Pwg1Pc5u2dv&P*qwdgb2CiCiT_e;UCbe)Y^N6Ka*`d zZN$C=(~wAo5CvpSEh!-Kh48sceP2KeP=sE~*9--is6|`V0mY-aFYC;puSH5V-7`Rpaj-cE! z4t}GOWiJ=eIdy_hePu-nPM5z#tuozgFKuPCv}_qzB@kJx_4s zcWsT)E5WBy<5D$AA%_DhbsogakzK98Q39r>Sz4W>hk#=v9nBKK{pSnqs2HMQ*4d6$ zxwFB3^gG?7sms_zctjyy(L+2r=uuA?f<9B{UPjL4Di!9-h6J7x45GNk&ZTsdSfg1* zm!|FC14B44pJr4uULZ1L zoOPiFK6b3=bBBTFvg_*LJO|FC%8tBvVeiRTERn7pVH+YK2u!txA=l5h zu|yBmmjV$}qr$a3P%#O4Z3lIC)cMxe!=4O56tHW8|UKDT9Z0x++rJW^&C!5MUMSo))am5b6QMfVY|)YQZMTn!xyR`hxrAzT?~ zX;*sPzQyQL?4B(%&qUj&rH>5^J!(;jMi1V(Z2LK;CF4T@LTk#9!;;qt%h3xh%oL z=&yc1HAXt?*GFNW{`)o~;u-i|0=U0cQSbo!(pv(MaLik%@$93LXQFhhkE>~&U}2uz z!=(aU(fqODS{NXcN=*TBd4t@Tzyz`_-Z0T2mwQlSP!p>9n<*($!Mr4I;Pue1CnAn$ z#b?w7)rT!?a_XZkP$V%;(fngzpnlt@UEt9W(Pip)h~7 zi1RhEeEn0zZ1#JEhS73E<^E;ZaFwzTohIFsj(z$LR^^{~fO#@pHV|#k-F@FfI`&Tm z1W3_kEwp8fz^w`odFZ5<#mvUnU4!%U(%^09c!g!H#iL!QM@|;`d1}mummu}X}{8U)w#!6>bH=DdpG zJn)3;!TPxoV8rn%{Rd=Y`W;`XHr5JUeZ}v8DF^63YV+g2*XBFIJ5kBw7U=?sQ3FkN z5V(`Kx_sd<d;iqKQ7#Y$WWk&tjBdQ8k3}#HD~}h*NI1XrWpMgs_uFv|3Ph ztYdlAJ`|>W>rho}XLy)?xyRZ2H8<65w3>{s*Uy`~xr{PuK3t70t3J)|OY!%5(@Cv} zCi(ZWBJm|A5<@v)?=tocMa9J5hy1|oGZk)57NC4Th?PBhMn3hbnz}~_RMWd|5QD%| z;ed;GUO*0LNP zg=r0!8Q|_&7#HYQsBP*Nj%Ni4B@V_)U~aZDn~$B2!PkfW)esFj~Pd!RuR}JxH(jAEGz6i&zO95hRVpw&CrZi1Q zW4F-Cqk8a}M*u(_{@iWBVZ&fhv7M2ow15q+?+c$>z*pGkijBHta9J>=oe1guaIxIdDLK&D5*uVVmuH;A%qe0u<-g2jKU! zzv!|e-&jpgIP6MbbljM7P*ltZa)8)p6|*d}3`>oB8sDv6M9o&6E~7XUj1K4kNsKKA z`Dkzf{%Q!cTZiHnM%)oos9F>4-)|Q|$E|uHahLY{D1t4^Wpf*3pV-E4M@@$=BykrX znfAl;dh4Xn^POhj^yXmSew8Eq5 zCM<4%o6Bc!a6ny$3le<=^)7xhG1B?M3g{bn0x$Z(<9PQok7H(CCBGyw`=BTONmOn8 zkLe2*k;w{HG#gcs$l1CPglRo-5GXqU!K_4aBWTq6v^SSm^PwGZ*#2Kl{|S;<<*o_U zmr4w_=*izd;H$>1+W8I8KzU)uO$Q#4mH~arnFjJ;>CVQ;CvDk)IUedBar5JQ@Otu+ zLC|76=pWYgLN4rv5ARc106vZ?$ zM;h*Rp+o1i&wliJwpQY|RqDgmiQUqNADbxp>Z6@L59wp^X7>&;O9}ihOIDK%SY36% z{B*!{4KhO(8X{qEp4}g)R$nJOOL;$WlS=D=z+_jfB0?B0? z(**0;@c(824d+M!JTUII;VpTxO1wx}$oja&WuGLE=!JhWmVzs7iY}3SkK#q+ZwM^Bqrtk+;@D)7V>G|; zF8P~hiz~~x=3!0lgVbAl!w)MTf>Z2+ju=QE`qYAA9@9JBx5>l1`Bio9D*whSH$dQY z>d$0C{ff?1q%W1Y&$1=8t#K)D2@)~&>dh5oNO%-wN{zIl!|2lyGj9SL*j?yct5hr! zxw1HYCwbjd=qK0^C^dj&3D|}KgQ)wz2GM_?O!m13a#tyQJKzEo)cV$v%d_CJ#Bgqy z`TF5-@JskAk>Um-84ZyYdI|XP?ZV^F2fdj;pmt4c=7_2@BEC>J?9ueHMGon}Ya#zxnK7eTtngLH z#vWf&$@WN3!0Ie);A`Zn5V%ZhPq<;$0N8q9{WAU9@NW*#iIMY~XC|<6Vd0+J2R~Ny z0o+}&o}K58n!d_ktOhRD?!h9tE!rfK?fWa}3yzk8$x78KDF-pF>+-6ztYM-Ypt!AD ztf#%_TAeal5-wC8iI@!Nx>PsQ;fSxt?tvJu1iEoukIsI($NYWYqdR)!@8nF6cB_N~ z9W-9EnLHvvnZZmG!UXJ&W6AYPdcbtTWrbI{ikSrzQ|AxyT4etclK;W*IiF=pp>5P+ zz#=x>v(-9hfWyc_1&X`0HN+a%ergVZK~%1C&!*6!zG`lo=)$>Q&z#xFi zaYg-qCmV>hP8(6BEh zgM(&1g}C0#aaNn47njzSgYWn&HH-SB{7Ep&fwSV(BIYvJmH zmctz3cLL_v+_5RNOsqAjr`4~h}7cdcdIK-4v*aJ^U>TPPv_eay4)Pi>1 z<7~Fu{IC*EYWo1l8FsPGpE6)9)s9BmwR~BEL#h;3)bgM>oiXicxuh6JL)LNL%7zW6 zH`v*nxm<^ag9}ybg?*?C6~=CcCQFHD%Q-%Ln1VIg70Z~;UH1QHNEm)2=kMRx{{egK z;c096Ic?X)m)nqi!1om&>B*inwbF)}r32WrTn4X9YgQEr#fvjMJ4EZn+PkBta>(?5 zH6LhQteYd-X9tqXpw`X0d8i z`c?Cq%#{qwnRsj;s$E+a`GGv{xb*GW zi}mUcoU%vu3`CUs=SqGL#K3e~P-*h_QUko4y$#smM`P-mE8*A6xKTctw<{opL%Bh@F)3WRPd5 z-fkeP+GOQ9^O)&dR-g6KXY<|B^TMNBaW`GXS-0~gfgW*=5`NhK z3uW9svy>+p)2_Y`Ewi`G!{`^Ctj3x?tJABG>H7Iwi5BLUh}TG*4d~vf8%!5tP}VhO zC)2Gba1yV8CDu>R3*^gsxoHXfU+5*VIOqURka@_fNXUu)ZfEYLAJLKjW}l#I-bo@C zt+gg1-1!?AwzMt|Tp5A2(|G4b4uP;4Kn_-wlm&%;L8$fvG=&cd@^hH13#MNI(~6v> zsuXxmM6DGtUF47#O9LOl4-$8$Ag}$hIqFYQ>@>%Bwq_WqsZ(znn)#3@(TK7Ck}N$>!U+p>SUCRg!S( zwG2&MvpTdAL{#Nx5?9h)M3Uw#ukN$Tl`lQZB@cnI%HPpE<&UxODz&Q{@F{U`&s3*` z4Nr2sPK(&{#xW6n6F!1w+7!5s)EP0Vudwl|nEeN9RTTZ=l~VSkVBy0@6!vBi z#HF=L8F6iXc5l%pxcdB0%N&hhu!P?Idz=d+QCiFOPP`^j)T>!*i}Cf17g^>0sxB?a z6jF=XnGRYWaJbKESZsBu*Uyie4uF6OFxW*)F8Ox z+U5NQlw4k`?iK5`Iv~)#nK$*)uZpw)2PD{$AHAQ(jZi_uFJ+D@s9&*M+%IWVZC^(* zISQ=39y|Y|FOx>dgN=;76cmd!X`Gw%&6jF-O!MC-LX5S%9Br_w<(#fV>6i6mO5p(_ z3Zs8zR=(^#VbUF7L`D=)o(Ps|;az@ioqFELKhw)v*QNn=0lhwEDG*Q%{`ym8UOG2i zx=z=8jHd6&F)o`CA3?fYKQ5L&=(|3s9XmJdyO!)ZM=60W2w>y>>VrG2q_X#KM5+Vj z2YMBJxsfiOfq@0%t!{X4ai!iWz+w%p+&y3?p`LT7I|ci0&b*>;0x%Gj#eblBv}RRe zAEI`RVO)q-A^D3|;A$*-kIUe)7C(tT`kwH0G^L=mB;9PhG>zR;Si`002H`7Ojr3K# z;MG`86<4<(x8?4eH2+5`MH&LHCuojQO|ZdYC`Vcs&8ccBE%cqd)b)C3BH+IAnLKI< z#!Z$r-r*NC9b0{^>)7KiO+nuzO-r@q9TTRIhdk&T8xxHv=)oolZpfwJ$9vE_N`Kno z#AwY90uOS8gTzqY1Y$@v$Jl@yCz>2+TsW!cyY5QfX*0Y`-{q3SqrdH}HlG*DT`SWf_VZv#%w_Ru9ir{Wvq?@W$AKw~mXYPLHl@ z3vJI{U#ocai6u17jV6HfHu1QhM;VB1gX0pMhz1q)^FkP1e8pZ1h<~!qD z>c{6AXReXXn`Up@AKw&j+&DVxzN}>Kp-G}Wo0@W+)AKiB$=kA12OT-^Xq%L7RF7iN zO-0*GyeFipg#r#huy5$&O=@!dSTM9O3p_Y;z?E3%xZKym1hcz7hsQ=0&%8X`Z#PnF z@Bhnq<@Kt(t=$&`uP4wXUt;v%jG)@xko*mj_bgpKSpWxAxu$Nl#RSp=zH7x2ZEM*yD=Rem%Y+?tx`#pvj3%agId*Njdo)1W;|d@_6Wbv9H(p^QYD|_qf_PM zL1`-~F{?rD2eln5&lEA{PS3oXYWZ#li54)X+Ru>zD7($~Sh^#J(hKq|aLd77&Z?&# z$q~Eex82y6_nIXlgusRb= z^lUq7z|f6_I1FXY04|Q+NT}vHvbYE;Yr31TZ|AGXEzgdYE*RtyUY zwJ)h<>+lbw(U&(Pdvv<|97DQ}xvI)FIkK(w%Lf?Ays>xrcC+Wr9r=5EEPO^^_vwV# zg{iJ2^@|tkkVb)vb+b=HT$Dc#-J0GyaKFRNu;x2WrKGkli0`^AJ$cy5yw0>hDRo~6 zRfhu6Zmr_96qHfr@#wQ-1=@$R20i5x`H)vs99Zp!7Kzi9>uL~+Esp+XyA#Rl1A@oa z)&LWsu46#(7z+mkkNwSGVs?wv0PvycAA#Zjh_asYp2a$-IXHbh@d=XLP&>n2x;FyT zkv(&Q6lQiV3a}820Ma&UsB_sl{=co0sc_rHQ`{9`E>n-49uV$NsaNAW^*`P9#6;d= zn{#tAuascBf#Fn?U~P6ZXTQ1@=5pMA+gGwkfcoQts}qU25p0r%D&1Gj!iy%FdWiTvv>N@d)6dllu9gTet0sX@t$S^A3-+fc{jP-3ZX| z#Do9%$hxZb;JU?32t0=s{sV)ZI(>dYNLCpE9$l1}1YHMhW7%=H{FMvSnfPdojZ>tTF;cpZAww}Ct z;bG40_>X8ww7&6${PNMuqqN^ld$1yXdN2-bho14nk|g)QzAL zVQ0RK3ghb&xfGGjlWIyR38J5OtuuTgNZ&16ZIQUP(BniG0jWjSr}d8i3_QlK1Nlwd zZUXDVM|3RC3~@*3+-`%a<_ze-Un=P9yO!Y+qCqPF5g0wC#Faa31u!9~ zcrejD9aueU$AQkIk6cZGE|NqoH_^mY+YXxVoSs}Tqhl{u$MtiFO!scG7FEv`6yo29 zWx6gc_n>?_0M&eqli4LFT~kWIV{AfxdKkc6>ChYcXgFQPSU3`tFF9BOg*|(P<`ruW z>rKx4oE(V9c*Jp}uM}B4^QzPPR1O$!285OJXSIgLwrs$@wZor7WTrYfTxV-NuFGL| z+^BE^F6B*zH_Ea1VvX`jkxzmA0e3jUwM`OC{v{`Y8|Z(^MF6TrtSmlPZx(EK4gRU? z9*|te9pi?WERu={v-ZUABTs8~05~6*j?>(>Poqj!OQHc>4~r4fHOJGz3nsfbaSZ}M zwD2({h1JSGbh$aI7SPib0)k<{4;WWr-l{l~5Uj0mqZQXDkWzaTIQP6sjr-ud17LvI z88Lec`1|Kaqjo)jmMVDR{MsrUB)`hb9&~b0?RJ)h?ZTrg*+9~Dl?)?b+wUZ2EM1V z9|+jML;}Vwu=}#^HG(NGI}~;fMgR=%CgK&`f3EsLWMy zc(LhzI_nUQy!Y#PjLAKX(nA3evAiE+H0p#cg06Q*^agl&&+p6&N=@R_)Y5A17Ure&v8qdqI zOyo~_Dl|4FK#t+?wW3ftZ2M3gAj|%9Yh75Zm63pwJJm_g=+b14CTN-vxJHw6=S3u& z`f3UlRlK?|qf2)WGH*x23W$;2Pbj_dCdOH!nU*D${JwQOZ5fq+W6LyL8te57He3#~ zBUkTr4!l(GzUug-1L#Vj6;n|QdLu|Nn7!o?%U2Z`{{np;m^7vWJ^mkRh@m*iu(P zsG@8V14=3&Q+5)hAVUzeC}D-vqEd(uhR6scK!~6~qGAY<5q4yc5J;HM3EKXxeO^4T zpI5%|x@vQs-#O>L&-ea(ic3+Dueh2PYUSSa3F1nbp(%H=0I3ML4 zy81UMXk?>2)X&LR?Q}MQ$8sKRpuAr)7jX%U+`U4|hqq3X*h}WR7naixP9smOy*gV` z_*19!cRxLS(AHv9D=k<*fBeqQUp%!!IHJG$JleO<9A>~xpKo`2OW7rsRuWvCi;SrD zG!o4i08~z<^=TBzKkH<};ejqKl)iY+Z;v-Vh6DX84<^_{$K2fCyX6=mAYwc z@l9L3x%k`G%24=RR-Z>`vVYU0@uWm&dPHz(Ae9?h>{|*y`CC`)o%=(z%pK*+fHj0C zODQww(lW*`k$=cT*0C7R98QZXS?J!l5!aATfDYff1hWvbhWtRmlazT6epy~#G%otn zACY=GA}x`2VQ;r^MF-AWIAfsOd{suM0di{LJ;nBX0^%WI6gW)5G(+-uJ*#=H+WczUcoe}+`(c68o%Kz zjm-WIYH7L`&ogWlw;k$iemoZ&k9zLI4M8*^@6@baceUMZo{)$3IZ= zMvu1+qo#GMnR+^YJE8PeN0@;KY`#V=NK@~MndcSSKYz7X9w={n!-xw{0mxzrIPac# zWELGC3OGFaoy7l8Ltp19Ly}Yus}*?aVcq$+^(*0iqf{GpB<&~g2+httpw$L6W}%}o zKP=cG^_yhH9K_X*K&?$+#sjx+wW_Do1@Y3y#-Toj-`?!L;`h_OcBU|36kp&!=jN_o|x0 z|MroOKM76vlhAsNuh4)dGxzO6n4D-zd~#e;U0?UiZ>GloEDRn0DA85FIi$qIT6pd8 zZS3V=0a$1}KX_?|`I)>7poQuh9|>Y#ZMGzM<2j37o}N*b zH8ZgeV8F&3plh?#{3Hn%Gb}E4V{T#2cKx8m>NG_Tf@;RwpgH47b@uy*oH@rtcm7p% zvo+u_br|CUAtzN`G?>%sWYIc}&2$50_{g6$;oTndl}U*$fjJWo$jc+coII`zW0zbf z*vTF)40cm*A=E_^;l||@O+>TbH%@*MS{uBfR14A~?5j41l0W_by^r)m#8=rHJd5cx zNVPinh1_jcx1NivJl4-6wqnq~1wfZfz4MFc+qt%s$x(yFbmMP(q2hI0FQbEGEnkh=pnkp}3Ab$w z>eDr)#Hy!U%)=I$`21w1*@}#3_ozQzc6?%6!Xn_Qp7)kZ@*SF;zZ)lTi5^=-zZt9y5ZT&V4W zU|VeI>d%4w#&(=Kd%;5DADWO`1m$$a#L3k@!!;P-VFH>^&emafk1y5_R9qOCImv#h zPS1`}-$;Po0Iije=d;#|S=8ye9F7Hje#ER*yY8hKSNrPtv7uUs=a%qH`Z}a?eMs$& zB}p*)F-}CRP?R)y1}049_h+$Rw_njLPfyn)8*ww|-($wcC!DDdFsirvAlE<@C3ke|lH9HpDP9IWD&%CG1TY>Q;K+iN}T&OJ&YR1WoZoa+Q?u+>wgR$C>&`=C13R zj7Zj@Lo&AxfqRQoL;b1>Z>MIi=nJ#fjxb4E7S)WKe`UqYJNyoC9Gl^KgF>!v>d8{^ z@nBar)lJoPxmHs3j_%{mPrCkMXwohzw*BeXqmY9)M)h!Dy6_`RXuD6ww!?MreID7u zU%iw9da`p!9wy>7%O&vkkWf!nz3iSSk0*M5)RVyoVxDq&BD$hTzv;%Xos zp72e4i3}-_pCg#(KAgdANoYt0{tER+!8)ZZk_fzL|PcKk-fapZBXBdoz|i(si@RqKqNOlv(J z>ucl=p`OY*S?bi}i#|X)q-|{sXR%lbW;~e?ylR`5qHp7{hn=2yhJWK!t+m)oEi$Al z!ORgEi-Iu*t2Ckh&8bZKEW7z1tZ%F6(MQ1MjN=6ERoNUB6RJ!_x z2A2FVTkOhrn5Y5=xr=GD9g)9;R1|^+utDVSw(ZO3(`-Hr;^|P^g*6%^@h?brjqrTz z%(Xx_l^&Xv$yoL8O?}piaUc-VQ*g^Gl|qux@mS@5FCTf)|6OrtN{zNF$u{@=%n#|g zCMU9NZ$;a!W*YDDeUiF)^3$+eqnhbk=6i619g8ze8j3&r(DnqnvM~GJ@of)74Knhw z0X>K-OAIq1vkzVCY}9)0vo3&uQ+10{4A2&SC?jNTqR z<-@}Q`nqQ;X{GD(G=IVKy#JUFPZ}C}H9uac6yuS)2YSEzx=+4DSzn}TP)HA|dN6tC z*N^m|^PsP#jk`#0jn^O;EklX5?szh;W5k((*?|wTe50CTh9{X&&U_f8&QkEFgvlY) zhpLo#vuYNVDc8FUfcMKkJp1C>7Z<&v14^cqlF;R9?&x|4B#+bY(tbrWSyy+ZT4z?- zLXXqfzF;h#BO=1pLrU6?2_in$OLI{v4^SUo_cR~2MO)=ESh=yy_Q`1MK;zF{*r`bKaDZ&q5dR1dS_bf- zc1)uX9>mO#fd69@tJwd+YFJ*g3>d{q7EhIV|FPq?@e29mE^%|y5 z5XlFdkT~u|kP$;`^S~;!Yqh~aiNUzgaHlN zxP3z@RK)0yx3wh=)Lac}GG=qAMlz9(tfWfH&8vMvuh>LomDej4!gH{2kA2c^h)!Fn$mNy>JUA~K{aJxUG*Mr<+N z)Qa&A2(@CelF6c&z%Jf<%aCHpNFE@*a1-JAp|&PRaH=-Oj3Wl(p}*>6wL6roUxRE- zeDOuGy>d*ra*l<)7jR$ncR+B&*`w;21Mb#6QRLS0swl<}n}vCQ8924PwI{;aBhR4+ zfIE6e5GHd-=I*F!)H)XjVh-e@t2&5~pW1%W)RQrVJ$IAC)z#S=X8~-GybqR|=GFLj zR!E;x8Gn_7C?6j7FkE_RZ_P`c8ksU;j`=*QGog;Ql7?hEpgxY+i8lfnmWV|_kAK&Y z{%?2d_FYTZiJ`M}imD!~HdXJ-1{jdOfZ9jyA0kQRu1ToWm|pcAg0qn#RP+2?$SHKw z_wfXiMfZ*=-X$p-%KhF^q9YGWeO-Rbt`@sn?Q@~nzixdx;yn+d6;AX$sOGk08;ar< zjoq3Jm2_QeX&cW}pf(i)%K)(1vv5N`14rx#m$hYpyG_3PGIAnJct9a>Y`f6u&=W%1 zW~HCRUNWpzocc6>>(B)$N-gBguipq|Vg`sKhypPpmj3PkWMaJb>*L<3mvFOFdTP9P zLS7I{`LC5&Auq_@qIS|hOJnYFw_uSUY}QryrrME%Tz=@nt9du^_|Zh2T9E|ntG*xp z%?pyOY&DWG9uUgJw8alPSS3RhS1-XN@FXA;qdb)~GfDo*^(&ELw;aN`BD6X0QemXu z4YY>Y=HiJjf{+ziuW*FRZjX{btFb|;Z6*#5Z=}4c?VhWB^2qnIL_@--D)>tcdDZ9Y9vq)b7R8fbTbHKt2^FMb(RP&t{i>>OrF1(aAWNib!Uz87^gr>SXbD!$qgwZV5D%&V z@t`Zhc+j{o9z;UTV4&hX-I)37isUSrXE`6rEu)VzM4t7pLB$ZOX+G8$B zh%ph*(GyvRg5%xTq9K1~O)82X18`d%g{z<+!*BZ~Av{qbtwAUgQ#5<4l+*BQIz@I9 z6XXRjLEWycHt%#P+ifRP@T$0K$>dQ})5T+I5pq`i%l3KeW#B;1m;~U~yk(jWHA8*u z(prS`_zxzOBM@uES22>=+6B!t9KZy*o0)9$s{9_f#O{Qa)VE?&G5jTx%Exrj_#rn)h%N~EfyP-&i}d6^lmn1YOjS?BKVO`l!+PLg_8Aw6}X^Q>b+UgN{L}Lr5BRcg0&!10i67g}YL=(@ENnonG|kDiH23za zWu;2u>1ojEK$TemYZxd$HC&AzAwiJP5rHRa^7ck}K`yTQ6>E?)e^(r5?mz^;h0m2I z(b-O@ivGL3e2;kR$f#H4{t?5mg?`#3Fj z?3lYE<;zp6{(=c}WrG?=NWkKFJTttyc$KQSB~HyIe40*(gN!$1=GvaqAa$Kx3^6dG z!9R0}eR(*!DS#E>mT8Ks4cFse!!c&po*~)x*K?`YCzJi;!<0owInx1Q=E3j6r}Nii zb1N-`B{*b8&nSQVH0b43GKk^H`_2R%G1Y~Y=1h$W5kaG8SS)9&x>tEJ6f5_Xp)^d% z@Gjn1dxGNbxGC$lfW^L7-lPMEtxclJAM3nh&ru&7$c0H{jF@zAkMi~oZjY1~qxePj zi%1^nH2dCc5i2I&`}n<1@t&=fXU}c@?z#KoKfXce8#|(wRC3p%#Wr}gSp9w zN3GX>QI9_wiAp>)a8)}!^^S!msZP=&`T_P_JLnbGZ^x?&U1HX@fI-&u4N!S_}m)`kh| zCum;~x%JM*1bKZziM`4nXW-Qw*4drM?&@jinqI3%(B8iOCk+H|&~tb{*E#pSuUezN zFbz}(3D_Pvquon8eS3SQK)QBtscO<^!Sl>ODdo33HPh`l)oD_^OJ7Q5Gvn_tklM#E zkT>I|_opyWh8s}O8!u+1pI+=4>>62*QH#KK-KFT)3cSNXmwCl`Ph_}&ana4uH=fG)wv$mFl#we`_U1Gwn#Ma>r*VaD> zaNrWlKF4YY0U*y)kN8MIc~tVU(eNX=({dg*c@WdyzNg9L1?tU7*zLy-8rR*}$FH>LDvLh=LBen7t|AXcg&5SiYJ3fwSZ)9H7tva`QbljM z_a}f%ECtBKBswOM_OP=x?u#mMZcX(o*VyU7_2Xp(r#j(bWoNdL&)7dUc%a zY(-|U=Ki4qIeel5X}Ox3S^3ZpxmM3NjmWLo0{99{WOd$(9_93(?U`@|RN=6*T?%pZ z?B8rxUdPSkkE}fR;%fSOaGoK#Pi-c`tqm#l%wswh6j*E52sKi8iqGe+xcT~QxmIL* zf&>$=30Ljizj+&Ia9>aFVfMgmW_AV${fz2`JpU=D{1B5_ltrxfr-AOx0V1|b@b1?W zKWp2#1sxz7U5FYnX+2uJ?2fmShzzH)81GEUkr!bb{=|4kUPunVG^v&R4xc-=I`7;o za=5#qF+BU?#Wvl`)lB_IfAqBMEI0g78)n@umqu8g-5LKW^m8z|7rF-QVFgkycg9C| z#L#03nLyHK&vugBE^_$D3*Sphi>ldd+Ua*s2@%iaxCEZuqPNJI4gLBrzAYVp2T@Qq zI*iiP{rYwz-y`%tJIeD%vJt_~bdMSZQBr!eKw;hag_d@o@F)lK1v7yjl+*L0?MjsD zna*R`x@u)gAGVhmy6OqDJ`~r-j@(Tckj`Sm2;}B&kC%J8zdc~;Y9!41ywotTM$e6Y zmi57Zp7ntjs%Z~AK&ZnzLX!tse)muKe%qC_C;MD!)&pEF?oRPN)gNPJ?b0Skl7gq* zS=g7p92u*KGt{{^0=!=sGbyM*3|AW$xjbIG)HK}#av`trbepEXs&Vk8yGrh|2Xc>J zyf0?ws_QclqGm8LyS=3%N)JZ4ed~)4j3~Sh6OV;LKu;WeVEF(02PSOtNVVT2a}Zku zub+~vYr&f*Xd|vvv;`*!62nt7cVOj}cj?K9SnZE%@d%Y=-wh}bOP<7bvyN8JH#&`9 zQdQedS$zT>KZhF}cX(>FKMs2_9_mv~RpGpZ$DD1u7m{13@dVvufN?KxI>ZZ@)bL&E zSXzd=PMOX=2(a@KCVUio5??v!nodsYJM=*fYxkh_g`O~|8rx@ySo)@%hf>9@IC%4Ma5X^wJ_76*m zXs2zBGHly9e-+XlU(2Xuqa*nhK@;G59Py6q(Gzm#5VYz%9D%&1eIHKRS0>cP0QlQ6EtgVlJOAXGXB6kiU)U@&L?}teMJB4*+l;| zb1)8S1NR+LsVdzJlKqzT+>5{tCS4D|*pzEqJR0&6!I2?1m&y>Wlt}w5v7HyB)=})p z?Suxb(S8f0ZlGoW@1+*4m;}cy&p(a0YZ;=XR!|)MKRcL%m7jzXbqt_?)PP=o6a8~f z=h(lh2Y-a{n?j7!4$s~1`v&qDsyeY1+Nh$IM2vrdMa;cUhN$+L;hTpRjqh^8Az~AD zLGnf@(bZnC;5wpg=)DhNridcKWqqL%tXxq~*|an}Go!bpGCe|nV!$-TbTPY9-oRkf z3D)1mC@2oUqbPKORUrer_~qr&2J#Z;SbR+Z^{UGgjpRW1?;+ugf_$F=)m@!xtE1Mv z`-pYR5$ciJ17mJQ$U(@x-z{`iRlbsTiBZHS_?rx9Ke2ibKS)&blq)^$TpB37Gu_pZ z5qh|pfA3?o2d2h@qCm7KSZRwr1qx$eWF)g2@>G?CN-%$lFxsR3^s33bN{oh$#EM1K z@>sqh8Dx9J_sS`JaU~2zfPFcXGwphebEiKab%C z)RsHyHXFyKj|%ZW3>h)Xw5DOtkgfLZBb7PhAZf5oRnr)L3$E_o7%15cB>?(Q&=Yfq z9L15Nyg$0(fc`UX#QyFp-!wq~X{ZaY-qQlL6ViVMf_0yp;|QJ;Nk#GO{&Uc6ueV!A z$TJE~Lv-&Iq<{zWbdC64RoVqYT%xHgw*IEdi#~jYzv3BL7=`yWYD7$EP;vY62f8Z!fv!#;=hl`6rL9J7%xnauc~45U)Ys0f-@GFE4%A{fheWtb z{Z5eQk%s2*j}$Jk(V+Z;+q&ChtsZM@Mqe9 zM$c0|HJ8(?ujW$a)4M0G3mKAhl$cRfroPG)K6GkdP}aFqy)Ej_5l73BxjmKdEc1K> zBaSHdoxWurfC9vbKgn@WH&>2CfqQ14^E@B*s&q9%ro(NDhP9dTNA$p7%3q(A`2J(IC+@t@ z`qi4U>fO;UP)q?yk+GfPWD`?E<0&}l@Gno#MfL=$tcHZUPX#jaC;$VbBk>UfWd1h> zXxFCsOP&GFG(*w5q`2UJztvop|NF5wHH~v8vwQgY)}ia+g3_;4Nrn+m)a_df9O?5+`%AGfOhBN(af$ z`HXNMU|_{RAj+7RHE`JaKSF7Au{5>AZf@%|AK4@9 zsGooam;vaZ{BeMkmVaW>RNXfms!gIFED^T8K(OcX%@9%+P3wV8O+xF*3z*;3VLl`u zEo+kw0;SMKKy9&g$XjNO%yl0PZtB7+9rlDhiHW6Zozfh0ZI4UrwRJO(bIHXLR>u{L z4lnO=Aop(VBnu09b#N5|$l@FAkbm#oa>AWku5%hQ)Q-s>CW*Yr_79EZ;r{G|E?rm% z7g@ZJyU-K#>FpeGd40(b;jxe{KWmb?Z}{9~vWmzi2B^TIXb6_k=@oXy&`LL`)YK5T zza+Mg3Cc1n@B2;vu9|L?5erl?!yT>q6y199(1Q3{Pm^)(tq#p^4YoJ;nNx4rhDX}e zgbhB8Gk57?jCAO=d0zO({Z&*6++Pk!Q_frcvxmi=$%$9h?MPxSs1iM54|vkyekIS} z;XhJLBVM_UGj86) zVWVI^$dZI!V`X$B%`+q~#YwUo?P>b~{JU~5X44$jO5nHK#bW9;o-Z@*dXugI2Dui| zlp&@3wiwDwYS&hGeQx{cE1Z0#2pBGNI5%lZ^z14tmtNG8OP9Z3b8e}Bv1&m}x*9$! zoJ1My3ZCk#nH?^LJ8Z zR#}`M`W7TNj5927S>0RSI+UQv_96yBiZ#&*Ck-8RGY-%W;`qqPF>2`>)<_1sYlL-H z-y(NCW=fOU_M`#Gzxo?|Jugax`!Is+^UHfPySTx{lEh`N{)ah_p}k@u<==-di~~DS zA#rAsK_@W>0Dqc&_%`#kNtfHQie5)j^oq%hiK z{&cnq$vM#32erS<)I>dSDQ986|6P*Y>ACr(2;|5tL&nXVEHZDq`Sf}I*cVqI2jR0h zPH*XV(PSKZI>>6tJilR#Q)#3EE&{e3Cu?;MwSILnhsj;8^W}uEt6C1JiE3?L6t~PD z!>wWZB38cbTnjysHRkB4(H@qQX#BI~iRHPO8&}fQ<9r*wi>JHCzdT*Yvd)($xwl{M z$__8>*pAbaE?;V`Id)|zlZw%59If1l zVN(l^t8n2%#PYG5&j6^iLw#@i>9*e|J&oHRD1Ji!^wqyP;foW@mh1nugPwV@xep9V zQevc-nb>--MN1Gm%`*h(AKB+Q<4Mxpq zYPvVjfhjT@nh4+NB>e<*U`n|so2Cf`gYQ`fL}|gO-7YA^U`}yWuI;FqqrJW_ zj~cL#sYDd0h46c^Zm3%)}P1*$7|D9tZsRLiAAbae>T8AfZUT*jD19%0Ns zm%}XK1-f&p$vFstqv4!l=GI}4(v@TWsEIrtX#U_j?~q`vW-t9|K93qQAAZxje`-NA z&C_&na%IqEhxKtx8ZMRQcgf1t(W*`{_HB}%oLnwe{$Siln`C*8 z3VGj^I{m6uW-2g)y^q?i11!Of|EW^_e+F+_;T=Cuo;wCks|>-h$R3DjDvX;FB)gOiV-jq%m&TXmOGR8)OTw=WHiS@0uM{ed1V%m;TS8VANxw+fj+^`VjN zD$UWmPcM4bD4UX#_TTD#-7a$55mnMnjl-ZU9AfDEN8aV>sh~*-o*Ny`;}dyCks0oZ zLF=@T4EGRZDdT&ob=4h7CCvZ{wCl+laCt9$l7snJkjp zV2%@qg$u@}9U23(K)xe1M2oIU;?Y&H!pK;y`bT!&)FwZt_uBDazy? zE*FFjD)G2?Ndu43iqLs(uv+y>Cb?!IF4c>f#IN&PCWEOF(p-#@NDKAF7u_JNf>FiR zD{`4zJ*>@U)?fP_gdFeVmgzC*(b3`USc=_p*N_>W8*bk*wt1K>!Ry&e!W2i0w&_x8 z)Nx6{Xf~L)02}vAr^3mWFAiZJTJIHpDA$*b_X#dyfM&qKaDXEeRzm zUnq8ih8WK?n6{`c>J&-EqZ(3;33K#375I+5;@TW@ksPF)kl&4080{m}MsNuE?uFf2 z>(2N5vr&7ns#~@klNPc7hRH=4qWg3t|ANfGzT|DMpLV4I=4php=^VFiwSj2A~wTbPw zr3Acl2)f$ed?0ak^VO0m);PY)AJToO=kQho%^j~Zzq>zs3j?)t_xd?GNv_qwHMZpB z^IFkV|Fb+`G>{VFfxc>X>aD{B*OuX7$ytMEC=IFksfa2@^tDScWjAP8FC~2Dq;mFS zQjQN85NxF}2LNF(v!JHZj zFm3tg^5Z53qvRZ3997pc*G^0T(&UCdoU!J!7 z5dq;UMCyOS0oBKyz5Ev6erkF5lblum_PDMl2e36`>kj1`p?Sc?hu2O|D6R2+KCZt5 zpMq}bvw&4J7D0QIe_yotx$Am!MEfP>zZxg^Fg|yIWoS94)a`i3$;F(r=8bihUi4ma zuv%O7A;uQj^O`#29d*tBm`(DdYga19CHQ}1I-n6x&)kIRSUlZ2yh#I65)M)PtBlxu zKDJXi5rR`tZECtUnJboQYnl6ko1ulqMGH)amyEL#Hlr!3zefuqm-excnU2r{0OO&} zWkH&X#MRn`I9kOMptV8X@wTn$*sNE+Qc4Ek0C}GGkuA+;C-}YGVQ1sG$pKo6?JX25 z&x*^|5p=y17df2cZwe%zWvv7$gZ)ynYH+qg1)plKCW)H)%ml_~VFdk^?C*Z_lR<0z zzWcp441MR_<#3wYPg=b{bNoJxZdG^%hEW+`GY+mUsNQ!g8rSCC1ST-Ma`Zc)32eNa z`OyScNPprBLR7#6mI_Q@+gG<8ws2iKFf~8In7}_C%3ogx4N>$?iGBZR$`g$o>uuKVe2y3b$L0Q*Dui2b<*(j-}{H_DZ(MKgfXq+s>2YQ(Ms zAZF|}6$~UjC?B0rWJLLQRX3h5e?uUabx?p$XflF5HS2r-D?f3^l&r0;0pXo!`<|Z{ zPaQu`%T@2p{LAuk0>-wGJwkclpo}88K}#8I`dDDmc)h!6<+0Fqpe#=C2xsbInxanLnT$MMr~|OOTT9 zb!PR!TMqlPYfgsYoJpqb50JwYPSTX61LSK#xBA|V=f3W;6@*bOPm8#lq6dp;NT2rj zNge)n9Lw7BC(D{xf{{V^yvzJk+Tua8=k5y=;&S$l(?0Bpm)%3dPVfXd%Q9mZhTM#g zA8GfCV=A$A5XKG)2{+<&Hh?xy*z*HyfAi&gm(X%ID-3^k6LI^cO&6HIEKMLn2;kn@EzJ|Emv8W0 zXby-)pl%;cwP@SeTq{xQ;ZfIrR{ApUMM{pg*bvYWf=gN47o8Y$c7DRVW()Grgk6cb z{&ZvgDk?y(^Dsg_Odh>B^v>ESK>mRLP?Xym z2)g%X0NT&!_Yys2+8uD({y2i8d|a}rEG~1$5t30C(x3Igy>XNF1HY=>Cn=I1Zo6on zTg)CTJc7VwCgSZKc{Q$LW3D;MrSMCzlCD@cx5&jn>T4Go^{qwP7D&*lXNw;}$GQ1ovd>2I;+b_O`ePtcQMjYbbSS9dP}juTm{2ryn&`3 zhwU>TdxVTI8o8L?rPK4U;a3Cj)|(F!|B7FHP}8sKt0njLhW7Q;OxQ)`)P}33QYQZB z!$pT*@550*cnAE2)z#hy)i0Gqp%9UEw6H#pqJ>t2C|p+L(+$En;QU-a8e?33z&*}A zGqX!jHdVu)oI<^NlN=Y-e0f4M_o9pzPX0&r!4Fux0?Ay{)FS(kx*D+>D8CAY%CF{X z8=(9GF(WXUP?@plP8oMirQrbQCvhl_qDqjCos>9hDdhabT?1h7y)=pcD8G~^AxTfDt881KxpN7tfC)q!h2K6cCERba4*{{%L<}4 z@mr%Tz(wyyZu61g!T*oLJ3Xn<7@eER(cx)&hjT-hlv}J6>x1*LEX)n^8dUo{j;utR zC;&HN0*4pbOXIGZ$Gxx+ch&m1WJVwx-M(mwL&@UU$#OMI^Psf2NQo^CS&tJ+XBOw% z092XK-u- z36S^`6>5+apZ|=9fCo;@@tZf6!t=P^u&t?bQCjpLam<(E@&u0&v+JCBXEWOnHPS>l zREj>fS5kxy(t(;5ViO#Pj)^OAcR5Qvxql8>tPV(l8CqL>=aRR6znuA`jC} zJJoc>LcUXt5gK>oh1prcIJ4t|)LR9cIk<_8{EaT;S@V$fE7}6#P{c39OAi~%uGJ<@ zNemMNYrpI8M_AieOLAD!eZK(Vmy|Esz!89c;;&V{1Fd(0FA=%EO?DNM=vXvAHM{a` zD3|V_J?1`8ju-mAD*Ers0L~9@UR&gh+Ua!xf~okQy*M`-V<22Y9U5Q>MNM=5KpzvK zW@LcztLvhHXGJP|cYqBM{La{`4GF9#bTtf7o?_18KL0OUtv??%~0(eg~ueWM4{ zS!WHFqX>u60aqDOI(m%NB_Y~+Ry(v>lE3OR{|Yv~M@#v0+n2e%+V>lV%m-}ivl6_C zTfmB2!e>!AGfzp*{zBVVYTpKbq#nO9?P4>Bo6M;^D_1a(706P(k2KozOpU{cUE>Fu zmwW@=2bdNB@-tijAV1MgYa!^WRi3J(lA9*Z2o(DhwXP29?h;!lQbO66gQXfzDEoS{ z2PTw#DJgSwmY)p|XgGe&hYbH`_t6rseYf?BYg;xEjF5x)X0hLE{|-j~&393WuTwDR%!sOEfr{^5!dsy*_p@QFq!EuHsU$xF#rE=5 zgk5>gV^#mg!e*iIE25lImgIn|FI(hC)q+A{Dqc!$RKrpC_`fg5{_3L8sTys#CR(v$u<1#uuD)r%k!NW6ZK{f_@>Aa zd9o3dmZi*RW@Q}NX36FY2ihJ($9(R+ziCtCrcF?!@gB7Iyum>Oej&o|w}ozed&{7Sy_MOm2{Z6$x=6s=ZTR)$_VL%(S=+Hsq{WcFlU$N#qY zWRz`=dsIgUxGf?TgsbMNnOnqtutn|FUb{3aSVMh{mx9)zR=}%tRPoi<%#v2pMEjFT z&+m{SdLOwy7N=0kfp3%Yl63RW2L z)eU4{6lpb*PK`Vo^1Fp;HnjM`Z-n4=&*R^T^c6easn~J-j~AZCr){by?_q3&4U#^W{*$u-B-S4JXQ@%D#x=Z z6QL(>+sPHHvK{xBzVd#`vry{Abz zo_-8hT7GCS=V&#_m(lZeTo5GSks@oViI#s#z@|aW>G23qxvU`oH1+9ei#~F(UTKHck@-!kz6vh%M)%tIMIBmJ?7qs^SX2d3U%_)iln+C1ChCq;#7ucn&^qd4yEcKq9=+=ebO z%*vJv$%h<#G?FL%2ZU;`gM=;}0X6$`wb$X#)L!ug=-OC7?uk9e*1e4&pZ}`*$YpJN z9|NkFJvstyP;)Bc8);|jb7dIq&g!JAp@)fBTg|b3dzw#k2&4fwZQ5g1<>U63j9kOR~R#4U#oz%$tItm?+> zT-QE|y~h6$d+8(^zopp)Q{Sa_jb89cuX+SYK~bmB%TSACJW7mo%=hOzEaFqQ7}N8D z?>*zOb)&DkC*mp_330xI$q@9nU)HXxD|58C7I zfbd*+>>VReoIiCRHgRD*O_5(;E?qIPU*F(@cDn;wVS;Y4RO8(FpF>MSzG`9Xu){Ar zcgs|<KPNnSk6og}B1wk9u`fF}D@$V#^Fn($@# zA6$>NOU~B3%tYhFhY7uH&9uHPuUDJA{LiRU%d?wX4#7*Z-~2^^VIM;W%Hu!zyb55z z=M^mUd0nN$P+jROV}vZYX?ZB_F_hYJz&(jQCm3OzHX30FaXl4;JvYhfdjLs(o4@>? zQcfDa+HIaCXqpL_Y?^)>=hyPfCtMG-(c2&3dZb{<%2uwZGO)?`b>)-H@T!}*o7s{!Ch*#><@nCPRTLheygI~UWCW6<`_4wx){^3m1srfDnwXXBr zvemHQ2{C023<5BF$IOz(hUYhrsbac%zb44Ud`%~!Wrw7xdZLcl&a~yB*=K(a$u9wxWSgEado6ZZFOaX znA7G4H;2{T`{PD(=ujKS)*F8bfjx?gQR@KM;|D<47=;sB$G#XsntlPoVHTXJoIcHo zbJ@ND--^75sJ>>Qj`pjebkS1*Dhtcj%T56wUH>ZBha%}43VkD@%^MJjp(j!Kw{csq zXtt+prJROQmX~=Gp%eV2inc3lw&JaAj^b03SV7XuzSUEzM-Zv{q1U$jqxX_`&f>OI zBweyq7AKbX_1xlgj=@yGm5L6k8u3nSuf6Vt8LSuG!1g}0*x#HEKBW)e9=QQ6aOy-VzZ z62v#vdJ(Le%w}-aB00etghhO?M_%|ng?p^TlU?mV_SK}kiSX&`q3+eKtM$@kHek$? z$A=9$x;vjGReTtKX9-jt30b2j=9`XNI*$)X4l<2f_`G@ic{4<4d$X*TOX#h? z5kAEL;Zq@eI8E9uX6>QX0Q2~^^P}wR^(Wa^Z1$w1n+}Wze5hszr_!>bsWA8(&lo$BnNb@qhz2y@+gi$(H(n`H8Q z^2S6|&!H*zK-GymkD!k;ZX3|IK+zL4KuAG~q6XefWR zvH(mTD{#v<=2D}~+BRFPJJFTtKd4;0+Dzn%}>tugA^J*dCRB)qzddDBdL8w!*;*gBU zRnO8eyVz4;L%M_$Zvx2RVW}TOSeNDqq%U zpd|(}n#mOe6{s>flY8HrjU?I3I0!CGhg+oQYDMNYcLc2Md@verzAY(6 zIQx!yb(3uA_q*MiVL>ipOL&JJ(QHK++LZr<^l87;<^u+`cL(@-HhQL?Y=bwIKcG86 z(bb~ZmrxxiI(XvBUsU%prAC0t+Kh$;TM zx}Y6fsGa-HBV&9gCnAkq$A_fV3%h3h+E0Gl`d;AmxBTm@+qeH9eaMn{lTD=09Y8e# zNS}8A>EjEE&rHc(ACW%#Nh<*(aZYi1vs4L-7D_L$juy{?T5W_dce?Oc;OL?An5 zG_w!W@REE(bXD9U>>icCt_L=w*?JT}}^=Nr|T>Opl-FkAwAd%w+ zZ|?#fSa!ITU{q@ij5-1xn1fIUrl%~@=8uVJ@qUsAff=>`31*u<*MWI;mEUr6gP2o% z4l_`gvHtQ8{+y3)RD7-7oWg9akwE~8}AQK|>0A9({e0a~tPT9UG;t!d8&dyJ_Y*5=cjjJ=!-6hUHeMjcn$z zSUtUK8E4q>H*X|$wg2)ezyML2&AU&0hu?a@!gPbw#lI{~3^H1c#h!BuZVT*M)QkL) zgO{6o^!^5EJYK@i2O_@>^y3L}uG~YW1^I!j-U4+0-4I?O%m-~eP=?yShu3wGA?hD4j*9 z*2(7XL;fLPWnq|8W~o8p7t~gAIKfc*gjEd|v3y9gt}Vj~xj2nXb8RF?`_`;n56NlY zGH(#4)mkaB7xnc7Xfa*Z%YUep;CLfwPmD1zj3m;VbD_#gShp0x9XEi=P@pB)Ym^8}WH_QxJypUH(xZE2h_9!#u>hrhB&6yc_$G1IF+psxp z5bpj#HaU>58Ipl~l+}LY_JBrbPh4H5oGEtJSVlLJjEqdF(#bw@iJd7W`o?ySvC~D_ zRTudvOm_@9600Grg)>^DF08HPF@je!%1;`pq1Q@ROKMJp`NRp` zVGf372ziEaA7u~XCXkBA+3QaZD()Yz>;vS1sewd}Iy_{w{M`qL#IDvM4-J}46Qvu) z3awq{Zf{|J1u|6s0VnJb$v3-MJ9S4j)cmnwz@z;O%B^ugr0S`rMuR zqv2-Tl5FF3=@VmB=)#}5*0|ea9r&PEbf}-<%R{R!BTtAHm|iY;1PCgPat@Z*TLeU0 z3)|0drOOWzoos?0izd(}L7V;$L;vG#P%6=KnY(FK~Q@tc>?KNv^ z@I(8vuG_bc<}bT9JvRXlkB!}-k^yj&j_s=`8|sT@RqZLAl!@TyY34I|BUu(ZNH2nQ z<}|)-+b?}OR1k-dE?x01tO{97hz@d7quR4Gjq)c79`4J&_0x~+NUKVhz@+v{>v-sR zyb3xL;ed;M^ZG7fc_itaoGm*(X$&cQR?3^KhEq0Lh!R2!>E0Wo6tRIX))mO;>CFO2 z$C`wi-|QsEHz37twW`^8QH5h$3j6jnCXk*V)^7IGop4pDhQSVa8#ltFIRCBA>@J1b z(3aq18>fgV4?0T2eZg*p<;>+^`JJaH7qe1hl5;Ajod98}7B^rS z?8iUTslJy@m;(I-$2N>+(1Y}#piR%1_)h8rN@u24@x#NtyYRAZK8rrICa5i#TosWW z2#PL&A09AbCOJkRbkFj#wJ(9m7}EmGEPOf??)ShWUaZ@uaSfo!*82`9yUVGmcB^0L z|3BotXH?T!`>(Hv9i^#Q5Cl|gfQSg8Bs!qLpfG|G3q?W52uN?C1S?euh=SCNiXb3F z%+LuzAO@sFg%~0wv=9k_03kp^$$tl&dFJOlFV6a(b>5uwW>~Xy%^LRF-(Bwgxvxw0 z!%L$Y$4Ys_@osxxlJ_2B0zO5Z)XI5bdR@nldt2E?5rne)oatmrFU}Og`+hbOqw#_! z{4+@)IMAw)eW^d4fZLt@Y`24Z@yB}vQmrz3qDCY0{!QWag$MH=mvm8g()dn4vcJne zdN)gSIj=x=?>ZSWY`$H6bOvvR&gmPlj!cX=2*`7S#k|5W@u*-;56!0E-~f# zYGnJfaF`wGV^ayz5ER9oVFTFmjOEH5$7g=z%FuonlK|gcAx+@mt`X6*;wS+UtK{sP z1KoEp@~vtgg!3%pdabMpctWt*eL`6w$+^1Db4=DI>`0qx2b)T9#F>V5wB-*Ru~0j~ z%k$KzhSsWS%%SXWu02~cPyMw;l-$MVu5lrLy)YfqsolNL_Q2KhY4uY~Ic8!QgLd8I zSL1eH-n(RyM^Nd&MMBruYW;)u?ih=~9H#75`Y214=NOcWiAbIg?KT-h4Pn*seJ{W9 zlu?|tMJ(tu@SNY%y27VO^ItXJxV7=6c|bDai`^#Y35+vgeG59_5q&qsLd zcSPF}$MUflfk-^2|G_8BM`Pk`^TWZ{gqiZV zwYey!u9RgdT&o%HfaJA0SQ0Ok+RYa$_nl}AzpA;(_{t`~=$*1g>9Ix0Bvlt|a;w~C zM>8bb-EazTQ88;LnS8D~qabWXgZo}RZo9-|R>?@DC-z?GrEM8(Jz&BeyH20`BXFDB z+k+8NLM8yd@al9O-Us*ISo{a)5Zv#|R;^kSzeq+M^OPXg*Nu4_%%eiBCuM@b0$sN# zvFY27vnu!4CxUZagruFCqiL#Jt>a z;6Bb_ZZLPuyywl_uitU&2~TvCG#Hr(Zf)8bQAvRYN(yqA1?kYQCxz;e0D~!l^$<-_ z%YX?|`d2``>KxlPsawhikSY|Z1pv&zt~dDL{_1_d6$}^Mr%|#4jk8pfIf2{uY%cy& z3OV$H{kd|##~yMB)wkDWo=v&p5}*$bs={n>N+%`&w&oPVv+-b@!vfv~5Zv}Tz8Om` z2yUs(d#U(th72$8%9qoL%vwOp1aubJ`)*yEcQoX*cKfS<5Xd~E0YUztkR6-k#1Z3_ zGo59v!z%obP`)ittYENC_3QZ;4U2dp71@FL{!VUFL4=aBe67Xo$3EYoCNdn_De1O? zKa>nyWFM$+|J9y116pAcW}L^HxElONW#;8`ue!KR4m??i=yumyGmoD=j$L6u-!Exs zTUB6FjhqH}7>;NjfeVn+Fk^1h{|ot}NJlV8lElGLUHqw8c~9}49@;>#2IE`kyyF?>dGgE@Go^=<($Cos;f0$w;|Rlzj~1S-#6I0|QUT|KL=XbT>LMjUdqXAh z_6{>6sbIuCNX6w1-N!tOVsp52c8^`u*p6uzCsH9w7>^_s z>I&gjA5?Q6iX z^0gk`SGSjdRk=PvkX`7JaU($uiD`DBVV?`afhkGR*zM(cb>E!wclY=xP7Z#*=uY5z=SD(_NhHM~;q-b_SQJcH zML6;5r%E2^0w&c+C@aP_odQZ;bL1+4)ffaje7&~_)PtXs9H;qS8jO?*ruv#_n#=1l zEiJ*(amXr^4QM}uXe7Jv|H&1iE`U(KNArU>e!frFc+irEk0PY&;725kKJ1PqH$T|o zAny(>0~)eENUZn%>9_CXJR?^&RjJM?SW-Q`DNk_Z=K70=-vrwg-t$wpi0gQpr*X-> zwy@AJq}=hl8q;wYW3~bVL1_(^!nglrA)~Ur<&Ttv@s6r^kgk~zPOS*A)nsQqW0{

5=3z&^$j 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 s$8SCT%`DKW|Nr6t& Date: Sun, 28 Sep 2025 23:47:39 +0200 Subject: [PATCH 07/31] Update examples_report.md --- tools/rexm/examples_report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index bb37b2db7..7c9de0b57 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -57,6 +57,7 @@ Example elements validated: | core_high_dpi | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_render_texture | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_undo_redo | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_input_actions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_basic_shapes | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_bouncing_ball | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_colors_palette | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From f76666af3f08765037f2b5b5b350eb70a61202ec Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 28 Sep 2025 23:48:10 +0200 Subject: [PATCH 08/31] REXM: Avoid updating metadata from `others` examples --- tools/rexm/rexm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 7f303538c..f6c297769 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -2331,7 +2331,7 @@ static const char *GenerateUUIDv4(void) // Update source code header and comments metadata static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *info) { - if (FileExists(exSrcPath) && IsFileExtension(exSrcPath, ".c")) + if (FileExists(exSrcPath) && IsFileExtension(exSrcPath, ".c") && (!TextIsEqual(info->category, "others"))) { // WARNING: Cache a copy of exSrcPath to avoid modifications by TextFormat() char exSourcePath[512] = { 0 }; From 584bc14929d3083c6a5a6c8979d6989cd17fc9a5 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:28:20 +0200 Subject: [PATCH 09/31] [rlgl] Add Software Rendering Support (#4832) * add base of rlsw.h * implement state support Also replace the triangle rasterization functions with macros that generate specific functions for each state of the rendering system. Also, add the OpenGL definitions in order to add a binding for rlgl. * branchless float saturation * apply perspective correction to colors * impl line clipping and rasterization + tweak function names * impl face culling * impl color blending * fixes and tweaks * add clear buffer bitmasks * small optimizations / tweaks * review ndc to screen projection * avoid to recalculate MVP when its not needed + tweaks * review the loading and management of textures to be closer to the OpenGL API * texture sampling optimization * review get pixel functions + review unorm/float conversion * add several buffer format support Several depth and color formats have been added for the framebuffer. 8-bit, 16-bit, and 24-bit formats are now available for depth. RGB 8-bit (332), RGB 16-bit (565), and RGB 24-bit (888) formats are now available for color. Alpha support is no longer present for the framebuffer at the moment, but it can easily be restored by adding the formats and reinterpolating the alpha in the areas that do not perform color blending. Additionally, this commit brings performance improvements. * tweaks * impl line width * impl points + point size * fix and improve polygon clipping functions * impl polygone modes * add some not planned functions - `glDepthMask` - `glColorMask` * framebuffer resizing + handle init failure * add quick notes about line clipping algorithms used * start to impl scissor test + review line clipping The support for the scissor test has been implemented for clearing as well as for triangle clipping. The implementation for lines and points is still missing. I also removed the 2D clipping of lines that used the Cohen-Sutherland algorithm, opting instead to always use the Liang-Barsky algorithm in all cases. This simplifies the implementation, and the 2D version would have caused issues when interpolating vertices in the future if we want to implement additional features. * review scissor clear * review `swScissor` * impl line scissor clipping * round screen coordinate (line rasterization) * impl point scissor clipping * remove unused defs * add getter functions * gl binding * add `glHint` and `glShadeModel` macros (not implmented) * binding tweaks * impl copy framebuffer function + glReadPixels * review `swCopyFramebuffer` * update rlgl.h * update rlgl.h * texture copy support * fix typo.. * add get error function * def sw alloc macros * reimpl get color buffer func just in case * remove normal interpolation * review texture wrap * fix ndc projection (viewport/scissor) * impl framebuffer blit function * reduce matrix compuations and memory usage * swBegin tweaks * preventing a possible division by zero * remove useless scissor related data * review color blending system * greatly improve float saturation * tweak lerp vertex function * use opitmized fract function in sw_texture_map * tweak framebuffer functions for better readability * optimized copy/blit functions for each dst format * review framebuffer filling functions * impl specific quad rendering func * use of a single global vertex buffer * fix 'sw_poly_point_render' * added `SW_RESTRICT` and redesigned `sw_lerp_vertex_PNCTH` * tweak the pipeline flow regarding the face culling avoids misprediction, improves vectorization if possible * new rendering path for axis aligned quads * oops, translating some comments * use of `restrict` for blending function parameters * update rlgl.h * adding `GRAPHICS_API_OPENGL_11_SOFTWARE` in `DrawMesh` * add `RL_OPENGL_11_SOFTWARE` enum * temp tweak * build fixes * fix DrawMesh for GL 1.1 * update swClose * review texture format + fix copy * set minimum req vertices to 3 (quads) * check swInit * review pixelformat * tweaks * fix animNormals (DrawMesh) * fallback color/texcoord (swDrawArrays) * review swMultMatrixf * fix texture pool alloc.. * review triangle scanlines increment all data * fix `sw_quad_sort_cw` * impl sdl platform * rm def * increase max clipped polygon vertices * improve triangle rasterization along Y axis improved robustness against numerical errors incremental interpolation along Y simplified function, fewer jumps * review current vertex data + increase max clipped polygon vertices (for extreme cases) * fix and improve polygon clipping Sets the vertex count to zero when the polygon is invalid Stops clipping when the vertex count drops below 3 * fix gradient calculation * cache texture size minus one + comments * tweaks * BGRA copy support * adding software backend option (cmake) * update Makefile * fix face culling * excluse some exemple with the software backend * review SW_CLAMP case in sw_texture_map * review sw_saturate * review line raster * fix sw_quad_is_aligned * review sw_raster_quad_axis_aligned * tweaks * codepoint fix (?) * fix var name... * rcore_drm software renderering * cleanup and tweaks * adding support for `GL_POINT_SIZE` and `GL_LINE_WIDTH` get * fix sampling issue * fix swBlendFunc --------- Co-authored-by: Ray --- CMakeOptions.txt | 2 +- cmake/LibraryConfigurations.cmake | 2 + examples/CMakeLists.txt | 4 + src/Makefile | 18 +- src/external/rlsw.h | 5230 +++++++++++++++++++++++++++++ src/platforms/rcore_desktop_sdl.c | 143 +- src/platforms/rcore_drm.c | 364 +- src/rlgl.h | 87 +- src/rmodels.c | 2 +- 9 files changed, 5739 insertions(+), 113 deletions(-) create mode 100644 src/external/rlsw.h diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 3c1c12742..32cfb814e 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -8,7 +8,7 @@ if(EMSCRIPTEN) endif() enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM;SDL" "Platform to build for.") -enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0" "Force a specific OpenGL Version?") +enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0;Software" "Force a specific OpenGL Version?") # Configuration options option(BUILD_EXAMPLES "Build the examples." ${PROJECT_IS_TOP_LEVEL}) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index 98c7f2362..8f127fc23 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -158,6 +158,8 @@ if (NOT ${OPENGL_VERSION} MATCHES "OFF") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") elseif (${OPENGL_VERSION} MATCHES "ES 3.0") set(GRAPHICS "GRAPHICS_API_OPENGL_ES3") + elseif (${OPENGL_VERSION} MATCHES "Software") + set(GRAPHICS "GRAPHICS_API_OPENGL_11_SOFTWARE") endif () if (NOT "${SUGGESTED_GRAPHICS}" STREQUAL "" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}") message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail.") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e63c1a7c8..b482df0fc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -105,6 +105,10 @@ elseif ("${PLATFORM}" STREQUAL "DRM") list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) +elseif ("${OPENGL_VERSION}" STREQUAL "Software") + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/rlgl_standalone.c) + list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/others/raylib_opengl_interop.c) + elseif (NOT SUPPORT_GESTURES_SYSTEM) # Items requiring gestures system list(REMOVE_ITEM example_sources ${CMAKE_CURRENT_SOURCE_DIR}/textures/textures_mouse_painting.c) diff --git a/src/Makefile b/src/Makefile index f2bdd811e..e767d21d0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -235,20 +235,22 @@ endif # NOTE: By default use OpenGL 3.3 on desktop platforms ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 - #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 - #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 - #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) + #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) GRAPHICS ?= GRAPHICS_API_OPENGL_33 endif ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 - #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 - #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 - #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) + #GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used diff --git a/src/external/rlsw.h b/src/external/rlsw.h new file mode 100644 index 000000000..2177467ec --- /dev/null +++ b/src/external/rlsw.h @@ -0,0 +1,5230 @@ +/** + * MIT License + * + * Copyright (c) 2025 Le Juez Victor + * + * 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 RLSW_H +#define RLSW_H + +#include +#include + +/* === RLSW Definition And Macros === */ + +#ifndef SW_MALLOC +# define SW_MALLOC(sz) malloc(sz) +#endif + +#ifndef SW_REALLOC +# define SW_REALLOC(ptr, newSz) realloc(ptr, newSz) +#endif + +#ifndef SW_FREE +# define SW_FREE(ptr) free(ptr) +#endif + +#ifndef SW_RESTRICT +# ifdef _MSC_VER +# define SW_RESTRICT __restrict +# else +# define SW_RESTRICT restrict +# endif +#endif + +#ifndef SW_GL_FRAMEBUFFER_COPY_BGRA +# define SW_GL_FRAMEBUFFER_COPY_BGRA true +#endif + +#ifndef SW_GL_BINDING_COPY_TEXTURE +# define SW_GL_BINDING_COPY_TEXTURE true +#endif + +#ifndef SW_COLOR_BUFFER_BITS +# define SW_COLOR_BUFFER_BITS 24 +#endif + +#ifndef SW_DEPTH_BUFFER_BITS +# define SW_DEPTH_BUFFER_BITS 16 +#endif + +#ifndef SW_MAX_PROJECTION_STACK_SIZE +# define SW_MAX_PROJECTION_STACK_SIZE 2 +#endif + +#ifndef SW_MAX_MODELVIEW_STACK_SIZE +# define SW_MAX_MODELVIEW_STACK_SIZE 8 +#endif + +#ifndef SW_MAX_TEXTURE_STACK_SIZE +# define SW_MAX_TEXTURE_STACK_SIZE 2 +#endif + +#ifndef SW_MAX_TEXTURES +# define SW_MAX_TEXTURES 128 +#endif + +#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES +// Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane. +// Considering the largest polygon involved is a quadrilateral (4 vertices), +// and that clipping occurs against both the frustum (6 planes) +// and the scissors (4 planes), +// the maximum number of vertices after clipping is: +// 4 (original vertices) + 6 (frustum planes) + 4 (scissors planes) = 14 +# define SW_MAX_CLIPPED_POLYGON_VERTICES 14 +#endif + +#ifndef SW_CLIP_EPSILON +# define SW_CLIP_EPSILON 1e-4f +#endif + +/* === OpenGL Compatibility Types === */ + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; +typedef short GLshort; +typedef int GLint; +typedef unsigned char GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef int GLsizei; +typedef float GLfloat; +typedef float GLclampf; +typedef double GLdouble; +typedef double GLclampd; + +/* === OpenGL Definitions === */ + +#define GL_FALSE 0 +#define GL_TRUE 1 + +#define GL_SCISSOR_TEST 0x0C11 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_DEPTH_TEST 0x0B71 +#define GL_CULL_FACE 0x0B44 +#define GL_BLEND 0x0BE2 + +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +//#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +//#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +//#define GL_COLOR_WRITEMASK 0x0C23 +//#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_COLOR 0x0B00 +//#define GL_CURRENT_NORMAL 0x0B02 +//#define GL_CURRENT_RASTER_COLOR 0x0B04 +//#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +//#define GL_CURRENT_RASTER_INDEX 0x0B05 +//#define GL_CURRENT_RASTER_POSITION 0x0B07 +//#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +//#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_POINT_SIZE 0x0B11 +#define GL_LINE_WIDTH 0x0B21 +//#define GL_INDEX_CLEAR_VALUE 0x0C20 +//#define GL_INDEX_MODE 0x0C30 +//#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +//#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +//#define GL_RENDER_MODE 0x0C40 +//#define GL_RGBA_MODE 0x0C31 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_VIEWPORT 0x0BA2 + +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_DEPTH_BUFFER_BIT 0x00000100 + +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 //< WARNING: Not implemented (defined for RLGL) +#define GL_COLOR_ARRAY 0x8076 +//#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 + +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +//#define GL_LINE_LOOP 0x0002 +//#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +//#define GL_TRIANGLE_STRIP 0x0005 +//#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +//#define GL_QUAD_STRIP 0x0008 +//#define GL_POLYGON 0x0009 + +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 + +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 + +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 + +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 + +#define GL_REPEAT 0x2901 +#define GL_CLAMP 0x2900 + +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 + +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 + +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +#define GL_ALPHA 0x1906 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 + +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 + +/* === Not Used Definitions === */ + +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_SMOOTH 0x1D01 +#define GL_NICEST 0x1102 +#define GL_CCW 0x0901 +#define GL_CW 0x0900 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 + +/* === OpenGL Binding === */ + +#define glReadPixels(x, y, w, h, f, t, p) swCopyFramebuffer((x), (y), (w), (h), (f), (t), (p)) +#define glEnable(state) swEnable((state)) +#define glDisable(state) swDisable((state)) +#define glGetFloatv(pname, params) swGetFloatv((pname), (params)) +#define glGetString(pname) swGetString((pname)) +#define glGetError() swGetError() +#define glViewport(x, y, w, h) swViewport((x), (y), (w), (h)) +#define glScissor(x, y, w, h) swScissor((x), (y), (w), (h)) +#define glClearColor(r, g, b, a) swClearColor((r), (g), (b), (a)) +#define glClear(bitmask) swClear((bitmask)) +#define glBlendFunc(sfactor, dfactor) swBlendFunc((sfactor), (dfactor)) +#define glPolygonMode(face, mode) swPolygonMode((mode)) +#define glCullFace(face) swCullFace((face)) +#define glPointSize(size) swPointSize((size)) +#define glLineWidth(width) swLineWidth((width)) +#define glMatrixMode(mode) swMatrixMode((mode)) +#define glPushMatrix() swPushMatrix() +#define glPopMatrix() swPopMatrix() +#define glLoadIdentity() swLoadIdentity() +#define glTranslatef(x, y, z) swTranslatef((x), (y), (z)) +#define glRotatef(a, x, y, z) swRotatef((a), (x), (y), (z)) +#define glScalef(x, y, z) swScalef((x), (y), (z)) +#define glMultMatrixf(v) swMultMatrixf((v)) +#define glFrustum(l, r, b, t, n, f) swFrustum((l), (r), (b), (t), (n), (f)) +#define glOrtho(l, r, b, t, n, f) swOrtho((l), (r), (b), (t), (n), (f)) +#define glBegin(mode) swBegin((mode)) +#define glEnd() swEnd() +#define glVertex2i(x, y) swVertex2i((x), (y)) +#define glVertex2f(x, y) swVertex2f((x), (y)) +#define glVertex2fv(v) swVertex2fv((v)) +#define glVertex3i(x, y, z) swVertex3i((x), (y), (z)) +#define glVertex3f(x, y, z) swVertex3f((x), (y), (z)) +#define glvertex3fv(v) swVertex3fv((v)) +#define glVertex4i(x, y, z, w) swVertex4i((x), (y), (z), (w)) +#define glVertex4f(x, y, z, w) swVertex4f((x), (y), (z), (w)) +#define glVertex4fv(v) swVertex4fv((v)) +#define glColor3ub(r, g, b) swColor3ub((r), (g), (b)) +#define glColor3ubv(v) swColor3ubv((v)) +#define glColor3f(r, g, b) swColor3f((r), (g), (b)) +#define glColor3fv(v) swColor3fv((v)) +#define glColor4ub(r, g, b, a) swColor4ub((r), (g), (b), (a)) +#define glColor4ubv(v) swColor4ubv((v)) +#define glColor4f(r, g, b, a) swColor4f((r), (g), (b), (a)) +#define glColor4fv(v) swColor4fv((v)) +#define glTexCoord2f(u, v) swTexCoord2f((u), (v)) +#define glTexCoord2fv(v) swTexCoord2fv((v)) + +#define glEnableClientState(t) ((void)(t)) +#define glDisableClientState(t) swBindArray((t), 0) +#define glVertexPointer(sz, t, s, p) swBindArray(SW_VERTEX_ARRAY, (p)) +#define glTexCoordPointer(sz, t, s, p) swBindArray(SW_TEXTURE_COORD_ARRAY, (p)) +#define glColorPointer(sz, t, s, p) swBindArray(SW_COLOR_ARRAY, (p)) +#define glDrawArrays(m, o, c) swDrawArrays((m), (o), (c)) +#define glGenTextures(c, v) swGenTextures((c), (v)) +#define glDeleteTextures(c, v) swDeleteTextures((c), (v)) +#define glTexImage2D(tr, l, if, w, h, b, f, t, p) swTexImage2D((w), (h), (f), (t), SW_GL_BINDING_COPY_TEXTURE, (p)) +#define glTexParameteri(tr, pname, param) swTexParameteri((pname), (param)) +#define glBindTexture(tr, id) swBindTexture((id)) + +/* === Not Implemented === */ + +#define glClearDepth(X) ((void)(X)) +#define glDepthMask(X) ((void)(X)) +#define glColorMask(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) +#define glPixelStorei(X,Y) ((void)(X),(void)(Y)) +#define glHint(X,Y) ((void)(X),(void)(Y)) +#define glShadeModel(X) ((void)(X)) +#define glFrontFace(X) ((void)(X)) +#define glDepthFunc(X) ((void)(X)) +#define glTexSubImage2D(X,Y,Z,W,A,B,C,D,E) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A),(void)(B),(void)(C),(void)(D),(void)(E)) +#define glGetTexImage(X,Y,Z,W,A) ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A)) +#define glDrawElements(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) +#define glNormal3f(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) +#define glNormal3fv(X) ((void)(X)) +#define glNormalPointer(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) + +/* === RLSW Enums === */ + +typedef enum { + SW_SCISSOR_TEST = GL_SCISSOR_TEST, + SW_TEXTURE_2D = GL_TEXTURE_2D, + SW_DEPTH_TEST = GL_DEPTH_TEST, + SW_CULL_FACE = GL_CULL_FACE, + SW_BLEND = GL_BLEND +} SWstate; + +typedef enum { + SW_VENDOR = GL_VENDOR, + SW_RENDERER = GL_RENDERER, + SW_VERSION = GL_VERSION, + SW_EXTENSIONS = GL_EXTENSIONS, + SW_COLOR_CLEAR_VALUE = GL_COLOR_CLEAR_VALUE, + SW_CURRENT_COLOR = GL_CURRENT_COLOR, + SW_CURRENT_TEXTURE_COORDS = GL_CURRENT_TEXTURE_COORDS, + SW_POINT_SIZE = GL_POINT_SIZE, + SW_LINE_WIDTH = GL_LINE_WIDTH, + SW_MODELVIEW_MATRIX = GL_MODELVIEW_MATRIX, + SW_MODELVIEW_STACK_DEPTH = GL_MODELVIEW_STACK_DEPTH, + SW_PROJECTION_MATRIX = GL_PROJECTION_MATRIX, + SW_PROJECTION_STACK_DEPTH = GL_PROJECTION_STACK_DEPTH, + SW_TEXTURE_MATRIX = GL_TEXTURE_MATRIX, + SW_TEXTURE_STACK_DEPTH = GL_TEXTURE_STACK_DEPTH, + SW_VIEWPORT = GL_VIEWPORT +} SWget; + +typedef enum { + SW_COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT, + SW_DEPTH_BUFFER_BIT = GL_DEPTH_BUFFER_BIT +} SWbuffer; + +typedef enum { + SW_PROJECTION = GL_PROJECTION, + SW_MODELVIEW = GL_MODELVIEW, + SW_TEXTURE = GL_TEXTURE +} SWmatrix; + +typedef enum { + SW_VERTEX_ARRAY = GL_VERTEX_ARRAY, + SW_TEXTURE_COORD_ARRAY = GL_TEXTURE_COORD_ARRAY, + SW_COLOR_ARRAY = GL_COLOR_ARRAY +} SWarray; + +typedef enum { + SW_POINTS = GL_POINTS, + SW_LINES = GL_LINES, + SW_TRIANGLES = GL_TRIANGLES, + SW_QUADS = GL_QUADS +} SWdraw; + +typedef enum { + SW_POINT = GL_POINT, + SW_LINE = GL_LINE, + SW_FILL = GL_FILL +} SWpoly; + +typedef enum { + SW_FRONT = GL_FRONT, + SW_BACK = GL_BACK, +} SWface; + +typedef enum { + SW_ZERO = GL_ZERO, + SW_ONE = GL_ONE, + SW_SRC_COLOR = GL_SRC_COLOR, + SW_ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR, + SW_SRC_ALPHA = GL_SRC_ALPHA, + SW_ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA, + SW_DST_ALPHA = GL_DST_ALPHA, + SW_ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA, + SW_DST_COLOR = GL_DST_COLOR, + SW_ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR, + SW_SRC_ALPHA_SATURATE = GL_SRC_ALPHA_SATURATE +} SWfactor; + +typedef enum { + SW_LUMINANCE = GL_LUMINANCE, + SW_LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA, + SW_RGB = GL_RGB, + SW_RGBA = GL_RGBA, +} SWformat; + +typedef enum { + SW_UNSIGNED_BYTE = GL_UNSIGNED_BYTE, + SW_BYTE = GL_BYTE, + SW_UNSIGNED_SHORT = GL_UNSIGNED_SHORT, + SW_SHORT = GL_SHORT, + SW_UNSIGNED_INT = GL_UNSIGNED_INT, + SW_INT = GL_INT, + SW_FLOAT = GL_FLOAT +} SWtype; + +typedef enum { + SW_NEAREST = GL_NEAREST, + SW_LINEAR = GL_LINEAR +} SWfilter; + +typedef enum { + SW_REPEAT = GL_REPEAT, + SW_CLAMP = GL_CLAMP, +} SWwrap; + +typedef enum { + SW_TEXTURE_MIN_FILTER = GL_TEXTURE_MIN_FILTER, + SW_TEXTURE_MAG_FILTER = GL_TEXTURE_MAG_FILTER, + SW_TEXTURE_WRAP_S = GL_TEXTURE_WRAP_S, + SW_TEXTURE_WRAP_T = GL_TEXTURE_WRAP_T +} SWtexparam; + +typedef enum { + SW_NO_ERROR = GL_NO_ERROR, + SW_INVALID_ENUM = GL_INVALID_ENUM, + SW_INVALID_VALUE = GL_INVALID_VALUE, + SW_STACK_OVERFLOW = GL_STACK_OVERFLOW, + SW_STACK_UNDERFLOW = GL_STACK_UNDERFLOW, + SW_INVALID_OPERATION = GL_INVALID_OPERATION, +} SWerrcode; + +/* === Public API === */ + +bool swInit(int w, int h); +void swClose(void); + +bool swResizeFramebuffer(int w, int h); +void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void* pixels); +void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void* pixels); +void* swGetColorBuffer(int* w, int* h); + +void swEnable(SWstate state); +void swDisable(SWstate state); + +void swGetFloatv(SWget name, float* v); +const char* swGetString(SWget name); +SWerrcode swGetError(void); + +void swViewport(int x, int y, int width, int height); +void swScissor(int x, int y, int width, int height); + +void swClearColor(float r, float g, float b, float a); +void swClear(uint32_t bitmask); + +void swBlendFunc(SWfactor sfactor, SWfactor dfactor); +void swPolygonMode(SWpoly mode); +void swCullFace(SWface face); + +void swPointSize(float size); +void swLineWidth(float width); + +void swMatrixMode(SWmatrix mode); +void swPushMatrix(void); +void swPopMatrix(void); +void swLoadIdentity(void); +void swTranslatef(float x, float y, float z); +void swRotatef(float angle, float x, float y, float z); +void swScalef(float x, float y, float z); +void swMultMatrixf(const float* mat); +void swFrustum(double left, double right, double bottom, double top, double znear, double zfar); +void swOrtho(double left, double right, double bottom, double top, double znear, double zfar); + +void swBegin(SWdraw mode); +void swEnd(void); + +void swVertex2i(int x, int y); +void swVertex2f(float x, float y); +void swVertex2fv(const float* v); +void swVertex3i(int x, int y, int z); +void swVertex3f(float x, float y, float z); +void swVertex3fv(const float* v); +void swVertex4i(int x, int y, int z, int w); +void swVertex4f(float x, float y, float z, float w); +void swVertex4fv(const float* v); + +void swColor3ub(uint8_t r, uint8_t g, uint8_t b); +void swColor3ubv(const uint8_t* v); +void swColor3f(float r, float g, float b); +void swColor3fv(const float* v); +void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +void swColor4ubv(const uint8_t* v); +void swColor4f(float r, float g, float b, float a); +void swColor4fv(const float* v); + +void swTexCoord2f(float u, float v); +void swTexCoord2fv(const float* v); + +void swBindArray(SWarray type, void *buffer); +void swDrawArrays(SWdraw mode, int offset, int count); + +void swGenTextures(int count, uint32_t* textures); +void swDeleteTextures(int count, uint32_t* textures); + +void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void* data); +void swTexParameteri(int param, int value); +void swBindTexture(uint32_t id); + +#endif // RLSW_H + +/* === RLSW Implementation === */ + +#ifdef RLSW_IMPL + +#include +#include +#include + +/* === Defines and Macros === */ + +#define SW_PI 3.14159265358979323846f +#define SW_DEG2RAD (SW_PI/180.0f) +#define SW_RAD2DEG (180.0f/SW_PI) + +#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS / 8) +#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS / 8) + +#define SW_STATE_CHECK(flags) ((RLSW.stateFlags & (flags)) == (flags)) + +#define SW_STATE_SCISSOR_TEST (1 << 0) +#define SW_STATE_TEXTURE_2D (1 << 1) +#define SW_STATE_DEPTH_TEST (1 << 2) +#define SW_STATE_CULL_FACE (1 << 3) +#define SW_STATE_BLEND (1 << 4) + +/* === Internal Structs === */ + +typedef enum { + SW_PIXELFORMAT_UNKNOWN = 0, + SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE, // 8 bit per pixel (no alpha) + SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp + SW_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + SW_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) +} sw_pixelformat_t; + +typedef void (*sw_factor_f)( + float* SW_RESTRICT factor, + const float* SW_RESTRICT src, + const float* SW_RESTRICT dst +); + +typedef float sw_matrix_t[4*4]; +typedef uint16_t sw_half_t; + +typedef struct { + + float position[4]; // Position coordinates + float texcoord[2]; // Texture coordinates + float color[4]; // Color + + float homogeneous[4]; // Homogeneous coordinates + float screen[2]; // Screen coordinates + +} sw_vertex_t; + +typedef struct { + + // Dirty hack for copied data + // TODO: Rework copied image handling + + union { + const void* cptr; //< NOTE: Is used for all data reads + void* ptr; //< WARNING: Should only be used to allocate and free data + } pixels; + + int width, height; //< Dimensions of the texture + int wMinus1, hMinus1; //< Dimensions minus one + sw_pixelformat_t format; //< Pixel format (internal representation) + + SWfilter minFilter; //< Minification filter + SWfilter magFilter; //< Magnification filter + + SWwrap sWrap; //< texcoord.x wrap mode + SWwrap tWrap; //< texcoord.y wrap mode + + float tx; //< Texel width + float ty; //< Texel height + + bool copy; //< Flag indicating whether memory has been allocated + +} sw_texture_t; + +typedef struct { + void *color; + void *depth; + int width; + int height; + int allocSz; +} sw_framebuffer_t; + +typedef struct { + + sw_framebuffer_t framebuffer; + float clearColor[4]; // Color used to clear the screen + float clearDepth; // Depth value used to clear the screen + + int vpCenter[2]; // Represents the center of the viewport + int vpHalfSize[2]; // Represents the half dimensions of the viewport + int vpSize[2]; // Represents the dimensions of the viewport (minus one) + int vpMin[2]; // Represents the minimum renderable point of the viewport (top-left) + int vpMax[2]; // Represents the maximum renderable point of the viewport (bottom-right) + + int scMin[2]; // Represents the minimum renderable point of the scissor rect (top-left) + int scMax[2]; // Represents the maximum renderable point of the scissor rect (bottom-right) + float scClipMin[2]; // Represents the minimum renderable point of the scissor rect in clip space + float scClipMax[2]; // Represents the maximum renderable point of the scissor rect in clip space + + uint32_t currentTexture; + + struct { + float* positions; + float* texcoords; + uint8_t* colors; + } array; + + struct { + float texcoord[2]; + float color[4]; + } current; + + sw_vertex_t vertexBuffer[SW_MAX_CLIPPED_POLYGON_VERTICES]; // Buffer used for storing primitive vertices, used for processing and rendering + int vertexCounter; // Number of vertices in 'ctx.vertexBuffer' + + SWdraw drawMode; // Current primitive mode (e.g., lines, triangles) + SWpoly polyMode; // Current polygon filling mode (e.g., lines, triangles) + int reqVertices; // Number of vertices required for the primitive being drawn + float pointRadius; // Rasterized point radius + float lineWidth; // Rasterized line width + + sw_matrix_t stackProjection[SW_MAX_PROJECTION_STACK_SIZE]; // Projection matrix stack for push/pop operations + sw_matrix_t stackModelview[SW_MAX_MODELVIEW_STACK_SIZE]; // Modelview matrix stack for push/pop operations + sw_matrix_t stackTexture[SW_MAX_TEXTURE_STACK_SIZE]; // Texture matrix stack for push/pop operations + uint32_t stackProjectionCounter; // Counter for matrix stack operations + uint32_t stackModelviewCounter; // Counter for matrix stack operations + uint32_t stackTextureCounter; // Counter for matrix stack operations + SWmatrix currentMatrixMode; // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION) + sw_matrix_t* currentMatrix; // Pointer to the currently used matrix according to the mode + sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally + bool isDirtyMVP; // Indicates if the MVP matrix should be rebuilt + + SWfactor srcFactor; + SWfactor dstFactor; + + sw_factor_f srcFactorFunc; + sw_factor_f dstFactorFunc; + + SWface cullFace; // Faces to cull + SWerrcode errCode; // Last error code + + sw_texture_t* loadedTextures; + int loadedTextureCount; + + uint32_t* freeTextureIds; + int freeTextureIdCount; + + uint32_t stateFlags; + +} sw_context_t; + +/* === Global Data === */ + +static sw_context_t RLSW = { 0 }; + +/* === Helper Functions === */ + +static inline void sw_matrix_id(sw_matrix_t dst) +{ + dst[0] = 1, dst[1] = 0, dst[2] = 0, dst[3] = 0; + dst[4] = 0, dst[5] = 1, dst[6] = 0, dst[7] = 0; + dst[8] = 0, dst[9] = 0, dst[10] = 1, dst[11] = 0; + dst[12] = 0, dst[13] = 0, dst[14] = 0, dst[15] = 1; +} + +static inline void sw_matrix_mul_rst(float* SW_RESTRICT dst, const float* SW_RESTRICT left, const float* SW_RESTRICT right) +{ + float l00 = left[0], l01 = left[1], l02 = left[2], l03 = left[3]; + float l10 = left[4], l11 = left[5], l12 = left[6], l13 = left[7]; + float l20 = left[8], l21 = left[9], l22 = left[10], l23 = left[11]; + float l30 = left[12], l31 = left[13], l32 = left[14], l33 = left[15]; + + dst[0] = l00 * right[0] + l01 * right[4] + l02 * right[8] + l03 * right[12]; + dst[4] = l10 * right[0] + l11 * right[4] + l12 * right[8] + l13 * right[12]; + dst[8] = l20 * right[0] + l21 * right[4] + l22 * right[8] + l23 * right[12]; + dst[12] = l30 * right[0] + l31 * right[4] + l32 * right[8] + l33 * right[12]; + + dst[1] = l00 * right[1] + l01 * right[5] + l02 * right[9] + l03 * right[13]; + dst[5] = l10 * right[1] + l11 * right[5] + l12 * right[9] + l13 * right[13]; + dst[9] = l20 * right[1] + l21 * right[5] + l22 * right[9] + l23 * right[13]; + dst[13] = l30 * right[1] + l31 * right[5] + l32 * right[9] + l33 * right[13]; + + dst[2] = l00 * right[2] + l01 * right[6] + l02 * right[10] + l03 * right[14]; + dst[6] = l10 * right[2] + l11 * right[6] + l12 * right[10] + l13 * right[14]; + dst[10] = l20 * right[2] + l21 * right[6] + l22 * right[10] + l23 * right[14]; + dst[14] = l30 * right[2] + l31 * right[6] + l32 * right[10] + l33 * right[14]; + + dst[3] = l00 * right[3] + l01 * right[7] + l02 * right[11] + l03 * right[15]; + dst[7] = l10 * right[3] + l11 * right[7] + l12 * right[11] + l13 * right[15]; + dst[11] = l20 * right[3] + l21 * right[7] + l22 * right[11] + l23 * right[15]; + dst[15] = l30 * right[3] + l31 * right[7] + l32 * right[11] + l33 * right[15]; +} + +static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right) +{ + float result[16]; + + sw_matrix_mul_rst(result, left, right); + + for (int i = 0; i < 16; i++) { + dst[i] = result[i]; + } +} + +static inline float sw_saturate(float x) +{ + union { float f; uint32_t u; } fb; + fb.f = x; + + // Check if x < 0.0f + // If sign bit is set (MSB), x is negative + if ((fb.u & 0x80000000) != 0) { + return 0.0f; + } + + // Check if x > 1.0f + // Works for positive floats: IEEE 754 ordering matches integer ordering + if (fb.u > 0x3F800000) { + return 1.0f; + } + + // x is in [0.0f, 1.0f] + return x; +} + +static inline float sw_fract(float x) +{ + return x - floorf(x); +} + +static inline int sw_clampi(int v, int min, int max) +{ + if (v < min) return min; + if (v > max) return max; + return v; +} + +static inline void sw_lerp_vertex_PTCH( + sw_vertex_t* SW_RESTRICT out, + const sw_vertex_t* SW_RESTRICT a, + const sw_vertex_t* SW_RESTRICT b, + float t) +{ + const float tInv = 1.0f - t; + + // Position interpolation (4 components) + out->position[0] = a->position[0] * tInv + b->position[0] * t; + out->position[1] = a->position[1] * tInv + b->position[1] * t; + out->position[2] = a->position[2] * tInv + b->position[2] * t; + out->position[3] = a->position[3] * tInv + b->position[3] * t; + + // Texture coordinate interpolation (2 components) + out->texcoord[0] = a->texcoord[0] * tInv + b->texcoord[0] * t; + out->texcoord[1] = a->texcoord[1] * tInv + b->texcoord[1] * t; + + // Color interpolation (4 components) + out->color[0] = a->color[0] * tInv + b->color[0] * t; + out->color[1] = a->color[1] * tInv + b->color[1] * t; + out->color[2] = a->color[2] * tInv + b->color[2] * t; + out->color[3] = a->color[3] * tInv + b->color[3] * t; + + // Homogeneous coordinate interpolation (4 components) + out->homogeneous[0] = a->homogeneous[0] * tInv + b->homogeneous[0] * t; + out->homogeneous[1] = a->homogeneous[1] * tInv + b->homogeneous[1] * t; + out->homogeneous[2] = a->homogeneous[2] * tInv + b->homogeneous[2] * t; + out->homogeneous[3] = a->homogeneous[3] * tInv + b->homogeneous[3] * t; +} + +static inline void sw_get_vertex_grad_PTCH( + sw_vertex_t* SW_RESTRICT out, + const sw_vertex_t* SW_RESTRICT a, + const sw_vertex_t* SW_RESTRICT b, + float scale) +{ + // Calculate gradients for Position + out->position[0] = (b->position[0] - a->position[0]) * scale; + out->position[1] = (b->position[1] - a->position[1]) * scale; + out->position[2] = (b->position[2] - a->position[2]) * scale; + out->position[3] = (b->position[3] - a->position[3]) * scale; + + // Calculate gradients for Texture coordinates + out->texcoord[0] = (b->texcoord[0] - a->texcoord[0]) * scale; + out->texcoord[1] = (b->texcoord[1] - a->texcoord[1]) * scale; + + // Calculate gradients for Color + out->color[0] = (b->color[0] - a->color[0]) * scale; + out->color[1] = (b->color[1] - a->color[1]) * scale; + out->color[2] = (b->color[2] - a->color[2]) * scale; + out->color[3] = (b->color[3] - a->color[3]) * scale; + + // Calculate gradients for Homogeneous coordinates + out->homogeneous[0] = (b->homogeneous[0] - a->homogeneous[0]) * scale; + out->homogeneous[1] = (b->homogeneous[1] - a->homogeneous[1]) * scale; + out->homogeneous[2] = (b->homogeneous[2] - a->homogeneous[2]) * scale; + out->homogeneous[3] = (b->homogeneous[3] - a->homogeneous[3]) * scale; +} + +static inline void sw_add_vertex_grad_PTCH( + sw_vertex_t* SW_RESTRICT out, + const sw_vertex_t* SW_RESTRICT gradients) +{ + // Add gradients to Position + out->position[0] += gradients->position[0]; + out->position[1] += gradients->position[1]; + out->position[2] += gradients->position[2]; + out->position[3] += gradients->position[3]; + + // Add gradients to Texture coordinates + out->texcoord[0] += gradients->texcoord[0]; + out->texcoord[1] += gradients->texcoord[1]; + + // Add gradients to Color + out->color[0] += gradients->color[0]; + out->color[1] += gradients->color[1]; + out->color[2] += gradients->color[2]; + out->color[3] += gradients->color[3]; + + // Add gradients to Homogeneous coordinates + out->homogeneous[0] += gradients->homogeneous[0]; + out->homogeneous[1] += gradients->homogeneous[1]; + out->homogeneous[2] += gradients->homogeneous[2]; + out->homogeneous[3] += gradients->homogeneous[3]; +} + +/* === Half Floating Point === */ + +static inline uint32_t sw_f16_to_f32_ui(uint16_t h) +{ + uint32_t s = (uint32_t)(h & 0x8000) << 16; + int32_t em = h & 0x7fff; + + // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15) + int32_t r = (em + (112 << 10)) << 13; + + // denormal: flush to zero + r = (em < (1 << 10)) ? 0 : r; + + // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases + // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 + r += (em >= (31 << 10)) ? (112 << 23) : 0; + + return s | r; +} + +static inline float sw_f16_to_f32(sw_half_t y) +{ + union { float f; uint32_t i; } v = { + .i = sw_f16_to_f32_ui(y) + }; + return v.f; +} + +static inline uint16_t sw_f16_from_f32_ui(uint32_t ui) +{ + int32_t s = (ui >> 16) & 0x8000; + int32_t em = ui & 0x7fffffff; + + // bias exponent and round to nearest; 112 is relative exponent bias (127-15) + int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; + + // underflow: flush to zero; 113 encodes exponent -14 + h = (em < (113 << 23)) ? 0 : h; + + // overflow: infinity; 143 encodes exponent 16 + h = (em >= (143 << 23)) ? 0x7c00 : h; + + // NaN; note that we convert all types of NaN to qNaN + h = (em > (255 << 23)) ? 0x7e00 : h; + + return (uint16_t)(s | h); +} + +static inline sw_half_t sw_f16_from_f32(float i) +{ + union { float f; uint32_t i; } v; + v.f = i; + return sw_f16_from_f32_ui(v.i); +} + +/* === Framebuffer Part === */ + +static inline bool sw_framebuffer_load(int w, int h) +{ + int size = w * h; + + RLSW.framebuffer.color = SW_MALLOC(SW_COLOR_PIXEL_SIZE * size); + if (RLSW.framebuffer.color == NULL) return false; + + RLSW.framebuffer.depth = SW_MALLOC(SW_DEPTH_PIXEL_SIZE * size); + if (RLSW.framebuffer.depth == NULL) return false; + + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + RLSW.framebuffer.allocSz = w * h; + + return true; +} + +static inline bool sw_framebuffer_resize(int w, int h) +{ + int newSize = w * h; + + if (newSize <= RLSW.framebuffer.allocSz) { + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + return true; + } + + void* newColor = SW_REALLOC(RLSW.framebuffer.color, newSize); + if (newColor == NULL) return false; + + void* newDepth = SW_REALLOC(RLSW.framebuffer.depth, newSize); + if (newDepth == NULL) return false; + + RLSW.framebuffer.color = newColor; + RLSW.framebuffer.depth = newDepth; + + RLSW.framebuffer.width = w; + RLSW.framebuffer.height = h; + RLSW.framebuffer.allocSz = newSize; + + return true; +} + +static inline void* sw_framebuffer_get_color_addr(const void* ptr, uint32_t offset) +{ + return (uint8_t*)ptr + offset * SW_COLOR_PIXEL_SIZE; +} + +static inline void sw_framebuffer_inc_color_addr(void** ptr) +{ + *ptr = (void*)(((uint8_t*)*ptr) + SW_COLOR_PIXEL_SIZE); +} + +static inline void sw_framebuffer_inc_const_color_addr(const void** ptr) +{ + *ptr = (const void*)(((const uint8_t*)*ptr) + SW_COLOR_PIXEL_SIZE); +} + +static inline void* sw_framebuffer_get_depth_addr(const void* ptr, uint32_t offset) +{ + return (uint8_t*)ptr + offset * SW_DEPTH_PIXEL_SIZE; +} + +static inline void sw_framebuffer_inc_depth_addr(void** ptr) +{ + *ptr = (void*)(((uint8_t*)*ptr) + SW_DEPTH_PIXEL_SIZE); +} + +#if (SW_COLOR_BUFFER_BITS == 8) // RGB - 332 + +static inline void sw_framebuffer_read_color(float dst[4], const void* src) +{ + uint8_t pixel = ((uint8_t*)src)[0]; + + dst[0] = ((pixel >> 5) & 0x07) * (1.0f / 7.0f); + dst[1] = ((pixel >> 2) & 0x07) * (1.0f / 7.0f); + dst[2] = (pixel & 0x03) * (1.0f / 3.0f); + dst[3] = 1.0f; +} + +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void* src) +{ + uint8_t pixel = ((const uint8_t*)src)[0]; + + uint8_t r = (pixel >> 5) & 0x07; + uint8_t g = (pixel >> 2) & 0x07; + uint8_t b = pixel & 0x03; + + dst[0] = (r * 255 + 3) / 7; + dst[1] = (g * 255 + 3) / 7; + dst[2] = (b * 255 + 1) / 3; + dst[3] = 255; +} + +static inline void sw_framebuffer_write_color(void* dst, const float color[3]) +{ + uint8_t r = ((uint8_t)(color[0] * UINT8_MAX) >> 5) & 0x07; + uint8_t g = ((uint8_t)(color[1] * UINT8_MAX) >> 5) & 0x07; + uint8_t b = ((uint8_t)(color[2] * UINT8_MAX) >> 6) & 0x03; + + ((uint8_t*)dst)[0] = (r << 5) | (g << 2) | b; +} + +static inline void sw_framebuffer_fill_color(void* ptr, int size, const float color[3]) +{ + uint8_t r8 = (uint8_t)(color[0] * 7.0f + 0.5f); + uint8_t g8 = (uint8_t)(color[1] * 7.0f + 0.5f); + uint8_t b8 = (uint8_t)(color[2] * 3.0f + 0.5f); + + uint8_t packedColor = ((r8 & 0x07) << 5) | ((g8 & 0x07) << 2) | (b8 & 0x03); + + uint8_t* p = (uint8_t*)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + uint8_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) { + *curPtr++ = packedColor; + } + } + } + else { + for (int i = 0; i < size; i++) { + *p++ = packedColor; + } + } +} + +#elif (SW_COLOR_BUFFER_BITS == 16) // RGB - 565 + +static inline void sw_framebuffer_read_color(float dst[4], const void* src) +{ + uint16_t pixel = ((uint16_t*)src)[0]; + + dst[0] = ((pixel >> 11) & 0x1F) * (1.0f / 31.0f); + dst[1] = ((pixel >> 5) & 0x3F) * (1.0f / 63.0f); + dst[2] = (pixel & 0x1F) * (1.0f / 31.0f); + dst[3] = 1.0f; +} + +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void* src) +{ + uint16_t pixel = ((const uint16_t*)src)[0]; + + uint8_t r = (pixel >> 11) & 0x1F; + uint8_t g = (pixel >> 5) & 0x3F; + uint8_t b = pixel & 0x1F; + + dst[0] = (r * 255 + 15) / 31; + dst[1] = (g * 255 + 31) / 63; + dst[2] = (b * 255 + 15) / 31; + dst[3] = 255; +} + +static inline void sw_framebuffer_write_color(void* dst, const float color[3]) +{ + uint8_t r = (uint8_t)(color[0] * 31.0f + 0.5f) & 0x1F; + uint8_t g = (uint8_t)(color[1] * 63.0f + 0.5f) & 0x3F; + uint8_t b = (uint8_t)(color[2] * 31.0f + 0.5f) & 0x1F; + + ((uint16_t*)dst)[0] = (r << 11) | (g << 5) | b; +} + +static inline void sw_framebuffer_fill_color(void* ptr, int size, const float color[3]) +{ + uint16_t r16 = (uint16_t)(color[0] * 31.0f + 0.5f); + uint16_t g_16 = (uint16_t)(color[1] * 63.0f + 0.5f); + uint16_t b_16 = (uint16_t)(color[2] * 31.0f + 0.5f); + + uint16_t packedColor = ((r16 & 0x1F) << 11) | ((g_16 & 0x3F) << 5) | (b_16 & 0x1F); + + uint16_t* p = (uint16_t*)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + uint16_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) { + *curPtr++ = packedColor; + } + } + } + else { + for (int i = 0; i < size; i++) { + *p++ = packedColor; + } + } +} + +#elif (SW_COLOR_BUFFER_BITS == 24) // RGB - 888 + +static inline void sw_framebuffer_read_color(float dst[4], const void* src) +{ + dst[0] = ((uint8_t*)src)[0] * (1.0f / 255.0f); + dst[1] = ((uint8_t*)src)[1] * (1.0f / 255.0f); + dst[2] = ((uint8_t*)src)[2] * (1.0f / 255.0f); + dst[3] = 1.0f; +} + +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void* src) +{ + dst[0] = ((uint8_t*)src)[0]; + dst[1] = ((uint8_t*)src)[1]; + dst[2] = ((uint8_t*)src)[2]; + dst[3] = 255; +} + +static inline void sw_framebuffer_write_color(void* dst, const float color[3]) +{ + ((uint8_t*)dst)[0] = (uint8_t)(color[0] * UINT8_MAX); + ((uint8_t*)dst)[1] = (uint8_t)(color[1] * UINT8_MAX); + ((uint8_t*)dst)[2] = (uint8_t)(color[2] * UINT8_MAX); +} + +static inline void sw_framebuffer_fill_color(void* ptr, int size, const float color[3]) +{ + uint8_t r = (uint8_t)(color[0] * 255.0f); + uint8_t g = (uint8_t)(color[1] * 255.0f); + uint8_t b = (uint8_t)(color[2] * 255.0f); + + uint8_t* p = (uint8_t*)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + uint8_t* curPtr = p + 3 * (y * RLSW.framebuffer.width + RLSW.scMin[0]); + for (int xCount = 0; xCount < wScissor; xCount++) { + *curPtr++ = r; + *curPtr++ = g; + *curPtr++ = b; + } + } + } + else { + for (int i = 0; i < size; i++) { + *p++ = r; + *p++ = g; + *p++ = b; + } + } +} + +#endif // SW_COLOR_BUFFER_BITS + +#if (SW_DEPTH_BUFFER_BITS == 8) + +static inline float sw_framebuffer_read_depth(const void* src) +{ + return (float)((uint8_t*)src)[0] * (1.0f / UINT8_MAX); +} + +static inline void sw_framebuffer_write_depth(void* dst, float depth) +{ + ((uint8_t*)dst)[0] = (uint8_t)(depth * UINT8_MAX); +} + +static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) +{ + uint8_t d8 = (uint8_t)(value * UINT8_MAX); + uint8_t* p = (uint8_t*)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + uint8_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) { + *curPtr++ = d8; + } + } + } + else { + for (int i = 0; i < size; i++) { + *p++ = d8; + } + } +} + +#elif (SW_DEPTH_BUFFER_BITS == 16) + +static inline float sw_framebuffer_read_depth(const void* src) +{ + return (float)((uint16_t*)src)[0] * (1.0f / UINT16_MAX); +} + +static inline void sw_framebuffer_write_depth(void* dst, float depth) +{ + ((uint16_t*)dst)[0] = (uint16_t)(depth * UINT16_MAX); +} + +static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) +{ + uint16_t d16 = (uint16_t)(value * UINT16_MAX); + uint16_t* p = (uint16_t*)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + uint16_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) { + *curPtr++ = d16; + } + } + } + else { + for (int i = 0; i < size; i++) { + *p++ = d16; + } + } +} + +#elif (SW_DEPTH_BUFFER_BITS == 24) + +static inline float sw_framebuffer_read_depth(const void* src) +{ + uint32_t depth24 = (((uint8_t*)src)[0] << 16) | + (((uint8_t*)src)[1] << 8) | + ((uint8_t*)src)[2]; + + return depth24 / (float)0xFFFFFF; +} + +static inline void sw_framebuffer_write_depth(void* dst, float depth) +{ + uint32_t depth24 = (uint32_t)(depth * 0xFFFFFF); + + ((uint8_t*)dst)[0] = (depth24 >> 16) & 0xFF; + ((uint8_t*)dst)[1] = (depth24 >> 8) & 0xFF; + ((uint8_t*)dst)[2] = depth24 & 0xFF; +} + +static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) +{ + uint32_t d32 = (uint32_t)(value * UINT32_MAX); + uint8_t d_byte0 = (uint8_t)((d32 >> 16) & 0xFF); + uint8_t d_byte1 = (uint8_t)((d32 >> 8) & 0xFF); + uint8_t d_byte2 = (uint8_t)(d32 & 0xFF); + + uint8_t* p = (uint8_t*)ptr; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + uint8_t* curPtr = p + 3 * (y * RLSW.framebuffer.width + RLSW.scMin[0]); + for (int xCount = 0; xCount < wScissor; xCount++) { + *curPtr++ = d_byte0; + *curPtr++ = d_byte1; + *curPtr++ = d_byte2; + } + } + } + else { + for (int i = 0; i < size; i++) { + *p++ = d_byte0; + *p++ = d_byte1; + *p++ = d_byte2; + } + } +} + +#endif // SW_DEPTH_BUFFER_BITS + +static inline void sw_framebuffer_fill(void* colorPtr, void* depthPtr, int size, float color[4], float depth_value) +{ +#if (SW_COLOR_BUFFER_BITS == 8) + // Calculate and pack 3:3:2 color + // Scale color components to the max value for each bit depth and round + uint8_t r8 = (uint8_t)(color[0] * 7.0f + 0.5f); + uint8_t g8 = (uint8_t)(color[1] * 7.0f + 0.5f); + uint8_t b8 = (uint8_t)(color[2] * 3.0f + 0.5f); + // Pack the components into a single byte + uint8_t packedColor = ((r8 & 0x07) << 5) | ((g8 & 0x07) << 2) | (b8 & 0x03); + uint8_t* cptr = (uint8_t*)colorPtr; +#elif (SW_COLOR_BUFFER_BITS == 16) + // Calculate and pack 5:6:5 color + // Scale color components to the max value for each bit depth and round + uint16_t r16 = (uint16_t)(color[0] * 31.0f + 0.5f); + uint16_t g16 = (uint16_t)(color[1] * 63.0f + 0.5f); + uint16_t b16 = (uint16_t)(color[2] * 31.0f + 0.5f); + // Pack the components into a 16-bit value + uint16_t packedColor = ((r16 & 0x1F) << 11) | ((g16 & 0x3F) << 5) | (b16 & 0x1F); + uint16_t* cptr = (uint16_t*)colorPtr; +#elif (SW_COLOR_BUFFER_BITS == 24) + // Calculate 8:8:8 color components + uint8_t r24 = (uint8_t)(color[0] * 255.0f); + uint8_t g24 = (uint8_t)(color[1] * 255.0f); + uint8_t b24 = (uint8_t)(color[2] * 255.0f); + uint8_t* cptr = (uint8_t*)colorPtr; +#endif + +#if (SW_DEPTH_BUFFER_BITS == 8) + // Calculate 8-bit depth + uint8_t d8 = (uint8_t)(depth_value * UINT8_MAX); + uint8_t* dptr = (uint8_t*)depthPtr; +#elif (SW_DEPTH_BUFFER_BITS == 16) + // Calculate 16-bit depth + uint16_t d16 = (uint16_t)(depth_value * UINT16_MAX); + uint16_t* dptr = (uint16_t*)depthPtr; +#elif (SW_DEPTH_BUFFER_BITS == 24) + // Calculate 24-bit depth and pre-calculate bytes + uint32_t d32 = (uint32_t)(depth_value * UINT32_MAX); + uint8_t dByte0 = (uint8_t)((d32 >> 16) & 0xFF); + uint8_t dByte1 = (uint8_t)((d32 >> 8) & 0xFF); + uint8_t dByte2 = (uint8_t)(d32 & 0xFF); + uint8_t* dptr = (uint8_t*)depthPtr; +#endif + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { + int rowStartIdx = y * RLSW.framebuffer.width + RLSW.scMin[0]; + + // Calculate starting pointers for the current row within the scissor rectangle +# if (SW_COLOR_BUFFER_BITS == 8) + uint8_t* curCPtr = cptr + rowStartIdx; +# elif (SW_COLOR_BUFFER_BITS == 16) + uint16_t* curCPtr = cptr + rowStartIdx; +# elif (SW_COLOR_BUFFER_BITS == 24) + uint8_t* curCPtr = cptr + 3 * rowStartIdx; +# endif + +# if (SW_DEPTH_BUFFER_BITS == 8) + uint8_t* curDPtr = dptr + rowStartIdx; +# elif (SW_DEPTH_BUFFER_BITS == 16) + uint16_t* curDPtr = dptr + rowStartIdx; +# elif (SW_DEPTH_BUFFER_BITS == 24) + uint8_t* curDPtr = dptr + 3 * rowStartIdx; +# endif + + // Fill the current row within the scissor rectangle + for (int xCount = 0; xCount < wScissor; xCount++) + { + // Write color +# if (SW_COLOR_BUFFER_BITS == 8) + *curCPtr++ = packedColor; +# elif (SW_COLOR_BUFFER_BITS == 16) + *curCPtr++ = packedColor; +# elif (SW_COLOR_BUFFER_BITS == 24) + *curCPtr++ = r24; + *curCPtr++ = g24; + *curCPtr++ = b24; +# endif + + // Write depth +# if (SW_DEPTH_BUFFER_BITS == 8) + *curDPtr++ = d8; +# elif (SW_DEPTH_BUFFER_BITS == 16) + *curDPtr++ = d16; +# elif (SW_DEPTH_BUFFER_BITS == 24) + *curDPtr++ = dByte0; + *curDPtr++ = dByte1; + *curDPtr++ = dByte2; +# endif + } + } + return; + } + + for (int i = 0; i < size; i++) + { + // Write color +# if (SW_COLOR_BUFFER_BITS == 8) + *cptr++ = packedColor; +# elif (SW_COLOR_BUFFER_BITS == 16) + *cptr++ = packedColor; +# elif (SW_COLOR_BUFFER_BITS == 24) + *cptr++ = r24; + *cptr++ = g24; + *cptr++ = b24; +# endif + + // Write depth +# if (SW_DEPTH_BUFFER_BITS == 8) + *dptr++ = d8; +# elif (SW_DEPTH_BUFFER_BITS == 16) + *dptr++ = d16; +# elif (SW_DEPTH_BUFFER_BITS == 24) + *dptr++ = dByte0; + *dptr++ = dByte1; + *dptr++ = dByte2; +# endif + } +} + +#define DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T* dst) \ +{ \ + const void* src = RLSW.framebuffer.color; \ + \ + for (int iy = y; iy < h; iy++) { \ + for (int ix = x; ix < w; ix++) { \ + uint8_t color[4]; \ + sw_framebuffer_read_color8(color, src); \ + +#define DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T* dst) \ +{ \ + const void* src = RLSW.framebuffer.color; \ + \ + for (int iy = y; iy < h; iy++) { \ + for (int ix = x; ix < w; ix++) { \ + float color[4]; \ + sw_framebuffer_read_color(color, src); \ + +#define DEFINE_FRAMEBUFFER_COPY_END() \ + sw_framebuffer_inc_const_color_addr(&src); \ + } \ + } \ +} + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(GRAYSCALE, uint8_t) +{ + // NTSC grayscale conversion: Y = 0.299R + 0.587G + 0.114B + uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + *dst++ = gray; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(GRAYALPHA, uint8_t) +{ + // Convert RGB to grayscale using NTSC formula + uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + + dst[0] = gray; + dst[1] = color[3]; // alpha + + dst += 2; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R5G6B5, uint16_t) +{ + // Convert 8-bit RGB to 5:6:5 format + uint8_t r5 = (color[0] * 31 + 127) / 255; + uint8_t g6 = (color[1] * 63 + 127) / 255; + uint8_t b5 = (color[2] * 31 + 127) / 255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; +#else // RGBA + uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; +#endif + + *dst++ = rgb565; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R8G8B8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R5G5B5A1, uint16_t) +{ + uint8_t r5 = (color[0] * 31 + 127) / 255; + uint8_t g5 = (color[1] * 31 + 127) / 255; + uint8_t b5 = (color[2] * 31 + 127) / 255; + uint8_t a1 = color[3] >= 128 ? 1 : 0; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1; +#else // RGBA + uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R4G4B4A4, uint16_t) +{ + uint8_t r4 = (color[0] * 15 + 127) / 255; + uint8_t g4 = (color[1] * 15 + 127) / 255; + uint8_t b4 = (color[2] * 15 + 127) / 255; + uint8_t a4 = (color[3] * 15 + 127) / 255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; +#else // RGBA + uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R8G8B8A8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R32, float) +{ + dst[0] = color[0]; + dst++; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R32G32B32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R32G32B32A32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R16, sw_half_t) +{ + dst[0] = sw_f16_from_f32(color[0]); + dst++; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R16G16B16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_COPY_END() + +DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(R16G16B16A16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + dst[3] = sw_f16_from_f32(color[3]); + + dst += 4; +} +DEFINE_FRAMEBUFFER_COPY_END() + +#define DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_blit_to_##name( \ + int xDst, int yDst, int wDst, int hDst, \ + int xSrc, int ySrc, int wSrc, int hSrc, \ + DST_PTR_T* dst) \ +{ \ + const uint8_t* srcBase = RLSW.framebuffer.color; \ + int fbWidth = RLSW.framebuffer.width; \ + \ + uint32_t xScale = ((uint32_t)wSrc << 16) / (uint32_t)wDst; \ + uint32_t yScale = ((uint32_t)hSrc << 16) / (uint32_t)hDst; \ + \ + for (int dy = 0; dy < hDst; dy++) { \ + uint32_t yFix = ((uint32_t)ySrc << 16) + dy * yScale; \ + int sy = yFix >> 16; \ + \ + for (int dx = 0; dx < wDst; dx++) { \ + uint32_t xFix = dx * xScale; \ + int sx = xFix >> 16; \ + const void* srcPtr = sw_framebuffer_get_color_addr(srcBase, sy * fbWidth + sx); \ + uint8_t color[4]; \ + sw_framebuffer_read_color8(color, srcPtr); \ + +#define DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(name, DST_PTR_T) \ +static inline void sw_framebuffer_blit_to_##name( \ + int xDst, int yDst, int wDst, int hDst, \ + int xSrc, int ySrc, int wSrc, int hSrc, \ + DST_PTR_T* dst) \ +{ \ + const uint8_t* srcBase = RLSW.framebuffer.color; \ + int fbWidth = RLSW.framebuffer.width; \ + \ + uint32_t xScale = ((uint32_t)wSrc << 16) / (uint32_t)wDst; \ + uint32_t yScale = ((uint32_t)hSrc << 16) / (uint32_t)hDst; \ + \ + for (int dy = 0; dy < hDst; dy++) { \ + uint32_t yFix = ((uint32_t)ySrc << 16) + dy * yScale; \ + int sy = yFix >> 16; \ + \ + for (int dx = 0; dx < wDst; dx++) { \ + uint32_t xFix = dx * xScale; \ + int sx = xFix >> 16; \ + const void* srcPtr = sw_framebuffer_get_color_addr(srcBase, sy * fbWidth + sx); \ + float color[4]; \ + sw_framebuffer_read_color(color, srcPtr); \ + +#define DEFINE_FRAMEBUFFER_BLIT_END() \ + } \ + } \ +} + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(GRAYSCALE, uint8_t) +{ + uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + *dst++ = gray; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(GRAYALPHA, uint8_t) +{ + uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + + dst[0] = gray; + dst[1] = color[3]; // alpha + + dst += 2; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R5G6B5, uint16_t) +{ + uint8_t r5 = (color[0] * 31 + 127) / 255; + uint8_t g6 = (color[1] * 63 + 127) / 255; + uint8_t b5 = (color[2] * 31 + 127) / 255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; +#else // RGBA + uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5; +#endif + + *dst++ = rgb565; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R8G8B8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R5G5B5A1, uint16_t) +{ + uint8_t r5 = (color[0] * 31 + 127) / 255; + uint8_t g5 = (color[1] * 31 + 127) / 255; + uint8_t b5 = (color[2] * 31 + 127) / 255; + uint8_t a1 = color[3] >= 128 ? 1 : 0; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1; +#else // RGBA + uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R4G4B4A4, uint16_t) +{ + uint8_t r4 = (color[0] * 15 + 127) / 255; + uint8_t g4 = (color[1] * 15 + 127) / 255; + uint8_t b4 = (color[2] * 15 + 127) / 255; + uint8_t a4 = (color[3] * 15 + 127) / 255; + +#if SW_GL_FRAMEBUFFER_COPY_BGRA + uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; +#else // RGBA + uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4; +#endif + + *dst++ = pixel; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R8G8B8A8, uint8_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R32, uint8_t) +{ + dst[0] = color[0]; + dst++; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R32G32B32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R32G32B32A32, float) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = color[2]; + dst[1] = color[1]; + dst[2] = color[0]; +#else // RGBA + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; +#endif + dst[3] = color[3]; + + dst += 4; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16, sw_half_t) +{ + dst[0] = sw_f16_from_f32(color[0]); + dst++; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16G16B16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + + dst += 3; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16G16B16A16, sw_half_t) +{ +#if SW_GL_FRAMEBUFFER_COPY_BGRA + dst[0] = sw_f16_from_f32(color[2]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[0]); +#else // RGBA + dst[0] = sw_f16_from_f32(color[0]); + dst[1] = sw_f16_from_f32(color[1]); + dst[2] = sw_f16_from_f32(color[2]); +#endif + dst[3] = sw_f16_from_f32(color[3]); + + dst += 4; +} +DEFINE_FRAMEBUFFER_BLIT_END() + +/* === Pixel Format Part === */ + +static inline int sw_get_pixel_format(SWformat format, SWtype type) +{ + int channels = 0; + int bitsPerChannel = 8; // Default: 8 bits per channel + + // Determine the number of channels (format) + switch (format) { + case SW_LUMINANCE: channels = 1; break; + case SW_LUMINANCE_ALPHA: channels = 2; break; + case SW_RGB: channels = 3; break; + case SW_RGBA: channels = 4; break; + default: + return SW_PIXELFORMAT_UNKNOWN; + } + + // Determine the depth of each channel (type) + switch (type) { + case SW_UNSIGNED_BYTE: bitsPerChannel = 8; break; + case SW_BYTE: bitsPerChannel = 8; break; + case SW_UNSIGNED_SHORT: bitsPerChannel = 16; break; + case SW_SHORT: bitsPerChannel = 16; break; + case SW_UNSIGNED_INT: bitsPerChannel = 32; break; + case SW_INT: bitsPerChannel = 32; break; + case SW_FLOAT: bitsPerChannel = 32; break; + default: + return SW_PIXELFORMAT_UNKNOWN; + } + + // Map the format and type to the correct internal format + if (bitsPerChannel == 8) { + if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + if (channels == 2) return SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; + if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8; + if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + } + else if (bitsPerChannel == 16) { + if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R16; + if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16; + if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16; + } + else if (bitsPerChannel == 32) { + if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R32; + if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; + if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; + } + + return SW_PIXELFORMAT_UNKNOWN; +} + +int sw_get_pixel_bytes(sw_pixelformat_t format) +{ + int bpp = 0; + + switch (format) { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 1; break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 2; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 4*3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 4*4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 2; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 2*3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 2*4; break; + default: break; + } + + return bpp; +} + +static inline void sw_get_pixel_grayscale(float* color, const void* pixels, uint32_t offset) +{ + float gray = (float)((uint8_t*)pixels)[offset] * (1.0f / 255); + + color[0] = gray; + color[1] = gray; + color[2] = gray; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_red_16(float* color, const void* pixels, uint32_t offset) +{ + float value = sw_f16_to_f32(((sw_half_t*)pixels)[offset]); + + color[0] = value; + color[1] = value; + color[2] = value; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_red_32(float* color, const void* pixels, uint32_t offset) +{ + float value = ((float*)pixels)[offset]; + + color[0] = value; + color[1] = value; + color[2] = value; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_grayscale_alpha(float* color, const void* pixels, uint32_t offset) +{ + const uint8_t* pixelData = (const uint8_t*)pixels + 2 * offset; + + color[0] = color[1] = color[2] = (float)pixelData[0] * (1.0f / 255); + color[3] = (float)pixelData[1] * (1.0f / 255); +} + +static inline void sw_get_pixel_rgb_565(float* color, const void* pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t*)pixels)[offset]; + + color[0] = (float)((pixel & 0xF800) >> 11) / 31; + color[1] = (float)((pixel & 0x7E0) >> 5) / 63; + color[2] = (float)(pixel & 0x1F) / 31; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_888(float* color, const void* pixels, uint32_t offset) +{ + const uint8_t* pixel = (const uint8_t*)pixels + 3 * offset; + + color[0] = (float)pixel[0] * (1.0f / 255); + color[1] = (float)pixel[1] * (1.0f / 255); + color[2] = (float)pixel[2] * (1.0f / 255); + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_161616(float* color, const void* pixels, uint32_t offset) +{ + const sw_half_t *pixel = (sw_half_t*)pixels + 3 * offset; + + color[0] = sw_f16_to_f32(pixel[0]); + color[1] = sw_f16_to_f32(pixel[1]); + color[2] = sw_f16_to_f32(pixel[2]); + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgb_323232(float* color, const void* pixels, uint32_t offset) +{ + const float *pixel = (float*)pixels + 3 * offset; + + color[0] = pixel[0]; + color[1] = pixel[1]; + color[2] = pixel[2]; + color[3] = 1.0f; +} + +static inline void sw_get_pixel_rgba_5551(float* color, const void* pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t*)pixels)[offset]; + + color[0] = (float)((pixel & 0xF800) >> 11) / 31; + color[1] = (float)((pixel & 0x7C0) >> 6) / 31; + color[2] = (float)((pixel & 0x3E) >> 1) / 31; + color[3] = (float)(pixel & 0x1); +} + +static inline void sw_get_pixel_rgba_4444(float* color, const void* pixels, uint32_t offset) +{ + uint16_t pixel = ((uint16_t*)pixels)[offset]; + + color[0] = (float)((pixel & 0xF000) >> 12) / 15; + color[1] = (float)((pixel & 0xF00) >> 8) / 15; + color[2] = (float)((pixel & 0xF0) >> 4) / 15; + color[3] = (float)(pixel & 0xF) / 15; +} + +static inline void sw_get_pixel_rgba_8888(float* color, const void* pixels, uint32_t offset) +{ + const uint8_t *pixel = (uint8_t*)pixels + 4 * offset; + + color[0] = (float)pixel[0] * (1.0f / 255); + color[1] = (float)pixel[1] * (1.0f / 255); + color[2] = (float)pixel[2] * (1.0f / 255); + color[3] = (float)pixel[3] * (1.0f / 255); +} + +static inline void sw_get_pixel_rgba_16161616(float* color, const void* pixels, uint32_t offset) +{ + const sw_half_t *pixel = (sw_half_t*)pixels + 4 * offset; + + color[0] = sw_f16_to_f32(pixel[0]); + color[1] = sw_f16_to_f32(pixel[1]); + color[2] = sw_f16_to_f32(pixel[2]); + color[3] = sw_f16_to_f32(pixel[3]); +} + +static inline void sw_get_pixel_rgba_32323232(float* color, const void* pixels, uint32_t offset) +{ + const float *pixel = (float*)pixels + 4 * offset; + + color[0] = pixel[0]; + color[1] = pixel[1]; + color[2] = pixel[2]; + color[3] = pixel[3]; +} + +static inline void sw_get_pixel(float* color, const void* pixels, uint32_t offset, sw_pixelformat_t format) +{ + switch (format) { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + sw_get_pixel_grayscale(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + sw_get_pixel_grayscale_alpha(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + sw_get_pixel_rgb_565(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: + sw_get_pixel_rgb_888(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + sw_get_pixel_rgba_5551(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + sw_get_pixel_rgba_4444(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + sw_get_pixel_rgba_8888(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: + sw_get_pixel_red_32(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: + sw_get_pixel_rgb_323232(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + sw_get_pixel_rgba_32323232(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: + sw_get_pixel_red_16(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: + sw_get_pixel_rgb_161616(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + sw_get_pixel_rgba_16161616(color, pixels, offset); + break; + case SW_PIXELFORMAT_UNKNOWN: + break; + } +} + +/* === Texture Sampling Part === */ + +static inline void sw_texture_sample_nearest(float* color, const sw_texture_t* tex, float u, float v) +{ + u = (tex->sWrap == SW_REPEAT) ? sw_fract(u) : sw_saturate(u); + v = (tex->tWrap == SW_REPEAT) ? sw_fract(v) : sw_saturate(v); + + int x = u * tex->width, y = v * tex->height; + + sw_get_pixel(color, tex->pixels.cptr, y * tex->width + x, tex->format); +} + +static inline void sw_texture_sample_linear(float* color, const sw_texture_t* tex, float u, float v) +{ + // REVIEW: With a bit more cleverness we could clearly reduce the + // number of operations here, but for now it works fine. + + float xf = u * tex->width - 0.5f; + float yf = v * tex->height - 0.5f; + + int x0 = (int)floorf(xf); + int y0 = (int)floorf(yf); + + float fx = xf - x0; + float fy = yf - y0; + + int x1 = x0 + 1; + int y1 = y0 + 1; + + if (tex->sWrap == SW_CLAMP) { + x0 = x0 > tex->wMinus1 ? tex->wMinus1 : x0; + x1 = x1 > tex->wMinus1 ? tex->wMinus1 : x1; + } + else { + x0 = (x0 % tex->width + tex->width) % tex->width; + x1 = (x1 % tex->width + tex->width) % tex->width; + } + + if (tex->tWrap == SW_CLAMP) { + y0 = y0 > tex->hMinus1 ? tex->hMinus1 : y0; + y1 = y1 > tex->hMinus1 ? tex->hMinus1 : y1; + } + else { + y0 = (y0 % tex->height + tex->height) % tex->height; + y1 = (y1 % tex->height + tex->height) % tex->height; + } + + float c00[4], c10[4], c01[4], c11[4]; + sw_get_pixel(c00, tex->pixels.cptr, y0 * tex->width + x0, tex->format); + sw_get_pixel(c10, tex->pixels.cptr, y0 * tex->width + x1, tex->format); + sw_get_pixel(c01, tex->pixels.cptr, y1 * tex->width + x0, tex->format); + sw_get_pixel(c11, tex->pixels.cptr, y1 * tex->width + x1, tex->format); + + for (int i = 0; i < 4; i++) { + float t = c00[i] + fx * (c10[i] - c00[i]); + float b = c01[i] + fx * (c11[i] - c01[i]); + color[i] = t + fy * (b - t); + } +} + +static inline void sw_texture_sample(float* color, const sw_texture_t* tex, float u, float v, + float duDx, float duDy, float dvDx, float dvDy) +{ + // Previous method: There is no need to compute the square root + // because using the squared value, the comparison remains `L2 > 1.0f * 1.0f` + //float du = sqrtf(duDx * duDx + duDy * duDy); + //float dv = sqrtf(dvDx * dvDx + dvDy * dvDy); + //float L = (du > dv) ? du : dv; + + // Calculate the derivatives for each axis + float du2 = duDx * duDx + duDy * duDy; + float dv2 = dvDx * dvDx + dvDy * dvDy; + float L2 = (du2 > dv2) ? du2 : dv2; + + SWfilter filter = (L2 > 1.0f) + ? tex->minFilter : tex->magFilter; + + switch (filter) { + case SW_NEAREST: + sw_texture_sample_nearest(color, tex, u, v); + break; + case SW_LINEAR: + sw_texture_sample_linear(color, tex, u, v); + break; + } +} + +/* === Color Blending Functions === */ + +static inline void sw_factor_zero(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = 0.0f; +} + +static inline void sw_factor_one(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = 1.0f; +} + +static inline void sw_factor_src_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3]; +} + +static inline void sw_factor_one_minus_src_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; + factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3]; +} + +static inline void sw_factor_src_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = src[3]; +} + +static inline void sw_factor_one_minus_src_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + float inv_alpha = 1.0f - src[3]; + factor[0] = factor[1] = factor[2] = factor[3] = inv_alpha; +} + +static inline void sw_factor_dst_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = factor[3] = dst[3]; +} + +static inline void sw_factor_one_minus_dst_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + float inv_alpha = 1.0f - dst[3]; + factor[0] = factor[1] = factor[2] = factor[3] = inv_alpha; +} + +static inline void sw_factor_dst_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3]; +} + +static inline void sw_factor_one_minus_dst_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; + factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3]; +} + +static inline void sw_factor_src_alpha_saturate(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +{ + factor[0] = factor[1] = factor[2] = 1.0f; + factor[3] = (src[3] < 1.0f) ? src[3] : 1.0f; +} + +static inline void sw_blend_colors(float* SW_RESTRICT dst/*[4]*/, const float* SW_RESTRICT src/*[4]*/) +{ + float srcFactor[4], dstFactor[4]; + + RLSW.srcFactorFunc(srcFactor, src, dst); + RLSW.dstFactorFunc(dstFactor, src, dst); + + dst[0] = srcFactor[0] * src[0] + dstFactor[0] * dst[0]; + dst[1] = srcFactor[1] * src[1] + dstFactor[1] * dst[1]; + dst[2] = srcFactor[2] * src[2] + dstFactor[2] * dst[2]; + dst[3] = srcFactor[3] * src[3] + dstFactor[3] * dst[3]; +} + +/* === Projection Helper Functions === */ + +static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4]) +{ + screen[0] = RLSW.vpCenter[0] + ndc[0] * RLSW.vpHalfSize[0]; + screen[1] = RLSW.vpCenter[1] - ndc[1] * RLSW.vpHalfSize[1]; +} + +/* === Polygon Clipping Part === */ + +#define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \ +static inline int sw_clip_##name( \ + sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES], \ + const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES], \ + int n) \ +{ \ + const sw_vertex_t *prev = &input[n - 1]; \ + int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \ + int outputCount = 0; \ + \ + for (int i = 0; i < n; i++) { \ + const sw_vertex_t *curr = &input[i]; \ + int currInside = FUNC_IS_INSIDE(curr->homogeneous); \ + \ + /* If transition between interior/exterior, calculate intersection point */ \ + if (prevInside != currInside) { \ + float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \ + sw_lerp_vertex_PTCH(&output[outputCount++], prev, curr, t); \ + } \ + \ + /* If current vertex inside, add it */ \ + if (currInside) { \ + output[outputCount++] = *curr; \ + } \ + \ + prev = curr; \ + prevInside = currInside; \ + } \ + \ + return outputCount; \ +} + +// Frustum clip functions + +#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON) +#define IS_INSIDE_PLANE_X_POS(h) ((h)[0] <= (h)[3]) +#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] <= (h)[3]) +#define IS_INSIDE_PLANE_Y_POS(h) ((h)[1] <= (h)[3]) +#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] <= (h)[3]) +#define IS_INSIDE_PLANE_Z_POS(h) ((h)[2] <= (h)[3]) +#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3]) + +#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3]) / ((hCurr)[3] - (hPrev)[3])) +#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0]) / (((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0]))) +#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0]) / (((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0]))) +#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1]) / (((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1]))) +#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1]) / (((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1]))) +#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2]) / (((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2]))) +#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2]) / (((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2]))) + +DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W) +DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS) +DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG) +DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS) +DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG) +DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS) +DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG) + +// Scissor clip functions + +#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0]) * (hPrev)[3] - (hPrev)[0]) / (((hCurr)[0] - (RLSW.scClipMin[0]) * (hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0]) * (hPrev)[3]))) +#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0]) * (hPrev)[3] - (hPrev)[0]) / (((hCurr)[0] - (RLSW.scClipMax[0]) * (hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0]) * (hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1]) * (hPrev)[3] - (hPrev)[1]) / (((hCurr)[1] - (RLSW.scClipMin[1]) * (hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1]) * (hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1]) * (hPrev)[3] - (hPrev)[1]) / (((hCurr)[1] - (RLSW.scClipMax[1]) * (hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1]) * (hPrev)[3]))) + +#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0]) * (h)[3]) +#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0]) * (h)[3]) +#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1]) * (h)[3]) +#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1]) * (h)[3]) + +DEFINE_CLIP_FUNC(scissor_x_min, IS_INSIDE_SCISSOR_X_MIN, COMPUTE_T_SCISSOR_X_MIN) +DEFINE_CLIP_FUNC(scissor_x_max, IS_INSIDE_SCISSOR_X_MAX, COMPUTE_T_SCISSOR_X_MAX) +DEFINE_CLIP_FUNC(scissor_y_min, IS_INSIDE_SCISSOR_Y_MIN, COMPUTE_T_SCISSOR_Y_MIN) +DEFINE_CLIP_FUNC(scissor_y_max, IS_INSIDE_SCISSOR_Y_MAX, COMPUTE_T_SCISSOR_Y_MAX) + +// Main clip function + +static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) +{ + static sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES]; + + int n = *vertexCounter; + + #define CLIP_AGAINST_PLANE(FUNC_CLIP) \ + { \ + n = FUNC_CLIP(tmp, polygon, n); \ + if (n < 3) { \ + *vertexCounter = 0; \ + return false; \ + } \ + for (int i = 0; i < n; i++) { \ + polygon[i] = tmp[i]; \ + } \ + } + + CLIP_AGAINST_PLANE(sw_clip_w); + CLIP_AGAINST_PLANE(sw_clip_x_pos); + CLIP_AGAINST_PLANE(sw_clip_x_neg); + CLIP_AGAINST_PLANE(sw_clip_y_pos); + CLIP_AGAINST_PLANE(sw_clip_y_neg); + CLIP_AGAINST_PLANE(sw_clip_z_pos); + CLIP_AGAINST_PLANE(sw_clip_z_neg); + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + CLIP_AGAINST_PLANE(sw_clip_scissor_x_min); + CLIP_AGAINST_PLANE(sw_clip_scissor_x_max); + CLIP_AGAINST_PLANE(sw_clip_scissor_y_min); + CLIP_AGAINST_PLANE(sw_clip_scissor_y_max); + } + + *vertexCounter = n; + + return (n >= 3); +} + +/* === Triangle Rendering Part === */ + +static inline bool sw_triangle_face_culling(void) +{ + // NOTE: Face culling is done before clipping to avoid unnecessary computations. + // To handle triangles crossing the w=0 plane correctly, + // we perform the winding order test in homogeneous coordinates directly, + // before the perspective division (division by w). + // This test determines the orientation of the triangle in the (x,y,w) plane, + // which corresponds to the projected 2D winding order sign, + // even with negative w values. + + // Preload homogeneous coordinates into local variables + const float* h0 = RLSW.vertexBuffer[0].homogeneous; + const float* h1 = RLSW.vertexBuffer[1].homogeneous; + const float* h2 = RLSW.vertexBuffer[2].homogeneous; + + // Compute a value proportional to the signed area in the projected 2D plane, + // calculated directly using homogeneous coordinates BEFORE division by w. + // This is the determinant of the matrix formed by the (x, y, w) components + // of the vertices, which correctly captures the winding order in homogeneous + // space and its relationship to the projected 2D winding order, even with + // negative w values. + // The determinant formula used here is: + // h0.x * (h1.y * h2.w - h2.y * h1.w) + + // h1.x * (h2.y * h0.w - h0.y * h2.w) + + // h2.x * (h0.y * h1.w - h1.y * h0.w) + + const float hSgnArea = + h0[0] * (h1[1] * h2[3] - h2[1] * h1[3]) + + h1[0] * (h2[1] * h0[3] - h0[1] * h2[3]) + + h2[0] * (h0[1] * h1[3] - h1[1] * h0[3]); + + // Discard the triangle if its winding order (determined by the sign + // of the homogeneous area/determinant) matches the culled direction. + // A positive hSgnArea typically corresponds to a counter-clockwise + // winding in the projected space when all w > 0. + // This test is robust for points with w > 0 or w < 0, correctly + // capturing the change in orientation when crossing the w=0 plane. + + // The culling logic remains the same based on the signed area/determinant. + // A value of 0 for hSgnArea means the points are collinear in (x, y, w) + // space, which corresponds to a degenerate triangle projection. + // Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false) + // and should be handled by the clipper if necessary. + return (RLSW.cullFace == SW_FRONT) + ? (hSgnArea < 0) // Cull if winding is "clockwise" in the projected sense + : (hSgnArea > 0); // Cull if winding is "counter-clockwise" in the projected sense +} + +static inline void sw_triangle_clip_and_project(void) +{ + sw_vertex_t* polygon = RLSW.vertexBuffer; + int* vertexCounter = &RLSW.vertexCounter; + + if (sw_polygon_clip(polygon, vertexCounter)) { + + // Transformation to screen space and normalization + for (int i = 0; i < *vertexCounter; i++) { + sw_vertex_t *v = &polygon[i]; + + // Calculation of the reciprocal of W for normalization + // as well as perspective-correct attributes + const float invW = 1.0f / v->homogeneous[3]; + v->homogeneous[3] = invW; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= invW; + v->homogeneous[1] *= invW; + v->homogeneous[2] *= invW; + + // Division of texture coordinates (perspective-correct) + v->texcoord[0] *= invW; + v->texcoord[1] *= invW; + + // Division of colors (perspective-correct) + v->color[0] *= invW; + v->color[1] *= invW; + v->color[2] *= invW; + v->color[3] *= invW; + + // Transformation to screen space + sw_project_ndc_to_screen(v->screen, v->homogeneous); + } + } +} + +#define DEFINE_TRIANGLE_RASTER_SCANLINE(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ +static inline void FUNC_NAME(const sw_texture_t* tex, const sw_vertex_t* start, \ + const sw_vertex_t* end, float duDy, float dvDy) \ +{ \ + /* Convert and center the screen coordinates */ \ + \ + int xStart = (int)(start->screen[0] + 0.5f); \ + int xEnd = (int)(end->screen[0] + 0.5f); \ + int y = (int)start->screen[1]; \ + \ + /* Compute the inverse horizontal distance along the X axis */ \ + \ + float dx = end->screen[0] - start->screen[0]; \ + if (fabsf(dx) < 1e-6f) return; \ + float dxRcp = 1.0f / dx; \ + \ + /* Compute the interpolation steps along the X axis */ \ + \ + float dzDx = (end->homogeneous[2] - start->homogeneous[2]) * dxRcp; \ + float dwDx = (end->homogeneous[3] - start->homogeneous[3]) * dxRcp; \ + \ + float dcDx[4]; \ + dcDx[0] = (end->color[0] - start->color[0]) * dxRcp; \ + dcDx[1] = (end->color[1] - start->color[1]) * dxRcp; \ + dcDx[2] = (end->color[2] - start->color[2]) * dxRcp; \ + dcDx[3] = (end->color[3] - start->color[3]) * dxRcp; \ + \ + float duDx, dvDx; \ + if (ENABLE_TEXTURE) { \ + duDx = (end->texcoord[0] - start->texcoord[0]) * dxRcp; \ + dvDx = (end->texcoord[1] - start->texcoord[1]) * dxRcp; \ + } \ + \ + /* Initializing the interpolation starting values */ \ + \ + float z = start->homogeneous[2]; \ + float w = start->homogeneous[3]; \ + \ + float color[4]; \ + color[0] = start->color[0]; \ + color[1] = start->color[1]; \ + color[2] = start->color[2]; \ + color[3] = start->color[3]; \ + \ + float u, v; \ + if (ENABLE_TEXTURE) { \ + u = start->texcoord[0]; \ + v = start->texcoord[1]; \ + } \ + \ + /* Pre-calculate the starting pointers for the framebuffer row */ \ + \ + void* cptr = sw_framebuffer_get_color_addr( \ + RLSW.framebuffer.color, y * RLSW.framebuffer.width + xStart); \ + \ + void* dptr = sw_framebuffer_get_depth_addr( \ + RLSW.framebuffer.depth, y * RLSW.framebuffer.width + xStart); \ + \ + /* Scanline rasterization */ \ + \ + for (int x = xStart; x < xEnd; x++) \ + { \ + /* Test and write depth */ \ + \ + if (ENABLE_DEPTH_TEST) \ + { \ + /* TODO: Implement different depth funcs? */ \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discard; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + /* Pixel color computation */ \ + \ + float wRcp = 1.0f / w; \ + \ + float srcColor[4] = { \ + color[0] * wRcp, \ + color[1] * wRcp, \ + color[2] * wRcp, \ + color[3] * wRcp \ + }; \ + \ + if (ENABLE_TEXTURE) \ + { \ + float texColor[4]; \ + float s = u * wRcp; \ + float t = v * wRcp; \ + sw_texture_sample(texColor, tex, s, t, duDx, duDy, dvDx, dvDy); \ + srcColor[0] *= texColor[0]; \ + srcColor[1] *= texColor[1]; \ + srcColor[2] *= texColor[2]; \ + srcColor[3] *= texColor[3]; \ + } \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, srcColor); \ + dstColor[0] = sw_saturate(dstColor[0]); \ + dstColor[1] = sw_saturate(dstColor[1]); \ + dstColor[2] = sw_saturate(dstColor[2]); \ + \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else \ + { \ + sw_framebuffer_write_color(cptr, srcColor); \ + } \ + \ + /* Increment the interpolation parameter, UVs, and pointers */ \ + \ + discard: \ + \ + z += dzDx; \ + w += dwDx; \ + color[0] += dcDx[0]; \ + color[1] += dcDx[1]; \ + color[2] += dcDx[2]; \ + color[3] += dcDx[3]; \ + if (ENABLE_TEXTURE) { \ + u += duDx; \ + v += dvDx; \ + } \ + \ + sw_framebuffer_inc_color_addr(&cptr); \ + sw_framebuffer_inc_depth_addr(&dptr); \ + } \ +} + +#define DEFINE_TRIANGLE_RASTER(FUNC_NAME, FUNC_SCANLINE, ENABLE_TEXTURE) \ +static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1, const sw_vertex_t* v2, \ + const sw_texture_t* tex) \ +{ \ + /* Swap vertices by increasing y */ \ + \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } \ + if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t* tmp = v1; v1 = v2; v2 = tmp; } \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } \ + \ + /* Extracting coordinates from the sorted vertices */ \ + \ + float x0 = v0->screen[0], y0 = v0->screen[1]; \ + float x1 = v1->screen[0], y1 = v1->screen[1]; \ + float x2 = v2->screen[0], y2 = v2->screen[1]; \ + \ + /* Compute height differences */ \ + \ + float h02 = y2 - y0; \ + float h01 = y1 - y0; \ + float h12 = y2 - y1; \ + \ + if (h02 < 1e-6f) { \ + return; \ + } \ + \ + /* Precompute the inverse values without additional checks */ \ + \ + float invH02 = 1.0f / h02; \ + float invH01 = (h01 > 1e-6f) ? 1.0f / h01 : 0.0f; \ + float invH12 = (h12 > 1e-6f) ? 1.0f / h12 : 0.0f; \ + \ + /* Pre-calculation of slopes */ \ + \ + float dx02 = (x2 - x0) * invH02; \ + float dx01 = (x1 - x0) * invH01; \ + float dx12 = (x2 - x1) * invH12; \ + \ + /* Y bounds (vertical clipping) */ \ + \ + int yTop = (int)(y0 + 0.5f); \ + int yMiddle = (int)(y1 + 0.5f); \ + int yBottom = (int)(y2 + 0.5f); \ + \ + /* Compute gradients for each side of the triangle */ \ + \ + sw_vertex_t vDy02, vDy01, vDy12; \ + sw_get_vertex_grad_PTCH(&vDy02, v0, v2, invH02); \ + sw_get_vertex_grad_PTCH(&vDy01, v0, v1, invH01); \ + sw_get_vertex_grad_PTCH(&vDy12, v1, v2, invH12); \ + \ + /* Initializing scanline variables */ \ + \ + sw_vertex_t vLeft = *v0; \ + vLeft.screen[0] = x0; \ + \ + sw_vertex_t vRight = *v0; \ + vRight.screen[0] = x0; \ + \ + /* Scanline for the upper part of the triangle */ \ + \ + for (int y = yTop; y < yMiddle; y++) \ + { \ + vLeft.screen[1] = vRight.screen[1] = y; \ + \ + if (vLeft.screen[0] < vRight.screen[0]) { \ + FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ + } \ + else { \ + FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ + } \ + \ + sw_add_vertex_grad_PTCH(&vLeft, &vDy02); \ + vLeft.screen[0] += dx02; \ + \ + sw_add_vertex_grad_PTCH(&vRight, &vDy01); \ + vRight.screen[0] += dx01; \ + } \ + \ + /* Scanline for the lower part of the triangle */ \ + \ + vRight = *v1, vRight.screen[0] = x1; \ + \ + for (int y = yMiddle; y < yBottom; y++) \ + { \ + vLeft.screen[1] = vRight.screen[1] = y; \ + \ + if (vLeft.screen[0] < vRight.screen[0]) { \ + FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ + } \ + else { \ + FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ + } \ + \ + sw_add_vertex_grad_PTCH(&vLeft, &vDy02); \ + vLeft.screen[0] += dx02; \ + \ + sw_add_vertex_grad_PTCH(&vRight, &vDy12); \ + vRight.screen[0] += dx12; \ + } \ +} + +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline, 0, 0, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX, 1, 0, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH, 0, 1, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_BLEND, 0, 0, 1) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH, 1, 1, 0) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_BLEND, 1, 0, 1) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH_BLEND, 0, 1, 1) +DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH_BLEND, 1, 1, 1) + +DEFINE_TRIANGLE_RASTER(sw_triangle_raster, sw_triangle_raster_scanline, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX, sw_triangle_raster_scanline_TEX, true) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH, sw_triangle_raster_scanline_DEPTH, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_BLEND, sw_triangle_raster_scanline_BLEND, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH, sw_triangle_raster_scanline_TEX_DEPTH, true) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND, sw_triangle_raster_scanline_TEX_BLEND, true) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND, sw_triangle_raster_scanline_DEPTH_BLEND, false) +DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND, sw_triangle_raster_scanline_TEX_DEPTH_BLEND, true) + +static inline void sw_triangle_render(void) +{ + if (RLSW.stateFlags & SW_STATE_CULL_FACE) { + if (!sw_triangle_face_culling()) { + return; + } + } + + sw_triangle_clip_and_project(); + + if (RLSW.vertexCounter < 3) { + return; + } + +# define TRIANGLE_RASTER(RASTER_FUNC) \ + { \ + for (int i = 0; i < RLSW.vertexCounter - 2; i++) { \ + RASTER_FUNC( \ + &RLSW.vertexBuffer[0], \ + &RLSW.vertexBuffer[i + 1], \ + &RLSW.vertexBuffer[i + 2], \ + &RLSW.loadedTextures[RLSW.currentTexture] \ + ); \ + } \ + } + + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + TRIANGLE_RASTER(sw_triangle_raster_DEPTH) + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX) + } + else { + TRIANGLE_RASTER(sw_triangle_raster) + } + +# undef TRIANGLE_RASTER +} + +/* === Quad Rendering Part === */ + +static inline bool sw_quad_face_culling(void) +{ + // NOTE: Face culling is done before clipping to avoid unnecessary computations. + // To handle quads crossing the w=0 plane correctly, + // we perform the winding order test in homogeneous coordinates directly, + // before the perspective division (division by w). + // For a convex quad with vertices P0, P1, P2, P3 in sequential order, + // the winding order of the quad is the same as the winding order + // of the triangle P0 P1 P2. We use the homogeneous triangle + // winding test on this first triangle. + + // Preload homogeneous coordinates into local variables + const float* h0 = RLSW.vertexBuffer[0].homogeneous; + const float* h1 = RLSW.vertexBuffer[1].homogeneous; + const float* h2 = RLSW.vertexBuffer[2].homogeneous; + + // NOTE: h3 is not needed for this test + // const float* h3 = RLSW.vertexBuffer[3].homogeneous; + + // Compute a value proportional to the signed area of the triangle P0 P1 P2 + // in the projected 2D plane, calculated directly using homogeneous coordinates + // BEFORE division by w. + // This is the determinant of the matrix formed by the (x, y, w) components + // of the vertices P0, P1, and P2. Its sign correctly indicates the winding order + // in homogeneous space and its relationship to the projected 2D winding order, + // even with negative w values. + // The determinant formula used here is: + // h0.x * (h1.y * h2.w - h2.y * h1.w) + + // h1.x * (h2.y * h0.w - h0.y * h2.w) + + // h2.x * (h0.y * h1.w - h1.y * h0.w) + + const float hSgnArea = + h0[0] * (h1[1] * h2[3] - h2[1] * h1[3]) + + h1[0] * (h2[1] * h0[3] - h0[1] * h2[3]) + + h2[0] * (h0[1] * h1[3] - h1[1] * h0[3]); + + // Perform face culling based on the winding order determined by the sign + // of the homogeneous area/determinant of triangle P0 P1 P2. + // This test is robust for points with w > 0 or w < 0 within the triangle, + // correctly capturing the change in orientation when crossing the w=0 plane. + + // A positive hSgnArea typically corresponds to a counter-clockwise + // winding in the projected space when all w > 0. + // A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w) + // space, which corresponds to a degenerate triangle projection. + // Such quads might also be degenerate or non-planar. They are typically + // not culled by this test (0 < 0 is false, 0 > 0 is false) + // and should be handled by the clipper if necessary. + + return (RLSW.cullFace == SW_FRONT) + ? (hSgnArea < 0.0f) // Cull if winding is "clockwise" in the projected sense + : (hSgnArea > 0.0f); // Cull if winding is "counter-clockwise" in the projected sense +} + +static inline void sw_quad_clip_and_project(void) +{ + sw_vertex_t* polygon = RLSW.vertexBuffer; + int* vertexCounter = &RLSW.vertexCounter; + + if (sw_polygon_clip(polygon, vertexCounter)) { + + // Transformation to screen space and normalization + for (int i = 0; i < *vertexCounter; i++) { + sw_vertex_t *v = &polygon[i]; + + // Calculation of the reciprocal of W for normalization + // as well as perspective-correct attributes + const float invW = 1.0f / v->homogeneous[3]; + v->homogeneous[3] = invW; + + // Division of XYZ coordinates by weight + v->homogeneous[0] *= invW; + v->homogeneous[1] *= invW; + v->homogeneous[2] *= invW; + + // Division of texture coordinates (perspective-correct) + v->texcoord[0] *= invW; + v->texcoord[1] *= invW; + + // Division of colors (perspective-correct) + v->color[0] *= invW; + v->color[1] *= invW; + v->color[2] *= invW; + v->color[3] *= invW; + + // Transformation to screen space + sw_project_ndc_to_screen(v->screen, v->homogeneous); + } + } +} + +static inline bool sw_quad_is_axis_aligned(void) +{ + int horizontal = 0; + int vertical = 0; + + for (int i = 0; i < 4; i++) + { + if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) { + return false; + } + + const float* v0 = RLSW.vertexBuffer[i].position; + const float* v1 = RLSW.vertexBuffer[(i + 1) % 4].position; + + float dx = v1[0] - v0[0]; + float dy = v1[1] - v0[1]; + + if (fabsf(dx) > 1e-6f && fabsf(dy) < 1e-6f) { + horizontal++; + } + else if (fabsf(dy) > 1e-6f && fabsf(dx) < 1e-6f) { + vertical++; + } + else { + // Diagonal edge -> not axis-aligned + return false; + } + } + + return (horizontal == 2 && vertical == 2); +} + +static inline void sw_quad_sort_cw(const sw_vertex_t** output) +{ + // Sort 4 quad vertices into clockwise order with fixed layout: + // + // v0 -- v1 + // | | + // v3 -- v2 + // + // The goal is: + // - v0: top-left (minimum Y, then minimum X) + // - v1: top-right (minimum Y row, maximum X) + // - v2: bottom-right (maximum Y, maximum X) + // - v3: bottom-left (maximum Y, minimum X) + + const sw_vertex_t* input = RLSW.vertexBuffer; + + // Separate vertices into top and bottom based on Y-coordinate + const sw_vertex_t* top[2] = {NULL, NULL}; + const sw_vertex_t* bottom[2] = {NULL, NULL}; + int topCount = 0, bottomCount = 0; + + // Find minimum and maximum Y + float minY = input[0].screen[1]; + float maxY = input[0].screen[1]; + + for (int i = 1; i < 4; i++) { + if (input[i].screen[1] < minY) minY = input[i].screen[1]; + if (input[i].screen[1] > maxY) maxY = input[i].screen[1]; + } + + // Separate vertices based on Y-coordinate + for (int i = 0; i < 4; i++) { + if (input[i].screen[1] == minY && topCount < 2) { + top[topCount++] = &input[i]; + } + else if (input[i].screen[1] == maxY && bottomCount < 2) { + bottom[bottomCount++] = &input[i]; + } + } + + // If we don't have enough top/bottom vertices (e.g., Y values are all different), + // classify vertices as top or bottom based on whether they're closer to minY or maxY + for (int i = 0; i < 4; i++) { + if (topCount < 2 && &input[i] != top[0] && &input[i] != bottom[0] && &input[i] != bottom[1]) { + if (fabs(input[i].screen[1] - minY) <= fabs(input[i].screen[1] - maxY)) { + top[topCount++] = &input[i]; + } + } + if (bottomCount < 2 && &input[i] != top[0] && &input[i] != top[1] && &input[i] != bottom[0]) { + if (fabs(input[i].screen[1] - maxY) < fabs(input[i].screen[1] - minY)) { + bottom[bottomCount++] = &input[i]; + } + } + } + + // Sort top vertices by X (left to right) + if (topCount == 2 && top[0]->screen[0] > top[1]->screen[0]) { + const sw_vertex_t* temp = top[0]; + top[0] = top[1]; + top[1] = temp; + } + + // Sort bottom vertices by X (left to right) + if (bottomCount == 2 && bottom[0]->screen[0] > bottom[1]->screen[0]) { + const sw_vertex_t* temp = bottom[0]; + bottom[0] = bottom[1]; + bottom[1] = temp; + } + + // Assign vertices in clockwise order as per the required layout + output[0] = top[0]; // v0: top-left + output[1] = top[topCount-1]; // v1: top-right + output[2] = bottom[bottomCount-1]; // v2: bottom-right + output[3] = bottom[0]; // v3: bottom-left +} + +// REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, still appear perfectly aligned from a certain point of view? +// Because in that case, we would still need to perform perspective division for textures and colors... +#define DEFINE_QUAD_RASTER_AXIS_ALIGNED(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ +static inline void FUNC_NAME(void) \ +{ \ + const sw_vertex_t* sortedVerts[4]; \ + sw_quad_sort_cw(sortedVerts); \ + \ + const sw_vertex_t* v0 = sortedVerts[0]; \ + const sw_vertex_t* v1 = sortedVerts[1]; \ + const sw_vertex_t* v2 = sortedVerts[2]; \ + const sw_vertex_t* v3 = sortedVerts[3]; \ + \ + /* Screen bounds (axis-aligned) */ \ + \ + int xMin = (int)(v0->screen[0] + 0.5f); \ + int yMin = (int)(v0->screen[1] + 0.5f); \ + int xMax = (int)(v2->screen[0] + 0.5f); \ + int yMax = (int)(v2->screen[1] + 0.5f); \ + \ + int width = xMax - xMin; \ + int height = yMax - yMin; \ + \ + if (width == 0 || height == 0) return; \ + \ + float wRcp = (width > 0.0f) ? 1.0f / width : 0.0f; \ + float hRcp = (height > 0.0f) ? 1.0f / height : 0.0f; \ + \ + /* Calculation of vertex gradients in X and Y */ \ + \ + float tcDx[2], tcDy[2]; \ + if (ENABLE_TEXTURE) { \ + tcDx[0] = (v1->texcoord[0] - v0->texcoord[0]) * wRcp; \ + tcDx[1] = (v1->texcoord[1] - v0->texcoord[1]) * wRcp; \ + tcDy[0] = (v3->texcoord[0] - v0->texcoord[0]) * hRcp; \ + tcDy[1] = (v3->texcoord[1] - v0->texcoord[1]) * hRcp; \ + } \ + \ + float cDx[4], cDy[4]; \ + cDx[0] = (v1->color[0] - v0->color[0]) * wRcp; \ + cDx[1] = (v1->color[1] - v0->color[1]) * wRcp; \ + cDx[2] = (v1->color[2] - v0->color[2]) * wRcp; \ + cDx[3] = (v1->color[3] - v0->color[3]) * wRcp; \ + cDy[0] = (v3->color[0] - v0->color[0]) * hRcp; \ + cDy[1] = (v3->color[1] - v0->color[1]) * hRcp; \ + cDy[2] = (v3->color[2] - v0->color[2]) * hRcp; \ + cDy[3] = (v3->color[3] - v0->color[3]) * hRcp; \ + \ + float zDx, zDy; \ + zDx = (v1->homogeneous[2] - v0->homogeneous[2]) * wRcp; \ + zDy = (v3->homogeneous[2] - v0->homogeneous[2]) * hRcp; \ + \ + /* Start of quad rasterization */ \ + \ + const sw_texture_t* tex; \ + if (ENABLE_TEXTURE) { \ + tex = &RLSW.loadedTextures[RLSW.currentTexture]; \ + } \ + \ + void* cDstBase = RLSW.framebuffer.color; \ + void* dDstBase = RLSW.framebuffer.depth; \ + int wDst = RLSW.framebuffer.width; \ + \ + float zScanline = v0->homogeneous[2]; \ + float uScanline = v0->texcoord[0]; \ + float vScanline = v0->texcoord[1]; \ + \ + float colorScanline[4] = { \ + v0->color[0], \ + v0->color[1], \ + v0->color[2], \ + v0->color[3] \ + }; \ + \ + for (int y = yMin; y < yMax; y++) \ + { \ + void* cptr = sw_framebuffer_get_color_addr(cDstBase, y * wDst + xMin); \ + void* dptr = sw_framebuffer_get_depth_addr(dDstBase, y * wDst + xMin); \ + \ + float z = zScanline; \ + float u = uScanline; \ + float v = vScanline; \ + \ + float color[4] = { \ + colorScanline[0], \ + colorScanline[1], \ + colorScanline[2], \ + colorScanline[3] \ + }; \ + \ + /* Scanline rasterization */ \ + \ + for (int x = xMin; x < xMax; x++) \ + { \ + /* Test and write depth */ \ + \ + if (ENABLE_DEPTH_TEST) \ + { \ + /* TODO: Implement different depth funcs? */ \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discard; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + /* Pixel color computation */ \ + \ + float srcColor[4] = { \ + color[0], \ + color[1], \ + color[2], \ + color[3] \ + }; \ + \ + if (ENABLE_TEXTURE) \ + { \ + float texColor[4]; \ + sw_texture_sample(texColor, tex, u, v, tcDx[0], tcDy[0], tcDx[1], tcDy[1]); \ + srcColor[0] *= texColor[0]; \ + srcColor[1] *= texColor[1]; \ + srcColor[2] *= texColor[2]; \ + srcColor[3] *= texColor[3]; \ + } \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, srcColor); \ + dstColor[0] = sw_saturate(dstColor[0]); \ + dstColor[1] = sw_saturate(dstColor[1]); \ + dstColor[2] = sw_saturate(dstColor[2]); \ + dstColor[3] = sw_saturate(dstColor[3]); \ + \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else \ + { \ + sw_framebuffer_write_color(cptr, srcColor); \ + } \ + \ + discard: \ + \ + z += zDx; \ + \ + color[0] += cDx[0]; \ + color[1] += cDx[1]; \ + color[2] += cDx[2]; \ + color[3] += cDx[3]; \ + \ + if (ENABLE_TEXTURE) { \ + u += tcDx[0]; \ + v += tcDx[1]; \ + } \ + \ + sw_framebuffer_inc_color_addr(&cptr); \ + sw_framebuffer_inc_depth_addr(&dptr); \ + } \ + \ + zScanline += zDy; \ + \ + colorScanline[0] += cDy[0]; \ + colorScanline[1] += cDy[1]; \ + colorScanline[2] += cDy[2]; \ + colorScanline[3] += cDy[3]; \ + \ + if (ENABLE_TEXTURE) { \ + uScanline += tcDy[0]; \ + vScanline += tcDy[1]; \ + } \ + } \ +} + +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned, 0, 0, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX, 1, 0, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH, 0, 1, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_BLEND, 0, 0, 1) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH, 1, 1, 0) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_BLEND, 1, 0, 1) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH_BLEND, 0, 1, 1) +DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND, 1, 1, 1) + +static inline void sw_quad_render(void) +{ + if (RLSW.stateFlags & SW_STATE_CULL_FACE) { + if (!sw_quad_face_culling()) { + return; + } + } + + sw_quad_clip_and_project(); + + if (RLSW.vertexCounter < 3) { + return; + } + + if (RLSW.vertexCounter == 4 && sw_quad_is_axis_aligned()) + { + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND(); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_quad_raster_axis_aligned_DEPTH_BLEND(); + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) { + sw_quad_raster_axis_aligned_TEX_BLEND(); + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) { + sw_quad_raster_axis_aligned_TEX_DEPTH(); + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + sw_quad_raster_axis_aligned_BLEND(); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + sw_quad_raster_axis_aligned_DEPTH(); + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) { + sw_quad_raster_axis_aligned_TEX(); + } + else { + sw_quad_raster_axis_aligned(); + } + return; + } + +# define TRIANGLE_RASTER(RASTER_FUNC) \ + { \ + for (int i = 0; i < RLSW.vertexCounter - 2; i++) { \ + RASTER_FUNC( \ + &RLSW.vertexBuffer[0], \ + &RLSW.vertexBuffer[i + 1], \ + &RLSW.vertexBuffer[i + 2], \ + &RLSW.loadedTextures[RLSW.currentTexture] \ + ); \ + } \ + } + + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + TRIANGLE_RASTER(sw_triangle_raster_BLEND) + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + TRIANGLE_RASTER(sw_triangle_raster_DEPTH) + } + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) { + TRIANGLE_RASTER(sw_triangle_raster_TEX) + } + else { + TRIANGLE_RASTER(sw_triangle_raster) + } + +# undef TRIANGLE_RASTER +} + +/* === Line Rendering Part === */ + +static inline bool sw_line_clip_coord(float q, float p, float* t0, float* t1) +{ + if (fabsf(p) < SW_CLIP_EPSILON) { + // Check if the line is entirely outside the window + if (q < -SW_CLIP_EPSILON) return 0; // Completely outside + return 1; // Completely inside or on the edges + } + + const float r = q / p; + + if (p < 0) { + if (r > *t1) return 0; + if (r > *t0) *t0 = r; + } + else { + if (r < *t0) return 0; + if (r < *t1) *t1 = r; + } + + return 1; +} + +static inline bool sw_line_clip(sw_vertex_t* v0, sw_vertex_t* v1) +{ + float t0 = 0.0f, t1 = 1.0f; + float dH[4], dC[4]; + + for (int i = 0; i < 4; i++) { + dH[i] = v1->homogeneous[i] - v0->homogeneous[i]; + dC[i] = v1->color[i] - v0->color[i]; + } + + // Clipping Liang-Barsky + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[0], -dH[3] + dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[0], -dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[1], -dH[3] + dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[1], -dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[2], -dH[3] + dH[2], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false; + + // Clipping Scissor + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0] * v0->homogeneous[3], RLSW.scClipMin[0] * dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[0] * v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0] * dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1] * v0->homogeneous[3], RLSW.scClipMin[1] * dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[1] * v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1] * dH[3], &t0, &t1)) return false; + } + + // Interpolation of new coordinates + if (t1 < 1.0f) { + for (int i = 0; i < 4; i++) { + v1->homogeneous[i] = v0->homogeneous[i] + t1 * dH[i]; + v1->color[i] = v0->color[i] + t1 * dC[i]; + } + } + if (t0 > 0.0f) { + for (int i = 0; i < 4; i++) { + v0->homogeneous[i] += t0 * dH[i]; + v0->color[i] += t0 * dC[i]; + } + } + + return true; +} + +static inline bool sw_line_clip_and_project(sw_vertex_t* v0, sw_vertex_t* v1) +{ + if (!sw_line_clip(v0, v1)) { + return false; + } + + // Convert homogeneous coordinates to NDC + v0->homogeneous[3] = 1.0f / v0->homogeneous[3]; + v1->homogeneous[3] = 1.0f / v1->homogeneous[3]; + for (int i = 0; i < 3; i++) { + v0->homogeneous[i] *= v0->homogeneous[3]; + v1->homogeneous[i] *= v1->homogeneous[3]; + } + + // Convert NDC coordinates to screen space + sw_project_ndc_to_screen(v0->screen, v0->homogeneous); + sw_project_ndc_to_screen(v1->screen, v1->homogeneous); + + return true; +} + +#define DEFINE_LINE_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ +static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ +{ \ + int x1 = (int)(v0->screen[0] + 0.5f); \ + int y1 = (int)(v0->screen[1] + 0.5f); \ + int x2 = (int)(v1->screen[0] + 0.5f); \ + int y2 = (int)(v1->screen[1] + 0.5f); \ + \ + int dx = x2 - x1; \ + int dy = y2 - y1; \ + \ + /* Handling of lines that are more horizontal or vertical */ \ + \ + if (dx == 0 && dy == 0) { \ + /* TODO: A point should be rendered here */ \ + return; \ + } \ + \ + bool yLonger = (abs(dy) > abs(dx)); \ + int longLen, shortLen; \ + \ + if (yLonger) { \ + longLen = dy; \ + shortLen = dx; \ + } \ + else { \ + longLen = dx; \ + shortLen = dy; \ + } \ + \ + /* Handling of traversal direction */ \ + \ + int sgnInc = (longLen < 0) ? -1 : 1; \ + int abslongLen = abs(longLen); \ + \ + /* Calculation of the increment step for the shorter coordinate */ \ + \ + int decInc = 0; \ + if (abslongLen != 0) { \ + decInc = (shortLen << 16) / abslongLen; \ + } \ + \ + float longLenRcp = (abslongLen != 0) ? (1.0f / abslongLen) : 0.0f; \ + \ + /* Calculation of interpolation steps */ \ + \ + const float zStep = (v1->homogeneous[2] - v0->homogeneous[2]) * longLenRcp; \ + const float rStep = (v1->color[0] - v0->color[0]) * longLenRcp; \ + const float gStep = (v1->color[1] - v0->color[1]) * longLenRcp; \ + const float bStep = (v1->color[2] - v0->color[2]) * longLenRcp; \ + const float aStep = (v1->color[3] - v0->color[3]) * longLenRcp; \ + \ + float z = v0->homogeneous[2]; \ + \ + float color[4] = { \ + v0->color[0], \ + v0->color[1], \ + v0->color[2], \ + v0->color[3] \ + }; \ + \ + const int fbWidth = RLSW.framebuffer.width; \ + void* cBuffer = RLSW.framebuffer.color; \ + void* dBuffer = RLSW.framebuffer.depth; \ + \ + int j = 0; \ + \ + if (yLonger) \ + { \ + for (int i = 0; i != longLen; i += sgnInc) \ + { \ + int offset = (y1 + i) * fbWidth + (x1 + (j >> 16)); \ + \ + void* dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ + \ + if (ENABLE_DEPTH_TEST) { \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discardA; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + void* cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, color); \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else \ + { \ + sw_framebuffer_write_color(cptr, color); \ + } \ + \ + discardA: \ + j += decInc; \ + z += zStep; \ + color[0] += rStep; \ + color[1] += gStep; \ + color[2] += bStep; \ + color[3] += aStep; \ + } \ + } \ + else \ + { \ + for (int i = 0; i != longLen; i += sgnInc) \ + { \ + int offset = (y1 + (j >> 16)) * fbWidth + (x1 + i); \ + \ + void* dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ + \ + if (ENABLE_DEPTH_TEST) { \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) goto discardB; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + void* cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, color); \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else \ + { \ + sw_framebuffer_write_color(cptr, color); \ + } \ + \ + discardB: \ + j += decInc; \ + z += zStep; \ + color[0] += rStep; \ + color[1] += gStep; \ + color[2] += bStep; \ + color[3] += aStep; \ + } \ + } \ +} + +#define DEFINE_LINE_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ +void FUNC_NAME(const sw_vertex_t* v1, const sw_vertex_t* v2) \ +{ \ + sw_vertex_t tv1, tv2; \ + \ + int x1 = (int)v1->screen[0]; \ + int y1 = (int)v1->screen[1]; \ + int x2 = (int)v2->screen[0]; \ + int y2 = (int)v2->screen[1]; \ + \ + int dx = x2 - x1; \ + int dy = y2 - y1; \ + \ + RASTER_FUNC(v1, v2); \ + \ + if (dx != 0 && abs(dy / dx) < 1) { \ + int wy = (int)((RLSW.lineWidth - 1.0f) * abs(dx) / sqrtf(dx * dx + dy * dy)); \ + wy >>= 1; /* Division by 2 via bit shift */ \ + for (int i = 1; i <= wy; i++) { \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[1] -= i; \ + tv2.screen[1] -= i; \ + RASTER_FUNC(&tv1, &tv2); \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[1] += i; \ + tv2.screen[1] += i; \ + RASTER_FUNC(&tv1, &tv2); \ + } \ + } \ + else if (dy != 0) { \ + int wx = (int)((RLSW.lineWidth - 1.0f) * abs(dy) / sqrtf(dx * dx + dy * dy)); \ + wx >>= 1; /* Division by 2 via bit shift */ \ + for (int i = 1; i <= wx; i++) { \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[0] -= i; \ + tv2.screen[0] -= i; \ + RASTER_FUNC(&tv1, &tv2); \ + tv1 = *v1, tv2 = *v2; \ + tv1.screen[0] += i; \ + tv2.screen[0] += i; \ + RASTER_FUNC(&tv1, &tv2); \ + } \ + } \ +} + +DEFINE_LINE_RASTER(sw_line_raster, 0, 0) +DEFINE_LINE_RASTER(sw_line_raster_DEPTH, 1, 0) +DEFINE_LINE_RASTER(sw_line_raster_BLEND, 0, 1) +DEFINE_LINE_RASTER(sw_line_raster_DEPTH_BLEND, 1, 1) + +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster, sw_line_raster) +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH, sw_line_raster_DEPTH) +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_BLEND, sw_line_raster_BLEND) +DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH_BLEND, sw_line_raster_DEPTH_BLEND) + +static inline void sw_line_render(sw_vertex_t* vertices) +{ + if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) { + return; + } + + if (RLSW.lineWidth >= 2.0f) { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_line_thick_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + sw_line_thick_raster_BLEND(&vertices[0], &vertices[1]); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + sw_line_thick_raster_DEPTH(&vertices[0], &vertices[1]); + } + else { + sw_line_thick_raster(&vertices[0], &vertices[1]); + } + } + else { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_line_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + sw_line_raster_BLEND(&vertices[0], &vertices[1]); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + sw_line_raster_DEPTH(&vertices[0], &vertices[1]); + } + else { + sw_line_raster(&vertices[0], &vertices[1]); + } + } +} + +/* === Point Rendering Part === */ + +static inline bool sw_point_clip_and_project(sw_vertex_t* v) +{ + if (v->homogeneous[3] != 1.0f) { + for (int_fast8_t i = 0; i < 3; i++) { + if (v->homogeneous[i] < -v->homogeneous[3] || v->homogeneous[i] > v->homogeneous[3]) { + return false; + } + } + v->homogeneous[3] = 1.0f / v->homogeneous[3]; + v->homogeneous[0] *= v->homogeneous[3]; + v->homogeneous[1] *= v->homogeneous[3]; + v->homogeneous[2] *= v->homogeneous[3]; + } + + sw_project_ndc_to_screen(v->screen, v->homogeneous); + + const int *min, *max; + + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + min = RLSW.scMin; + max = RLSW.scMax; + } + else { + min = RLSW.vpMin; + max = RLSW.vpMax; + } + + bool insideX = v->screen[0] - RLSW.pointRadius < max[0] + && v->screen[0] + RLSW.pointRadius > min[0]; + + bool insideY = v->screen[1] - RLSW.pointRadius < max[1] + && v->screen[1] + RLSW.pointRadius > min[1]; + + return insideX && insideY; +} + +#define DEFINE_POINT_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND, CHECK_BOUNDS) \ +static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \ +{ \ + if (CHECK_BOUNDS == 1) \ + { \ + if (x < RLSW.vpMin[0] || x >= RLSW.vpMax[0]) return; \ + if (y < RLSW.vpMin[1] || y >= RLSW.vpMax[1]) return; \ + } \ + else if (CHECK_BOUNDS == SW_SCISSOR_TEST) \ + { \ + if (x < RLSW.scMin[0] || x >= RLSW.scMax[0]) return; \ + if (y < RLSW.scMin[1] || y >= RLSW.scMax[1]) return; \ + } \ + \ + int offset = y * RLSW.framebuffer.width + x; \ + \ + void* dptr = sw_framebuffer_get_depth_addr( \ + RLSW.framebuffer.depth, offset \ + ); \ + \ + if (ENABLE_DEPTH_TEST) \ + { \ + float depth = sw_framebuffer_read_depth(dptr); \ + if (z > depth) return; \ + } \ + \ + sw_framebuffer_write_depth(dptr, z); \ + \ + void* cptr = sw_framebuffer_get_color_addr( \ + RLSW.framebuffer.color, offset \ + ); \ + \ + if (ENABLE_COLOR_BLEND) \ + { \ + float dstColor[4]; \ + sw_framebuffer_read_color(dstColor, cptr); \ + \ + sw_blend_colors(dstColor, color); \ + sw_framebuffer_write_color(cptr, dstColor); \ + } \ + else \ + { \ + sw_framebuffer_write_color(cptr, color); \ + } \ +} + +#define DEFINE_POINT_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ +static inline void FUNC_NAME(sw_vertex_t* v) \ +{ \ + int cx = v->screen[0]; \ + int cy = v->screen[1]; \ + float cz = v->homogeneous[2]; \ + int radius = RLSW.pointRadius; \ + const float* color = v->color; \ + \ + int x = 0; \ + int y = radius; \ + int d = 3 - 2 * radius; \ + \ + while (x <= y) { \ + for (int i = -x; i <= x; i++) { \ + RASTER_FUNC(cx + i, cy + y, cz, color); \ + RASTER_FUNC(cx + i, cy - y, cz, color); \ + } \ + for (int i = -y; i <= y; i++) { \ + RASTER_FUNC(cx + i, cy + x, cz, color); \ + RASTER_FUNC(cx + i, cy - x, cz, color); \ + } \ + if (d > 0) { \ + y--; \ + d = d + 4 * (x - y) + 10; \ + } \ + else { \ + d = d + 4 * x + 6; \ + } \ + x++; \ + } \ +} + +DEFINE_POINT_RASTER(sw_point_raster, 0, 0, 0) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH, 1, 0, 0) +DEFINE_POINT_RASTER(sw_point_raster_BLEND, 0, 1, 0) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND, 1, 1, 0) + +DEFINE_POINT_RASTER(sw_point_raster_CHECK, 0, 0, 1) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK, 1, 0, 1) +DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK, 0, 1, 1) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK, 1, 1, 1) + +DEFINE_POINT_RASTER(sw_point_raster_CHECK_SCISSOR, 0, 0, SW_SCISSOR_TEST) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK_SCISSOR, 1, 0, SW_SCISSOR_TEST) +DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK_SCISSOR, 0, 1, SW_SCISSOR_TEST) +DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR, 1, 1, SW_SCISSOR_TEST) + +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster, sw_point_raster_CHECK) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH, sw_point_raster_DEPTH_CHECK) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND, sw_point_raster_BLEND_CHECK) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND, sw_point_raster_DEPTH_BLEND_CHECK) + +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_SCISSOR, sw_point_raster_CHECK_SCISSOR) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_SCISSOR, sw_point_raster_DEPTH_CHECK_SCISSOR) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND_SCISSOR, sw_point_raster_BLEND_CHECK_SCISSOR) +DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND_SCISSOR, sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR) + +static inline void sw_point_render(sw_vertex_t* v) +{ + if (!sw_point_clip_and_project(v)) { + return; + } + + if (RLSW.pointRadius >= 1.0f) { + if (SW_STATE_CHECK(SW_STATE_SCISSOR_TEST)) { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_point_thick_raster_DEPTH_BLEND_SCISSOR(v); + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + sw_point_thick_raster_BLEND_SCISSOR(v); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + sw_point_thick_raster_DEPTH_SCISSOR(v); + } + else { + sw_point_thick_raster_SCISSOR(v); + } + } + else { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_point_thick_raster_DEPTH_BLEND(v); + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + sw_point_thick_raster_BLEND(v); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + sw_point_thick_raster_DEPTH(v); + } + else { + sw_point_thick_raster(v); + } + } + } + else { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { + sw_point_raster_DEPTH_BLEND( + v->screen[0], v->screen[1], + v->homogeneous[2], v->color + ); + } + else if (SW_STATE_CHECK(SW_STATE_BLEND)) { + sw_point_raster_BLEND( + v->screen[0], v->screen[1], + v->homogeneous[2], v->color + ); + } + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { + sw_point_raster_DEPTH( + v->screen[0], v->screen[1], + v->homogeneous[2], v->color + ); + } + else { + sw_point_raster( + v->screen[0], v->screen[1], + v->homogeneous[2], v->color + ); + } + } +} + +/* === Polygon Modes Rendering Part === */ + +static inline void sw_poly_point_render(void) +{ + for (int i = 0; i < RLSW.vertexCounter; i++) { + sw_point_render(&RLSW.vertexBuffer[i]); + } +} + +static inline void sw_poly_line_render(void) +{ + const sw_vertex_t* vertices = RLSW.vertexBuffer; + int cm1 = RLSW.vertexCounter - 1; + + for (int i = 0; i < cm1; i++) { + sw_line_render((sw_vertex_t[2]){ + vertices[i], vertices[i + 1] + }); + } + + sw_line_render((sw_vertex_t[2]){ + vertices[cm1], vertices[0] + }); +} + +static inline void sw_poly_fill_render(void) +{ + switch (RLSW.drawMode) { + case SW_POINTS: + sw_point_render(&RLSW.vertexBuffer[0]); + break; + case SW_LINES: + sw_line_render(RLSW.vertexBuffer); + break; + case SW_TRIANGLES: + sw_triangle_render(); + break; + case SW_QUADS: + sw_quad_render(); + break; + } +} + +/* === Some Validity Check Helper === */ + +static inline bool sw_is_texture_valid(uint32_t id) +{ + bool valid = true; + + if (id == 0) valid = false; + else if (id >= SW_MAX_TEXTURES) valid = false; + else if (RLSW.loadedTextures[id].pixels.cptr == 0) valid = false; + + return true; +} + +static inline bool sw_is_texture_filter_valid(int filter) +{ + return (filter == SW_NEAREST || filter == SW_LINEAR); +} + +static inline bool sw_is_texture_wrap_valid(int wrap) +{ + return (wrap == SW_REPEAT || wrap == SW_CLAMP); +} + +static inline bool sw_is_draw_mode_valid(int mode) +{ + bool result = false; + + switch (mode) { + case SW_POINTS: + case SW_LINES: + case SW_TRIANGLES: + case SW_QUADS: + result = true; + break; + default: + break; + } + + return result; +} + +static inline bool sw_is_poly_mode_valid(int mode) +{ + bool result = false; + + switch (mode) { + case SW_POINT: + case SW_LINE: + case SW_FILL: + result = true; + break; + default: + break; + } + + return result; +} + +static inline bool sw_is_face_valid(int face) +{ + return (face == SW_FRONT || face == SW_BACK); +} + +static inline bool sw_is_blend_src_factor_valid(int blend) +{ + bool result = false; + + switch (blend) { + case SW_ZERO: + case SW_ONE: + case SW_SRC_COLOR: + case SW_ONE_MINUS_SRC_COLOR: + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_DST_COLOR: + case SW_ONE_MINUS_DST_COLOR: + case SW_SRC_ALPHA_SATURATE: + result = true; + break; + default: + break; + } + + return result; +} + +static inline bool sw_is_blend_dst_factor_valid(int blend) +{ + bool result = false; + + switch (blend) { + case SW_ZERO: + case SW_ONE: + case SW_SRC_COLOR: + case SW_ONE_MINUS_SRC_COLOR: + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_DST_COLOR: + case SW_ONE_MINUS_DST_COLOR: + result = true; + break; + default: + break; + } + + return result; +} + +/* === Public Implementation === */ + +bool swInit(int w, int h) +{ + if (!sw_framebuffer_load(w, h)) { + swClose(); return false; + } + + swViewport(0, 0, w, h); + swScissor(0, 0, w, h); + + RLSW.loadedTextures = SW_MALLOC(SW_MAX_TEXTURES * sizeof(sw_texture_t)); + if (RLSW.loadedTextures == NULL) { swClose(); return false; } + + RLSW.freeTextureIds = SW_MALLOC(SW_MAX_TEXTURES * sizeof(uint32_t)); + if (RLSW.loadedTextures == NULL) { swClose(); return false; } + + RLSW.clearColor[0] = 0.0f; + RLSW.clearColor[1] = 0.0f; + RLSW.clearColor[2] = 0.0f; + RLSW.clearColor[3] = 1.0f; + RLSW.clearDepth = 1.0f; + + RLSW.currentMatrixMode = SW_MODELVIEW; + RLSW.currentMatrix = &RLSW.stackModelview[0]; + + sw_matrix_id(RLSW.stackProjection[0]); + sw_matrix_id(RLSW.stackModelview[0]); + sw_matrix_id(RLSW.stackTexture[0]); + sw_matrix_id(RLSW.matMVP); + + RLSW.stackProjectionCounter = 1; + RLSW.stackModelviewCounter = 1; + RLSW.stackTextureCounter = 1; + RLSW.isDirtyMVP = false; + + RLSW.current.texcoord[0] = 0.0f; + RLSW.current.texcoord[1] = 0.0f; + + RLSW.current.color[0] = 1.0f; + RLSW.current.color[1] = 1.0f; + RLSW.current.color[2] = 1.0f; + RLSW.current.color[3] = 1.0f; + + RLSW.srcFactor = SW_SRC_ALPHA; + RLSW.dstFactor = SW_ONE_MINUS_SRC_ALPHA; + + RLSW.srcFactorFunc = sw_factor_src_alpha; + RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; + + RLSW.polyMode = SW_FILL; + RLSW.cullFace = SW_BACK; + + static const float defTex[3*2*2] = + { + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + }; + + RLSW.loadedTextures[0].pixels.cptr = defTex; + RLSW.loadedTextures[0].width = 2; + RLSW.loadedTextures[0].height = 2; + RLSW.loadedTextures[0].wMinus1 = 1; + RLSW.loadedTextures[0].hMinus1 = 1; + RLSW.loadedTextures[0].format = SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; + RLSW.loadedTextures[0].minFilter = SW_NEAREST; + RLSW.loadedTextures[0].magFilter = SW_NEAREST; + RLSW.loadedTextures[0].sWrap = SW_REPEAT; + RLSW.loadedTextures[0].tWrap = SW_REPEAT; + RLSW.loadedTextures[0].tx = 0.5f; + RLSW.loadedTextures[0].ty = 0.5f; + RLSW.loadedTextures[0].copy = false; + + RLSW.loadedTextureCount = 1; + + return true; +} + +void swClose(void) +{ + for (int i = 1; i < RLSW.loadedTextureCount; i++) { + sw_texture_t* texture = &RLSW.loadedTextures[i]; + if (sw_is_texture_valid(i) && texture->copy) { + SW_FREE(texture->pixels.ptr); + } + } + + SW_FREE(RLSW.framebuffer.color); + SW_FREE(RLSW.framebuffer.depth); + SW_FREE(RLSW.loadedTextures); + SW_FREE(RLSW.freeTextureIds); + + RLSW = (sw_context_t) { 0 }; +} + +bool swResizeFramebuffer(int w, int h) +{ + return sw_framebuffer_resize(w, h); +} + +void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void* pixels) +{ + sw_pixelformat_t pFormat = sw_get_pixel_format(format, type); + + if (w <= 0) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (h <= 0) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (w > RLSW.framebuffer.width) + w = RLSW.framebuffer.width; + + if (h > RLSW.framebuffer.height) + h = RLSW.framebuffer.height; + + x = sw_clampi(x, 0, w); + y = sw_clampi(y, 0, h); + + switch (pFormat) { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + sw_framebuffer_copy_to_R5G6B5(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: + sw_framebuffer_copy_to_R8G8B8(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: + sw_framebuffer_copy_to_R32(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: + sw_framebuffer_copy_to_R32G32B32(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + sw_framebuffer_copy_to_R32G32B32A32(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: + sw_framebuffer_copy_to_R16(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: + sw_framebuffer_copy_to_R16G16B16(x, y, w, h, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + sw_framebuffer_copy_to_R16G16B16A16(x, y, w, h, pixels); + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, + int xSrc, int ySrc, int wSrc, int hSrc, + SWformat format, SWtype type, void* pixels) +{ + sw_pixelformat_t pFormat = sw_get_pixel_format(format, type); + + if (wSrc <= 0) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (hSrc <= 0) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (wSrc > RLSW.framebuffer.width) + wSrc = RLSW.framebuffer.width; + + if (hSrc > RLSW.framebuffer.height) + hSrc = RLSW.framebuffer.height; + + xSrc = sw_clampi(xSrc, 0, wSrc); + ySrc = sw_clampi(ySrc, 0, hSrc); + + switch (pFormat) { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + sw_framebuffer_blit_to_R5G6B5(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: + sw_framebuffer_blit_to_R8G8B8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + sw_framebuffer_blit_to_R5G5B5A1(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + sw_framebuffer_blit_to_R4G4B4A4(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + sw_framebuffer_blit_to_R8G8B8A8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: + sw_framebuffer_blit_to_R32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: + sw_framebuffer_blit_to_R32G32B32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + sw_framebuffer_blit_to_R32G32B32A32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: + sw_framebuffer_blit_to_R16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: + sw_framebuffer_blit_to_R16G16B16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + sw_framebuffer_blit_to_R16G16B16A16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void* swGetColorBuffer(int* w, int* h) +{ + if (w) *w = RLSW.framebuffer.width; + if (h) *h = RLSW.framebuffer.height; + + return RLSW.framebuffer.color; +} + +void swEnable(SWstate state) +{ + switch (state) { + case SW_SCISSOR_TEST: + RLSW.stateFlags |= SW_STATE_SCISSOR_TEST; + break; + case SW_TEXTURE_2D: + RLSW.stateFlags |= SW_STATE_TEXTURE_2D; + break; + case SW_DEPTH_TEST: + RLSW.stateFlags |= SW_STATE_DEPTH_TEST; + break; + case SW_CULL_FACE: + RLSW.stateFlags |= SW_STATE_CULL_FACE; + break; + case SW_BLEND: + RLSW.stateFlags |= SW_STATE_BLEND; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void swDisable(SWstate state) +{ + switch (state) { + case SW_SCISSOR_TEST: + RLSW.stateFlags &= ~SW_STATE_SCISSOR_TEST; + break; + case SW_TEXTURE_2D: + RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; + break; + case SW_DEPTH_TEST: + RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; + break; + case SW_CULL_FACE: + RLSW.stateFlags &= ~SW_STATE_CULL_FACE; + break; + case SW_BLEND: + RLSW.stateFlags &= ~SW_STATE_BLEND; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void swGetIntegerv(SWget name, int* v) +{ + switch (name) { + case SW_MODELVIEW_STACK_DEPTH: + *v = SW_MODELVIEW_STACK_DEPTH; + break; + case SW_PROJECTION_STACK_DEPTH: + *v = SW_PROJECTION_STACK_DEPTH; + break; + case SW_TEXTURE_STACK_DEPTH: + *v = SW_TEXTURE_STACK_DEPTH; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +void swGetFloatv(SWget name, float* v) +{ + switch (name) { + case SW_COLOR_CLEAR_VALUE: + v[0] = RLSW.clearColor[0]; + v[1] = RLSW.clearColor[1]; + v[2] = RLSW.clearColor[2]; + v[3] = RLSW.clearColor[3]; + break; + case SW_CURRENT_COLOR: + v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[0]; + v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[1]; + v[2] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[2]; + v[3] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[3]; + break; + case SW_CURRENT_TEXTURE_COORDS: + v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[0]; + v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[1]; + break; + case SW_POINT_SIZE: + v[0] = 2.0f * RLSW.pointRadius; + break; + case SW_LINE_WIDTH: + v[0] = RLSW.lineWidth; + break; + case SW_MODELVIEW_MATRIX: + for (int i = 0; i < 16; i++) { + v[i] = RLSW.stackModelview[RLSW.stackModelviewCounter - 1][i]; + } + break; + case SW_PROJECTION_MATRIX: + for (int i = 0; i < 16; i++) { + v[i] = RLSW.stackProjection[RLSW.stackProjectionCounter - 1][i]; + } + break; + case SW_TEXTURE_MATRIX: + for (int i = 0; i < 16; i++) { + v[i] = RLSW.stackTexture[RLSW.stackTextureCounter - 1][i]; + } + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } +} + +const char* swGetString(SWget name) +{ + const char* result = NULL; + + switch (name) { + case SW_VENDOR: + result = "RLSW Header"; + break; + case SW_RENDERER: + result = "RLSW Software Renderer"; + break; + case SW_VERSION: + result = "RLSW 1.0"; + break; + case SW_EXTENSIONS: + result = "None"; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + break; + } + + return result; +} + +SWerrcode swGetError(void) +{ + SWerrcode ret = RLSW.errCode; + RLSW.errCode = SW_NO_ERROR; + return ret; +} + +void swViewport(int x, int y, int width, int height) +{ + if (width < 0 || height < 0) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + RLSW.vpSize[0] = width; + RLSW.vpSize[1] = height; + + RLSW.vpHalfSize[0] = (int)(width / 2.0f + 0.5f); + RLSW.vpHalfSize[1] = (int)(height / 2.0f + 0.5f); + + RLSW.vpCenter[0] = x + RLSW.vpHalfSize[0]; + RLSW.vpCenter[1] = y + RLSW.vpHalfSize[1]; + + RLSW.vpMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1); + RLSW.vpMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1); + RLSW.vpMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); + RLSW.vpMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); +} + +void swScissor(int x, int y, int width, int height) +{ + if (width < 0 || height < 0) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + RLSW.scMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1); + RLSW.scMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1); + RLSW.scMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); + RLSW.scMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); + + RLSW.scClipMin[0] = (2.0f * (float)RLSW.scMin[0] / (float)RLSW.vpSize[0]) - 1.0f; + RLSW.scClipMax[0] = (2.0f * (float)RLSW.scMax[0] / (float)RLSW.vpSize[0]) - 1.0f; + RLSW.scClipMax[1] = 1.0f - (2.0f * (float)RLSW.scMin[1] / (float)RLSW.vpSize[1]); + RLSW.scClipMin[1] = 1.0f - (2.0f * (float)RLSW.scMax[1] / (float)RLSW.vpSize[1]); +} + +void swClearColor(float r, float g, float b, float a) +{ + RLSW.clearColor[0] = r; + RLSW.clearColor[1] = g; + RLSW.clearColor[2] = b; + RLSW.clearColor[3] = a; +} + +void swClear(uint32_t bitmask) +{ + int size = RLSW.framebuffer.width * RLSW.framebuffer.height; + + if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) { + sw_framebuffer_fill( + RLSW.framebuffer.color, RLSW.framebuffer.depth, + size, RLSW.clearColor, RLSW.clearDepth + ); + } + else if (bitmask & (SW_COLOR_BUFFER_BIT)) { + sw_framebuffer_fill_color(RLSW.framebuffer.color, size, RLSW.clearColor); + } + else if (bitmask & SW_DEPTH_BUFFER_BIT) { + sw_framebuffer_fill_depth(RLSW.framebuffer.depth, size, RLSW.clearDepth); + } +} + +void swBlendFunc(SWfactor sfactor, SWfactor dfactor) +{ + if (!sw_is_blend_src_factor_valid(sfactor) + || !sw_is_blend_dst_factor_valid(dfactor)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + RLSW.srcFactor = sfactor; + RLSW.dstFactor = dfactor; + + switch (sfactor) { + case SW_ZERO: + RLSW.srcFactorFunc = sw_factor_zero; + break; + case SW_ONE: + RLSW.srcFactorFunc = sw_factor_one; + break; + case SW_SRC_COLOR: + RLSW.srcFactorFunc = sw_factor_src_color; + break; + case SW_ONE_MINUS_SRC_COLOR: + RLSW.srcFactorFunc = sw_factor_one_minus_src_color; + break; + case SW_SRC_ALPHA: + RLSW.srcFactorFunc = sw_factor_src_alpha; + break; + case SW_ONE_MINUS_SRC_ALPHA: + RLSW.srcFactorFunc = sw_factor_one_minus_src_alpha; + break; + case SW_DST_ALPHA: + RLSW.srcFactorFunc = sw_factor_dst_alpha; + break; + case SW_ONE_MINUS_DST_ALPHA: + RLSW.srcFactorFunc = sw_factor_one_minus_dst_alpha; + break; + case SW_DST_COLOR: + RLSW.srcFactorFunc = sw_factor_dst_color; + break; + case SW_ONE_MINUS_DST_COLOR: + RLSW.srcFactorFunc = sw_factor_one_minus_dst_color; + break; + case SW_SRC_ALPHA_SATURATE: + RLSW.srcFactorFunc = sw_factor_src_alpha_saturate; + break; + } + + switch (dfactor) { + case SW_ZERO: + RLSW.dstFactorFunc = sw_factor_zero; + break; + case SW_ONE: + RLSW.dstFactorFunc = sw_factor_one; + break; + case SW_SRC_COLOR: + RLSW.dstFactorFunc = sw_factor_src_color; + break; + case SW_ONE_MINUS_SRC_COLOR: + RLSW.dstFactorFunc = sw_factor_one_minus_src_color; + break; + case SW_SRC_ALPHA: + RLSW.dstFactorFunc = sw_factor_src_alpha; + break; + case SW_ONE_MINUS_SRC_ALPHA: + RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; + break; + case SW_DST_ALPHA: + RLSW.dstFactorFunc = sw_factor_dst_alpha; + break; + case SW_ONE_MINUS_DST_ALPHA: + RLSW.dstFactorFunc = sw_factor_one_minus_dst_alpha; + break; + case SW_DST_COLOR: + RLSW.dstFactorFunc = sw_factor_dst_color; + break; + case SW_ONE_MINUS_DST_COLOR: + RLSW.dstFactorFunc = sw_factor_one_minus_dst_color; + break; + case SW_SRC_ALPHA_SATURATE: + // NOTE: Should never be reached + break; + } +} + +void swPolygonMode(SWpoly mode) +{ + if (!sw_is_poly_mode_valid(mode)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + RLSW.polyMode = mode; +} + +void swCullFace(SWface face) +{ + if (!sw_is_face_valid(face)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + RLSW.cullFace = face; +} + +void swPointSize(float size) +{ + RLSW.pointRadius = floorf(size * 0.5f); +} + +void swLineWidth(float width) +{ + RLSW.lineWidth = roundf(width); +} + +void swMatrixMode(SWmatrix mode) +{ + switch (mode) { + case SW_PROJECTION: + RLSW.currentMatrix = &RLSW.stackProjection[RLSW.stackProjectionCounter - 1]; + break; + case SW_MODELVIEW: + RLSW.currentMatrix = &RLSW.stackModelview[RLSW.stackModelviewCounter - 1]; + break; + case SW_TEXTURE: + RLSW.currentMatrix = &RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + break; + default: + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + RLSW.currentMatrixMode = mode; +} + +void swPushMatrix(void) +{ + switch (RLSW.currentMatrixMode) { + case SW_PROJECTION: + { + if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE) { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + + int iOld = RLSW.stackProjectionCounter - 1; + int iNew = RLSW.stackProjectionCounter++; + + for (int i = 0; i < 16; i++) { + RLSW.stackProjection[iNew][i] = RLSW.stackProjection[iOld][i]; + } + + RLSW.currentMatrix = &RLSW.stackProjection[iNew]; + } + break; + case SW_MODELVIEW: + { + if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE) { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + + int iOld = RLSW.stackModelviewCounter - 1; + int iNew = RLSW.stackModelviewCounter++; + + for (int i = 0; i < 16; i++) { + RLSW.stackModelview[iNew][i] = RLSW.stackModelview[iOld][i]; + } + + RLSW.currentMatrix = &RLSW.stackModelview[iNew]; + } + break; + case SW_TEXTURE: + { + if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE) { + RLSW.errCode = SW_STACK_OVERFLOW; + return; + } + + int iOld = RLSW.stackTextureCounter - 1; + int iNew = RLSW.stackTextureCounter++; + + for (int i = 0; i < 16; i++) { + RLSW.stackTexture[iNew][i] = RLSW.stackTexture[iOld][i]; + } + + RLSW.currentMatrix = &RLSW.stackTexture[iNew]; + } + break; + } +} + +void swPopMatrix(void) +{ + switch (RLSW.currentMatrixMode) { + case SW_PROJECTION: + { + if (RLSW.stackProjectionCounter <= 0) { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + + RLSW.currentMatrix = &RLSW.stackProjection[--RLSW.stackProjectionCounter]; + RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed + } + break; + case SW_MODELVIEW: + { + if (RLSW.stackModelviewCounter <= 0) { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + + RLSW.currentMatrix = &RLSW.stackModelview[--RLSW.stackModelviewCounter]; + RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed + } + break; + case SW_TEXTURE: + { + if (RLSW.stackTextureCounter <= 0) { + RLSW.errCode = SW_STACK_UNDERFLOW; + return; + } + + RLSW.currentMatrix = &RLSW.stackTexture[--RLSW.stackTextureCounter]; + } + break; + } +} + +void swLoadIdentity(void) +{ + sw_matrix_id(*RLSW.currentMatrix); + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swTranslatef(float x, float y, float z) +{ + sw_matrix_t mat; + sw_matrix_id(mat); + + mat[12] = x; + mat[13] = y; + mat[14] = z; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swRotatef(float angle, float x, float y, float z) +{ + angle *= SW_DEG2RAD; + + float lengthSq = x*x + y*y + z*z; + + if (lengthSq != 1.0f && lengthSq != 0.0f) { + float invLength = 1.0f/sqrtf(lengthSq); + x *= invLength; + y *= invLength; + z *= invLength; + } + + float sinres = sinf(angle); + float cosres = cosf(angle); + float t = 1.0f - cosres; + + sw_matrix_t mat; + + mat[0] = x*x*t + cosres; + mat[1] = y*x*t + z*sinres; + mat[2] = z*x*t - y*sinres; + mat[3] = 0.0f; + + mat[4] = x*y*t - z*sinres; + mat[5] = y*y*t + cosres; + mat[6] = z*y*t + x*sinres; + mat[7] = 0.0f; + + mat[8] = x*z*t + y*sinres; + mat[9] = y*z*t - x*sinres; + mat[10] = z*z*t + cosres; + mat[11] = 0.0f; + + mat[12] = 0.0f; + mat[13] = 0.0f; + mat[14] = 0.0f; + mat[15] = 1.0f; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swScalef(float x, float y, float z) +{ + sw_matrix_t mat; + + mat[0] = x, mat[1] = 0, mat[2] = 0, mat[3] = 0; + mat[4] = 0, mat[5] = y, mat[6] = 0, mat[7] = 0; + mat[8] = 0, mat[9] = 0, mat[10] = z, mat[11] = 0; + mat[12] = 0, mat[13] = 0, mat[14] = 0, mat[15] = 1; + + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swMultMatrixf(const float* mat) +{ + sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); + + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swFrustum(double left, double right, double bottom, double top, double znear, double zfar) +{ + sw_matrix_t mat; + + double rl = right - left; + double tb = top - bottom; + double fn = zfar - znear; + + mat[0] = (znear*2.0)/rl; + mat[1] = 0.0f; + mat[2] = 0.0f; + mat[3] = 0.0f; + + mat[4] = 0.0f; + mat[5] = (znear*2.0)/tb; + mat[6] = 0.0f; + mat[7] = 0.0f; + + mat[8] = (right + left)/rl; + mat[9] = (top + bottom)/tb; + mat[10] = -(zfar + znear)/fn; + mat[11] = -1.0f; + + mat[12] = 0.0f; + mat[13] = 0.0f; + mat[14] = -(zfar*znear*2.0)/fn; + mat[15] = 0.0f; + + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); + + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swOrtho(double left, double right, double bottom, double top, double znear, double zfar) +{ + sw_matrix_t mat; + + double rl = right - left; + double tb = top - bottom; + double fn = zfar - znear; + + mat[0] = 2.0f/rl; + mat[1] = 0.0f; + mat[2] = 0.0f; + mat[3] = 0.0f; + + mat[4] = 0.0f; + mat[5] = 2.0f/tb; + mat[6] = 0.0f; + mat[7] = 0.0f; + + mat[8] = 0.0f; + mat[9] = 0.0f; + mat[10] = -2.0f/fn; + mat[11] = 0.0f; + + mat[12] = -(left + right)/rl; + mat[13] = -(top + bottom)/tb; + mat[14] = -(zfar + znear)/fn; + mat[15] = 1.0f; + + sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); + + if (RLSW.currentMatrixMode != SW_TEXTURE) { + RLSW.isDirtyMVP = true; + } +} + +void swBegin(SWdraw mode) +{ + /* --- Check if the draw mode is valid --- */ + + if (!sw_is_draw_mode_valid(mode)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + /* --- Recalculate the MVP if this is needed --- */ + + if (RLSW.isDirtyMVP) { + sw_matrix_mul_rst( + RLSW.matMVP, + RLSW.stackModelview[RLSW.stackModelviewCounter - 1], + RLSW.stackProjection[RLSW.stackProjectionCounter - 1] + ); + RLSW.isDirtyMVP = false; + } + + /* --- Obtaining the number of vertices needed for this primitive --- */ + + switch (mode) { + case SW_POINTS: + RLSW.reqVertices = 1; + break; + case SW_LINES: + RLSW.reqVertices = 2; + break; + case SW_TRIANGLES: + RLSW.reqVertices = 3; + break; + case SW_QUADS: + RLSW.reqVertices = 4; + break; + } + + /* --- Initialize some values --- */ + + RLSW.vertexCounter = 0; + RLSW.drawMode = mode; +} + +void swEnd(void) +{ + RLSW.drawMode = 0; +} + +void swVertex2i(int x, int y) +{ + const float v[4] = { (float)x, (float)y, 0.0f, 1.0f }; + swVertex4fv(v); +} + +void swVertex2f(float x, float y) +{ + const float v[4] = { x, y, 0.0f, 1.0f }; + swVertex4fv(v); +} + +void swVertex2fv(const float* v) +{ + const float v4[4] = { v[0], v[1], 0.0f, 1.0f }; + swVertex4fv(v4); +} + +void swVertex3i(int x, int y, int z) +{ + const float v[4] = { (float)x, (float)y, (float)z, 1.0f }; + swVertex4fv(v); +} + +void swVertex3f(float x, float y, float z) +{ + const float v[4] = { x, y, z, 1.0f }; + swVertex4fv(v); +} + +void swVertex3fv(const float* v) +{ + const float v4[4] = { v[0], v[1], v[2], 1.0f }; + swVertex4fv(v4); +} + +void swVertex4i(int x, int y, int z, int w) +{ + const float v[4] = { (float)x, (float)y, (float)z, (float)w }; + swVertex4fv(v); +} + +void swVertex4f(float x, float y, float z, float w) +{ + const float v[4] = { x, y, z, w }; + swVertex4fv(v); +} + +void swVertex4fv(const float* v) +{ + /* --- Copy the position in the current vertex --- */ + + sw_vertex_t* vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; + + for (int i = 0; i < 4; i++) { + vertex->position[i] = v[i]; + } + + /* --- Copy additonal vertex data --- */ + + for (int i = 0; i < 2; i++) { + vertex->texcoord[i] = RLSW.current.texcoord[i]; + } + + for (int i = 0; i < 4; i++) { + vertex->color[i] = RLSW.current.color[i]; + } + + /* --- Calculation of homogeneous coordinates --- */ + + const float* m = RLSW.matMVP; + + vertex->homogeneous[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3]; + vertex->homogeneous[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3]; + vertex->homogeneous[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3]; + vertex->homogeneous[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]; + + /* --- Immediate rendering of the primitive if the required number is reached --- */ + + if (RLSW.vertexCounter == RLSW.reqVertices) { + switch (RLSW.polyMode) { + case SW_FILL: + sw_poly_fill_render(); + break; + case SW_LINE: + sw_poly_line_render(); + break; + case SW_POINT: + sw_poly_point_render(); + break; + } + RLSW.vertexCounter = 0; + } +} + +void swColor3ub(uint8_t r, uint8_t g, uint8_t b) +{ + float cv[4]; + cv[0] = (float)r / 255; + cv[1] = (float)g / 255; + cv[2] = (float)b / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3ubv(const uint8_t* v) +{ + float cv[4]; + cv[0] = (float)v[0] / 255; + cv[1] = (float)v[1] / 255; + cv[2] = (float)v[2] / 255; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3f(float r, float g, float b) +{ + float cv[4]; + cv[0] = r; + cv[1] = g; + cv[2] = b; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor3fv(const float* v) +{ + float cv[4]; + cv[0] = v[0]; + cv[1] = v[1]; + cv[2] = v[2]; + cv[3] = 1.0f; + + swColor4fv(cv); +} + +void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + float cv[4]; + cv[0] = (float)r / 255; + cv[1] = (float)g / 255; + cv[2] = (float)b / 255; + cv[3] = (float)a / 255; + + swColor4fv(cv); +} + +void swColor4ubv(const uint8_t* v) +{ + float cv[4]; + cv[0] = (float)v[0] / 255; + cv[1] = (float)v[1] / 255; + cv[2] = (float)v[2] / 255; + cv[3] = (float)v[3] / 255; + + swColor4fv(cv); +} + +void swColor4f(float r, float g, float b, float a) +{ + float cv[4]; + cv[0] = r; + cv[1] = g; + cv[2] = b; + cv[3] = a; + + swColor4fv(cv); +} + +void swColor4fv(const float* v) +{ + for (int i = 0; i < 4; i++) { + RLSW.current.color[i] = v[i]; + } +} + +void swTexCoord2f(float u, float v) +{ + const float* m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + + RLSW.current.texcoord[0] = m[0] * u + m[4] * v + m[12]; + RLSW.current.texcoord[1] = m[1] * u + m[5] * v + m[13]; +} + +void swTexCoord2fv(const float* v) +{ + const float* m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + + RLSW.current.texcoord[0] = m[0] * v[0] + m[4] * v[1] + m[12]; + RLSW.current.texcoord[1] = m[1] * v[0] + m[5] * v[1] + m[13]; +} + +void swBindArray(SWarray type, void *buffer) +{ + switch (type) { + case SW_VERTEX_ARRAY: + RLSW.array.positions = buffer; + break; + case SW_TEXTURE_COORD_ARRAY: + RLSW.array.texcoords = buffer; + break; + case SW_COLOR_ARRAY: + RLSW.array.colors = buffer; + break; + default: + break; + } +} + +void swDrawArrays(SWdraw mode, int offset, int count) +{ + if (RLSW.array.positions == 0) { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + swBegin(mode); + { + swTexCoord2f(0.0f, 0.0f); + swColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + for (int i = offset; i < count; i++) { + if (RLSW.array.texcoords) swTexCoord2fv(RLSW.array.texcoords + 2 * i); + if (RLSW.array.colors) swColor4ubv(RLSW.array.colors + 4 * i); + swVertex3fv(RLSW.array.positions + 3 * i); + } + } + swEnd(); +} + +void swGenTextures(int count, uint32_t* textures) +{ + if (count == 0 || textures == NULL) { + return; + } + + for (int i = 0; i < count; i++) { + if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) { + RLSW.errCode = SW_STACK_OVERFLOW; //< Out of memory, not really stack overflow + return; + } + uint32_t id = 0; + if (RLSW.freeTextureIdCount > 0) { + id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; + } + else { + id = RLSW.loadedTextureCount++; + } + RLSW.loadedTextures[id] = RLSW.loadedTextures[0]; + textures[i] = id; + } +} + +void swDeleteTextures(int count, uint32_t* textures) +{ + if (count == 0 || textures == NULL) { + return; + } + + for (int i = 0; i < count; i++) { + if (!sw_is_texture_valid(textures[i])) { + RLSW.errCode = SW_INVALID_VALUE; + continue; + } + if (RLSW.loadedTextures[textures[i]].copy) { + SW_FREE(RLSW.loadedTextures[textures[i]].pixels.ptr); + } + RLSW.loadedTextures[textures[i]].pixels.cptr = NULL; + RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = textures[i]; + } +} + +void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void* data) +{ + uint32_t id = RLSW.currentTexture; + + if (!sw_is_texture_valid(id)) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + int pixelFormat = sw_get_pixel_format(format, type); + + if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + + sw_texture_t* texture = &RLSW.loadedTextures[id]; + + if (copy) { + int bytes = sw_get_pixel_bytes(pixelFormat); + int size = bytes * width * height; + texture->pixels.ptr = SW_MALLOC(size); + if (texture->pixels.ptr == NULL) { + RLSW.errCode = SW_STACK_OVERFLOW; //< Out of memory... + return; + } + for (int i = 0; i < size; i++) { + ((uint8_t*)texture->pixels.ptr)[i] = ((uint8_t*)data)[i]; + } + } + else { + texture->pixels.cptr = data; + } + + texture->width = width; + texture->height = height; + texture->wMinus1 = width - 1; + texture->hMinus1 = height - 1; + texture->format = pixelFormat; + texture->tx = 1.0f / width; + texture->ty = 1.0f / height; + texture->copy = copy; +} + +void swTexParameteri(int param, int value) +{ + uint32_t id = RLSW.currentTexture; + + if (!sw_is_texture_valid(id)) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + sw_texture_t* texture = &RLSW.loadedTextures[id]; + + switch (param) { + + case SW_TEXTURE_MIN_FILTER: + if (!sw_is_texture_filter_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->minFilter = value; + break; + + case SW_TEXTURE_MAG_FILTER: + if (!sw_is_texture_filter_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->magFilter = value; + break; + + case SW_TEXTURE_WRAP_S: + if (!sw_is_texture_wrap_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->sWrap = value; + break; + + case SW_TEXTURE_WRAP_T: + if (!sw_is_texture_wrap_valid(value)) { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->tWrap = value; + break; + + default: + RLSW.errCode = SW_INVALID_ENUM; + return; + + } +} + +void swBindTexture(uint32_t id) +{ + if (id >= SW_MAX_TEXTURES) { + RLSW.errCode = SW_INVALID_VALUE; + return; + } + + if (RLSW.loadedTextures[id].pixels.cptr == NULL) { + RLSW.errCode = SW_INVALID_OPERATION; + return; + } + + RLSW.currentTexture = id; +} + +#endif // RLSW_IMPL diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 71265e718..00a6ccc47 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -63,18 +63,20 @@ #include "SDL.h" #endif -#if defined(GRAPHICS_API_OPENGL_ES2) - // It seems it does not need to be included to work - //#include "SDL_opengles2.h" -#else - // SDL OpenGL functionality (if required, instead of internal renderer) - #ifdef USING_SDL3_PROJECT - #include "SDL3/SDL_opengl.h" - #elif USING_SDL2_PROJECT - #include "SDL2/SDL_opengl.h" - #else - #include "SDL_opengl.h" - #endif +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #if defined(GRAPHICS_API_OPENGL_ES2) + // It seems it does not need to be included to work + //#include "SDL_opengles2.h" + #else + // SDL OpenGL functionality (if required, instead of internal renderer) + #ifdef USING_SDL3_PROJECT + #include "SDL3/SDL_opengl.h" + #elif USING_SDL2_PROJECT + #include "SDL2/SDL_opengl.h" + #else + #include "SDL_opengl.h" + #endif + #endif #endif //---------------------------------------------------------------------------------- @@ -1229,7 +1231,14 @@ void DisableCursor(void) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // NOTE: We use a preprocessor condition here because `rlCopyFramebuffer` is only declared for software rendering + SDL_Surface* surface = SDL_GetWindowSurface(platform.window); + rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels); + SDL_UpdateWindowSurface(platform.window); +#else SDL_GL_SwapWindow(platform.window); +#endif } //---------------------------------------------------------------------------------- @@ -1577,13 +1586,15 @@ void PollInputEvents(void) if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character (codepoint) to the queue - #if defined(USING_VERSION_SDL3) + + #if defined(USING_VERSION_SDL3) size_t textLen = strlen(event.text.text); unsigned int codepoint = (unsigned int)SDL_StepUTF8(&event.text.text, &textLen); - #else + #else int codepointSize = 0; int codepoint = GetCodepointNextSDL(event.text.text, &codepointSize); - #endif + #endif + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint; CORE.Input.Keyboard.charPressedQueueCount++; } @@ -1887,7 +1898,6 @@ int InitPlatform(void) //---------------------------------------------------------------------------- unsigned int flags = 0; flags |= SDL_WINDOW_SHOWN; - flags |= SDL_WINDOW_OPENGL; flags |= SDL_WINDOW_INPUT_FOCUS; flags |= SDL_WINDOW_MOUSE_FOCUS; flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured @@ -1922,44 +1932,50 @@ int InitPlatform(void) // NOTE: Some OpenGL context attributes must be set before window creation - // Check selection OpenGL version - if (rlGetVersion() == RL_OPENGL_21) + if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - } - else if (rlGetVersion() == RL_OPENGL_33) - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - } - else if (rlGetVersion() == RL_OPENGL_43) - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - } - else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context - { - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - } + // Add the flag telling the window to use an OpenGL context + flags |= SDL_WINDOW_OPENGL; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } + else if (rlGetVersion() == RL_OPENGL_33) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + } + else if (rlGetVersion() == RL_OPENGL_43) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context + #endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + } } // Init window @@ -1970,10 +1986,12 @@ int InitPlatform(void) #endif // Init OpenGL context - platform.glContext = SDL_GL_CreateContext(platform.window); + if (rlGetVersion() != RL_OPENGL_11_SOFTWARE) + { + platform.glContext = SDL_GL_CreateContext(platform.window); + } - // Check window and glContext have been initialized successfully - if ((platform.window != NULL) && (platform.glContext != NULL)) + if ((platform.window != NULL) && ((rlGetVersion() == RL_OPENGL_11_SOFTWARE) || (platform.glContext != NULL))) { CORE.Window.ready = true; @@ -1994,8 +2012,14 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - if (CORE.Window.flags & FLAG_VSYNC_HINT) SDL_GL_SetSwapInterval(1); - else SDL_GL_SetSwapInterval(0); + if (platform.glContext != NULL) + { + SDL_GL_SetSwapInterval((CORE.Window.flags & FLAG_VSYNC_HINT)? 1 : 0); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(SDL_GL_GetProcAddress); + } } else { @@ -2003,9 +2027,6 @@ int InitPlatform(void) return -1; } - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(SDL_GL_GetProcAddress); //---------------------------------------------------------------------------- // Initialize input events system @@ -2077,7 +2098,7 @@ int InitPlatform(void) void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor - SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context + if (platform.glContext != NULL) SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state } diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a399092ad..fe0ac5fd8 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -65,12 +65,17 @@ // so the enum KEY_F12 from raylib is used #undef KEY_F12 -#include // Generic Buffer Management (native platform for EGL on DRM) #include // Direct Rendering Manager user-level library interface #include // Direct Rendering Manager mode setting (KMS) interface -#include "EGL/egl.h" // Native platform windowing system interface -#include "EGL/eglext.h" // EGL extensions +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #include // Generic Buffer Management (native platform for EGL on DRM) + #include "EGL/egl.h" // Native platform windowing system interface + #include "EGL/eglext.h" // EGL extensions +#else + #include // For mmap when copying to the dumb buffer + #include // For the conversion of certain error messages +#endif // NOTE: DRM cache enables triple buffered DRM caching #if defined(SUPPORT_DRM_CACHE) @@ -103,15 +108,19 @@ typedef struct { drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector drmModeCrtc *crtc; // CRT Controller int modeIndex; // Index of the used mode of connector->modes + uint32_t prevFB; // Previous DRM framebufer (during frame swapping) + +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) struct gbm_device *gbmDevice; // GBM device struct gbm_surface *gbmSurface; // GBM surface struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) - uint32_t prevFB; // Previous GBM framebufer (during frame swapping) - EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) EGLContext context; // Graphic context, mode in which drawing can be done EGLConfig config; // Graphic config +#else + uint32_t prevDumbHandle; // Handle to the previous dumb buffer (during frame swapping) +#endif // Keyboard data int defaultKeyboardMode; // Default keyboard mode @@ -780,6 +789,8 @@ void SwapScreenBuffer() // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // Hardware rendering buffer swap with EGL eglSwapBuffers(platform.device, platform.surface); if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); @@ -805,6 +816,209 @@ void SwapScreenBuffer(void) if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); platform.prevBO = bo; +#else + // Software rendering buffer swap + if ((-1 == platform.fd) || !platform.connector || (platform.modeIndex < 0)) + { + TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + return; + } + + // Get the software rendered color buffer + int bufferWidth, bufferHeight; + void *colorBuffer = swGetColorBuffer(&bufferWidth, &bufferHeight); + if (!colorBuffer) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to get software color buffer"); + return; + } + + // Retrieving the dimensions of the display mode used + drmModeModeInfo *mode = &platform.connector->modes[platform.modeIndex]; + uint32_t width = mode->hdisplay; + uint32_t height = mode->vdisplay; + + // Dumb buffers use a fixed format based on bpp +#if SW_COLOR_BUFFER_BITS == 24 + const uint32_t bpp = 32; // 32 bits per pixel (XRGB8888 format) + const uint32_t depth = 24; // Color depth, here only 24 bits, alpha is not used +#else + // REVIEW: Not sure how it will be interpreted (RGB or RGBA?) + const uint32_t bpp = SW_COLOR_BUFFER_BITS; + const uint32_t depth = SW_COLOR_BUFFER_BITS; +#endif + + // Create a dumb buffer for software rendering + struct drm_mode_create_dumb creq = {0}; + creq.width = width; + creq.height = height; + creq.bpp = bpp; + + int result = drmIoctl(platform.fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (result < 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to create dumb buffer: %s", strerror(errno)); + return; + } + + // Create framebuffer with the correct format + uint32_t fb = 0; + result = drmModeAddFB(platform.fd, + width, height, + depth, bpp, creq.pitch, + creq.handle, &fb); + if (result != 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d (%s)", result, strerror(errno)); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Map the dumb buffer to copy our software rendered buffer + struct drm_mode_map_dumb mreq = {0}; + mreq.handle = creq.handle; + result = drmIoctl(platform.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (result != 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to map dumb buffer: %s", strerror(errno)); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Map the buffer into userspace + void *dumbBuffer = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, platform.fd, mreq.offset); + if (dumbBuffer == MAP_FAILED) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to mmap dumb buffer: %s", strerror(errno)); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Copy the software rendered buffer to the dumb buffer with scaling if needed + if (bufferWidth == width && bufferHeight == height) + { + // Direct copy if sizes match + swCopyFramebuffer(0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer); + } + else + { + // Scale the software buffer to match the display mode + swBlitFramebuffer(0, 0, width, height, 0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer); + } + + // Unmap the buffer + munmap(dumbBuffer, creq.size); + + // Find a CRTC compatible with the connector + uint32_t crtcId = 0; + if (platform.crtc) + { + crtcId = platform.crtc->crtc_id; + } + else + { + // Find a CRTC that's compatible with this connector + drmModeRes *res = drmModeGetResources(platform.fd); + if (!res) + { + TRACELOG(LOG_ERROR, "DISPLAY: Failed to get DRM resources"); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Check which CRTCs are compatible with this connector + drmModeEncoder *encoder = NULL; + if (platform.connector->encoder_id) + { + encoder = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); + } + + if (encoder && encoder->crtc_id) + { + crtcId = encoder->crtc_id; + platform.crtc = drmModeGetCrtc(platform.fd, crtcId); + } + else + { + // Find a free CRTC + for (int i = 0; i < res->count_crtcs; i++) + { + drmModeCrtc *crtc = drmModeGetCrtc(platform.fd, res->crtcs[i]); + if (crtc && !crtc->buffer_id) // CRTC is free + { + crtcId = res->crtcs[i]; + if (platform.crtc) drmModeFreeCrtc(platform.crtc); + platform.crtc = crtc; + break; + } + if (crtc) drmModeFreeCrtc(crtc); + } + } + + if (encoder) drmModeFreeEncoder(encoder); + drmModeFreeResources(res); + + if (!crtcId) + { + TRACELOG(LOG_ERROR, "DISPLAY: No compatible CRTC found"); + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + } + + // Set CRTC with better error handling + result = drmModeSetCrtc(platform.fd, crtcId, fb, 0, 0, + &platform.connector->connector_id, 1, + mode); + if (result != 0) + { + TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d (%s)", result, strerror(errno)); + TRACELOG(LOG_ERROR, "DISPLAY: CRTC ID: %u, FB ID: %u, Connector ID: %u", crtcId, fb, platform.connector->connector_id); + TRACELOG(LOG_ERROR, "DISPLAY: Mode: %dx%d@%d", mode->hdisplay, mode->vdisplay, mode->vrefresh); + + drmModeRmFB(platform.fd, fb); + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = creq.handle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return; + } + + // Clean up previous framebuffer + if (platform.prevFB) + { + result = drmModeRmFB(platform.fd, platform.prevFB); + if (result != 0) + { + TRACELOG(LOG_WARNING, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + } + + platform.prevFB = fb; + + // Clean up previous dumb buffer + if (platform.prevDumbHandle) + { + struct drm_mode_destroy_dumb dreq = {0}; + dreq.handle = platform.prevDumbHandle; + drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + } + + platform.prevDumbHandle = creq.handle; +#endif } #endif // SUPPORT_DRM_CACHE @@ -950,10 +1164,15 @@ int InitPlatform(void) platform.connector = NULL; platform.modeIndex = -1; platform.crtc = NULL; + platform.prevFB = 0; + +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) platform.gbmDevice = NULL; platform.gbmSurface = NULL; platform.prevBO = NULL; - platform.prevFB = 0; +#else + platform.prevDumbHandle = 0; +#endif // Initialize graphic device: display/window and graphic context //---------------------------------------------------------------------------- @@ -965,11 +1184,12 @@ int InitPlatform(void) if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: Default graphic device DRM opened successfully"); #else TRACELOG(LOG_WARNING, "DISPLAY: No graphic card set, trying platform-gpu-card"); - platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: platform-gpu-card opened successfully"); if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { + if (platform.fd != -1) close(platform.fd); TRACELOG(LOG_WARNING, "DISPLAY: Failed to open platform-gpu-card, trying card1"); platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card1 opened successfully"); @@ -977,10 +1197,19 @@ int InitPlatform(void) if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { + if (platform.fd != -1) close(platform.fd); TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card1, trying card0"); platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card0 opened successfully"); } + + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) + { + if (platform.fd != -1) close(platform.fd); + TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card0, trying card2"); + platform.fd = open("/dev/dri/card2", O_RDWR); + if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card2 opened successfully"); + } #endif if (platform.fd == -1) @@ -993,29 +1222,58 @@ int InitPlatform(void) if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); + close(platform.fd); return -1; } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); + // Connector detection for (size_t i = 0; i < res->count_connectors; i++) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); - TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); + if (!con) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get connector %i", i); + continue; + } + + TRACELOG(LOG_TRACE, "DISPLAY: Connector %i modes detected: %i", i, con->count_modes); + TRACELOG(LOG_TRACE, "DISPLAY: Connector %i status: %s", i, + (con->connection == DRM_MODE_CONNECTED) ? "CONNECTED" : + (con->connection == DRM_MODE_DISCONNECTED) ? "DISCONNECTED" : + (con->connection == DRM_MODE_UNKNOWNCONNECTION) ? "UNKNOWN" : "OTHER"); // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output - if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id)) + // WARNING: Accept CONNECTED, UNKNOWN and even those without encoder_id connectors for software mode + if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0)//(con->encoder_id)) { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // For hardware rendering, we need an encoder_id + if (con->encoder_id) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected with encoder", i); + platform.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i); + } +#else + // For software rendering, we can accept even without encoder_id + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i suitable for software rendering", i); platform.connector = con; break; +#endif } - else + + if (!platform.connector) { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i NOT suitable (deleting)", i); drmModeFreeConnector(con); } } @@ -1024,14 +1282,18 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); + close(platform.fd); return -1; } +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (!enc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1040,7 +1302,9 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1055,7 +1319,9 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1064,7 +1330,7 @@ int InitPlatform(void) } const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT; - const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60; + const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60; // Try to find an exact matching mode platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); @@ -1083,7 +1349,9 @@ int InitPlatform(void) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); + drmModeFreeConnector(platform.connector); drmModeFreeResources(res); + close(platform.fd); return -1; } @@ -1092,19 +1360,45 @@ int InitPlatform(void) TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, - (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', + (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p', platform.connector->modes[platform.modeIndex].vrefresh); + drmModeFreeEncoder(enc); + enc = NULL; +#else + // For software rendering, the first available mode can be used + if (platform.connector->count_modes > 0) + { + platform.modeIndex = 0; + CORE.Window.display.width = platform.connector->modes[0].hdisplay; + CORE.Window.display.height = platform.connector->modes[0].vdisplay; + + TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u) for software rendering", + platform.connector->modes[0].name, + platform.connector->modes[0].hdisplay, + platform.connector->modes[0].vdisplay, + (platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p', + platform.connector->modes[0].vrefresh); + } + else + { + TRACELOG(LOG_WARNING, "DISPLAY: No modes available for connector"); + drmModeFreeConnector(platform.connector); + drmModeFreeResources(res); + close(platform.fd); + return -1; + } +#endif + // Use the width and height of the surface for render CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; - drmModeFreeEncoder(enc); - enc = NULL; - drmModeFreeResources(res); res = NULL; +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + // Hardware rendering initialization with EGL platform.gbmDevice = gbm_create_device(platform.fd); if (!platform.gbmDevice) { @@ -1273,6 +1567,32 @@ int InitPlatform(void) return -1; } + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); +#else + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: + // -> CORE.Window.screen.width/CORE.Window.screen.height + // -> CORE.Window.render.width/CORE.Window.render.height + // -> CORE.Window.screenScale + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + // Setup window ready state for software rendering + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully (Software Rendering)"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); +#endif + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); // If graphic device is no properly initialized, we end program @@ -1285,12 +1605,8 @@ int InitPlatform(void) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - - // Initialize timming system + // Initialize timing system //---------------------------------------------------------------------------- // NOTE: timming system must be initialized before the input events system InitTimer(); @@ -1336,6 +1652,7 @@ void ClosePlatform(void) platform.prevFB = 0; } +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) if (platform.prevBO) { gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); @@ -1353,6 +1670,7 @@ void ClosePlatform(void) gbm_device_destroy(platform.gbmDevice); platform.gbmDevice = NULL; } +#endif if (platform.crtc) { @@ -1374,6 +1692,7 @@ void ClosePlatform(void) platform.fd = -1; } +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) { @@ -1392,6 +1711,7 @@ void ClosePlatform(void) eglTerminate(platform.device); platform.device = EGL_NO_DISPLAY; } +#endif CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called diff --git a/src/rlgl.h b/src/rlgl.h index 2a1a5d5f8..5e8cf2bfa 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -149,7 +149,8 @@ #endif // Security check in case no GRAPHICS_API_OPENGL_* defined -#if !defined(GRAPHICS_API_OPENGL_11) && \ +#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) && \ + !defined(GRAPHICS_API_OPENGL_11) && \ !defined(GRAPHICS_API_OPENGL_21) && \ !defined(GRAPHICS_API_OPENGL_33) && \ !defined(GRAPHICS_API_OPENGL_43) && \ @@ -159,7 +160,7 @@ #endif // Security check in case multiple GRAPHICS_API_OPENGL_* defined -#if defined(GRAPHICS_API_OPENGL_11) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE) #if defined(GRAPHICS_API_OPENGL_21) #undef GRAPHICS_API_OPENGL_21 #endif @@ -174,6 +175,11 @@ #endif #endif +// Software implementation uses OpenGL 1.1 functionality +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #define GRAPHICS_API_OPENGL_11 +#endif + // OpenGL 2.1 uses most of OpenGL 3.3 Core functionality // WARNING: Specific parts are checked with #if defines #if defined(GRAPHICS_API_OPENGL_21) @@ -427,7 +433,8 @@ typedef struct rlRenderBatch { // OpenGL version typedef enum { - RL_OPENGL_11 = 1, // OpenGL 1.1 + RL_OPENGL_11_SOFTWARE = 0, // Software rendering + RL_OPENGL_11, // OpenGL 1.1 RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) @@ -768,6 +775,10 @@ RLAPI unsigned int rlLoadFramebuffer(void); // Loa RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +RLAPI void rlCopyFramebuffer(int x, int y, int w, int h, int format, void* pixels); +RLAPI void rlResizeFramebuffer(int width, int height); +#endif // Shaders management RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings @@ -834,24 +845,32 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif #if defined(GRAPHICS_API_OPENGL_11) - #if defined(__APPLE__) - #include // OpenGL 1.1 library for OSX - #include // OpenGL extensions library + #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + #define RLSW_IMPL + #define SW_MALLOC(sz) RL_MALLOC(sz) + #define SW_REALLOC(ptr, newSz) RL_REALLOC(ptr, newSz) + #define SW_FREE(ptr) RL_FREE(ptr) + #include "external/rlsw.h" // OpenGL 1.1 software implementation #else - // APIENTRY for OpenGL function pointer declarations is required - #if !defined(APIENTRY) - #if defined(_WIN32) - #define APIENTRY __stdcall - #else - #define APIENTRY + #if defined(__APPLE__) + #include // OpenGL 1.1 library for OSX + #include // OpenGL extensions library + #else + // APIENTRY for OpenGL function pointer declarations is required + #if !defined(APIENTRY) + #if defined(_WIN32) + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #endif + // WINGDIAPI definition. Some Windows OpenGL headers need it + #if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) #endif - #endif - // WINGDIAPI definition. Some Windows OpenGL headers need it - #if !defined(WINGDIAPI) && defined(_WIN32) - #define WINGDIAPI __declspec(dllimport) - #endif - #include // OpenGL 1.1 library + #include // OpenGL 1.1 library + #endif #endif #endif @@ -2337,6 +2356,14 @@ void rlglInit(int width, int height) glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) #endif +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + if (!swInit(width, height)) + { + TRACELOG(RL_LOG_ERROR, "RLGL: Software renderer initialization failed!"); + exit(-1); + } +#endif + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Store screen size into global variables RLGL.State.framebufferWidth = width; @@ -2363,6 +2390,10 @@ void rlglClose(void) glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); #endif + +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + swClose(); +#endif } // Load OpenGL extensions @@ -2672,7 +2703,9 @@ void *rlGetProcAddress(const char *procName) int rlGetVersion(void) { int glVersion = 0; -#if defined(GRAPHICS_API_OPENGL_11) +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) + glVersion = RL_OPENGL_11_SOFTWARE; +#elif defined(GRAPHICS_API_OPENGL_11) glVersion = RL_OPENGL_11; #endif #if defined(GRAPHICS_API_OPENGL_21) @@ -3713,6 +3746,20 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) return pixels; } +#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) +void rlCopyFramebuffer(int x, int y, int w, int h, int format, void* pixels) +{ + unsigned int glInternalFormat, glFormat, glType; + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); + swCopyFramebuffer(x, y, w, h, glFormat, glType, pixels); +} + +void rlResizeFramebuffer(int width, int height) +{ + swResizeFramebuffer(width, height); +} +#endif + // Read screen pixel data (color buffer) unsigned char *rlReadScreenPixels(int width, int height) { @@ -5313,4 +5360,4 @@ static Matrix rlMatrixInvert(Matrix mat) } #endif -#endif // RLGL_IMPLEMENTATION +#endif // RLGL_IMPLEMENTATION \ No newline at end of file diff --git a/src/rmodels.c b/src/rmodels.c index c54ef03fa..9ded580c3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1429,7 +1429,7 @@ void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int // Draw a 3d mesh with material and transform void DrawMesh(Mesh mesh, Material material, Matrix transform) { -#if defined(GRAPHICS_API_OPENGL_11) +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE) #define GL_VERTEX_ARRAY 0x8074 #define GL_NORMAL_ARRAY 0x8075 #define GL_COLOR_ARRAY 0x8076 From 43c81d216c8c26839ce781d484d906a83e5341fc Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Sep 2025 11:00:35 +0200 Subject: [PATCH 10/31] REVIEWED: VS2022: New example `core_input_actions` UUID --- .../examples/core_input_actions.vcxproj | 2 +- projects/VS2022/raylib.sln | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/projects/VS2022/examples/core_input_actions.vcxproj b/projects/VS2022/examples/core_input_actions.vcxproj index 8b64adc77..9b43826f8 100644 --- a/projects/VS2022/examples/core_input_actions.vcxproj +++ b/projects/VS2022/examples/core_input_actions.vcxproj @@ -51,7 +51,7 @@ - {6B1A933E-71B8-4C1F-9E79-02D98830E671} + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} Win32Proj core_input_actions 10.0 diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index faca55fa0..c0295d4c6 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -345,7 +345,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_inline_styling", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_undo_redo", "examples\core_undo_redo.vcxproj", "{3B27F358-2679-4F38-B297-17B536F580BB}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_actions", "examples\core_input_actions.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_input_actions", "examples\core_input_actions.vcxproj", "{718FCBD0-591D-448C-B7D5-9F1CA8544E7B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -4249,30 +4249,30 @@ Global {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x64.Build.0 = Release|x64 {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.ActiveCfg = Release|Win32 {3B27F358-2679-4F38-B297-17B536F580BB}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|ARM64.Build.0 = Debug|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.ActiveCfg = Debug|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x64.Build.0 = Debug|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.ActiveCfg = Debug|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Debug|x86.Build.0 = Debug|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.ActiveCfg = Release|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|ARM64.Build.0 = Release|ARM64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.ActiveCfg = Release|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x64.Build.0 = Release|x64 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.ActiveCfg = Release|Win32 + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4447,7 +4447,7 @@ Global {49C67F03-1A56-4F96-B278-39B66EC93678} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {D496308F-3C3C-40B3-A3ED-EA327D244B3E} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} {3B27F358-2679-4F38-B297-17B536F580BB} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {718FCBD0-591D-448C-B7D5-9F1CA8544E7B} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 3da2fc1bf876e7a81a058a661bee7b627fca51ba Mon Sep 17 00:00:00 2001 From: Antonio Jose Ramos Marquez Date: Mon, 29 Sep 2025 17:16:51 +0200 Subject: [PATCH 11/31] [rtextures] fix initialization align in Image to avoid issues with some platforms (#5214) --- src/rtextures.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 6366e4645..a85467d8c 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -811,8 +811,8 @@ Image GenImageColor(int width, int height, Color color) .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -863,8 +863,8 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -900,8 +900,8 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -949,8 +949,8 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner, .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -974,8 +974,8 @@ Image GenImageChecked(int width, int height, int checksX, int checksY, Color col .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -997,8 +997,8 @@ Image GenImageWhiteNoise(int width, int height, float factor) .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -1048,8 +1048,8 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; @@ -1113,8 +1113,8 @@ Image GenImageCellular(int width, int height, int tileSize) .data = pixels, .width = width, .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 + .mipmaps = 1, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; return image; From 317c1eaeeb2bd67e54711639cbb13391dfa44c87 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Mon, 29 Sep 2025 16:44:48 +0100 Subject: [PATCH 12/31] Add ascii post processing (#5213) --- .../resources/shaders/glsl100/ascii.fs | 67 ++++++++++ .../resources/shaders/glsl120/ascii.fs | 65 ++++++++++ .../resources/shaders/glsl330/ascii.fs | 66 ++++++++++ examples/shaders/shaders_ascii_effect.c | 119 ++++++++++++++++++ examples/shaders/shaders_ascii_effect.png | Bin 0 -> 45176 bytes 5 files changed, 317 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl100/ascii.fs create mode 100644 examples/shaders/resources/shaders/glsl120/ascii.fs create mode 100644 examples/shaders/resources/shaders/glsl330/ascii.fs create mode 100644 examples/shaders/shaders_ascii_effect.c create mode 100644 examples/shaders/shaders_ascii_effect.png diff --git a/examples/shaders/resources/shaders/glsl100/ascii.fs b/examples/shaders/resources/shaders/glsl100/ascii.fs new file mode 100644 index 000000000..a4a930c3c --- /dev/null +++ b/examples/shaders/resources/shaders/glsl100/ascii.fs @@ -0,0 +1,67 @@ +#version 100 + +precision mediump float; + +// Input from the vertex shader +varying vec2 fragTexCoord; + +// Output color for the screen +varying vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; +uniform float fontSize; + +float greyScale(in vec3 col) { + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float character(float n, vec2 p) +{ + p = floor(p * vec2(4.0, -4.0) + 2.5); + + // Check if the coordinate is inside the 5x5 grid (0 to 4). + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { + + if (int(mod(n / exp2(p.x + 5.0 * p.y), 2.0)) == 1) { + return 1.0; // The bit is on, so draw this part of the character. + } + } + + return 0.0; // The bit is off, or we are outside the grid. +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize * 1.8); + vec2 uvCellSize = charPixelSize / resolution; + + vec2 cellUV = floor(fragTexCoord / uvCellSize) * uvCellSize; + + vec3 cellColor = texture2D(texture0, cellUV).rgb; + float gray = greyScale(cellColor); + + float n = 4096; + + // limited character set + if (gray > 0.2) n = 65600.0; // : + if (gray > 0.3) n = 163153.0; // * + if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.5) n = 13121101.0; // & + if (gray > 0.6) n = 15252014.0; // 8 + if (gray > 0.7) n = 13195790.0; // @ + if (gray > 0.8) n = 11512810.0; // # + + vec2 localUV = (fragTexCoord - cellUV) / uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV * 2.0 - 1.0; + + float charShape = character(n, p); + + vec3 final_col = cellColor * charShape; + + gl_FragColor = vec4(final_col, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/ascii.fs b/examples/shaders/resources/shaders/glsl120/ascii.fs new file mode 100644 index 000000000..3521c8b33 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl120/ascii.fs @@ -0,0 +1,65 @@ +#version 120 + +// Input from the vertex shader +varying vec2 fragTexCoord; + +// Output color for the screen +varying vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; +uniform float fontSize; + +float greyScale(in vec3 col) { + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float character(float n, vec2 p) +{ + p = floor(p * vec2(4.0, -4.0) + 2.5); + + // Check if the coordinate is inside the 5x5 grid (0 to 4). + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { + + if (int(mod(n / exp2(p.x + 5.0 * p.y), 2.0)) == 1) { + return 1.0; // The bit is on, so draw this part of the character. + } + } + + return 0.0; // The bit is off, or we are outside the grid. +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize * 1.8); + vec2 uvCellSize = charPixelSize / resolution; + + vec2 cellUV = floor(fragTexCoord / uvCellSize) * uvCellSize; + + vec3 cellColor = texture2D(texture0, cellUV).rgb; + float gray = greyScale(cellColor); + + float n = 4096; + + // limited character set + if (gray > 0.2) n = 65600.0; // : + if (gray > 0.3) n = 163153.0; // * + if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.5) n = 13121101.0; // & + if (gray > 0.6) n = 15252014.0; // 8 + if (gray > 0.7) n = 13195790.0; // @ + if (gray > 0.8) n = 11512810.0; // # + + vec2 localUV = (fragTexCoord - cellUV) / uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV * 2.0 - 1.0; + + float charShape = character(n, p); + + vec3 final_col = cellColor * charShape; + + gl_FragColor = vec4(final_col, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/ascii.fs b/examples/shaders/resources/shaders/glsl330/ascii.fs new file mode 100644 index 000000000..4477f948d --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/ascii.fs @@ -0,0 +1,66 @@ +#version 330 + +// Input from the vertex shader +in vec2 fragTexCoord; + +// Output color for the screen +out vec4 finalColor; + +uniform sampler2D texture0; +uniform vec2 resolution; +uniform float fontSize; + +float greyScale(in vec3 col) { + return dot(col, vec3(0.2126, 0.7152, 0.0722)); +} + +float character(float n, vec2 p) +{ + p = floor(p * vec2(4.0, -4.0) + 2.5); + + // Check if the coordinate is inside the 5x5 grid (0 to 4). + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { + + if (int(mod(n / exp2(p.x + 5.0 * p.y), 2.0)) == 1) { + return 1.0; // The bit is on, so draw this part of the character. + } + } + + return 0.0; // The bit is off, or we are outside the grid. +} + +// ----------------------------------------------------------------------------- +// Main shader logic +// ----------------------------------------------------------------------------- + +void main() +{ + vec2 charPixelSize = vec2(fontSize, fontSize * 1.8); + vec2 uvCellSize = charPixelSize / resolution; + + vec2 cellUV = floor(fragTexCoord / uvCellSize) * uvCellSize; + + vec3 cellColor = texture(texture0, cellUV).rgb; + float gray = greyScale(cellColor); + + float n = 4096; + + // limited character set + if (gray > 0.2) n = 65600.0; // : + if (gray > 0.3) n = 163153.0; // * + if (gray > 0.4) n = 15255086.0; // o + if (gray > 0.5) n = 13121101.0; // & + if (gray > 0.6) n = 15252014.0; // 8 + if (gray > 0.7) n = 13195790.0; // @ + if (gray > 0.8) n = 11512810.0; // # + + vec2 localUV = (fragTexCoord - cellUV) / uvCellSize; // Range [0.0, 1.0] + + vec2 p = localUV * 2.0 - 1.0; + + float charShape = character(n, p); + + vec3 final_col = cellColor * charShape; + + finalColor = vec4(final_col, 1.0); +} \ No newline at end of file diff --git a/examples/shaders/shaders_ascii_effect.c b/examples/shaders/shaders_ascii_effect.c new file mode 100644 index 000000000..c505d8516 --- /dev/null +++ b/examples/shaders/shaders_ascii_effect.c @@ -0,0 +1,119 @@ +/******************************************************************************************* +* +* raylib [shaders] example - ascii effect +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Maicon Santana (@maiconpintoabreu) +* +********************************************************************************************/ + +#include "raylib.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii effect"); + + // Texture to test static drawing + Texture2D fudesumi = LoadTexture("resources/fudesumi.png"); + // Texture to test moving drawing + Texture2D raysan = LoadTexture("resources/raysan.png"); + + // Load shader to be used on postprocessing + Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/ascii.fs", GLSL_VERSION)); + + // These locations are used to send data to the GPU. + int resolutionLoc = GetShaderLocation(shader, "resolution"); + int fontSizeLoc = GetShaderLocation(shader, "fontSize"); + + // Set the character size for the ASCII effect + float fontSize = 4.0f; + + // Send the updated values to the shader + float resolution[2] = { (float)screenWidth, (float)screenHeight }; + SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2); + SetShaderValue(shader, fontSizeLoc, &fontSize, SHADER_UNIFORM_FLOAT); + + Vector2 circlePos = (Vector2){40.0f, (float)screenHeight * 0.5f}; + float circleSpeed = 1.0f; + + // RenderTexture to apply the postprocessing later + RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (circlePos.x > 200.0f || circlePos.x < 40.0f) { + circleSpeed *= -1; + } + circlePos.x += circleSpeed; + + // Draw + //---------------------------------------------------------------------------------- + + BeginTextureMode(target); + ClearBackground(WHITE); // The background of the scene itself + + DrawTexture(fudesumi, 500, -30, WHITE); // Using custom shader + DrawTextureV(raysan, circlePos, WHITE); + + EndTextureMode(); + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginShaderMode(shader); + + // Draw the scene texture (that we rendered earlier) to the screen. + // The shader will process every pixel of this texture. + DrawTextureRec(target.texture, + (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, + (Vector2){ 0, 0 }, + WHITE); + EndShaderMode(); + + DrawRectangle(0, 0, screenWidth, 40, BLACK); + DrawText("Ascii effect", 120, 10, 20, LIGHTGRAY); + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shader); // Unload shader + UnloadTexture(fudesumi); // Unload texture + UnloadTexture(raysan); // Unload texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shaders/shaders_ascii_effect.png b/examples/shaders/shaders_ascii_effect.png new file mode 100644 index 0000000000000000000000000000000000000000..eee5cc90b620c14cc111bb9a8348765cb77e827d GIT binary patch literal 45176 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 ntY^P`$Sott Date: Mon, 29 Sep 2025 18:01:42 +0200 Subject: [PATCH 13/31] REVIEWED: example: `shaders_ascii_rendering` --- .../resources/shaders/glsl330/ascii.fs | 43 ++++++++---------- ...cii_effect.c => shaders_ascii_rendering.c} | 36 +++++++-------- ...effect.png => shaders_ascii_rendering.png} | Bin 3 files changed, 37 insertions(+), 42 deletions(-) rename examples/shaders/{shaders_ascii_effect.c => shaders_ascii_rendering.c} (81%) rename examples/shaders/{shaders_ascii_effect.png => shaders_ascii_rendering.png} (100%) diff --git a/examples/shaders/resources/shaders/glsl330/ascii.fs b/examples/shaders/resources/shaders/glsl330/ascii.fs index 4477f948d..014966835 100644 --- a/examples/shaders/resources/shaders/glsl330/ascii.fs +++ b/examples/shaders/resources/shaders/glsl330/ascii.fs @@ -10,42 +10,42 @@ uniform sampler2D texture0; uniform vec2 resolution; uniform float fontSize; -float greyScale(in vec3 col) { +float GreyScale(in vec3 col) +{ return dot(col, vec3(0.2126, 0.7152, 0.0722)); } -float character(float n, vec2 p) +float GetCharacter(float n, vec2 p) { - p = floor(p * vec2(4.0, -4.0) + 2.5); + p = floor(p*vec2(4.0, -4.0) + 2.5); - // Check if the coordinate is inside the 5x5 grid (0 to 4). - if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { - - if (int(mod(n / exp2(p.x + 5.0 * p.y), 2.0)) == 1) { - return 1.0; // The bit is on, so draw this part of the character. + // Check if the coordinate is inside the 5x5 grid (0 to 4) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + if (int(mod(n/exp2(p.x + 5.0 * p.y), 2.0)) == 1) + { + return 1.0; // The bit is on, so draw this part of the character } } - return 0.0; // The bit is off, or we are outside the grid. + return 0.0; // The bit is off, or we are outside the grid } // ----------------------------------------------------------------------------- // Main shader logic // ----------------------------------------------------------------------------- - void main() { - vec2 charPixelSize = vec2(fontSize, fontSize * 1.8); - vec2 uvCellSize = charPixelSize / resolution; - - vec2 cellUV = floor(fragTexCoord / uvCellSize) * uvCellSize; + vec2 charPixelSize = vec2(fontSize, fontSize*1.8); + vec2 uvCellSize = charPixelSize/resolution; + vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize; vec3 cellColor = texture(texture0, cellUV).rgb; - float gray = greyScale(cellColor); + float gray = GreyScale(cellColor); float n = 4096; - // limited character set + // Limited character set if (gray > 0.2) n = 65600.0; // : if (gray > 0.3) n = 163153.0; // * if (gray > 0.4) n = 15255086.0; // o @@ -54,13 +54,10 @@ void main() if (gray > 0.7) n = 13195790.0; // @ if (gray > 0.8) n = 11512810.0; // # - vec2 localUV = (fragTexCoord - cellUV) / uvCellSize; // Range [0.0, 1.0] + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] + vec2 p = localUV*2.0 - 1.0; - vec2 p = localUV * 2.0 - 1.0; + float charShape = GetCharacter(n, p); - float charShape = character(n, p); - - vec3 final_col = cellColor * charShape; - - finalColor = vec4(final_col, 1.0); + finalColor = vec4(cellColor*charShape, 1.0); } \ No newline at end of file diff --git a/examples/shaders/shaders_ascii_effect.c b/examples/shaders/shaders_ascii_rendering.c similarity index 81% rename from examples/shaders/shaders_ascii_effect.c rename to examples/shaders/shaders_ascii_rendering.c index c505d8516..5444caeee 100644 --- a/examples/shaders/shaders_ascii_effect.c +++ b/examples/shaders/shaders_ascii_rendering.c @@ -1,8 +1,8 @@ /******************************************************************************************* * -* raylib [shaders] example - ascii effect +* raylib [shaders] example - ascii rendering * -* Example complexity rating: [★☆☆☆] 1/4 +* Example complexity rating: [★★☆☆] 2/4 * * Example originally created with raylib 5.5, last time updated with raylib 5.6 * @@ -33,7 +33,7 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii effect"); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii rendering"); // Texture to test static drawing Texture2D fudesumi = LoadTexture("resources/fudesumi.png"); @@ -43,7 +43,7 @@ int main(void) // Load shader to be used on postprocessing Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/ascii.fs", GLSL_VERSION)); - // These locations are used to send data to the GPU. + // These locations are used to send data to the GPU int resolutionLoc = GetShaderLocation(shader, "resolution"); int fontSizeLoc = GetShaderLocation(shader, "fontSize"); @@ -69,33 +69,31 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if (circlePos.x > 200.0f || circlePos.x < 40.0f) { - circleSpeed *= -1; - } circlePos.x += circleSpeed; + + if ((circlePos.x > 200.0f) || (circlePos.x < 40.0f)) circleSpeed *= -1; + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- - + // Draw our scene to a render texture first BeginTextureMode(target); - ClearBackground(WHITE); // The background of the scene itself + ClearBackground(WHITE); - DrawTexture(fudesumi, 500, -30, WHITE); // Using custom shader + DrawTexture(fudesumi, 500, -30, WHITE); DrawTextureV(raysan, circlePos, WHITE); - + EndTextureMode(); + BeginDrawing(); - ClearBackground(RAYWHITE); BeginShaderMode(shader); - - // Draw the scene texture (that we rendered earlier) to the screen. - // The shader will process every pixel of this texture. - DrawTextureRec(target.texture, - (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, - (Vector2){ 0, 0 }, - WHITE); + // Draw the render texture containing scene + // The shader will process every pixel on the screen + DrawTextureRec(target.texture, + (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, + (Vector2){ 0, 0 }, WHITE); EndShaderMode(); DrawRectangle(0, 0, screenWidth, 40, BLACK); diff --git a/examples/shaders/shaders_ascii_effect.png b/examples/shaders/shaders_ascii_rendering.png similarity index 100% rename from examples/shaders/shaders_ascii_effect.png rename to examples/shaders/shaders_ascii_rendering.png From beabb1300d859b8503bdbec098956df3e3843b48 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Sep 2025 18:03:42 +0200 Subject: [PATCH 14/31] Revert "Update miniaudio to v0.11.23 (#5208)" This reverts commit 4cd131cf292bf4f5e06c7db89e9224fe36894330. --- src/external/miniaudio.h | 6147 ++++++++++++-------------------------- 1 file changed, 1983 insertions(+), 4164 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 1e4e2c655..c74bebeb3 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.23 - 2025-09-11 +miniaudio - v0.11.22 - 2025-02-24 David Reid - mackron@gmail.com @@ -12,10 +12,18 @@ GitHub: https://github.com/mackron/miniaudio /* 1. Introduction =============== -To use miniaudio, just include "miniaudio.h" like any other header and add "miniaudio.c" to your -source tree. If you don't want to add it to your source tree you can compile and link to it like -any other library. Note that ABI compatibility is not guaranteed between versions, even with bug -fix releases, so take care if compiling as a shared object. +To use miniaudio, include "miniaudio.h": + + ```c + #include "miniaudio.h" + ``` + +The implementation is contained in "miniaudio.c". Just compile this like any other source file. You +can include miniaudio.c if you want to compile your project as a single translation unit: + + ```c + #include "miniaudio.c" + ``` miniaudio includes both low level and high level APIs. The low level API is good for those who want to do all of their mixing themselves and only require a light weight interface to the underlying @@ -295,7 +303,7 @@ The engine encapsulates both the resource manager and the node graph to create a use high level API. The resource manager and node graph APIs are covered in more later sections of this manual. -The code below shows how you can initialize an engine using its default configuration. +The code below shows how you can initialize an engine using it's default configuration. ```c ma_result result; @@ -383,7 +391,7 @@ Sounds are not started by default. Start a sound with `ma_sound_start()` and sto `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use `ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound -to be started and/or stopped at a specific time. This can be done with the following functions: +the be started and/or stopped at a specific time. This can be done with the following functions: ```c ma_sound_set_start_time_in_pcm_frames() @@ -455,11 +463,6 @@ is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled wit miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details. -This library has been designed to be added directly to your source tree which is the preferred way -of using it, but you can compile it as a normal library if that's your preference. Be careful if -compiling as a shared object because miniaudio is not ABI compatible between any release, including -bug fix releases. It's recommended you link statically. - Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, @@ -529,7 +532,7 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use `-std=c*` compiler flags, nor `-ansi`. -You can enable the use of AudioWorklets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling +You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling with the following options: -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY @@ -878,7 +881,7 @@ read data within a certain range of the underlying data. To do this you can use This is useful if you have a sound bank where many sounds are stored in the same file and you want the data source to only play one of those sub-sounds. Note that once the range is set, everything -that takes a position, such as cursors and loop points, should always be relative to the start of +that takes a position, such as cursors and loop points, should always be relatvie to the start of the range. When the range is set, any previously defined loop point will be reset. Custom loop points can also be used with data sources. By default, data sources will loop after @@ -886,7 +889,7 @@ they reach the end of the data source, but if you need to loop at a specific loc the following: ```c - result = ma_data_source_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); + result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); if (result != MA_SUCCESS) { return result; // Failed to set the loop point. } @@ -3747,7 +3750,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 23 +#define MA_VERSION_REVISION 22 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3854,8 +3857,6 @@ typedef ma_uint16 wchar_t; #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ #endif -#define MA_UINT64_MAX (((ma_uint64)0xFFFFFFFF << 32) | (ma_uint64)0xFFFFFFFF) /* Weird shifting syntax is for VC6 compatibility. */ - /* Platform/backend detection. */ #if defined(_WIN32) || defined(__COSMOPOLITAN__) @@ -3864,55 +3865,29 @@ typedef ma_uint16 wchar_t; #define MA_WIN32_UWP #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) #define MA_WIN32_GDK - #elif defined(NXDK) - #define MA_WIN32_NXDK #else #define MA_WIN32_DESKTOP #endif - - /* The original Xbox. */ - #if defined(NXDK) /* <-- Add other Xbox compiler toolchains here, and then add a toolchain-specific define in case we need to discriminate between them later. */ - #define MA_XBOX - - #if defined(NXDK) - #define MA_XBOX_NXDK - #endif - #endif #endif -#if defined(__MSDOS__) || defined(MSDOS) || defined(_MSDOS) || defined(__DOS__) - #define MA_DOS - - /* No threading allowed on DOS. */ - #ifndef MA_NO_THREADING - #define MA_NO_THREADING - #endif - - /* No runtime linking allowed on DOS. */ - #ifndef MA_NO_RUNTIME_LINKING - #define MA_NO_RUNTIME_LINKING - #endif -#endif -#if !defined(MA_WIN32) && !defined(MA_DOS) /* If it's not Win32, assume POSIX. */ +#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ #define MA_POSIX - #if !defined(MA_NO_THREADING) - /* - Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. - You can use this to avoid including pthread.h in the header section. The downside is that it - results in some fixed sized structures being declared for the various types that are used in - miniaudio. The risk here is that these types might be too small for a given platform. This - risk is yours to take and no support will be offered if you enable this option. - */ - #ifndef MA_NO_PTHREAD_IN_HEADER - #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ - typedef pthread_t ma_pthread_t; - typedef pthread_mutex_t ma_pthread_mutex_t; - typedef pthread_cond_t ma_pthread_cond_t; - #else - typedef ma_uintptr ma_pthread_t; - typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; - typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; - #endif + /* + Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. + You can use this to avoid including pthread.h in the header section. The downside is that it + results in some fixed sized structures being declared for the various types that are used in + miniaudio. The risk here is that these types might be too small for a given platform. This + risk is yours to take and no support will be offered if you enable this option. + */ + #ifndef MA_NO_PTHREAD_IN_HEADER + #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ + typedef pthread_t ma_pthread_t; + typedef pthread_mutex_t ma_pthread_mutex_t; + typedef pthread_cond_t ma_pthread_cond_t; + #else + typedef ma_uintptr ma_pthread_t; + typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; + typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; #endif #if defined(__unix__) @@ -3939,11 +3914,8 @@ typedef ma_uint16 wchar_t; #if defined(__PROSPERO__) #define MA_PROSPERO #endif - #if defined(__3DS__) - #define MA_3DS - #endif - #if defined(__SWITCH__) || defined(__NX__) - #define MA_SWITCH + #if defined(__NX__) + #define MA_NX #endif #if defined(__BEOS__) || defined(__HAIKU__) #define MA_BEOS @@ -3953,13 +3925,12 @@ typedef ma_uint16 wchar_t; #endif #endif -#if !defined(MA_FALLTHROUGH) && defined(__cplusplus) && __cplusplus >= 201703L - #define MA_FALLTHROUGH [[fallthrough]] +#if defined(__has_c_attribute) + #if __has_c_attribute(fallthrough) + #define MA_FALLTHROUGH [[fallthrough]] + #endif #endif -#if !defined(MA_FALLTHROUGH) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L - #define MA_FALLTHROUGH [[fallthrough]] -#endif -#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) +#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) #if __has_attribute(fallthrough) #define MA_FALLTHROUGH __attribute__((fallthrough)) #endif @@ -3996,7 +3967,7 @@ typedef ma_uint16 wchar_t; #define MA_NO_INLINE __attribute__((noinline)) #else #define MA_INLINE MA_GNUC_INLINE_HINT - #define MA_NO_INLINE + #define MA_NO_INLINE __attribute__((noinline)) #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline @@ -4379,7 +4350,7 @@ typedef struct typedef struct { - ma_uint32 state; + ma_int32 state; } ma_lcg; @@ -6598,7 +6569,7 @@ This section contains the APIs for device playback and capture. Here is where yo ************************************************************************************************************************************************************/ #ifndef MA_NO_DEVICE_IO /* Some backends are only supported on certain platforms. */ -#if defined(MA_WIN32) && !defined(MA_XBOX) +#if defined(MA_WIN32) #define MA_SUPPORT_WASAPI #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ @@ -7455,7 +7426,6 @@ struct ma_context ma_proc snd_pcm_hw_params_set_rate_resample; ma_proc snd_pcm_hw_params_set_rate; ma_proc snd_pcm_hw_params_set_rate_near; - ma_proc snd_pcm_hw_params_set_rate_minmax; ma_proc snd_pcm_hw_params_set_buffer_size_near; ma_proc snd_pcm_hw_params_set_periods_near; ma_proc snd_pcm_hw_params_set_access; @@ -8016,7 +7986,6 @@ struct ma_device /*AAudioStream**/ ma_ptr pStreamPlayback; /*AAudioStream**/ ma_ptr pStreamCapture; ma_mutex rerouteLock; - ma_atomic_bool32 isTearingDown; ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; @@ -11286,7 +11255,7 @@ typedef struct ma_log* pLog; /* When set to NULL, will use the context's log. */ ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native sample rate of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ @@ -11450,11 +11419,11 @@ MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */ -MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor); -MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength); -MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor); -MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength); +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); @@ -11575,23 +11544,17 @@ IMPLEMENTATION #endif #if !defined(MA_WIN32) - #if !defined(MA_NO_THREADING) - #include - #include /* For pthreads. */ - #endif - - #include /* select() (used for ma_sleep()). */ - #include /* For nanosleep() */ - #include +#include +#include /* select() (used for ma_sleep()). */ +#include #endif -/* For fstat(), etc. */ -#if defined(MA_XBOX_NXDK) - #include /* Suggestion for NXDK: Add a sys/stat.h wrapper for compatibility. */ -#else - #include +#ifdef MA_NX +#include /* For nanosleep() */ #endif +#include /* For fstat(), etc. */ + #ifdef MA_EMSCRIPTEN #include #endif @@ -11898,7 +11861,7 @@ static MA_INLINE ma_bool32 ma_has_neon(void) #endif #ifndef MA_RESTRICT - #if defined(__clang__) || defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))) + #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) #define MA_RESTRICT __restrict #else #define MA_RESTRICT @@ -11992,7 +11955,7 @@ static void ma_sleep__posix(ma_uint32 milliseconds) (void)milliseconds; MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ #else - #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_SWITCH) + #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) struct timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = milliseconds % 1000 * 1000000; @@ -12034,7 +11997,7 @@ static MA_INLINE void ma_yield(void) #endif #endif #else - __asm__ __volatile__ ("rep; nop"); + __asm__ __volatile__ ("pause"); #endif #elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) /* ARM */ @@ -12057,7 +12020,7 @@ static MA_INLINE unsigned int ma_disable_denormals(void) { unsigned int prevState; - #if defined(_MSC_VER) && !defined(MA_XBOX_NXDK) + #if defined(_MSC_VER) { /* Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't @@ -12080,7 +12043,7 @@ static MA_INLINE unsigned int ma_disable_denormals(void) } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(MA_SUPPORT_SSE2) && defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { prevState = _mm_getcsr(); _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); @@ -12104,7 +12067,7 @@ static MA_INLINE unsigned int ma_disable_denormals(void) static MA_INLINE void ma_restore_denormals(unsigned int prevState) { - #if defined(_MSC_VER) && !defined(MA_XBOX_NXDK) + #if defined(_MSC_VER) { /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ #if _MSC_VER <= 1200 @@ -12120,7 +12083,7 @@ static MA_INLINE void ma_restore_denormals(unsigned int prevState) } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(MA_SUPPORT_SSE2) && defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { _mm_setcsr(prevState); } @@ -12756,29 +12719,6 @@ MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } -MA_API MA_NO_INLINE int ma_wcscmp(const wchar_t* str1, const wchar_t* str2) -{ - if (str1 == str2) return 0; - - /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ - if (str1 == NULL) return -1; - if (str2 == NULL) return 1; - - for (;;) { - if (str1[0] == L'\0') { - break; - } - if (str1[0] != str2[0]) { - break; - } - - str1 += 1; - str2 += 1; - } - - return ((unsigned short*)str1)[0] - ((unsigned short*)str2)[0]; -} - MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) { int result; @@ -12796,22 +12736,6 @@ MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA return result; } -MA_API MA_NO_INLINE size_t ma_wcslen(const wchar_t* str) -{ - const wchar_t* end; - - if (str == NULL) { - return 0; - } - - end = str; - while (end[0] != '\0') { - end += 1; - } - - return end - str; -} - MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz; @@ -12834,7 +12758,7 @@ MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_ca MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) { - size_t sz = ma_wcslen(src)+1; + size_t sz = wcslen(src)+1; wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); if (dst == NULL) { return NULL; @@ -13265,7 +13189,7 @@ MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpe return MA_INVALID_ARGS; } -#if (defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(MA_XBOX_NXDK) +#if defined(_MSC_VER) && _MSC_VER >= 1400 err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return ma_result_from_errno(err); @@ -13307,7 +13231,7 @@ _wfopen() isn't always available in all compilation environments. This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. */ -#if defined(_WIN32) && !defined(MA_XBOX_NXDK) +#if defined(_WIN32) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define MA_HAS_WFOPEN #endif @@ -13323,34 +13247,29 @@ MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_ return MA_INVALID_ARGS; } - #if defined(MA_HAS_WFOPEN) +#if defined(MA_HAS_WFOPEN) { /* Use _wfopen() on Windows. */ - #if defined(_MSC_VER) && _MSC_VER >= 1400 - { - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return ma_result_from_errno(err); - } + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return ma_result_from_errno(err); } - #else - { - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return ma_result_from_errno(errno); - } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return ma_result_from_errno(errno); } - #endif - + #endif (void)pAllocationCallbacks; } - #elif !defined(MA_XBOX_NXDK) && !defined(MA_DOS) /* If your compiler does not support wcsrtombs(), add it here. */ +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ { - /* - Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can - think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for - maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. - */ mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; @@ -13391,16 +13310,11 @@ MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_ ma_free(pFilePathMB, pAllocationCallbacks); } - #else - { - /* Getting here means there is no way to open the file with a wide character string. */ - *ppFile = NULL; - } - #endif if (*ppFile == NULL) { return MA_ERROR; } +#endif return MA_SUCCESS; } @@ -13409,7 +13323,7 @@ MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_ static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) { -#if MA_SIZE_MAX > 0xFFFFFFFF +#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { @@ -13429,7 +13343,7 @@ static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 si static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) { -#if MA_SIZE_MAX > 0xFFFFFFFF +#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { @@ -13558,18 +13472,6 @@ static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst Logging **************************************************************************************************************************************************************/ -#ifndef ma_va_copy - #if !defined(_MSC_VER) || _MSC_VER >= 1800 - #if (defined(__GNUC__) && __GNUC__ < 3) - #define ma_va_copy(dst, src) ((dst) = (src)) /* This is untested. Not sure if this is correct for old GCC. */ - #else - #define ma_va_copy(dst, src) va_copy((dst), (src)) - #endif - #else - #define ma_va_copy(dst, src) ((dst) = (src)) - #endif -#endif - MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) { switch (logLevel) @@ -13810,15 +13712,9 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat int length; char pFormattedMessageStack[1024]; char* pFormattedMessageHeap = NULL; - va_list args2; /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ - ma_va_copy(args2, args); - { - length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args2); - } - va_end(args2); - + length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); if (length < 0) { return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ } @@ -13859,10 +13755,17 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat char* pFormattedMessage = NULL; va_list args2; - ma_va_copy(args2, args); + #if _MSC_VER >= 1800 { - formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); + va_copy(args2, args); } + #else + { + args2 = args; + } + #endif + + formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); va_end(args2); if (formattedLen <= 0) { @@ -14061,7 +13964,7 @@ miniaudio's purposes. #define MA_LCG_A 48271 #define MA_LCG_C 0 -static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_lcg_seed() to use an explicit seed. */ +static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) { @@ -14110,7 +14013,7 @@ static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_in } -#if 0 /* Currently unused. */ + static MA_INLINE void ma_seed(ma_int32 seed) { ma_lcg_seed(&g_maLCG, seed); @@ -14135,7 +14038,6 @@ static MA_INLINE float ma_rand_f32(void) { return ma_lcg_rand_f32(&g_maLCG); } -#endif static MA_INLINE float ma_rand_range_f32(float lo, float hi) { @@ -14195,7 +14097,6 @@ Atomics **************************************************************************************************************************************************************/ /* c89atomic.h begin */ #ifndef ma_atomic_h -#define ma_atomic_h #if defined(__cplusplus) extern "C" { #endif @@ -14207,40 +14108,11 @@ extern "C" { #endif #endif typedef int ma_atomic_memory_order; -#if !defined(MA_ATOMIC_MODERN_MSVC) && \ - !defined(MA_ATOMIC_LEGACY_MSVC) && \ - !defined(MA_ATOMIC_LEGACY_MSVC_ASM) && \ - !defined(MA_ATOMIC_MODERN_GCC) && \ - !defined(MA_ATOMIC_LEGACY_GCC) && \ - !defined(MA_ATOMIC_LEGACY_GCC_ASM) - #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) || defined(__BORLANDC__) - #if (defined(_MSC_VER) && _MSC_VER > 1600) - #define MA_ATOMIC_MODERN_MSVC - #else - #if defined(MA_X64) - #define MA_ATOMIC_LEGACY_MSVC - #else - #define MA_ATOMIC_LEGACY_MSVC_ASM - #endif - #endif - #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || defined(__clang__) - #define MA_ATOMIC_MODERN_GCC - #else - #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) - #define MA_ATOMIC_LEGACY_GCC - #else - #define MA_ATOMIC_LEGACY_GCC_ASM - #endif - #endif -#endif -#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) - #include - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 +#define MA_ATOMIC_HAS_8 +#define MA_ATOMIC_HAS_16 +#define MA_ATOMIC_HAS_32 +#define MA_ATOMIC_HAS_64 +#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ ma_atomicType result; \ switch (order) \ @@ -14266,1501 +14138,720 @@ typedef int ma_atomic_memory_order; } break; \ } \ return result; - typedef ma_uint32 ma_atomic_flag; - static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 1, order, _InterlockedExchange, ma_atomic_flag, long); - } - #else - { - (void)order; - return (ma_atomic_flag)_InterlockedExchange((volatile long*)dst, (long)1); - } - #endif - } - static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 0, order, _InterlockedExchange, ma_atomic_flag, long); - } - #else - { - (void)order; - _InterlockedExchange((volatile long*)dst, (long)0); - } - #endif - } - static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) - { - (void)order; - return (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, 0, 0); - } -#endif -#if defined(MA_ATOMIC_LEGACY_MSVC_ASM) - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - typedef ma_uint32 ma_atomic_flag; - static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - ma_atomic_flag result = 0; - (void)order; - __asm { - mov ecx, dst - mov eax, 1 - xchg [ecx], eax - mov result, eax - } - return result; - } - static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm { - mov esi, dst - mov dword ptr [esi], 0 - } - } else { - __asm { - mov esi, dst - mov eax, 0 - xchg [esi], eax - } - } - } - static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) - { - ma_atomic_flag result = 0; - if (order == ma_atomic_memory_order_relaxed) { - __asm { - mov esi, dst - mov eax, [esi] - mov result, eax - } - } else if (order <= ma_atomic_memory_order_release) { - __asm { - mov esi, dst - mov eax, [esi] - lock add dword ptr [esp], 0 - mov result, eax - } - } else { - __asm { - lock add dword ptr [esp], 0 - mov esi, dst - mov eax, [esi] - mov result, eax - lock add dword ptr [esp], 0 - } - } - return result; - } -#endif -#if defined(MA_ATOMIC_MODERN_GCC) - #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED - #define ma_atomic_memory_order_consume __ATOMIC_CONSUME - #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define ma_atomic_memory_order_release __ATOMIC_RELEASE - #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - typedef ma_uint32 ma_atomic_flag; - #define ma_atomic_flag_test_and_set_explicit(dst, order) __atomic_exchange_n(dst, 1, order) - #define ma_atomic_flag_clear_explicit(dst, order) __atomic_store_n(dst, 0, order) - #define ma_atomic_flag_load_explicit(dst, order) __atomic_load_n(dst, order) -#endif -#if defined(MA_ATOMIC_LEGACY_GCC) - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - typedef ma_uint32 ma_atomic_flag; - static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, 1); - } - static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - if (order > ma_atomic_memory_order_release) { - __sync_synchronize(); - } - __sync_lock_release(dst); - } - static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) - { - (void)order; - return __sync_val_compare_and_swap((ma_atomic_flag*)dst, 0, 0); - } -#endif -#if defined(MA_ATOMIC_LEGACY_GCC_ASM) - #define ma_atomic_memory_order_relaxed 1 - #define ma_atomic_memory_order_consume 2 - #define ma_atomic_memory_order_acquire 3 - #define ma_atomic_memory_order_release 4 - #define ma_atomic_memory_order_acq_rel 5 - #define ma_atomic_memory_order_seq_cst 6 - #if defined(MA_X86) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory") - #elif defined(MA_X64) - #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory") - #else - #error Unsupported architecture. - #endif - #define MA_ATOMIC_XCHG_GCC_X86(instructionSizeSuffix, result, dst, src) \ - __asm__ __volatile__( \ - "xchg"instructionSizeSuffix" %0, %1" \ - : "=r"(result), \ - "=m"(*dst) \ - : "0"(src), \ - "m"(*dst) \ - : "memory" \ - ) - #define MA_ATOMIC_LOAD_RELAXED_GCC_X86(instructionSizeSuffix, result, dst) \ - __asm__ __volatile__( \ - "mov"instructionSizeSuffix" %1, %0" \ - : "=r"(result) \ - : "m"(*dst) \ - ) - #define MA_ATOMIC_LOAD_RELEASE_GCC_X86(instructionSizeSuffix, result, dst) \ - ma_atomic_thread_fence(ma_atomic_memory_order_release); \ - __asm__ __volatile__( \ - "mov"instructionSizeSuffix" %1, %0" \ - : "=r"(result) \ - : "m"(*dst) \ - : "memory" \ - ) - #define MA_ATOMIC_LOAD_SEQ_CST_GCC_X86(instructionSizeSuffix, result, dst) \ - ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst); \ - __asm__ __volatile__( \ - "mov"instructionSizeSuffix" %1, %0" \ - : "=r"(result) \ - : "m"(*dst) \ - : "memory" \ - ); \ - ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) - typedef ma_uint32 ma_atomic_flag; - static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - ma_atomic_flag result; - #if defined(MA_X86) || defined(MA_X64) - { - (void)order; - MA_ATOMIC_XCHG_GCC_X86("l", result, dst, 1); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order) - { - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm__ __volatile__( - "movl $0, %0" - : "=m"(*dst) - ); - } else if (order == ma_atomic_memory_order_release) { - __asm__ __volatile__( - "movl $0, %0" - : "=m"(*dst) - : - : "memory" - ); - } else { - ma_atomic_flag tmp = 0; - __asm__ __volatile__( - "xchgl %0, %1" - : "=r"(tmp), - "=m"(*dst) - : "0"(tmp), - "m"(*dst) - : "memory" - ); - } - } - #else - { - #error Unsupported architecture. - } - #endif - } - static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order) - { - #if defined(MA_X86) || defined(MA_X64) - { - ma_atomic_flag result; - if (order == ma_atomic_memory_order_relaxed) { - MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst); - } else if (order <= ma_atomic_memory_order_release) { - MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst); - } else { - MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst); - } - return result; - } - #else - { - #error Unsupported architecture. - } - #endif - } -#endif -#define ma_atomic_flag_test_and_set(dst) ma_atomic_flag_test_and_set_explicit(dst, ma_atomic_memory_order_acquire) -#define ma_atomic_flag_clear(dst) ma_atomic_flag_clear_explicit(dst, ma_atomic_memory_order_release) -typedef ma_atomic_flag ma_atomic_spinlock; -static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) -{ - for (;;) { - if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { - break; - } - while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { - } - } -} -static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) -{ - ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); -} -ma_atomic_spinlock ma_atomic_global_lock; -#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC_ASM) || defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM) - #if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i486__)) || (defined(_M_IX86) && _M_IX86 >= 400))) - #if defined(MA_ATOMIC_LEGACY_MSVC) && defined(MA_X64) - #else - #define MA_ATOMIC_IS_LOCK_FREE_8 1 - #define MA_ATOMIC_IS_LOCK_FREE_16 1 - #endif - #define MA_ATOMIC_IS_LOCK_FREE_32 1 - #if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i586__)) || (defined(_M_IX86) && _M_IX86 >= 500))) - #define MA_ATOMIC_IS_LOCK_FREE_64 1 - #else - #endif - #else - #endif - #if defined(MA_ARM32) || defined(MA_ARM64) - #define MA_ATOMIC_IS_LOCK_FREE_8 1 - #define MA_ATOMIC_IS_LOCK_FREE_16 1 - #define MA_ATOMIC_IS_LOCK_FREE_32 1 - #if defined(MA_ARM64) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) - #define MA_ATOMIC_IS_LOCK_FREE_64 1 - #endif - #endif - #if defined(MA_ATOMIC_PPC32) || defined(MA_ATOMIC_PPC64) - #if (defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7))) && !defined(__clang__) - #else - #define MA_ATOMIC_IS_LOCK_FREE_8 1 - #define MA_ATOMIC_IS_LOCK_FREE_16 1 - #endif - #define MA_ATOMIC_IS_LOCK_FREE_32 1 - #if defined(MA_ATOMIC_PPC64) - #define MA_ATOMIC_IS_LOCK_FREE_64 1 - #endif - #endif - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) - { - (void)ptr; - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - return 1; - #else - return 0; - #endif - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) - { - (void)ptr; - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - return 1; - #else - return 0; - #endif - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) - { - (void)ptr; - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - return 1; - #else - return 0; - #endif - } - static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) - { - (void)ptr; - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - return 1; - #else - return 0; - #endif - } -#endif -#define MA_ATOMIC_COMPARE_AND_SWAP_LOCK(sizeInBits, dst, expected, replacement) \ - ma_uint##sizeInBits result; \ - ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ - { \ - result = *dst; \ - if (result == expected) { \ - *dst = replacement; \ - } \ - } \ - ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ - return result -#define MA_ATOMIC_LOAD_EXPLICIT_LOCK(sizeInBits, ptr, order) \ - ma_uint##sizeInBits result; \ - ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ - { \ - result = *ptr; \ - (void)order; \ - } \ - ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ - return result -#define MA_ATOMIC_STORE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \ - ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ - { \ - *dst = src; \ - (void)order; \ - } \ - ma_atomic_spinlock_unlock(&ma_atomic_global_lock) -#define MA_ATOMIC_STORE_EXPLICIT_CAS(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits oldValue; \ - do { \ - oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ - } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \ - (void)order -#define MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits result; \ - ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ - { \ - result = *dst; \ - *dst = src; \ - (void)order; \ - } \ - ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ - return result -#define MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits oldValue; \ - do { \ - oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ - } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \ - (void)order; \ - return oldValue -#define MA_ATOMIC_FETCH_ADD_LOCK(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits result; \ - ma_atomic_spinlock_lock(&ma_atomic_global_lock); \ - { \ - result = *dst; \ - *dst += src; \ - (void)order; \ - } \ - ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \ - return result -#define MA_ATOMIC_FETCH_ADD_CAS(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits oldValue; \ - ma_uint##sizeInBits newValue; \ - do { \ - oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ - newValue = oldValue + src; \ - } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ - (void)order; \ - return oldValue -#define MA_ATOMIC_FETCH_AND_CAS(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits oldValue; \ - ma_uint##sizeInBits newValue; \ - do { \ - oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ - newValue = (ma_uint##sizeInBits)(oldValue & src); \ - } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ - (void)order; \ - return oldValue -#define MA_ATOMIC_FETCH_OR_CAS(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits oldValue; \ - ma_uint##sizeInBits newValue; \ - do { \ - oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ - newValue = (ma_uint##sizeInBits)(oldValue | src); \ - } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ - (void)order; \ - return oldValue -#define MA_ATOMIC_FETCH_XOR_CAS(sizeInBits, dst, src, order) \ - ma_uint##sizeInBits oldValue; \ - ma_uint##sizeInBits newValue; \ - do { \ - oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \ - newValue = (ma_uint##sizeInBits)(oldValue ^ src); \ - } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \ - (void)order; \ - return oldValue -#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) - #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, replacement, order, intrin, ma_atomicType, msvcType) \ + #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ ma_atomicType result; \ switch (order) \ { \ case ma_atomic_memory_order_relaxed: \ { \ - result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ case ma_atomic_memory_order_consume: \ case ma_atomic_memory_order_acquire: \ { \ - result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ case ma_atomic_memory_order_release: \ { \ - result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ case ma_atomic_memory_order_acq_rel: \ case ma_atomic_memory_order_seq_cst: \ default: \ { \ - result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \ + result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ } \ return result; - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - #define ma_atomic_compare_and_swap_8( dst, expected, replacement) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)replacement, (char)expected) - #else - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); - } + #define ma_atomic_memory_order_relaxed 0 + #define ma_atomic_memory_order_consume 1 + #define ma_atomic_memory_order_acquire 2 + #define ma_atomic_memory_order_release 3 + #define ma_atomic_memory_order_acq_rel 4 + #define ma_atomic_memory_order_seq_cst 5 + #if _MSC_VER < 1600 && defined(MA_X86) + #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY #endif - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - #define ma_atomic_compare_and_swap_16(dst, expected, replacement) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)replacement, (short)expected) - #else - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); - } + #if _MSC_VER < 1600 + #undef MA_ATOMIC_HAS_8 + #undef MA_ATOMIC_HAS_16 #endif - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - #define ma_atomic_compare_and_swap_32(dst, expected, replacement) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)replacement, (long)expected) - #else - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); - } + #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #include #endif - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - #define ma_atomic_compare_and_swap_64(dst, expected, replacement) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)replacement, (ma_int64)expected) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + ma_uint8 result = 0; + __asm { + mov ecx, dst + mov al, expected + mov dl, desired + lock cmpxchg [ecx], dl + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + ma_uint16 result = 0; + __asm { + mov ecx, dst + mov ax, expected + mov dx, desired + lock cmpxchg [ecx], dx + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + ma_uint32 result = 0; + __asm { + mov ecx, dst + mov eax, expected + mov edx, desired + lock cmpxchg [ecx], edx + mov result, eax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + ma_uint32 resultEAX = 0; + ma_uint32 resultEDX = 0; + __asm { + mov esi, dst + mov eax, dword ptr expected + mov edx, dword ptr expected + 4 + mov ebx, dword ptr desired + mov ecx, dword ptr desired + 4 + lock cmpxchg8b qword ptr [esi] + mov resultEAX, eax + mov resultEDX, edx + } + return ((ma_uint64)resultEDX << 32) | resultEAX; + } + #endif #else - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); - } + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) + #endif #endif - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); - } - #else + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { + ma_uint8 result = 0; (void)order; - return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); + __asm { + mov ecx, dst + mov al, src + lock xchg [ecx], al + mov result, al + } + return result; } - #endif - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order); - } #endif - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); - } - #else + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { + ma_uint16 result = 0; (void)order; - return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); + __asm { + mov ecx, dst + mov ax, src + lock xchg [ecx], ax + mov result, ax + } + return result; } - #endif - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order); - } #endif - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); - } - #else + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { + ma_uint32 result = 0; (void)order; - return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); + __asm { + mov ecx, dst + mov eax, src + lock xchg [ecx], eax + mov result, eax + } + return result; } - #endif - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order); - } #endif - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { + #else + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); - } - #else - { - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); - } - #endif - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order); - } - #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - #if defined(MA_ARM) - { MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); - } #else - { (void)order; return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); - } #endif - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); } - #else + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); + #else (void)order; return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); - } #endif - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); } - #else + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); + #else (void)order; return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); - } #endif - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - #if defined(MA_32BIT) - { - MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order); } - #else - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); - } - #else - { - (void)order; - return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); - } - #endif - } - #endif - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); - } #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); - } + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); #else - { (void)order; - return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); - } + return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); } - #else - { - (void)order; - return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); - } - #endif - } #else - { - MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); - } #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + #endif + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); - } - #else - { - (void)order; - return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); - } - #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - #if defined(MA_32BIT) - { - MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order); - } - #else - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); - } - #else - { - (void)order; - return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); - } - #endif - } - #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order); - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order); - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order); - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order); - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ARM) - { - MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); - } - #endif - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - #if defined(MA_X64) - #define ma_atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(MA_ARM64) - #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order - #else - static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) - { - volatile ma_uint32 barrier = 0; - ma_atomic_fetch_add_explicit_32(&barrier, 0, order); + ma_uint64 oldValue; + do { + oldValue = *dst; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; } #endif - #define ma_atomic_signal_fence(order) _ReadWriteBarrier(), (void)order -#endif -#if defined(MA_ATOMIC_LEGACY_MSVC_ASM) - static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - ma_uint8 result = 0; - __asm { - mov ecx, dst - mov al, expected - mov dl, replacement - lock cmpxchg [ecx], dl - mov result, al - } - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - ma_uint16 result = 0; - __asm { - mov ecx, dst - mov ax, expected - mov dx, replacement - lock cmpxchg [ecx], dx - mov result, ax - } - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - ma_uint32 result = 0; - __asm { - mov ecx, dst - mov eax, expected - mov edx, replacement - lock cmpxchg [ecx], edx - mov result, eax - } - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - ma_uint32 resultEAX = 0; - ma_uint32 resultEDX = 0; - __asm { - mov esi, dst - mov eax, dword ptr expected - mov edx, dword ptr expected + 4 - mov ebx, dword ptr replacement - mov ecx, dword ptr replacement + 4 - lock cmpxchg8b qword ptr [esi] - mov resultEAX, eax - mov resultEDX, edx - } - return ((ma_uint64)resultEDX << 32) | resultEAX; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - ma_uint8 result = 0; - if (order == ma_atomic_memory_order_relaxed) { + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; __asm { - mov esi, dst - mov al, [esi] + mov ecx, dst + mov al, src + lock xadd [ecx], al mov result, al } - } else if (order <= ma_atomic_memory_order_release) { - __asm { - mov esi, dst - mov al, [esi] - lock add dword ptr [esp], 0 - mov result, al - } - } else { - __asm { - lock add dword ptr [esp], 0 - mov esi, dst - mov al, [esi] - mov result, al - lock add dword ptr [esp], 0 - } + return result; } - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order); - } #endif - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - ma_uint16 result = 0; - if (order == ma_atomic_memory_order_relaxed) { + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; __asm { - mov esi, dst - mov ax, [esi] + mov ecx, dst + mov ax, src + lock xadd [ecx], ax mov result, ax } - } else if (order <= ma_atomic_memory_order_release) { - __asm { - mov esi, dst - mov ax, [esi] - lock add dword ptr [esp], 0 - mov result, ax - } - } else { - __asm { - lock add dword ptr [esp], 0 - mov esi, dst - mov ax, [esi] - mov result, ax - lock add dword ptr [esp], 0 - } + return result; } - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order); - } #endif - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - ma_uint32 result = 0; - if (order == ma_atomic_memory_order_relaxed) { - __asm { - mov esi, dst - mov eax, [esi] - mov result, eax - } - } else if (order <= ma_atomic_memory_order_release) { - __asm { - mov esi, dst - mov eax, [esi] - lock add dword ptr [esp], 0 - mov result, eax - } - } else { - __asm { - lock add dword ptr [esp], 0 - mov esi, dst - mov eax, [esi] - mov result, eax - lock add dword ptr [esp], 0 - } - } - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order) - { - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0); - } - static MA_INLINE void __stdcall ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm { - mov esi, dst - mov al, src - mov [esi], al - } - } else { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { + ma_uint32 result = 0; + (void)order; __asm { - mov esi, dst - mov al, src - xchg [esi], al - } - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order); - } - #endif - } - } - static MA_INLINE void __stdcall ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm { - mov esi, dst - mov ax, src - mov [esi], ax - } - } else { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - __asm { - mov esi, dst - mov ax, src - xchg [esi], ax - } - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order); - } - #endif - } - } - static MA_INLINE void __stdcall ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm { - mov esi, dst - mov eax, src - mov [esi], eax - } - } else { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - __asm { - mov esi, dst + mov ecx, dst mov eax, src - xchg [esi], eax + lock xadd [ecx], eax + mov result, eax } + return result; } - #else + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order); - } + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); + #else + (void)order; + return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); #endif - } - } - static MA_INLINE void __stdcall ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order); - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order); - } + } #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); + #else + (void)order; + return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); + #else + (void)order; + return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); + #else + (void)order; + return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + #endif + } + #else + #endif + #endif + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) { - ma_uint8 result = 0; (void)order; __asm { - mov ecx, dst - mov al, src - lock xchg [ecx], al - mov result, al + lock add [esp], 0 } - return result; } + #else + #if defined(MA_X64) + #define ma_atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(MA_ARM64) + #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); - } + static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) + { + volatile ma_uint32 barrier = 0; + ma_atomic_fetch_add_explicit_32(&barrier, 0, order); + } #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + #endif + #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) { - ma_uint16 result = 0; + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); + #else (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xchg [ecx], ax - mov result, ax - } - return result; - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); - } + return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { - ma_uint32 result = 0; + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); + #else (void)order; - __asm { - mov ecx, dst - mov eax, src - xchg [ecx], eax - mov result, eax - } - return result; - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); - } + return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order); } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - ma_uint8 result = 0; (void)order; - __asm { - mov ecx, dst - mov al, src - lock xadd [ecx], al - mov result, al - } - return result; - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); - } + return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { - ma_uint16 result = 0; + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); + #else (void)order; - __asm { - mov ecx, dst - mov ax, src - lock xadd [ecx], ax - mov result, ax - } - return result; - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); - } + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) + } + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - ma_uint32 result = 0; + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; - __asm { - mov ecx, dst - mov eax, src - lock xadd [ecx], eax - mov result, eax - } - return result; + return oldValue; } - #else + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - ma_uint8 result = 0; + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; - __asm { - mov ecx, dst - mov al, src - neg al - lock xadd [ecx], al - mov result, al - } - return result; + return oldValue; } - #else + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order); - } - #endif - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - ma_uint16 result = 0; + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; - __asm { - mov ecx, dst - mov ax, src - neg ax - lock xadd [ecx], ax - mov result, ax - } - return result; + return oldValue; } - #else + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order); - } - #endif - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - ma_uint32 result = 0; + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; - __asm { - mov ecx, dst - mov eax, src - neg eax - lock xadd [ecx], eax - mov result, eax - } - return result; + return oldValue; } - #else + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order); - } + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; #endif - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_ADD_CAS(64, dst, (ma_uint64)(-(ma_int64)src), order); - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); - } - static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); - } - static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); - } - static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); - } - static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); - } - static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) - { - (void)order; - __asm { - lock add dword ptr [esp], 0 } - } - #define ma_atomic_signal_fence(order) __asm {}; (void)order -#endif -#if defined(MA_ATOMIC_MODERN_GCC) + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #else + typedef ma_uint32 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) + #endif +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE + #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE + #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED + #define ma_atomic_memory_order_consume __ATOMIC_CONSUME + #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define ma_atomic_memory_order_release __ATOMIC_RELEASE + #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) + #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) @@ -15773,14 +14864,14 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) @@ -15801,19 +14892,19 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { - __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { - __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { - __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } #if defined(__clang__) @@ -15822,1134 +14913,636 @@ ma_atomic_spinlock ma_atomic_global_lock; #pragma clang diagnostic ignored "-Watomic-alignment" #endif #endif - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { - __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } #if defined(__clang__) #pragma clang diagnostic pop #endif -#endif -#if defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM) - #define ma_atomic_signal_fence(order) __asm__ __volatile__("":::"memory") - #if defined(MA_ATOMIC_LEGACY_GCC) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) + #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) +#else + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #if defined(__GNUC__) #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - return __sync_val_compare_and_swap(dst, expected, replacement); - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - return __sync_val_compare_and_swap(dst, expected, replacement); - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - return __sync_val_compare_and_swap(dst, expected, replacement); - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - return __sync_val_compare_and_swap(dst, expected, replacement); - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - (void)order; - return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - (void)order; - return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - (void)order; - return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - (void)order; - return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order); - } - #endif - } static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); - } - #endif + return __sync_lock_test_and_set(dst, src); } static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); - } - #endif + ma_uint16 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; } static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); - } - #endif + ma_uint32 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; } static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - if (order > ma_atomic_memory_order_acquire) { - __sync_synchronize(); - } - return __sync_lock_test_and_set(dst, src); - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); - } - #endif - } - #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) - #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) - #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) - #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) - static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - (void)order; - return __sync_fetch_and_add(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - (void)order; - return __sync_fetch_and_sub(dst, src); - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(64, dst, (ma_uint64)(-(ma_int64)src), order); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - (void)order; - return __sync_fetch_and_and(dst, src); - } - #else - { - MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - (void)order; - return __sync_fetch_and_or(dst, src); - } - #else - { - MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) - { - (void)order; - return __sync_fetch_and_xor(dst, src); - } - #else - { - MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); - } - #endif - } - #elif defined(MA_ATOMIC_LEGACY_GCC_ASM) - #define MA_ATOMIC_CMPXCHG_GCC_X86(instructionSizeSuffix, result, dst, expected, replacement) \ - __asm__ __volatile__( \ - "lock; cmpxchg"instructionSizeSuffix" %2, %1" \ - : "=a"(result), \ - "=m"(*dst) \ - : "r"(replacement), \ - "0"(expected), \ - "m"(*dst) \ - : "cc", "memory") - #define MA_ATOMIC_XADD_GCC_X86(instructionSizeSuffix, result, dst, src) \ - __asm__ __volatile__( \ - "lock; xadd"instructionSizeSuffix" %0, %1" \ - : "=a"(result), \ - "=m"(*dst) \ - : "0"(src), \ - "m"(*dst) \ - : "cc", "memory") - static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - { - MA_ATOMIC_CMPXCHG_GCC_X86("b", result, dst, expected, replacement); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - { - MA_ATOMIC_CMPXCHG_GCC_X86("w", result, dst, expected, replacement); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - { - MA_ATOMIC_CMPXCHG_GCC_X86("l", result, dst, expected, replacement); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint64 result; - #if defined(MA_X86) - { - ma_uint32 resultEAX; - ma_uint32 resultEDX; - __asm__ __volatile__( - "pushl %%ebx\n" - "movl %4, %%ebx\n" - "lock cmpxchg8b (%%edi)\n" - "popl %%ebx\n" - : "=a"(resultEAX), - "=d"(resultEDX) - : "a"((ma_uint32)(expected & 0xFFFFFFFF)), - "d"((ma_uint32)(expected >> 32)), - "r"((ma_uint32)(replacement & 0xFFFFFFFF)), - "c"((ma_uint32)(replacement >> 32)), - "D"(dst) - : "memory", "cc"); - result = ((ma_uint64)resultEDX << 32) | resultEAX; - } - #elif defined(MA_X64) - { - MA_ATOMIC_CMPXCHG_GCC_X86("q", result, dst, expected, replacement); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint8 result; - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - MA_ATOMIC_LOAD_RELAXED_GCC_X86("b", result, dst); - } else if (order <= ma_atomic_memory_order_release) { - MA_ATOMIC_LOAD_RELEASE_GCC_X86("b", result, dst); - } else { - MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("b", result, dst); - } - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint16 result; - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - MA_ATOMIC_LOAD_RELAXED_GCC_X86("w", result, dst); - } else if (order <= ma_atomic_memory_order_release) { - MA_ATOMIC_LOAD_RELEASE_GCC_X86("w", result, dst); - } else { - MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("w", result, dst); - } - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint32 result; - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst); - } else if (order <= ma_atomic_memory_order_release) { - MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst); - } else { - MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst); - } - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint64 result; - #if defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - MA_ATOMIC_LOAD_RELAXED_GCC_X86("q", result, dst); - } else if (order <= ma_atomic_memory_order_release) { - MA_ATOMIC_LOAD_RELEASE_GCC_X86("q", result, dst); - } else { - MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("q", result, dst); - } - } - #elif defined(MA_X86) - { - (void)order; - return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, dst, order); - } - #endif - } - static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint8 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - { - MA_ATOMIC_XCHG_GCC_X86("b", result, dst, src); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint16 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - { - MA_ATOMIC_XCHG_GCC_X86("w", result, dst, src); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint32 result; - (void)order; - #if defined(MA_X86) || defined(MA_X64) - { - MA_ATOMIC_XCHG_GCC_X86("l", result, dst, src); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order); - } - #endif - } - static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) - { - ma_uint64 result; - (void)order; - #if defined(MA_X86) - { - MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order); - } - #elif defined(MA_X64) - { - MA_ATOMIC_XCHG_GCC_X86("q", result, dst, src); - } - #else - { - #error Unsupported architecture. - } - #endif - return result; - } - #else - { - MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order); - } - #endif - } - static MA_INLINE void ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm__ __volatile__ ( - "movb %1, %0" - : "=m"(*dst) - : "r"(src) - ); - } else { - __asm__ __volatile__ ( - "xchgb %1, %0" - : "=m"(*dst) - : "r"(src) - : "memory" - ); - } - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order); - } - #endif - } - static MA_INLINE void ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm__ __volatile__ ( - "movw %1, %0" - : "=m"(*dst) - : "r"(src) - ); - } else { - __asm__ __volatile__ ( - "xchgw %1, %0" - : "=m"(*dst) - : "r"(src) - : "memory" - ); - } - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order); - } - #endif - } - static MA_INLINE void ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) || defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm__ __volatile__ ( - "movl %1, %0" - : "=m"(*dst) - : "r"(src) - ); - } else { - __asm__ __volatile__ ( - "xchgl %1, %0" - : "=m"(*dst) - : "r"(src) - : "memory" - ); - } - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order); - } - #endif - } - static MA_INLINE void ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X64) - { - if (order == ma_atomic_memory_order_relaxed) { - __asm__ __volatile__ ( - "movq %1, %0" - : "=m"(*dst) - : "r"(src) - ); - } else { - __asm__ __volatile__ ( - "xchgq %1, %0" - : "=m"(*dst) - : "r"(src) - : "memory" - ); - } - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order); - } - #endif - } - #else - { - MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order); - } - #endif + ma_uint64 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; } static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) || defined(MA_X64) - { - ma_uint8 result; - (void)order; - MA_ATOMIC_XADD_GCC_X86("b", result, dst, src); - return result; - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order); - } - #endif + (void)order; + return __sync_fetch_and_add(dst, src); } static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) || defined(MA_X64) - { - ma_uint16 result; - (void)order; - MA_ATOMIC_XADD_GCC_X86("w", result, dst, src); - return result; - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order); - } - #endif + (void)order; + return __sync_fetch_and_add(dst, src); } static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) || defined(MA_X64) - { - ma_uint32 result; - (void)order; - MA_ATOMIC_XADD_GCC_X86("l", result, dst, src); - return result; - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order); - } - #endif + (void)order; + return __sync_fetch_and_add(dst, src); } static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64)) - { - #if defined(MA_X86) - { - MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order); - } - #elif defined(MA_X64) - { - ma_uint64 result; - MA_ATOMIC_XADD_GCC_X86("q", result, dst, src); - (void)order; - return result; - } - #else - { - #error Unsupported architecture. - } - #endif - } - #else - { - MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order); - } - #endif + (void)order; + return __sync_fetch_and_add(dst, src); } static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order); + (void)order; + return __sync_fetch_and_sub(dst, src); } static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order); + (void)order; + return __sync_fetch_and_sub(dst, src); } static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order); + (void)order; + return __sync_fetch_and_sub(dst, src); } static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order); - } - static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order); - } - static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order); - } - static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order); - } - static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) - { - MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order); + (void)order; + return __sync_fetch_and_sub(dst, src); } static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order); + (void)order; + return __sync_fetch_and_or(dst, src); } static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order); + (void)order; + return __sync_fetch_and_or(dst, src); } static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order); + (void)order; + return __sync_fetch_and_or(dst, src); } static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order); + (void)order; + return __sync_fetch_and_or(dst, src); } static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order); + (void)order; + return __sync_fetch_and_xor(dst, src); } static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order); + (void)order; + return __sync_fetch_and_xor(dst, src); } static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order); + (void)order; + return __sync_fetch_and_xor(dst, src); } static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order); + (void)order; + return __sync_fetch_and_xor(dst, src); } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #else - #error Unsupported compiler. + #if defined(MA_X86) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(MA_X64) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #else + #error Unsupported architecture. Please submit a feature request. + #endif + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + volatile ma_uint64 result; + #if defined(MA_X86) + ma_uint32 resultEAX; + ma_uint32 resultEDX; + __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); + result = ((ma_uint64)resultEDX << 32) | resultEAX; + #elif defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 result; + (void)order; + #if defined(MA_X86) + do { + result = *dst; + } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_X86) + ma_uint64 oldValue; + ma_uint64 newValue; + (void)order; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + return oldValue; + #elif defined(MA_X64) + ma_uint64 result; + (void)order; + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + return result; + #endif + } + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } #endif + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); + } + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); + } + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); + } + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); + } + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define ma_atomic_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #endif #if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint8 result; - (void)successOrder; - (void)failureOrder; - result = ma_atomic_compare_and_swap_8(dst, *expected, replacement); - if (result == *expected) { - return 1; - } else { - *expected = result; - return 0; + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint8 expectedValue; + ma_uint8 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_8(expected, result, failureOrder); + return 0; + } } - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint16 result; - (void)successOrder; - (void)failureOrder; - result = ma_atomic_compare_and_swap_16(dst, *expected, replacement); - if (result == *expected) { - return 1; - } else { - *expected = result; - return 0; + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint16 expectedValue; + ma_uint16 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_16(expected, result, failureOrder); + return 0; + } } - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint32 result; - (void)successOrder; - (void)failureOrder; - result = ma_atomic_compare_and_swap_32(dst, *expected, replacement); - if (result == *expected) { - return 1; - } else { - *expected = result; - return 0; + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint32 expectedValue; + ma_uint32 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_32(expected, result, failureOrder); + return 0; + } } - } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) - { - ma_uint64 result; - (void)successOrder; - (void)failureOrder; - result = ma_atomic_compare_and_swap_64(dst, *expected, replacement); - if (result == *expected) { - return 1; - } else { - *expected = result; - return 0; + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint64 expectedValue; + ma_uint64 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_64(expected, result, failureOrder); + return 0; + } } + #endif + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) +#endif +#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) + { + (void)ptr; + #if defined(MA_64BIT) + return 1; + #else + #if defined(MA_X86) || defined(MA_X64) + return 1; + #else + return 0; + #endif + #endif } - #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, replacement, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder) - #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder) #endif #if defined(MA_64BIT) static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) @@ -16968,17 +15561,17 @@ ma_atomic_spinlock ma_atomic_global_lock; { return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)replacement); + return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); } #elif defined(MA_32BIT) static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) @@ -16997,26 +15590,36 @@ ma_atomic_spinlock ma_atomic_global_lock; { return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); } - static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); } - static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)replacement); + return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); } #else #error Unsupported architecture. #endif -#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) -#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_ptr(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_ptr(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) @@ -17029,14 +15632,14 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_16( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_32( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_64( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) @@ -17057,6 +15660,14 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) #define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) #define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) @@ -17069,14 +15680,14 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) #define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) -#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder) -#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) #define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) #define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) @@ -17097,6 +15708,14 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) #define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) #define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) @@ -17109,14 +15728,14 @@ ma_atomic_spinlock ma_atomic_global_lock; #define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_i64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i16(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_i64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) @@ -17193,28 +15812,28 @@ static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, do r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if32 d; - d.f = replacement; + d.f = desired; return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if64 d; - d.f = replacement; + d.f = desired; return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if32 d; - d.f = replacement; + d.f = desired; return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { ma_atomic_if64 d; - d.f = replacement; + d.f = desired; return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) @@ -17305,10 +15924,10 @@ static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, d #define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_strong_f64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) -#define ma_atomic_compare_exchange_weak_f64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) @@ -17319,24 +15938,39 @@ static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, d #define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) #define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) -static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float replacement) +static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) { ma_atomic_if32 r; ma_atomic_if32 e, d; e.f = expected; - d.f = replacement; + d.f = desired; r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); return r.f; } -static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double replacement) +static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) { ma_atomic_if64 r; ma_atomic_if64 e, d; e.f = expected; - d.f = replacement; + d.f = desired; r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); return r.f; } +typedef ma_atomic_flag ma_atomic_spinlock; +static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) +{ + for (;;) { + if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { + break; + } + while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + } + } +} +static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) +{ + ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); +} #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif @@ -17542,7 +16176,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority int result; pthread_attr_t* pAttr = NULL; -#if !defined(MA_EMSCRIPTEN) && !defined(MA_3DS) && !defined(MA_SWITCH) +#if !defined(__EMSCRIPTEN__) && !defined(__3DS__) /* Try setting the thread priority. It's not critical if anything fails here. */ pthread_attr_t attr; if (pthread_attr_init(&attr) == 0) { @@ -17574,18 +16208,9 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority } #endif - #if defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE >= 0 - { - if (stackSize > 0) { - pthread_attr_setstacksize(&attr, stackSize); - } + if (stackSize > 0) { + pthread_attr_setstacksize(&attr, stackSize); } - #else - { - (void)stackSize; /* Suppress unused parameter warning. */ - } - #endif - if (scheduler != -1) { int priorityMin = sched_get_priority_min(scheduler); @@ -17642,21 +16267,6 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority } if (result != 0) { - /* - There have been reports that attempting to create a realtime thread can sometimes fail. In this case, - fall back to a normal priority thread. - - I'm including a compile-time option here to disable this functionality for those who have a hard - requirement on realtime threads and would rather an explicit failure. - */ - #ifndef MA_NO_PTHREAD_REALTIME_PRIORITY_FALLBACK - { - if(result == EPERM && priority == ma_thread_priority_realtime) { - return ma_thread_create__posix(pThread, ma_thread_priority_normal, stackSize, entryProc, pData); - } - } - #endif - return ma_result_from_errno(result); } @@ -17928,7 +16538,7 @@ static ma_result ma_event_signal__win32(ma_event* pEvent) static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) { - *pSemaphore = CreateSemaphore(NULL, (LONG)initialValue, LONG_MAX, NULL); + *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); if (*pSemaphore == NULL) { return ma_result_from_GetLastError(GetLastError()); } @@ -18822,12 +17432,10 @@ static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) return (ma_uint16)(toc & 0x0000FFFF); } -#if 0 /* Currently unused, but might make use of this later. */ static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) { return (ma_uint16)((toc & 0xFFFF0000) >> 16); } -#endif static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) { @@ -19292,13 +17900,6 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) Dynamic Linking *******************************************************************************/ -/* Disable run-time linking on certain backends and platforms. */ -#ifndef MA_NO_RUNTIME_LINKING - #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) || defined(MA_SWITCH) || defined(MA_DOS) - #define MA_NO_RUNTIME_LINKING - #endif -#endif - #ifdef MA_POSIX /* No need for dlfcn.h if we're not using runtime linking. */ #ifndef MA_NO_RUNTIME_LINKING @@ -19308,124 +17909,104 @@ Dynamic Linking MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) { - #ifndef MA_NO_RUNTIME_LINKING - { - ma_handle handle; +#ifndef MA_NO_RUNTIME_LINKING + ma_handle handle; - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) + handle = (ma_handle)LoadLibraryA(filename); #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - return handle; - } #else - { - /* Runtime linking is disabled. */ - (void)pLog; - (void)filename; - return NULL; - } + handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + return handle; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)filename; + return NULL; +#endif } MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) { - #ifndef MA_NO_RUNTIME_LINKING - { - #ifdef MA_WIN32 +#ifndef MA_NO_RUNTIME_LINKING + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ + #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) { - FreeLibrary((HMODULE)handle); + dlclose((void*)handle); } #else { - /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */ - #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) - { - dlclose((void*)handle); - } - #else - { - (void)handle; - } - #endif + (void)handle; } #endif - - (void)pLog; - } - #else - { - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - } #endif + + (void)pLog; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; +#endif } MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) { - #ifndef MA_NO_RUNTIME_LINKING - { - ma_proc proc; +#ifndef MA_NO_RUNTIME_LINKING + ma_proc proc; - ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - #ifdef _WIN32 - { - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); - } - #else - { - #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" - #endif - proc = (ma_proc)dlsym((void*)handle, symbol); - #if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) - #pragma GCC diagnostic pop - #endif - } - #endif +#ifdef _WIN32 + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); +#else +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + proc = (ma_proc)dlsym((void*)handle, symbol); +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__) + #pragma GCC diagnostic pop +#endif +#endif - if (proc == NULL) { - ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pLog; /* It's possible for pContext to be unused. */ - return proc; + if (proc == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); } - #else - { - /* Runtime linking is disabled. */ - (void)pLog; - (void)handle; - (void)symbol; - return NULL; - } - #endif + + (void)pLog; /* It's possible for pContext to be unused. */ + return proc; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + (void)symbol; + return NULL; +#endif } @@ -19439,6 +18020,13 @@ DEVICE I/O ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ +/* Disable run-time linking on certain backends and platforms. */ +#ifndef MA_NO_RUNTIME_LINKING + #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) + #define MA_NO_RUNTIME_LINKING + #endif +#endif + #ifdef MA_APPLE #include #endif @@ -19451,6 +18039,12 @@ DEVICE I/O #ifdef MA_POSIX #include + #include + + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif #endif /* This must be set to at least 26. */ @@ -19705,7 +18299,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -#if defined(MA_WIN32) && !defined(MA_XBOX) +#if defined(MA_WIN32) /* WASAPI error codes. */ #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) @@ -19920,11 +18514,6 @@ typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); #endif /* MA_WIN32_DESKTOP */ -static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; -static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; -/*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ -/*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - MA_API size_t ma_strlen_WCHAR(const WCHAR* str) { size_t len = 0; @@ -19988,7 +18577,7 @@ Timing *******************************************************************************/ #if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - static MA_INLINE void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -20000,7 +18589,7 @@ Timing pTimer->counter = counter.QuadPart; } - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -20011,7 +18600,7 @@ Timing } #elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200) static ma_uint64 g_ma_TimerFrequency = 0; - static MA_INLINE void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { mach_timebase_info_data_t baseTime; mach_timebase_info(&baseTime); @@ -20020,7 +18609,7 @@ Timing pTimer->counter = mach_absolute_time(); } - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter = mach_absolute_time(); ma_uint64 oldTimeCounter = pTimer->counter; @@ -20045,7 +18634,7 @@ Timing #define MA_CLOCK_ID CLOCK_REALTIME #endif - static MA_INLINE void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { struct timespec newTime; clock_gettime(MA_CLOCK_ID, &newTime); @@ -20053,7 +18642,7 @@ Timing pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; } - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter; ma_uint64 oldTimeCounter; @@ -20067,7 +18656,7 @@ Timing return (newTimeCounter - oldTimeCounter) / 1000000000.0; } #else - static MA_INLINE void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { struct timeval newTime; gettimeofday(&newTime, NULL); @@ -20075,7 +18664,7 @@ Timing pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; } - static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter; ma_uint64 oldTimeCounter; @@ -20659,6 +19248,14 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n } +#if defined(MA_WIN32) + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ +#endif + + MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ { @@ -21370,7 +19967,7 @@ static ma_result ma_context_init__null(ma_context* pContext, const ma_context_co WIN32 COMMON *******************************************************************************/ -#if defined(MA_WIN32) && !defined(MA_XBOX) +#if defined(MA_WIN32) #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() @@ -21385,7 +19982,7 @@ WIN32 COMMON #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) #endif -#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) && !defined(MA_XBOX_NXDK) +#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) typedef size_t DWORD_PTR; #endif @@ -21812,21 +20409,11 @@ typedef enum MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ } MA_AUDIO_STREAM_CATEGORY; -typedef enum -{ - MA_AUDCLNT_STREAMOPTIONS_NONE, - MA_AUDCLNT_STREAMOPTIONS_RAW, - MA_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT, - MA_AUDCLNT_STREAMOPTIONS_AMBISONICS, - MA_AUDCLNT_STREAMOPTIONS_POST_VOLUME_LOOPBACK -} MA_AUDCLNT_STREAMOPTIONS; - typedef struct { ma_uint32 cbSize; BOOL bIsOffload; MA_AUDIO_STREAM_CATEGORY eCategory; - MA_AUDCLNT_STREAMOPTIONS Options; } ma_AudioClientProperties; /* IUnknown */ @@ -23001,7 +21588,6 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device { ma_IMMDeviceEnumerator* pDeviceEnumerator; HRESULT hr; - HRESULT CoInitializeResult; MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); @@ -23015,17 +21601,12 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm happy enough to use this hack instead. - - CoUninitialize should only be called if we successfully initialized. S_OK and S_FALSE both mean that we need to - call CoUninitialize since the internal ref count was increased. RPC_E_CHANGED_MODE means that CoInitializeEx was - called with a different COINIT value, and we don't call CoUninitialize in that case. Other errors are possible, - so we check for S_OK and S_FALSE specifically. */ - CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); { hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); - } - if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) { ma_CoUninitialize(pContext); } + } + ma_CoUninitialize(pContext); if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */ ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); @@ -23369,7 +21950,7 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de pActivationParams = &activationParams; /* When requesting a specific device ID we need to use a special device ID. */ - MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (ma_wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ + MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ pDeviceID = &virtualDeviceID; } else { pActivationParams = NULL; /* No activation parameters required. */ @@ -28098,9 +26679,6 @@ typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; typedef snd_pcm_state_t ma_snd_pcm_state_t; -/* snd_pcm_state_t */ -#define MA_SND_PCM_STATE_XRUN SND_PCM_STATE_XRUN - /* snd_pcm_stream_t */ #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE @@ -28296,7 +26874,6 @@ typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) ( typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); -typedef int (* ma_snd_pcm_hw_params_set_rate_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir); typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); @@ -30063,9 +28640,8 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; - ma_snd_pcm_hw_params_set_channels_minmax_proc _snd_pcm_hw_params_set_channels_minmax = snd_pcm_hw_params_set_channels_minmax; ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; - ma_snd_pcm_hw_params_set_rate_proc _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; + ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; @@ -30117,9 +28693,9 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; - ma_snd_pcm_poll_descriptors_proc _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; - ma_snd_pcm_poll_descriptors_count_proc _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; - ma_snd_pcm_poll_descriptors_revents_proc _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; + ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; + ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; + ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; @@ -30135,7 +28711,6 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; - pContext->alsa.snd_pcm_hw_params_set_rate_minmax = (ma_proc)_snd_pcm_hw_params_set_rate_minmax; pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; @@ -30861,7 +29436,7 @@ typedef void (* ma_pa_threaded_mainloop_unlock_proc) ( typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); -typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (const ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); @@ -30870,13 +29445,13 @@ typedef void (* ma_pa_context_unref_proc) ( typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); -typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (const ma_pa_context* c); +typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); -typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (const ma_pa_operation* o); +typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); @@ -30885,12 +29460,12 @@ typedef void (* ma_pa_stream_unref_proc) ( typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); -typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (const ma_pa_stream* s); +typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); -typedef const char* (* ma_pa_stream_get_device_name_proc) (const ma_pa_stream* s); +typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); @@ -30898,15 +29473,15 @@ typedef void (* ma_pa_stream_set_moved_callback_proc) ( typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); -typedef int (* ma_pa_stream_is_corked_proc) (const ma_pa_stream* s); +typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); -typedef size_t (* ma_pa_stream_writable_size_proc) (const ma_pa_stream* s); -typedef size_t (* ma_pa_stream_readable_size_proc) (const ma_pa_stream* s); +typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); +typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); typedef struct { @@ -31202,7 +29777,7 @@ static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, } /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ - result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? MA_PA_CONTEXT_NOFLAGS : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); @@ -31935,7 +30510,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi const ma_pa_buffer_attr* pActualAttr = NULL; const ma_pa_channel_map* pActualChannelMap = NULL; ma_uint32 iChannel; - int streamFlags; + ma_pa_stream_flags_t streamFlags; MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->pulse); @@ -31993,13 +30568,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.channels = pDescriptorCapture->channels; } - /* PulseAudio has a maximum channel count of 32. We'll get a crash if this is exceeded. */ - if (ss.channels > 32) { - ss.channels = 32; - } - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { @@ -32056,7 +30626,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi streamFlags |= MA_PA_STREAM_DONT_MOVE; } - error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, (ma_pa_stream_flags_t)streamFlags); + error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); result = ma_result_from_pulse(error); @@ -32150,13 +30720,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.channels = pDescriptorPlayback->channels; } - /* PulseAudio has a maximum channel count of 32. We'll get a crash if this is exceeded. */ - if (ss.channels > 32) { - ss.channels = 32; - } - /* Use a default channel map. */ - ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap); + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap); /* Use the requested sample rate if one was specified. */ @@ -32218,7 +30783,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi streamFlags |= MA_PA_STREAM_DONT_MOVE; } - error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, (ma_pa_stream_flags_t)streamFlags, NULL, NULL); + error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); result = ma_result_from_pulse(error); @@ -32773,7 +31338,6 @@ typedef JackProcessCallback ma_JackProcessCallback; typedef JackBufferSizeCallback ma_JackBufferSizeCallback; typedef JackShutdownCallback ma_JackShutdownCallback; #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE -#define ma_JackNullOption JackNullOption #define ma_JackNoStartServer JackNoStartServer #define ma_JackPortIsInput JackPortIsInput #define ma_JackPortIsOutput JackPortIsOutput @@ -32788,7 +31352,6 @@ typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg) typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); typedef void (* ma_JackShutdownCallback) (void* arg); #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" -#define ma_JackNullOption 0 #define ma_JackNoStartServer 1 #define ma_JackPortIsInput 1 #define ma_JackPortIsOutput 2 @@ -32829,7 +31392,7 @@ static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_clie maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); - pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? ma_JackNullOption : ma_JackNoStartServer, &status, NULL); + pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); if (pClient == NULL) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } @@ -38431,7 +36994,7 @@ OSS Backend #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" -static int ma_open_temp_device__oss(void) +static int ma_open_temp_device__oss() { /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ int fd = open("/dev/mixer", O_RDONLY, 0); @@ -39271,30 +37834,25 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs (void)error; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); - /* When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. */ - if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Tearing down device.\n"); + job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); + job.data.device.aaudio.reroute.pDevice = pDevice; + + if (pStream == pDevice->aaudio.pStreamCapture) { + job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; } else { - job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); - job.data.device.aaudio.reroute.pDevice = pDevice; - - if (pStream == pDevice->aaudio.pStreamCapture) { - job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; - } else { - job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; - } - - result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); - if (result != MA_SUCCESS) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); - return; - } + job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; + } + + result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); + return; } } @@ -39611,7 +38169,7 @@ static ma_result ma_close_streams__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - /* When rerouting, streams may have been closed and never re-opened. Hence the extra checks below. */ + /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); pDevice->aaudio.pStreamCapture = NULL; @@ -39628,12 +38186,6 @@ static ma_result ma_device_uninit__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - /* - Note: Closing the streams may cause a timeout error, which would then trigger rerouting in our error callback. - We must not schedule a reroute when device is getting destroyed. - */ - ma_atomic_bool32_set(&pDevice->aaudio.isTearingDown, MA_TRUE); - /* Wait for any rerouting to finish before attempting to close the streams. */ ma_mutex_lock(&pDevice->aaudio.rerouteLock); { @@ -39641,7 +38193,7 @@ static ma_result ma_device_uninit__aaudio(ma_device* pDevice) } ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - /* Destroy rerouting lock. */ + /* Destroy re-routing lock. */ ma_mutex_uninit(&pDevice->aaudio.rerouteLock); return MA_SUCCESS; @@ -39877,22 +38429,17 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice) static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) { - const ma_int32 maxAttempts = 4; /* Reasonable retry limit. */ - ma_result result; - ma_int32 iAttempt; + int32_t retries = 0; MA_ASSERT(pDevice != NULL); - /* We got disconnected! Retry a few times, until we find a connected device! */ - iAttempt = 0; - while (iAttempt++ < maxAttempts) { - /* Device tearing down? No need to reroute! */ - if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) { - result = MA_SUCCESS; /* Caller should continue as normal. */ - break; - } - + /* + TODO: Stop retrying if main thread is about to uninit device. + */ + ma_mutex_lock(&pDevice->aaudio.rerouteLock); + { +error_disconnected: /* The first thing to do is close the streams. */ ma_close_streams__aaudio(pDevice); @@ -39948,16 +38495,14 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - /* Reroute failed! */ - break; + goto done; } result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); ma_close_streams__aaudio(pDevice); - /* Reroute failed! */ - break; + goto done; } /* We'll only ever do this in response to a reroute. */ @@ -39968,23 +38513,26 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { result = ma_device_start__aaudio(pDevice); if (result != MA_SUCCESS) { - if (iAttempt < maxAttempts) { - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", iAttempt); - } else { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, giving up."); + /* We got disconnected! Retry a few times, until we find a connected device! */ + retries += 1; + if (retries <= 3) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries); + goto error_disconnected; } + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change."); + goto done; } } else { - ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ + ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ } } - - if (result == MA_SUCCESS) { - /* Reroute successful! */ - break; - } + + result = MA_SUCCESS; } - +done: + /* Re-routing done */ + ma_mutex_unlock(&pDevice->aaudio.rerouteLock); + return result; } @@ -40150,7 +38698,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { - ma_result result = MA_SUCCESS; + ma_result result; ma_device* pDevice; MA_ASSERT(pJob != NULL); @@ -40158,22 +38706,19 @@ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; MA_ASSERT(pDevice != NULL); - ma_mutex_lock(&pDevice->aaudio.rerouteLock); - { - /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ - result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); - if (result != MA_SUCCESS) { - /* - Getting here means we failed to reroute the device. The best thing I can think of here is to - just stop the device. - */ - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); - ma_device_stop(pDevice); - } + /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ + result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); + if (result != MA_SUCCESS) { + /* + Getting here means we failed to reroute the device. The best thing I can think of here is to + just stop the device. + */ + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure."); + ma_device_stop(pDevice); + return result; } - ma_mutex_unlock(&pDevice->aaudio.rerouteLock); - return result; + return MA_SUCCESS; } #else /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ @@ -42691,13 +41236,13 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { ma_device* pDevice = (ma_device*)pData; -#if defined(MA_WIN32) && !defined(MA_XBOX) +#ifdef MA_WIN32 HRESULT CoInitializeResult; #endif MA_ASSERT(pDevice != NULL); -#if defined(MA_WIN32) && !defined(MA_XBOX) +#ifdef MA_WIN32 CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); #endif @@ -42788,8 +41333,8 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_event_signal(&pDevice->stopEvent); } -#if defined(MA_WIN32) && !defined(MA_XBOX) - if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) { +#ifdef MA_WIN32 + if (CoInitializeResult == S_OK) { ma_CoUninitialize(pDevice->pContext); } #endif @@ -42813,92 +41358,67 @@ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ - #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - { - /* TODO: Remove this once the new single threaded backend system is in place in 0.12. */ - #if !defined(MA_XBOX) - { - if (pContext->win32.CoInitializeResult == S_OK || pContext->win32.CoInitializeResult == S_FALSE) { - ma_CoUninitialize(pContext); /* TODO: Remove this once the new single threaded backend system is in place in 0.12. */ - } - } - #endif - - #if defined(MA_WIN32_DESKTOP) - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); - #endif - - ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); - } - #else - { - (void)pContext; +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pContext->win32.CoInitializeResult == S_OK) { + ma_CoUninitialize(pContext); } + + #if defined(MA_WIN32_DESKTOP) + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); #endif + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); +#else + (void)pContext; +#endif + return MA_SUCCESS; } static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { - /* - TODO: Reassess all of this stuff and move everything to the relevant backends. For example, I think - GetForegroundWindow() and GetDesktopWindow() are only used by the DirectSound backend. - */ - #if (defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)) && !defined(MA_XBOX) - { - #if defined(MA_WIN32_DESKTOP) - { - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); - } - #endif - - /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); - if (pContext->win32.hOle32DLL == NULL) { +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #if defined(MA_WIN32_DESKTOP) + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); - } - #else - { - (void)pContext; /* Unused. */ - } + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #endif - /* TODO: Remove this once the new single threaded backend system is in place in 0.12. */ - #if !defined(MA_XBOX) - { - pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + /* Ole32.dll */ + pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); + if (pContext->win32.hOle32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; } - #endif + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); +#else + (void)pContext; /* Unused. */ +#endif + + pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); return MA_SUCCESS; } #else @@ -45496,7 +44016,7 @@ static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, m ma_uint64 i; for (i = 0; i < count; i += 1) { - dst_s32[i] = (ma_int32)src_s16[i] << 16; + dst_s32[i] = src_s16[i] << 16; } (void)ditherMode; @@ -57888,12 +56408,8 @@ MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 } /* Null terminate. Don't increment the length here. */ - if (pBufferOut != NULL) { - if (bufferCap > len) { - pBufferOut[len] = '\0'; - } else if (bufferCap > 0) { - pBufferOut[bufferCap - 1] = '\0'; - } + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = '\0'; } return len; @@ -58104,7 +56620,7 @@ MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCoun Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. */ - pRB->subbufferStrideInBytes = ma_align(pRB->subbufferSizeInBytes, MA_SIMD_ALIGNMENT); + pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); @@ -60999,7 +59515,7 @@ MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo } -#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && (defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_NXDK)) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) #define MA_USE_WIN32_FILEIO #endif @@ -61076,34 +59592,25 @@ static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { - #if !defined(MA_XBOX_NXDK) - { - HANDLE hFile; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; + HANDLE hFile; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; - (void)pVFS; + (void)pVFS; - /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ - ma_win32_fileio_init(); + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); - ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); - hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - return ma_result_from_GetLastError(GetLastError()); - } - - *pFile = hFile; - return MA_SUCCESS; + hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return ma_result_from_GetLastError(GetLastError()); } - #else - { - /* No CreateFileW() available. */ - return MA_NOT_IMPLEMENTED; - } - #endif + + *pFile = hFile; + return MA_SUCCESS; } static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) @@ -61274,28 +59781,19 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { + BY_HANDLE_FILE_INFORMATION fi; + BOOL result; + (void)pVFS; - #if !defined(MA_XBOX_NXDK) - { - BY_HANDLE_FILE_INFORMATION fi; - BOOL result; - - result = GetFileInformationByHandle((HANDLE)file, &fi); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); - - return MA_SUCCESS; + result = GetFileInformationByHandle((HANDLE)file, &fi); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); } - #else - { - /* GetFileInformationByHandle() is unavailable. */ - return MA_NOT_IMPLEMENTED; - } - #endif + + pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); + + return MA_SUCCESS; } #else static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) @@ -61633,8 +60131,6 @@ static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* p static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { - ma_result result; - if (pInfo == NULL) { return MA_INVALID_ARGS; } @@ -61646,43 +60142,10 @@ static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_inf } #if defined(MA_USE_WIN32_FILEIO) - result = ma_default_vfs_info__win32(pVFS, file, pInfo); + return ma_default_vfs_info__win32(pVFS, file, pInfo); #else - result = ma_default_vfs_info__stdio(pVFS, file, pInfo); + return ma_default_vfs_info__stdio(pVFS, file, pInfo); #endif - - if (result == MA_NOT_IMPLEMENTED) { - /* Not implemented. Fall back to seek/tell/seek. */ - ma_result result; - ma_int64 cursor; - ma_int64 sizeInBytes; - - result = ma_default_vfs_tell(pVFS, file, &cursor); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_default_vfs_seek(pVFS, file, 0, ma_seek_origin_end); - if (result != MA_SUCCESS) { - return result; - } - - result = ma_default_vfs_tell(pVFS, file, &sizeInBytes); - if (result != MA_SUCCESS) { - return result; - } - - pInfo->sizeInBytes = sizeInBytes; - - result = ma_default_vfs_seek(pVFS, file, cursor, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; - } - - MA_ASSERT(result == MA_SUCCESS); - } - - return result; } @@ -61870,8 +60333,8 @@ extern "C" { #define MA_DR_WAV_STRINGIFY(x) #x #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) #define MA_DR_WAV_VERSION_MAJOR 0 -#define MA_DR_WAV_VERSION_MINOR 14 -#define MA_DR_WAV_VERSION_REVISION 1 +#define MA_DR_WAV_VERSION_MINOR 13 +#define MA_DR_WAV_VERSION_REVISION 18 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include #define MA_DR_WAVE_FORMAT_PCM 0x1 @@ -61887,9 +60350,8 @@ MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* p MA_API const char* ma_dr_wav_version_string(void); typedef enum { - MA_DR_WAV_SEEK_SET, - MA_DR_WAV_SEEK_CUR, - MA_DR_WAV_SEEK_END + ma_dr_wav_seek_origin_start, + ma_dr_wav_seek_origin_current } ma_dr_wav_seek_origin; typedef enum { @@ -61926,7 +60388,6 @@ MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); -typedef ma_bool32 (* ma_dr_wav_tell_proc)(void* pUserData, ma_int64* pCursor); typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); typedef struct { @@ -61971,11 +60432,6 @@ typedef enum ma_dr_wav_metadata_type_list_info_genre = 1 << 15, ma_dr_wav_metadata_type_list_info_album = 1 << 16, ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, - ma_dr_wav_metadata_type_list_info_location = 1 << 18, - ma_dr_wav_metadata_type_list_info_organization = 1 << 19, - ma_dr_wav_metadata_type_list_info_keywords = 1 << 20, - ma_dr_wav_metadata_type_list_info_medium = 1 << 21, - ma_dr_wav_metadata_type_list_info_description = 1 << 22, ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software | ma_dr_wav_metadata_type_list_info_copyright | ma_dr_wav_metadata_type_list_info_title @@ -61984,12 +60440,7 @@ typedef enum | ma_dr_wav_metadata_type_list_info_date | ma_dr_wav_metadata_type_list_info_genre | ma_dr_wav_metadata_type_list_info_album - | ma_dr_wav_metadata_type_list_info_tracknumber - | ma_dr_wav_metadata_type_list_info_location - | ma_dr_wav_metadata_type_list_info_organization - | ma_dr_wav_metadata_type_list_info_keywords - | ma_dr_wav_metadata_type_list_info_medium - | ma_dr_wav_metadata_type_list_info_description, + | ma_dr_wav_metadata_type_list_info_tracknumber, ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label | ma_dr_wav_metadata_type_list_note | ma_dr_wav_metadata_type_list_labelled_cue_region, @@ -62006,8 +60457,8 @@ typedef struct { ma_uint32 cuePointId; ma_uint32 type; - ma_uint32 firstSampleOffset; - ma_uint32 lastSampleOffset; + ma_uint32 firstSampleByteOffset; + ma_uint32 lastSampleByteOffset; ma_uint32 sampleFraction; ma_uint32 playCount; } ma_dr_wav_smpl_loop; @@ -62042,7 +60493,7 @@ typedef struct ma_uint8 dataChunkId[4]; ma_uint32 chunkStart; ma_uint32 blockStart; - ma_uint32 sampleOffset; + ma_uint32 sampleByteOffset; } ma_dr_wav_cue_point; typedef struct { @@ -62144,7 +60595,6 @@ typedef struct ma_dr_wav_read_proc onRead; ma_dr_wav_write_proc onWrite; ma_dr_wav_seek_proc onSeek; - ma_dr_wav_tell_proc onTell; void* pUserData; ma_allocation_callbacks allocationCallbacks; ma_dr_wav_container container; @@ -62187,9 +60637,9 @@ typedef struct ma_bool8 isUnsigned; } aiff; } ma_dr_wav; -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); @@ -62261,9 +60711,9 @@ MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, siz MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_WAV_NO_CONVERSION_API -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_WAV_NO_STDIO MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); @@ -62303,8 +60753,8 @@ extern "C" { #define MA_DR_FLAC_STRINGIFY(x) #x #define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) #define MA_DR_FLAC_VERSION_MAJOR 0 -#define MA_DR_FLAC_VERSION_MINOR 13 -#define MA_DR_FLAC_VERSION_REVISION 1 +#define MA_DR_FLAC_VERSION_MINOR 12 +#define MA_DR_FLAC_VERSION_REVISION 43 #define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) #include #if defined(_MSC_VER) && _MSC_VER >= 1700 @@ -62367,9 +60817,8 @@ typedef enum } ma_dr_flac_container; typedef enum { - MA_DR_FLAC_SEEK_SET, - MA_DR_FLAC_SEEK_CUR, - MA_DR_FLAC_SEEK_END + ma_dr_flac_seek_origin_start, + ma_dr_flac_seek_origin_current } ma_dr_flac_seek_origin; typedef struct { @@ -62445,7 +60894,6 @@ typedef struct } ma_dr_flac_metadata; typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); -typedef ma_bool32 (* ma_dr_flac_tell_proc)(void* pUserData, ma_int64* pCursor); typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); typedef struct { @@ -62457,7 +60905,6 @@ typedef struct { ma_dr_flac_read_proc onRead; ma_dr_flac_seek_proc onSeek; - ma_dr_flac_tell_proc onTell; void* pUserData; size_t unalignedByteCount; ma_dr_flac_cache_t unalignedCache; @@ -62517,10 +60964,10 @@ typedef struct ma_dr_flac_bs bs; ma_uint8 pExtraData[1]; } ma_dr_flac; -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); @@ -62534,9 +60981,9 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName #endif MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_FLAC_NO_STDIO MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); @@ -62584,12 +61031,6 @@ MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterat #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#ifndef MA_DR_MP3_NO_SIMD - #if (defined(MA_NO_NEON) && defined(MA_ARM)) || (defined(MA_NO_SSE2) && (defined(MA_X86) || defined(MA_X64))) - #define MA_DR_MP3_NO_SIMD - #endif -#endif - /* dr_mp3_h begin */ #ifndef ma_dr_mp3_h #define ma_dr_mp3_h @@ -62599,8 +61040,8 @@ extern "C" { #define MA_DR_MP3_STRINGIFY(x) #x #define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) #define MA_DR_MP3_VERSION_MAJOR 0 -#define MA_DR_MP3_VERSION_MINOR 7 -#define MA_DR_MP3_VERSION_REVISION 1 +#define MA_DR_MP3_VERSION_MINOR 6 +#define MA_DR_MP3_VERSION_REVISION 40 #define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) #include #define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 @@ -62609,7 +61050,7 @@ MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* p MA_API const char* ma_dr_mp3_version_string(void); typedef struct { - int frame_bytes, channels, sample_rate, layer, bitrate_kbps; + int frame_bytes, channels, hz, layer, bitrate_kbps; } ma_dr_mp3dec_frame_info; typedef struct { @@ -62622,9 +61063,8 @@ MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); typedef enum { - MA_DR_MP3_SEEK_SET, - MA_DR_MP3_SEEK_CUR, - MA_DR_MP3_SEEK_END + ma_dr_mp3_seek_origin_start, + ma_dr_mp3_seek_origin_current } ma_dr_mp3_seek_origin; typedef struct { @@ -62633,24 +61073,8 @@ typedef struct ma_uint16 mp3FramesToDiscard; ma_uint16 pcmFramesToDiscard; } ma_dr_mp3_seek_point; -typedef enum -{ - MA_DR_MP3_METADATA_TYPE_ID3V1, - MA_DR_MP3_METADATA_TYPE_ID3V2, - MA_DR_MP3_METADATA_TYPE_APE, - MA_DR_MP3_METADATA_TYPE_XING, - MA_DR_MP3_METADATA_TYPE_VBRI -} ma_dr_mp3_metadata_type; -typedef struct -{ - ma_dr_mp3_metadata_type type; - const void* pRawData; - size_t rawDataSize; -} ma_dr_mp3_metadata; typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); -typedef ma_bool32 (* ma_dr_mp3_tell_proc)(void* pUserData, ma_int64* pCursor); -typedef void (* ma_dr_mp3_meta_proc)(void* pUserData, const ma_dr_mp3_metadata* pMetadata); typedef struct { ma_uint32 channels; @@ -62663,9 +61087,7 @@ typedef struct ma_uint32 sampleRate; ma_dr_mp3_read_proc onRead; ma_dr_mp3_seek_proc onSeek; - ma_dr_mp3_meta_proc onMeta; void* pUserData; - void* pUserDataMeta; ma_allocation_callbacks allocationCallbacks; ma_uint32 mp3FrameChannels; ma_uint32 mp3FrameSampleRate; @@ -62674,20 +61096,13 @@ typedef struct ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; ma_uint64 currentPCMFrame; ma_uint64 streamCursor; - ma_uint64 streamLength; - ma_uint64 streamStartOffset; ma_dr_mp3_seek_point* pSeekPoints; ma_uint32 seekPointCount; - ma_uint32 delayInPCMFrames; - ma_uint32 paddingInPCMFrames; - ma_uint64 totalPCMFrameCount; - ma_bool32 isVBR; - ma_bool32 isCBR; size_t dataSize; size_t dataCapacity; size_t dataConsumed; ma_uint8* pData; - ma_bool32 atEnd; + ma_bool32 atEnd : 1; struct { const ma_uint8* pData; @@ -62695,12 +61110,9 @@ typedef struct size_t currentReadPos; } memory; } ma_dr_mp3; -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_MP3_NO_STDIO -MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); #endif @@ -62713,8 +61125,8 @@ MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #ifndef MA_DR_MP3_NO_STDIO @@ -63267,10 +61679,8 @@ static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav MA_ASSERT(pWav != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == MA_DR_WAV_SEEK_CUR) { - maSeekOrigin = ma_seek_origin_current; - } else if (origin == MA_DR_WAV_SEEK_END) { - maSeekOrigin = ma_seek_origin_end; + if (origin == ma_dr_wav_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; } result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); @@ -63280,26 +61690,6 @@ static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav return MA_TRUE; } - -static ma_bool32 ma_wav_dr_callback__tell(void* pUserData, ma_int64* pCursor) -{ - ma_wav* pWav = (ma_wav*)pUserData; - ma_result result; - - MA_ASSERT(pWav != NULL); - MA_ASSERT(pCursor != NULL); - - if (pWav->onTell == NULL) { - return MA_FALSE; /* Not implemented. */ - } - - result = pWav->onTell(pWav->pReadSeekTellUserData, pCursor); - if (result != MA_SUCCESS) { - return MA_FALSE; /* Failed to tell. */ - } - - return MA_TRUE; -} #endif static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) @@ -63394,7 +61784,7 @@ MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p { ma_bool32 wavResult; - wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, ma_wav_dr_callback__tell, pWav, pAllocationCallbacks); + wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } @@ -63973,10 +62363,8 @@ static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_fl MA_ASSERT(pFlac != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == MA_DR_FLAC_SEEK_CUR) { - maSeekOrigin = ma_seek_origin_current; - } else if (origin == MA_DR_FLAC_SEEK_END) { - maSeekOrigin = ma_seek_origin_end; + if (origin == ma_dr_flac_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; } result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); @@ -63986,26 +62374,6 @@ static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_fl return MA_TRUE; } - -static ma_bool32 ma_flac_dr_callback__tell(void* pUserData, ma_int64* pCursor) -{ - ma_flac* pFlac = (ma_flac*)pUserData; - ma_result result; - - MA_ASSERT(pFlac != NULL); - MA_ASSERT(pCursor != NULL); - - if (pFlac->onTell == NULL) { - return MA_FALSE; /* Not implemented. */ - } - - result = pFlac->onTell(pFlac->pReadSeekTellUserData, pCursor); - if (result != MA_SUCCESS) { - return MA_FALSE; /* Failed to tell. */ - } - - return MA_TRUE; -} #endif static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) @@ -64057,7 +62425,7 @@ MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_ #if !defined(MA_NO_FLAC) { - pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, ma_flac_dr_callback__tell, pFlac, pAllocationCallbacks); + pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -64618,12 +62986,9 @@ static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3 MA_ASSERT(pMP3 != NULL); - if (origin == MA_DR_MP3_SEEK_SET) { - maSeekOrigin = ma_seek_origin_start; - } else if (origin == MA_DR_MP3_SEEK_END) { - maSeekOrigin = ma_seek_origin_end; - } else { - maSeekOrigin = ma_seek_origin_current; + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_mp3_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; } result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); @@ -64633,21 +62998,6 @@ static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3 return MA_TRUE; } - -static ma_bool32 ma_mp3_dr_callback__tell(void* pUserData, ma_int64* pCursor) -{ - ma_mp3* pMP3 = (ma_mp3*)pUserData; - ma_result result; - - MA_ASSERT(pMP3 != NULL); - - result = pMP3->onTell(pMP3->pReadSeekTellUserData, pCursor); - if (result != MA_SUCCESS) { - return MA_FALSE; - } - - return MA_TRUE; -} #endif static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) @@ -64748,7 +63098,7 @@ MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p { ma_bool32 mp3Result; - mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, ma_mp3_dr_callback__tell, NULL, pMP3, pAllocationCallbacks); + mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } @@ -66647,16 +64997,14 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e ext1 = extension; ext2 = ma_path_extension_w(path); - #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(MA_XBOX_NXDK) +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) + return _wcsicmp(ext1, ext2) == 0; +#else + /* + I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This + isn't the most efficient way to do it, but it should work OK. + */ { - return _wcsicmp(ext1, ext2) == 0; - } - #elif !defined(MA_XBOX_NXDK) && !defined(MA_DOS) - { - /* - I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This - isn't the most efficient way to do it, but it should work OK. - */ char ext1MB[4096]; char ext2MB[4096]; const wchar_t* pext1 = ext1; @@ -66676,13 +65024,7 @@ static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* e return strcasecmp(ext1MB, ext2MB) == 0; } - #else - { - /* Getting here means we don't have a way to do a case-sensitive comparison for wide strings. Fall back to a simple case-sensitive comparison. */ - /* TODO: Implement our own wchar_t-to-char conversion routine and then use the char* version for comparing. */ - return ma_wcscmp(ext1, ext2) == 0; - } - #endif +#endif } #endif /* MA_HAS_PATH_API */ @@ -67777,18 +66119,10 @@ static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, m { ma_encoder* pEncoder = (ma_encoder*)pUserData; ma_result result; - ma_seek_origin maSeekOrigin; MA_ASSERT(pEncoder != NULL); - maSeekOrigin = ma_seek_origin_start; - if (origin == MA_DR_WAV_SEEK_CUR) { - maSeekOrigin = ma_seek_origin_current; - } else if (origin == MA_DR_WAV_SEEK_END) { - maSeekOrigin = ma_seek_origin_end; - } - - result = pEncoder->onSeek(pEncoder, offset, maSeekOrigin); + result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); if (result != MA_SUCCESS) { return MA_FALSE; } else { @@ -69310,7 +67644,7 @@ static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) ma_uint32 block; /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ - MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * (int) sizeof(block)), sizeof(block)); + MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); if (ma_is_little_endian()) { return block; @@ -69386,7 +67720,7 @@ static ma_uint32 ma_hash_string_32(const char* str) static ma_uint32 ma_hash_string_w_32(const wchar_t* str) { - return ma_hash_32(str, (int)ma_wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); + return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); } @@ -69546,7 +67880,6 @@ static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_ return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); } -#if 0 /* Currently unused, but might make use of this later. */ static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); @@ -69554,7 +67887,6 @@ static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_ return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); } -#endif static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) { @@ -70677,19 +69009,16 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m /* Failed to post job. Probably ran out of memory. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + /* + Fences were acquired before posting the job, but since the job was not able to + be posted, we need to make sure we release them so nothing gets stuck waiting. + */ + if (pInitFence != NULL) { ma_fence_release(pInitFence); } + if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_uninit(pInitNotification); } else { - /* - Fences were acquired before posting the job, but since the job was not able to - be posted, we need to make sure we release them so nothing gets stuck waiting. - - In the WAIT_INIT case, these will have already been released in ma_job_process() - so we should only release fences in this branch. - */ - if (pInitFence != NULL) { ma_fence_release(pInitFence); } - if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } - /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */ ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); @@ -78345,7 +76674,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con } if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { - ma_data_source_set_loop_point_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); } ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0)); @@ -78407,7 +76736,6 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); if (result != MA_SUCCESS) { - ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); goto done; } @@ -79213,12 +77541,7 @@ MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) { - ma_uint32 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); - if (sampleRate == 0) { - return 0; /* Prevent a division by zero. */ - } - - return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / sampleRate; + return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); } MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) @@ -79302,7 +77625,7 @@ MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeco return ma_sound_seek_to_pcm_frame(pSound, frameIndex); } -MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -79335,7 +77658,7 @@ MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFo } } -MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor) +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) { ma_uint64 seekTarget; @@ -79357,7 +77680,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_ui } } -MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength) +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -79371,7 +77694,7 @@ MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_ui return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); } -MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor) +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) { ma_result result; ma_uint64 cursorInPCMFrames; @@ -79397,7 +77720,7 @@ MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* p return MA_SUCCESS; } -MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength) +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) { if (pSound == NULL) { return MA_INVALID_ARGS; @@ -80216,12 +78539,12 @@ MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint ma_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) { + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { return MA_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, MA_DR_WAV_SEEK_CUR)) { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { return MA_FALSE; } bytesRemainingToSeek = 0; @@ -80232,17 +78555,17 @@ MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_SET); + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); } - if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_SET)) { + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { return MA_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_CUR); + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); } - if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) { + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { return MA_FALSE; } offset -= 0x7FFFFFFF; @@ -80265,7 +78588,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserD if (!onSeek(pUserData, offset, origin)) { return MA_FALSE; } - if (origin == MA_DR_WAV_SEEK_SET) { + if (origin == ma_dr_wav_seek_origin_start) { *pCursor = offset; } else { *pCursor += offset; @@ -80384,12 +78707,12 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_pa ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); } else { break; } @@ -80433,7 +78756,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_par pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); } else { break; } @@ -80773,7 +79096,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { ma_uint8 buffer[4]; size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, MA_DR_WAV_SEEK_CUR)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { return bytesRead; } bytesRead += 28; @@ -80868,7 +79191,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse return bytesRead; } allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; } else { @@ -80951,16 +79274,6 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_location, "IARL")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_location); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_organization, "ICMS")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_organization); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_keywords, "IKEY")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_keywords); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_medium, "IMED")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_medium); - } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_description, "ISBJ")) { - subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_description); } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } @@ -80968,13 +79281,13 @@ MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parse MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); if (subchunkBytesRead < subchunkDataSize) { ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, MA_DR_WAV_SEEK_CUR)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { break; } bytesRead += 1; @@ -81011,7 +79324,7 @@ MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) return ma_dr_wav_bytes_to_u16(pFMT->subFormat); } } -MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pReadSeekTellUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onRead == NULL || onSeek == NULL) { return MA_FALSE; @@ -81019,8 +79332,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRe MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; - pWav->onTell = onTell; - pWav->pUserData = pReadSeekTellUserData; + pWav->pUserData = pReadSeekUserData; pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { return MA_FALSE; @@ -81234,14 +79546,14 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); } else { - if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, MA_DR_WAV_SEEK_CUR) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { return MA_FALSE; } } cursor += fmt.extendedSize; bytesReadSoFar += fmt.extendedSize; } - if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), MA_DR_WAV_SEEK_CUR) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { return MA_FALSE; } cursor += (header.sizeInBytes - bytesReadSoFar); @@ -81392,26 +79704,15 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p return MA_FALSE; } offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); - pWav->dataChunkDataPos = cursor + offset; - dataChunkSize = chunkSize; - if (dataChunkSize > offset) { - dataChunkSize -= offset; - } else { - dataChunkSize = 0; + if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; } - if (sequential) { - if (foundChunk_fmt) { - if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { - return MA_FALSE; - } - cursor += offset; - break; - } else { - return MA_FALSE; - } + cursor += offset; + pWav->dataChunkDataPos = cursor; + dataChunkSize = chunkSize; + if (sequential || !isProcessingMetadata) { + break; } else { - chunkSize += header.paddingSize; - chunkSize -= sizeof(offsetAndBlockSizeData); if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { break; } @@ -81475,17 +79776,6 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p pWav->pMetadata = metadataParser.pMetadata; pWav->metadataCount = metadataParser.metadataCount; } - if (pWav->onTell != NULL && pWav->onSeek != NULL) { - if (pWav->onSeek(pWav->pUserData, 0, MA_DR_WAV_SEEK_END) == MA_TRUE) { - ma_int64 fileSize; - if (pWav->onTell(pWav->pUserData, &fileSize)) { - if (dataChunkSize + pWav->dataChunkDataPos > (ma_uint64)fileSize) { - dataChunkSize = (ma_uint64)fileSize - pWav->dataChunkDataPos; - } - } - } else { - } - } if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { dataChunkSize = 0; for (;;) { @@ -81505,14 +79795,8 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; pWav->bitsPerSample = fmt.bitsPerSample; - pWav->translatedFormatTag = translatedFormatTag; - if (!ma_dr_wav__is_compressed_format_tag(translatedFormatTag)) { - ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); - if (bytesPerFrame > 0) { - dataChunkSize -= (dataChunkSize % bytesPerFrame); - } - } pWav->bytesRemaining = dataChunkSize; + pWav->translatedFormatTag = translatedFormatTag; pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; @@ -81567,20 +79851,20 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p #endif return MA_TRUE; } -MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_wav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); } -MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { return MA_FALSE; } return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); } -MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return MA_FALSE; } return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); @@ -81742,8 +80026,8 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } @@ -81777,7 +80061,7 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); } } break; case ma_dr_wav_metadata_type_acid: @@ -81863,20 +80147,15 @@ MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_ if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { const char* pID = NULL; switch (pMetadata->type) { - case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; - case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; - case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; - case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; - case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; - case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; - case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; - case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; - case ma_dr_wav_metadata_type_list_info_location: pID = "IARL"; break; - case ma_dr_wav_metadata_type_list_info_organization: pID = "ICMS"; break; - case ma_dr_wav_metadata_type_list_info_keywords: pID = "IKEY"; break; - case ma_dr_wav_metadata_type_list_info_medium: pID = "IMED"; break; - case ma_dr_wav_metadata_type_list_info_description: pID = "ISBJ"; break; + case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; + case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; + case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; + case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; + case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; + case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; + case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; + case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; default: break; } MA_DR_WAV_ASSERT(pID != NULL); @@ -82091,7 +80370,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr } pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; if (pFormat->container == ma_dr_wav_container_riff) { - ma_uint32 chunkSizeRIFF = 36 + (ma_uint32)initialDataChunkSize; + ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; runningPos += ma_dr_wav__write(pWav, "RIFF", 4); runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); runningPos += ma_dr_wav__write(pWav, "WAVE", 4); @@ -82214,31 +80493,7 @@ MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, } MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - int whence = SEEK_SET; - if (origin == MA_DR_WAV_SEEK_CUR) { - whence = SEEK_CUR; - } else if (origin == MA_DR_WAV_SEEK_END) { - whence = SEEK_END; - } - return fseek((FILE*)pUserData, offset, whence) == 0; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_stdio(void* pUserData, ma_int64* pCursor) -{ - FILE* pFileStdio = (FILE*)pUserData; - ma_int64 result; - MA_DR_WAV_ASSERT(pFileStdio != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); -#if defined(_WIN32) && !defined(NXDK) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64(pFileStdio); - #else - result = ftell(pFileStdio); - #endif -#else - result = ftell(pFileStdio); -#endif - *pCursor = result; - return MA_TRUE; + return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) { @@ -82247,7 +80502,7 @@ MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, cons MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { ma_bool32 result; - result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, ma_dr_wav__on_tell_stdio, (void*)pFile, pAllocationCallbacks); + result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (result != MA_TRUE) { fclose(pFile); return result; @@ -82384,27 +80639,25 @@ MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, s MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - ma_int64 newCursor; MA_DR_WAV_ASSERT(pWav != NULL); - newCursor = pWav->memoryStream.currentReadPos; - if (origin == MA_DR_WAV_SEEK_SET) { - newCursor = 0; - } else if (origin == MA_DR_WAV_SEEK_CUR) { - newCursor = (ma_int64)pWav->memoryStream.currentReadPos; - } else if (origin == MA_DR_WAV_SEEK_END) { - newCursor = (ma_int64)pWav->memoryStream.dataSize; + if (origin == ma_dr_wav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { + return MA_FALSE; + } + } else { + if (pWav->memoryStream.currentReadPos < (size_t)-offset) { + return MA_FALSE; + } + } + pWav->memoryStream.currentReadPos += offset; } else { - MA_DR_WAV_ASSERT(!"Invalid seek origin"); - return MA_FALSE; + if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { + pWav->memoryStream.currentReadPos = offset; + } else { + return MA_FALSE; + } } - newCursor += offset; - if (newCursor < 0) { - return MA_FALSE; - } - if ((size_t)newCursor > pWav->memoryStream.dataSize) { - return MA_FALSE; - } - pWav->memoryStream.currentReadPos = (size_t)newCursor; return MA_TRUE; } MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) @@ -82438,35 +80691,25 @@ MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataI MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - ma_int64 newCursor; MA_DR_WAV_ASSERT(pWav != NULL); - newCursor = pWav->memoryStreamWrite.currentWritePos; - if (origin == MA_DR_WAV_SEEK_SET) { - newCursor = 0; - } else if (origin == MA_DR_WAV_SEEK_CUR) { - newCursor = (ma_int64)pWav->memoryStreamWrite.currentWritePos; - } else if (origin == MA_DR_WAV_SEEK_END) { - newCursor = (ma_int64)pWav->memoryStreamWrite.dataSize; + if (origin == ma_dr_wav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { + offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); + } + } else { + if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { + offset = -(int)pWav->memoryStreamWrite.currentWritePos; + } + } + pWav->memoryStreamWrite.currentWritePos += offset; } else { - MA_DR_WAV_ASSERT(!"Invalid seek origin"); - return MA_INVALID_ARGS; + if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + pWav->memoryStreamWrite.currentWritePos = offset; + } else { + pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; + } } - newCursor += offset; - if (newCursor < 0) { - return MA_FALSE; - } - if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) { - return MA_FALSE; - } - pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor; - return MA_TRUE; -} -MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_memory(void* pUserData, ma_int64* pCursor) -{ - ma_dr_wav* pWav = (ma_dr_wav*)pUserData; - MA_DR_WAV_ASSERT(pWav != NULL); - MA_DR_WAV_ASSERT(pCursor != NULL); - *pCursor = (ma_int64)pWav->memoryStream.currentReadPos; return MA_TRUE; } MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) @@ -82478,7 +80721,7 @@ MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, siz if (data == NULL || dataSize == 0) { return MA_FALSE; } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { return MA_FALSE; } pWav->memoryStream.data = (const ma_uint8*)data; @@ -82491,7 +80734,7 @@ MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void if (data == NULL || dataSize == 0) { return MA_FALSE; } - if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) { + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { return MA_FALSE; } pWav->memoryStream.data = (const ma_uint8*)data; @@ -82550,30 +80793,30 @@ MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) } if (pWav->onSeek && !pWav->isSequentialWrite) { if (pWav->container == ma_dr_wav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, MA_DR_WAV_SEEK_SET)) { + if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, MA_DR_WAV_SEEK_SET)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == ma_dr_wav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, MA_DR_WAV_SEEK_SET)) { + if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, MA_DR_WAV_SEEK_SET)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == ma_dr_wav_container_rf64) { int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, MA_DR_WAV_SEEK_SET)) { + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, MA_DR_WAV_SEEK_SET)) { + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } @@ -82620,7 +80863,7 @@ MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBuf if (bytesToSeek > 0x7FFFFFFF) { bytesToSeek = 0x7FFFFFFF; } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR) == MA_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { break; } bytesRead += bytesToSeek; @@ -82719,7 +80962,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) if (pWav->onWrite != NULL) { return MA_FALSE; } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, MA_DR_WAV_SEEK_SET)) { + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { return MA_FALSE; } if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { @@ -82800,7 +81043,7 @@ MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFr } while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, MA_DR_WAV_SEEK_CUR)) { + if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { return MA_FALSE; } pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; @@ -82926,12 +81169,12 @@ MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToW MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { ma_uint64 totalFramesRead = 0; - static const ma_int32 adaptationTable[] = { + static ma_int32 adaptationTable[] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; - static const ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static const ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; MA_DR_WAV_ASSERT(pWav != NULL); MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { @@ -83064,11 +81307,11 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint { ma_uint64 totalFramesRead = 0; ma_uint32 iChannel; - static const ma_int32 indexTable[16] = { + static ma_int32 indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; - static const ma_int32 stepTable[89] = { + static ma_int32 stepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, @@ -83091,7 +81334,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR); + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } @@ -83106,7 +81349,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR); + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } @@ -83181,7 +81424,7 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint return totalFramesRead; } #ifndef MA_DR_WAV_NO_CONVERSION_API -static const unsigned short ma_dr_wav_gAlawTable[256] = { +static unsigned short g_ma_dr_wavAlawTable[256] = { 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, @@ -83199,7 +81442,7 @@ static const unsigned short ma_dr_wav_gAlawTable[256] = { 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; -static const unsigned short ma_dr_wav_gMulawTable[256] = { +static unsigned short g_ma_dr_wavMulawTable[256] = { 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, @@ -83219,11 +81462,11 @@ static const unsigned short ma_dr_wav_gMulawTable[256] = { }; static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) { - return (short)ma_dr_wav_gAlawTable[sampleIn]; + return (short)g_ma_dr_wavAlawTable[sampleIn]; } static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) { - return (short)ma_dr_wav_gMulawTable[sampleIn]; + return (short)g_ma_dr_wavMulawTable[sampleIn]; } MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { @@ -84382,7 +82625,7 @@ MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, u } return pSampleData; } -MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_wav wav; if (channelsOut) { @@ -84394,12 +82637,12 @@ MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRe if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_wav wav; if (channelsOut) { @@ -84411,12 +82654,12 @@ MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_wav wav; if (channelsOut) { @@ -84428,7 +82671,7 @@ MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRe if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); @@ -85863,23 +84106,23 @@ static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFro MA_DR_FLAC_ASSERT(offsetFromStart > 0); if (offsetFromStart > 0x7FFFFFFF) { ma_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, MA_DR_FLAC_SEEK_CUR)) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } } } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, MA_DR_FLAC_SEEK_SET)) { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { return MA_FALSE; } } @@ -88357,7 +86600,6 @@ typedef struct { ma_dr_flac_read_proc onRead; ma_dr_flac_seek_proc onSeek; - ma_dr_flac_tell_proc onTell; ma_dr_flac_meta_proc onMeta; ma_dr_flac_container container; void* pUserData; @@ -88486,12 +86728,11 @@ static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) { ma_uint64 runningFilePos = 42; ma_uint64 seektablePos = 0; ma_uint32 seektableSize = 0; - (void)onTell; for (;;) { ma_dr_flac_metadata metadata; ma_uint8 isLastBlock = 0; @@ -88749,7 +86990,7 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea { if (onMeta) { metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { isLastBlock = MA_TRUE; } else { onMeta(pUserDataMD, &metadata); @@ -88759,7 +87000,7 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: { if (onMeta) { - if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { isLastBlock = MA_TRUE; } } @@ -88783,7 +87024,7 @@ static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRea } break; } if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { isLastBlock = MA_TRUE; } } @@ -89047,7 +87288,6 @@ typedef struct { ma_dr_flac_read_proc onRead; ma_dr_flac_seek_proc onSeek; - ma_dr_flac_tell_proc onTell; void* pUserData; ma_uint64 currentBytePos; ma_uint64 firstBytePos; @@ -89066,29 +87306,29 @@ static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* buf } static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) { - if (origin == MA_DR_FLAC_SEEK_SET) { + if (origin == ma_dr_flac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_SET)) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { return MA_FALSE; } oggbs->currentBytePos = offset; return MA_TRUE; } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { return MA_FALSE; } oggbs->currentBytePos = offset; - return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR); + return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); } } else { while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_CUR)) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } oggbs->currentBytePos += offset; @@ -89114,7 +87354,7 @@ static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr continue; } if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, MA_DR_FLAC_SEEK_CUR)) { + if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } continue; @@ -89176,7 +87416,7 @@ static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) } bytesToEndOfPacketOrPage += segmentSize; } - ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, MA_DR_FLAC_SEEK_CUR); + ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { @@ -89229,44 +87469,36 @@ static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac int bytesSeeked = 0; MA_DR_FLAC_ASSERT(oggbs != NULL); MA_DR_FLAC_ASSERT(offset >= 0); - if (origin == MA_DR_FLAC_SEEK_SET) { - if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, MA_DR_FLAC_SEEK_SET)) { + if (origin == ma_dr_flac_seek_origin_start) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { return MA_FALSE; } if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { return MA_FALSE; } - return ma_dr_flac__on_seek_ogg(pUserData, offset, MA_DR_FLAC_SEEK_CUR); - } else if (origin == MA_DR_FLAC_SEEK_CUR) { - while (bytesSeeked < offset) { - int bytesRemainingToSeek = offset - bytesSeeked; - MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); - if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { - bytesSeeked += bytesRemainingToSeek; - (void)bytesSeeked; - oggbs->bytesRemainingInPage -= bytesRemainingToSeek; - break; - } - if (oggbs->bytesRemainingInPage > 0) { - bytesSeeked += (int)oggbs->bytesRemainingInPage; - oggbs->bytesRemainingInPage = 0; - } - MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); - if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { - return MA_FALSE; - } + return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); + } + MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; } - } else if (origin == MA_DR_FLAC_SEEK_END) { - return MA_FALSE; } return MA_TRUE; } -static ma_bool32 ma_dr_flac__on_tell_ogg(void* pUserData, ma_int64* pCursor) -{ - (void)pUserData; - (void)pCursor; - return MA_FALSE; -} static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; @@ -89283,7 +87515,7 @@ static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 runningGranulePosition = 0; for (;;) { if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { - ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, MA_DR_FLAC_SEEK_SET); + ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); return MA_FALSE; } runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; @@ -89302,7 +87534,7 @@ static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 } } } - if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, MA_DR_FLAC_SEEK_SET)) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { return MA_FALSE; } if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { @@ -89397,7 +87629,7 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d if (mappingVersion[0] != 1) { return MA_FALSE; } - if (!onSeek(pUserData, 2, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } if (onRead(pUserData, sig, 4) != 4) { @@ -89442,17 +87674,17 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d return MA_FALSE; } } else { - if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } } } else { - if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } } } else { - if (!onSeek(pUserData, pageBodySize, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } } @@ -89466,7 +87698,7 @@ static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_d return MA_TRUE; } #endif -static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) +static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) { ma_bool32 relaxed; ma_uint8 id[4]; @@ -89476,14 +87708,12 @@ static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_fla MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; - pInit->onTell = onTell; pInit->onMeta = onMeta; pInit->container = container; pInit->pUserData = pUserData; pInit->pUserDataMD = pUserDataMD; pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; - pInit->bs.onTell = onTell; pInit->bs.pUserData = pUserData; ma_dr_flac__reset_cache(&pInit->bs); relaxed = container != ma_dr_flac_container_unknown; @@ -89506,7 +87736,7 @@ static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_fla if (flags & 0x10) { headerSize += 10; } - if (!onSeek(pUserData, headerSize, MA_DR_FLAC_SEEK_CUR)) { + if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { return MA_FALSE; } pInit->runningFilePos += headerSize; @@ -89549,7 +87779,7 @@ static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_ pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } -static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) +static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac_init_info init; ma_uint32 allocationSize; @@ -89564,7 +87794,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on ma_allocation_callbacks allocationCallbacks; ma_dr_flac* pFlac; ma_dr_flac__init_cpu_caps(); - if (!ma_dr_flac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) { + if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { @@ -89597,7 +87827,6 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; - pOggbs->onTell = onTell; pOggbs->pUserData = pUserData; pOggbs->currentBytePos = init.oggFirstBytePos; pOggbs->firstBytePos = init.oggFirstBytePos; @@ -89612,17 +87841,15 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on if (init.hasMetadataBlocks) { ma_dr_flac_read_proc onReadOverride = onRead; ma_dr_flac_seek_proc onSeekOverride = onSeek; - ma_dr_flac_tell_proc onTellOverride = onTell; void* pUserDataOverride = pUserData; #ifndef MA_DR_FLAC_NO_OGG if (init.container == ma_dr_flac_container_ogg) { onReadOverride = ma_dr_flac__on_read_ogg; onSeekOverride = ma_dr_flac__on_seek_ogg; - onTellOverride = ma_dr_flac__on_tell_ogg; pUserDataOverride = (void*)pOggbs; } #endif - if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { #ifndef MA_DR_FLAC_NO_OGG ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif @@ -89648,7 +87875,6 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on pOggbs = NULL; pFlac->bs.onRead = ma_dr_flac__on_read_ogg; pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; - pFlac->bs.onTell = ma_dr_flac__on_tell_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } @@ -89668,7 +87894,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, MA_DR_FLAC_SEEK_SET)) { + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { ma_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { @@ -89681,7 +87907,7 @@ static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc on break; } } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, MA_DR_FLAC_SEEK_SET)) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } @@ -89724,31 +87950,8 @@ static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t } static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - int whence = SEEK_SET; - if (origin == MA_DR_FLAC_SEEK_CUR) { - whence = SEEK_CUR; - } else if (origin == MA_DR_FLAC_SEEK_END) { - whence = SEEK_END; - } - return fseek((FILE*)pUserData, offset, whence) == 0; -} -static ma_bool32 ma_dr_flac__on_tell_stdio(void* pUserData, ma_int64* pCursor) -{ - FILE* pFileStdio = (FILE*)pUserData; - ma_int64 result; - MA_DR_FLAC_ASSERT(pFileStdio != NULL); - MA_DR_FLAC_ASSERT(pCursor != NULL); -#if defined(_WIN32) && !defined(NXDK) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64(pFileStdio); - #else - result = ftell(pFileStdio); - #endif -#else - result = ftell(pFileStdio); -#endif - *pCursor = result; - return MA_TRUE; + MA_DR_FLAC_ASSERT(offset >= 0); + return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) { @@ -89757,7 +87960,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocati if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -89772,7 +87975,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_all if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -89787,7 +87990,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_ if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -89802,7 +88005,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -89830,35 +88033,24 @@ static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_ static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - ma_int64 newCursor; MA_DR_FLAC_ASSERT(memoryStream != NULL); - newCursor = memoryStream->currentReadPos; - if (origin == MA_DR_FLAC_SEEK_SET) { - newCursor = 0; - } else if (origin == MA_DR_FLAC_SEEK_CUR) { - newCursor = (ma_int64)memoryStream->currentReadPos; - } else if (origin == MA_DR_FLAC_SEEK_END) { - newCursor = (ma_int64)memoryStream->dataSize; + MA_DR_FLAC_ASSERT(offset >= 0); + if (offset > (ma_int64)memoryStream->dataSize) { + return MA_FALSE; + } + if (origin == ma_dr_flac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + return MA_FALSE; + } } else { - MA_DR_FLAC_ASSERT(!"Invalid seek origin"); - return MA_FALSE; + if ((ma_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + return MA_FALSE; + } } - newCursor += offset; - if (newCursor < 0) { - return MA_FALSE; - } - if ((size_t)newCursor > memoryStream->dataSize) { - return MA_FALSE; - } - memoryStream->currentReadPos = (size_t)newCursor; - return MA_TRUE; -} -static ma_bool32 ma_dr_flac__on_tell_memory(void* pUserData, ma_int64* pCursor) -{ - ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; - MA_DR_FLAC_ASSERT(memoryStream != NULL); - MA_DR_FLAC_ASSERT(pCursor != NULL); - *pCursor = (ma_int64)memoryStream->currentReadPos; return MA_TRUE; } MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) @@ -89868,7 +88060,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, co memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, &memoryStream, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -89893,7 +88085,7 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_ memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -89911,21 +88103,21 @@ MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_ } return pFlac; } -MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) { @@ -92218,7 +90410,7 @@ on_error: MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac* pFlac; if (channelsOut) { @@ -92230,13 +90422,13 @@ MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac* pFlac; if (channelsOut) { @@ -92248,13 +90440,13 @@ MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_flac* pFlac; if (channelsOut) { @@ -92266,7 +90458,7 @@ MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRea if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } @@ -93070,10 +91262,6 @@ static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) } while ((exp_q2 -= e) > 0); return y; } -#if (defined(__GNUC__) && (__GNUC__ >= 14)) && !defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wstringop-overflow" -#endif static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) { static const ma_uint8 g_scf_partitions[3][28] = { @@ -93132,10 +91320,7 @@ static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_ scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } -#if (defined(__GNUC__) && (__GNUC__ >= 14)) && !defined(__clang__) - #pragma GCC diagnostic pop -#endif -static const float ma_dr_mp3_g_pow43[129 + 16] = { +static const float g_ma_dr_mp3_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f }; @@ -93145,7 +91330,7 @@ static float ma_dr_mp3_L3_pow_43(int x) int sign, mult = 256; if (x < 129) { - return ma_dr_mp3_g_pow43[16 + x]; + return g_ma_dr_mp3_pow43[16 + x]; } if (x < 1024) { @@ -93154,7 +91339,7 @@ static float ma_dr_mp3_L3_pow_43(int x) } sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return ma_dr_mp3_g_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { @@ -93224,7 +91409,7 @@ static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); } else { - *dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } @@ -93252,7 +91437,7 @@ static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; - *dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } MA_DR_MP3_CHECK_BITS; @@ -94083,7 +92268,7 @@ MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); info->frame_bytes = i + frame_size; info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->sample_rate = ma_dr_mp3_hdr_sample_rate_hz(hdr); + info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); @@ -94306,41 +92491,19 @@ static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(c } static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) { - size_t bytesRead; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - if (bytesToRead == 0) { - return 0; - } - bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; return bytesRead; } -static size_t ma_dr_mp3__on_read_clamped(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) -{ - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pMP3->onRead != NULL); - if (pMP3->streamLength == MA_UINT64_MAX) { - return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead); - } else { - ma_uint64 bytesRemaining; - bytesRemaining = (pMP3->streamLength - pMP3->streamCursor); - if (bytesToRead > bytesRemaining) { - bytesToRead = (size_t)bytesRemaining; - } - return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead); - } -} static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) { MA_DR_MP3_ASSERT(offset >= 0); - MA_DR_MP3_ASSERT(origin == MA_DR_MP3_SEEK_SET || origin == MA_DR_MP3_SEEK_CUR); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { return MA_FALSE; } - if (origin == MA_DR_MP3_SEEK_SET) { + if (origin == ma_dr_mp3_seek_origin_start) { pMP3->streamCursor = (ma_uint64)offset; - } else{ + } else { pMP3->streamCursor += offset; } return MA_TRUE; @@ -94350,18 +92513,18 @@ static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_ if (offset <= 0x7FFFFFFF) { return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); } - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_SET)) { + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { return MA_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { - if (!ma_dr_mp3__on_seek(pMP3, (int)offset, MA_DR_MP3_SEEK_CUR)) { + if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { return MA_FALSE; } offset = 0; } else { - if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_CUR)) { + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { return MA_FALSE; } offset -= 0x7FFFFFFF; @@ -94369,18 +92532,7 @@ static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_ } return MA_TRUE; } -static void ma_dr_mp3__on_meta(ma_dr_mp3* pMP3, ma_dr_mp3_metadata_type type, const void* pRawData, size_t rawDataSize) -{ - if (pMP3->onMeta) { - ma_dr_mp3_metadata metadata; - MA_DR_MP3_ZERO_OBJECT(&metadata); - metadata.type = type; - metadata.pRawData = pRawData; - metadata.rawDataSize = rawDataSize; - pMP3->onMeta(pMP3->pUserDataMeta, &metadata); - } -} -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { ma_uint32 pcmFramesRead = 0; MA_DR_MP3_ASSERT(pMP3 != NULL); @@ -94407,7 +92559,7 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { pMP3->atEnd = MA_TRUE; @@ -94426,20 +92578,16 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d return 0; } pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); - pMP3->dataConsumed += (size_t)info.frame_bytes; - pMP3->dataSize -= (size_t)info.frame_bytes; + if (info.frame_bytes > 0) { + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; + } if (pcmFramesRead > 0) { pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.sample_rate; - if (pMP3FrameInfo != NULL) { - *pMP3FrameInfo = info; - } - if (ppMP3FrameData != NULL) { - *ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes; - } + pMP3->mp3FrameSampleRate = info.hz; break; } else if (info.frame_bytes == 0) { size_t bytesRead; @@ -94456,7 +92604,7 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { pMP3->atEnd = MA_TRUE; return 0; @@ -94466,7 +92614,7 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_d }; return pcmFramesRead; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { ma_uint32 pcmFramesRead = 0; ma_dr_mp3dec_frame_info info; @@ -94482,44 +92630,36 @@ static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_m pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; - pMP3->mp3FrameSampleRate = info.sample_rate; - if (pMP3FrameInfo != NULL) { - *pMP3FrameInfo = info; - } - if (ppMP3FrameData != NULL) { - *ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos; - } + pMP3->mp3FrameSampleRate = info.hz; break; } else if (info.frame_bytes > 0) { pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - pMP3->streamCursor += (size_t)info.frame_bytes; } else { break; } } pMP3->memory.currentReadPos += (size_t)info.frame_bytes; - pMP3->streamCursor += (size_t)info.frame_bytes; return pcmFramesRead; } -static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); + return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); } else { - return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData); + return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); } } static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) { MA_DR_MP3_ASSERT(pMP3 != NULL); - return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, NULL, NULL); + return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); } #if 0 static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) { ma_uint32 pcmFrameCount; MA_DR_MP3_ASSERT(pMP3 != NULL); - pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFrameCount == 0) { return 0; } @@ -94529,249 +92669,33 @@ static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) return pcmFrameCount; } #endif -static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3dec_frame_info firstFrameInfo; - const ma_uint8* pFirstFrameData; - ma_uint32 firstFramePCMFrameCount; - ma_uint32 detectedMP3FrameCount = 0xFFFFFFFF; MA_DR_MP3_ASSERT(pMP3 != NULL); MA_DR_MP3_ASSERT(onRead != NULL); ma_dr_mp3dec_init(&pMP3->decoder); pMP3->onRead = onRead; pMP3->onSeek = onSeek; - pMP3->onMeta = onMeta; pMP3->pUserData = pUserData; - pMP3->pUserDataMeta = pUserDataMeta; pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { return MA_FALSE; } - pMP3->streamCursor = 0; - pMP3->streamLength = MA_UINT64_MAX; - pMP3->streamStartOffset = 0; - pMP3->delayInPCMFrames = 0; - pMP3->paddingInPCMFrames = 0; - pMP3->totalPCMFrameCount = MA_UINT64_MAX; - #if 1 - if (onSeek != NULL && onTell != NULL) { - if (onSeek(pUserData, 0, MA_DR_MP3_SEEK_END)) { - ma_int64 streamLen; - int streamEndOffset = 0; - if (onTell(pUserData, &streamLen)) { - if (streamLen > 128) { - char id3[3]; - if (onSeek(pUserData, streamEndOffset - 128, MA_DR_MP3_SEEK_END)) { - if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') { - streamEndOffset -= 128; - streamLen -= 128; - if (onMeta != NULL) { - ma_uint8 tag[128]; - tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G'; - if (onRead(pUserData, tag + 3, 125) == 125) { - ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V1, tag, 128); - } - } - } else { - } - } else { - } - } else { - } - if (streamLen > 32) { - char ape[32]; - if (onSeek(pUserData, streamEndOffset - 32, MA_DR_MP3_SEEK_END)) { - if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') { - ma_uint32 tagSize = - ((ma_uint32)ape[24] << 0) | - ((ma_uint32)ape[25] << 8) | - ((ma_uint32)ape[26] << 16) | - ((ma_uint32)ape[27] << 24); - streamEndOffset -= 32 + tagSize; - streamLen -= 32 + tagSize; - if (onMeta != NULL) { - if (onSeek(pUserData, streamEndOffset, MA_DR_MP3_SEEK_END)) { - size_t apeTagSize = (size_t)tagSize + 32; - ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(apeTagSize, pAllocationCallbacks); - if (pTagData != NULL) { - if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { - ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_APE, pTagData, apeTagSize); - } - ma_dr_mp3_free(pTagData, pAllocationCallbacks); - } - } - } - } - } - } else { - } - if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) { - return MA_FALSE; - } - pMP3->streamLength = (ma_uint64)streamLen; - if (pMP3->memory.pData != NULL) { - pMP3->memory.dataSize = (size_t)pMP3->streamLength; - } - } else { - if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) { - return MA_FALSE; - } - } - } else { - } - } else { - } - #endif - #if 1 - { - char header[10]; - if (onRead(pUserData, header, 10) == 10) { - if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') { - ma_uint32 tagSize = - (((ma_uint32)header[6] & 0x7F) << 21) | - (((ma_uint32)header[7] & 0x7F) << 14) | - (((ma_uint32)header[8] & 0x7F) << 7) | - (((ma_uint32)header[9] & 0x7F) << 0); - if (header[5] & 0x10) { - tagSize += 10; - } - if (onMeta != NULL) { - size_t tagSizeWithHeader = 10 + tagSize; - ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(tagSizeWithHeader, pAllocationCallbacks); - if (pTagData != NULL) { - MA_DR_MP3_COPY_MEMORY(pTagData, header, 10); - if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) { - ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader); - } - ma_dr_mp3_free(pTagData, pAllocationCallbacks); - } - } else { - if (onSeek != NULL) { - if (!onSeek(pUserData, tagSize, MA_DR_MP3_SEEK_CUR)) { - return MA_FALSE; - } - } else { - char discard[1024]; - while (tagSize > 0) { - size_t bytesToRead = tagSize; - if (bytesToRead > sizeof(discard)) { - bytesToRead = sizeof(discard); - } - if (onRead(pUserData, discard, bytesToRead) != bytesToRead) { - return MA_FALSE; - } - tagSize -= (ma_uint32)bytesToRead; - } - } - } - pMP3->streamStartOffset += 10 + tagSize; - pMP3->streamCursor = pMP3->streamStartOffset; - } else { - if (onSeek != NULL) { - if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) { - return MA_FALSE; - } - } else { - } - } - } else { - return MA_FALSE; - } - } - #endif - firstFramePCMFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData); - if (firstFramePCMFrameCount > 0) { - MA_DR_MP3_ASSERT(pFirstFrameData != NULL); - #if 1 - MA_DR_MP3_ASSERT(firstFrameInfo.frame_bytes > 0); - { - ma_dr_mp3_bs bs; - ma_dr_mp3_L3_gr_info grInfo[4]; - const ma_uint8* pTagData = pFirstFrameData; - ma_dr_mp3_bs_init(&bs, pFirstFrameData + MA_DR_MP3_HDR_SIZE, firstFrameInfo.frame_bytes - MA_DR_MP3_HDR_SIZE); - if (MA_DR_MP3_HDR_IS_CRC(pFirstFrameData)) { - ma_dr_mp3_bs_get_bits(&bs, 16); - } - if (ma_dr_mp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { - ma_bool32 isXing = MA_FALSE; - ma_bool32 isInfo = MA_FALSE; - const ma_uint8* pTagDataBeg; - pTagDataBeg = pFirstFrameData + MA_DR_MP3_HDR_SIZE + (bs.pos/8); - pTagData = pTagDataBeg; - isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g'); - isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o'); - if (isXing || isInfo) { - ma_uint32 bytes = 0; - ma_uint32 flags = pTagData[7]; - pTagData += 8; - if (flags & 0x01) { - detectedMP3FrameCount = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3]; - pTagData += 4; - } - if (flags & 0x02) { - bytes = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3]; - (void)bytes; - pTagData += 4; - } - if (flags & 0x04) { - pTagData += 100; - } - if (flags & 0x08) { - pTagData += 4; - } - if (pTagData[0]) { - pTagData += 21; - if (pTagData - pFirstFrameData + 14 < firstFrameInfo.frame_bytes) { - int delayInPCMFrames; - int paddingInPCMFrames; - delayInPCMFrames = (( (ma_uint32)pTagData[0] << 4) | ((ma_uint32)pTagData[1] >> 4)) + (528 + 1); - paddingInPCMFrames = ((((ma_uint32)pTagData[1] & 0xF) << 8) | ((ma_uint32)pTagData[2] )) - (528 + 1); - if (paddingInPCMFrames < 0) { - paddingInPCMFrames = 0; - } - pMP3->delayInPCMFrames = (ma_uint32)delayInPCMFrames; - pMP3->paddingInPCMFrames = (ma_uint32)paddingInPCMFrames; - } - } - if (isXing) { - pMP3->isVBR = MA_TRUE; - } else if (isInfo) { - pMP3->isCBR = MA_TRUE; - } - if (onMeta != NULL) { - ma_dr_mp3_metadata_type metadataType = isXing ? MA_DR_MP3_METADATA_TYPE_XING : MA_DR_MP3_METADATA_TYPE_VBRI; - size_t tagDataSize; - tagDataSize = (size_t)firstFrameInfo.frame_bytes; - tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData); - ma_dr_mp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize); - } - pMP3->pcmFramesRemainingInMP3Frame = 0; - pMP3->streamStartOffset += (ma_uint32)(firstFrameInfo.frame_bytes); - pMP3->streamCursor = pMP3->streamStartOffset; - ma_dr_mp3dec_init(&pMP3->decoder); - } - } else { - } - } - #endif - } else { + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); return MA_FALSE; } - if (detectedMP3FrameCount != 0xFFFFFFFF) { - pMP3->totalPCMFrameCount = detectedMP3FrameCount * firstFramePCMFrameCount; - } pMP3->channels = pMP3->mp3FrameChannels; pMP3->sampleRate = pMP3->mp3FrameSampleRate; return MA_TRUE; } -MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { return MA_FALSE; } MA_DR_MP3_ZERO_OBJECT(pMP3); - return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); } static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { @@ -94792,40 +92716,29 @@ static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_ static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) { ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - ma_int64 newCursor; MA_DR_MP3_ASSERT(pMP3 != NULL); - newCursor = pMP3->memory.currentReadPos; - if (origin == MA_DR_MP3_SEEK_SET) { - newCursor = 0; - } else if (origin == MA_DR_MP3_SEEK_CUR) { - newCursor = (ma_int64)pMP3->memory.currentReadPos; - } else if (origin == MA_DR_MP3_SEEK_END) { - newCursor = (ma_int64)pMP3->memory.dataSize; + if (origin == ma_dr_mp3_seek_origin_current) { + if (byteOffset > 0) { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { + byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); + } + } else { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(int)pMP3->memory.currentReadPos; + } + } + pMP3->memory.currentReadPos += byteOffset; } else { - MA_DR_MP3_ASSERT(!"Invalid seek origin"); - return MA_FALSE; + if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { + pMP3->memory.currentReadPos = byteOffset; + } else { + pMP3->memory.currentReadPos = pMP3->memory.dataSize; + } } - newCursor += byteOffset; - if (newCursor < 0) { - return MA_FALSE; - } - if ((size_t)newCursor > pMP3->memory.dataSize) { - return MA_FALSE; - } - pMP3->memory.currentReadPos = (size_t)newCursor; return MA_TRUE; } -static ma_bool32 ma_dr_mp3__on_tell_memory(void* pUserData, ma_int64* pCursor) +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { - ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; - MA_DR_MP3_ASSERT(pMP3 != NULL); - MA_DR_MP3_ASSERT(pCursor != NULL); - *pCursor = (ma_int64)pMP3->memory.currentReadPos; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; if (pMP3 == NULL) { return MA_FALSE; } @@ -94836,21 +92749,7 @@ MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void pMP3->memory.pData = (const ma_uint8*)pData; pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; - result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, ma_dr_mp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks); - if (result == MA_FALSE) { - return MA_FALSE; - } - if (pMP3->streamLength <= (ma_uint64)MA_SIZE_MAX) { - pMP3->memory.dataSize = (size_t)pMP3->streamLength; - } - if (pMP3->streamStartOffset > (ma_uint64)MA_SIZE_MAX) { - return MA_FALSE; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_dr_mp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks); + return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); } #ifndef MA_DR_MP3_NO_STDIO #include @@ -94861,75 +92760,35 @@ static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t } static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) { - int whence = SEEK_SET; - if (origin == MA_DR_MP3_SEEK_CUR) { - whence = SEEK_CUR; - } else if (origin == MA_DR_MP3_SEEK_END) { - whence = SEEK_END; - } - return fseek((FILE*)pUserData, offset, whence) == 0; -} -static ma_bool32 ma_dr_mp3__on_tell_stdio(void* pUserData, ma_int64* pCursor) -{ - FILE* pFileStdio = (FILE*)pUserData; - ma_int64 result; - MA_DR_MP3_ASSERT(pFileStdio != NULL); - MA_DR_MP3_ASSERT(pCursor != NULL); -#if defined(_WIN32) && !defined(NXDK) - #if defined(_MSC_VER) && _MSC_VER > 1200 - result = _ftelli64(pFileStdio); - #else - result = ftell(pFileStdio); - #endif -#else - result = ftell(pFileStdio); -#endif - *pCursor = result; - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (pMP3 == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; -} -MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_bool32 result; - FILE* pFile; - if (pMP3 == NULL) { - return MA_FALSE; - } - MA_DR_MP3_ZERO_OBJECT(pMP3); - if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { - return MA_FALSE; - } - result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks); - if (result != MA_TRUE) { - fclose(pFile); - return result; - } - return MA_TRUE; + return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_mp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); + ma_bool32 result; + FILE* pFile; + if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; } MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) { - return ma_dr_mp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks); + ma_bool32 result; + FILE* pFile; + if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; } #endif MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) @@ -95000,38 +92859,17 @@ static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 frames MA_DR_MP3_ASSERT(pMP3 != NULL); MA_DR_MP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { - ma_uint32 framesToConsume; - if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) { - ma_uint32 framesToSkip = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame); - pMP3->currentPCMFrame += framesToSkip; - pMP3->pcmFramesConsumedInMP3Frame += framesToSkip; - pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip; - } - framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); - if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) { - if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { - ma_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame; - if (framesToConsume > framesRemainigToPadding) { - framesToConsume = (ma_uint32)framesRemainigToPadding; - } - } else { - break; - } - } + ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); if (pBufferOut != NULL) { - #if defined(MA_DR_MP3_FLOAT_OUTPUT) - { - float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); - } - #else - { - ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); - ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); - } - #endif + #if defined(MA_DR_MP3_FLOAT_OUTPUT) + float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + #else + ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); + ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); + #endif } pMP3->currentPCMFrame += framesToConsume; pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; @@ -95041,9 +92879,6 @@ static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 frames if (framesToRead == 0) { break; } - if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) { - break; - } MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { break; @@ -95123,7 +92958,7 @@ static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) { MA_DR_MP3_ASSERT(pMP3 != NULL); MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); - if (!ma_dr_mp3__on_seek_64(pMP3, pMP3->streamStartOffset, MA_DR_MP3_SEEK_SET)) { + if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { return MA_FALSE; } ma_dr_mp3_reset(pMP3); @@ -95189,7 +93024,7 @@ static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uin seekPoint.mp3FramesToDiscard = 0; seekPoint.pcmFramesToDiscard = 0; } - if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, MA_DR_MP3_SEEK_SET)) { + if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { return MA_FALSE; } ma_dr_mp3_reset(pMP3); @@ -95200,7 +93035,7 @@ static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uin if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; } - pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL); + pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); if (pcmFramesRead == 0) { return MA_FALSE; } @@ -95242,7 +93077,7 @@ MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint6 totalMP3FrameCount = 0; for (;;) { ma_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } @@ -95266,26 +93101,10 @@ MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint6 MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) { ma_uint64 totalPCMFrameCount; - if (pMP3 == NULL) { + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { return 0; } - if (pMP3->totalPCMFrameCount != MA_UINT64_MAX) { - totalPCMFrameCount = pMP3->totalPCMFrameCount; - if (totalPCMFrameCount >= pMP3->delayInPCMFrames) { - totalPCMFrameCount -= pMP3->delayInPCMFrames; - } else { - } - if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) { - totalPCMFrameCount -= pMP3->paddingInPCMFrames; - } else { - } - return totalPCMFrameCount; - } else { - if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { - return 0; - } - return totalPCMFrameCount; - } + return totalPCMFrameCount; } MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) { @@ -95355,7 +93174,7 @@ MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSe MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { return MA_FALSE; } @@ -95379,7 +93198,7 @@ MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSe } mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL); + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; @@ -95517,18 +93336,18 @@ static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_c } return pFrames; } -MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { ma_dr_mp3 mp3; - if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) { + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); From 0e57a572b447e45ac5afcc8bc83795dd4158c5c4 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Sep 2025 18:05:07 +0200 Subject: [PATCH 15/31] REVIEWED: `rlsw` module and related files --- src/external/rlsw.h | 3512 +++++++++++++---------------- src/platforms/rcore_desktop_sdl.c | 2 +- src/platforms/rcore_drm.c | 46 +- src/rlgl.h | 24 +- 4 files changed, 1640 insertions(+), 1944 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 2177467ec..49050f848 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -1,26 +1,81 @@ -/** - * MIT License - * - * Copyright (c) 2025 Le Juez Victor - * - * 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. - */ +/********************************************************************************************** +* +* rlsw v1.0 - An OpenGL 1.1-style software renderer implementation +* +* DESCRIPTION: +* rlsw is a custom OpenGL 1.1-style implementation on software, intended to provide all +* functionality available on rlgl.h library used by raylib, becoming a direct software +* rendering replacement for OpenGL 1.1 backend and allowing to run raylib on GPU-less +* devices when required. +* +* FEATURES: +* - Rendering to custom internal framebuffer with multiple color modes supported: +* - Color buffer: RGB - 8-bit (3:3:2) | RGB - 16-bit (5:6:5) | RGB - 24-bit (8:8:8) +* - Depth buffer: D - 8-bit (unorm) | D - 16-bit (unorm) | D - 24-bit (unorm) +* - Rendering modes supported: POINT, LINES, TRIANGLE, QUADS +* - Additional features: Polygon modes, Point width, Line width +* - Clipping support for all rendering modes +* - Texture features supported: +* - All uncompressed texture formats supported by raylib +* - Texture Minification/Magnification checks +* - Point and Bilinear filtering +* - Texture Wrap Modes with separate checks for S/T coordinates +* - Vertex Arrays support with direct primitive drawing mode +* - Matrix Stack support (Matrix Push/Pop) +* - Other GL misc features: +* - GL-style getter functions +* - Framebuffer resizing +* - Perspective correction +* - Scissor clipping +* - Depth testing +* - Blend modes +* - Face culling +* +* ADDITIONAL NOTES: +* Check PR for more info: https://github.com/raysan5/raylib/pull/4832 +* +* CONFIGURATION: +* #define RLSW_IMPLEMENTATION +* Generates the implementation of the library into the included file +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation +* +* rlsw capabilities could be customized just defining some internal +* values before library inclusion (default values listed): +* +* #define SW_GL_FRAMEBUFFER_COPY_BGRA true +* #define SW_GL_BINDING_COPY_TEXTURE true +* #define SW_COLOR_BUFFER_BITS 24 +* #define SW_DEPTH_BUFFER_BITS 16 +* #define SW_MAX_PROJECTION_STACK_SIZE 2 +* #define SW_MAX_MODELVIEW_STACK_SIZE 8 +* #define SW_MAX_TEXTURE_STACK_SIZE 2 +* #define SW_MAX_TEXTURES 128 +* +* +* LICENSE: MIT +* +* Copyright (c) 2025-2026 Le Juez Victor (@Bigfoot71), reviewed by Ramon Santamaria (@raysan5) +* +* 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 RLSW_H #define RLSW_H @@ -28,94 +83,102 @@ #include #include -/* === RLSW Definition And Macros === */ +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Function specifiers definition +#ifndef SWAPI + #define SWAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif #ifndef SW_MALLOC -# define SW_MALLOC(sz) malloc(sz) + #define SW_MALLOC(sz) malloc(sz) #endif #ifndef SW_REALLOC -# define SW_REALLOC(ptr, newSz) realloc(ptr, newSz) + #define SW_REALLOC(ptr, newSz) realloc(ptr, newSz) #endif #ifndef SW_FREE -# define SW_FREE(ptr) free(ptr) + #define SW_FREE(ptr) free(ptr) #endif #ifndef SW_RESTRICT -# ifdef _MSC_VER -# define SW_RESTRICT __restrict -# else -# define SW_RESTRICT restrict -# endif + #ifdef _MSC_VER + #define SW_RESTRICT __restrict + #else + #define SW_RESTRICT restrict + #endif #endif #ifndef SW_GL_FRAMEBUFFER_COPY_BGRA -# define SW_GL_FRAMEBUFFER_COPY_BGRA true + #define SW_GL_FRAMEBUFFER_COPY_BGRA true #endif #ifndef SW_GL_BINDING_COPY_TEXTURE -# define SW_GL_BINDING_COPY_TEXTURE true + #define SW_GL_BINDING_COPY_TEXTURE true #endif #ifndef SW_COLOR_BUFFER_BITS -# define SW_COLOR_BUFFER_BITS 24 + #define SW_COLOR_BUFFER_BITS 24 #endif #ifndef SW_DEPTH_BUFFER_BITS -# define SW_DEPTH_BUFFER_BITS 16 + #define SW_DEPTH_BUFFER_BITS 16 #endif #ifndef SW_MAX_PROJECTION_STACK_SIZE -# define SW_MAX_PROJECTION_STACK_SIZE 2 + #define SW_MAX_PROJECTION_STACK_SIZE 2 #endif #ifndef SW_MAX_MODELVIEW_STACK_SIZE -# define SW_MAX_MODELVIEW_STACK_SIZE 8 + #define SW_MAX_MODELVIEW_STACK_SIZE 8 #endif #ifndef SW_MAX_TEXTURE_STACK_SIZE -# define SW_MAX_TEXTURE_STACK_SIZE 2 + #define SW_MAX_TEXTURE_STACK_SIZE 2 #endif #ifndef SW_MAX_TEXTURES -# define SW_MAX_TEXTURES 128 + #define SW_MAX_TEXTURES 128 #endif -#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES // Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane. // Considering the largest polygon involved is a quadrilateral (4 vertices), -// and that clipping occurs against both the frustum (6 planes) -// and the scissors (4 planes), +// and that clipping occurs against both the frustum (6 planes) and the scissors (4 planes), // the maximum number of vertices after clipping is: // 4 (original vertices) + 6 (frustum planes) + 4 (scissors planes) = 14 -# define SW_MAX_CLIPPED_POLYGON_VERTICES 14 +#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES + #define SW_MAX_CLIPPED_POLYGON_VERTICES 14 #endif #ifndef SW_CLIP_EPSILON -# define SW_CLIP_EPSILON 1e-4f + #define SW_CLIP_EPSILON 1e-4f #endif -/* === OpenGL Compatibility Types === */ - -typedef unsigned int GLenum; -typedef unsigned char GLboolean; -typedef unsigned int GLbitfield; -typedef void GLvoid; -typedef signed char GLbyte; -typedef short GLshort; -typedef int GLint; -typedef unsigned char GLubyte; -typedef unsigned short GLushort; -typedef unsigned int GLuint; -typedef int GLsizei; -typedef float GLfloat; -typedef float GLclampf; -typedef double GLdouble; -typedef double GLclampd; - -/* === OpenGL Definitions === */ +//---------------------------------------------------------------------------------- +// OpenGL Compatibility Types +//---------------------------------------------------------------------------------- +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; +typedef short GLshort; +typedef int GLint; +typedef unsigned char GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef int GLsizei; +typedef float GLfloat; +typedef float GLclampf; +typedef double GLdouble; +typedef double GLclampd; +//---------------------------------------------------------------------------------- +// OpenGL Definitions +// NOTE: Not used/supported definitions are commented +//---------------------------------------------------------------------------------- #define GL_FALSE 0 #define GL_TRUE 1 @@ -168,7 +231,7 @@ typedef double GLclampd; #define GL_TEXTURE 0x1702 #define GL_VERTEX_ARRAY 0x8074 -#define GL_NORMAL_ARRAY 0x8075 //< WARNING: Not implemented (defined for RLGL) +#define GL_NORMAL_ARRAY 0x8075 // WARNING: Not implemented (defined for RLGL) #define GL_COLOR_ARRAY 0x8076 //#define GL_INDEX_ARRAY 0x8077 #define GL_TEXTURE_COORD_ARRAY 0x8078 @@ -237,8 +300,7 @@ typedef double GLclampd; #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 -/* === Not Used Definitions === */ - +// OpenGL Definitions NOT USED #define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_UNPACK_ALIGNMENT 0x0CF5 @@ -256,8 +318,9 @@ typedef double GLclampd; #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 -/* === OpenGL Binding === */ - +//---------------------------------------------------------------------------------- +// OpenGL Bindings to rlsw +//---------------------------------------------------------------------------------- #define glReadPixels(x, y, w, h, f, t, p) swCopyFramebuffer((x), (y), (w), (h), (f), (t), (p)) #define glEnable(state) swEnable((state)) #define glDisable(state) swDisable((state)) @@ -317,8 +380,7 @@ typedef double GLclampd; #define glTexParameteri(tr, pname, param) swTexParameteri((pname), (param)) #define glBindTexture(tr, id) swBindTexture((id)) -/* === Not Implemented === */ - +// OpenGL functions NOT IMPLEMENTED by rlsw #define glClearDepth(X) ((void)(X)) #define glDepthMask(X) ((void)(X)) #define glColorMask(X,Y,Z,W) ((void)(X),(void)(Y),(void)(Z),(void)(W)) @@ -334,8 +396,9 @@ typedef double GLclampd; #define glNormal3fv(X) ((void)(X)) #define glNormalPointer(X,Y,Z) ((void)(X),(void)(Y),(void)(Z)) -/* === RLSW Enums === */ - +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- typedef enum { SW_SCISSOR_TEST = GL_SCISSOR_TEST, SW_TEXTURE_2D = GL_TEXTURE_2D, @@ -455,102 +518,108 @@ typedef enum { SW_INVALID_OPERATION = GL_INVALID_OPERATION, } SWerrcode; -/* === Public API === */ +//------------------------------------------------------------------------------------ +// Functions Declaration - Public API +//------------------------------------------------------------------------------------ +SWAPI bool swInit(int w, int h); +SWAPI void swClose(void); -bool swInit(int w, int h); -void swClose(void); +SWAPI bool swResizeFramebuffer(int w, int h); +SWAPI void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels); +SWAPI void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels); +SWAPI void *swGetColorBuffer(int *w, int *h); -bool swResizeFramebuffer(int w, int h); -void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void* pixels); -void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void* pixels); -void* swGetColorBuffer(int* w, int* h); +SWAPI void swEnable(SWstate state); +SWAPI void swDisable(SWstate state); -void swEnable(SWstate state); -void swDisable(SWstate state); +SWAPI void swGetFloatv(SWget name, float *v); +SWAPI const char *swGetString(SWget name); +SWAPI SWerrcode swGetError(void); -void swGetFloatv(SWget name, float* v); -const char* swGetString(SWget name); -SWerrcode swGetError(void); +SWAPI void swViewport(int x, int y, int width, int height); +SWAPI void swScissor(int x, int y, int width, int height); -void swViewport(int x, int y, int width, int height); -void swScissor(int x, int y, int width, int height); +SWAPI void swClearColor(float r, float g, float b, float a); +SWAPI void swClear(uint32_t bitmask); -void swClearColor(float r, float g, float b, float a); -void swClear(uint32_t bitmask); +SWAPI void swBlendFunc(SWfactor sfactor, SWfactor dfactor); +SWAPI void swPolygonMode(SWpoly mode); +SWAPI void swCullFace(SWface face); -void swBlendFunc(SWfactor sfactor, SWfactor dfactor); -void swPolygonMode(SWpoly mode); -void swCullFace(SWface face); +SWAPI void swPointSize(float size); +SWAPI void swLineWidth(float width); -void swPointSize(float size); -void swLineWidth(float width); +SWAPI void swMatrixMode(SWmatrix mode); +SWAPI void swPushMatrix(void); +SWAPI void swPopMatrix(void); +SWAPI void swLoadIdentity(void); +SWAPI void swTranslatef(float x, float y, float z); +SWAPI void swRotatef(float angle, float x, float y, float z); +SWAPI void swScalef(float x, float y, float z); +SWAPI void swMultMatrixf(const float *mat); +SWAPI void swFrustum(double left, double right, double bottom, double top, double znear, double zfar); +SWAPI void swOrtho(double left, double right, double bottom, double top, double znear, double zfar); -void swMatrixMode(SWmatrix mode); -void swPushMatrix(void); -void swPopMatrix(void); -void swLoadIdentity(void); -void swTranslatef(float x, float y, float z); -void swRotatef(float angle, float x, float y, float z); -void swScalef(float x, float y, float z); -void swMultMatrixf(const float* mat); -void swFrustum(double left, double right, double bottom, double top, double znear, double zfar); -void swOrtho(double left, double right, double bottom, double top, double znear, double zfar); +SWAPI void swBegin(SWdraw mode); +SWAPI void swEnd(void); -void swBegin(SWdraw mode); -void swEnd(void); +SWAPI void swVertex2i(int x, int y); +SWAPI void swVertex2f(float x, float y); +SWAPI void swVertex2fv(const float *v); +SWAPI void swVertex3i(int x, int y, int z); +SWAPI void swVertex3f(float x, float y, float z); +SWAPI void swVertex3fv(const float *v); +SWAPI void swVertex4i(int x, int y, int z, int w); +SWAPI void swVertex4f(float x, float y, float z, float w); +SWAPI void swVertex4fv(const float *v); -void swVertex2i(int x, int y); -void swVertex2f(float x, float y); -void swVertex2fv(const float* v); -void swVertex3i(int x, int y, int z); -void swVertex3f(float x, float y, float z); -void swVertex3fv(const float* v); -void swVertex4i(int x, int y, int z, int w); -void swVertex4f(float x, float y, float z, float w); -void swVertex4fv(const float* v); +SWAPI void swColor3ub(uint8_t r, uint8_t g, uint8_t b); +SWAPI void swColor3ubv(const uint8_t *v); +SWAPI void swColor3f(float r, float g, float b); +SWAPI void swColor3fv(const float *v); +SWAPI void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SWAPI void swColor4ubv(const uint8_t *v); +SWAPI void swColor4f(float r, float g, float b, float a); +SWAPI void swColor4fv(const float *v); -void swColor3ub(uint8_t r, uint8_t g, uint8_t b); -void swColor3ubv(const uint8_t* v); -void swColor3f(float r, float g, float b); -void swColor3fv(const float* v); -void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a); -void swColor4ubv(const uint8_t* v); -void swColor4f(float r, float g, float b, float a); -void swColor4fv(const float* v); +SWAPI void swTexCoord2f(float u, float v); +SWAPI void swTexCoord2fv(const float *v); -void swTexCoord2f(float u, float v); -void swTexCoord2fv(const float* v); +SWAPI void swBindArray(SWarray type, void *buffer); +SWAPI void swDrawArrays(SWdraw mode, int offset, int count); -void swBindArray(SWarray type, void *buffer); -void swDrawArrays(SWdraw mode, int offset, int count); +SWAPI void swGenTextures(int count, uint32_t *textures); +SWAPI void swDeleteTextures(int count, uint32_t *textures); -void swGenTextures(int count, uint32_t* textures); -void swDeleteTextures(int count, uint32_t* textures); - -void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void* data); -void swTexParameteri(int param, int value); -void swBindTexture(uint32_t id); +SWAPI void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void *data); +SWAPI void swTexParameteri(int param, int value); +SWAPI void swBindTexture(uint32_t id); #endif // RLSW_H -/* === RLSW Implementation === */ - -#ifdef RLSW_IMPL +/*********************************************************************************** +* +* RLSW IMPLEMENTATION +* +************************************************************************************/ +#define RLSW_IMPLEMENTATION +#if defined(RLSW_IMPLEMENTATION) #include #include -#include +#include // Required for: floorf(), fabsf() -/* === Defines and Macros === */ +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define SW_PI 3.14159265358979323846f +#define SW_DEG2RAD (SW_PI/180.0f) +#define SW_RAD2DEG (180.0f/SW_PI) -#define SW_PI 3.14159265358979323846f -#define SW_DEG2RAD (SW_PI/180.0f) -#define SW_RAD2DEG (180.0f/SW_PI) +#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS/8) +#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS/8) -#define SW_COLOR_PIXEL_SIZE (SW_COLOR_BUFFER_BITS / 8) -#define SW_DEPTH_PIXEL_SIZE (SW_DEPTH_BUFFER_BITS / 8) - -#define SW_STATE_CHECK(flags) ((RLSW.stateFlags & (flags)) == (flags)) +#define SW_STATE_CHECK(flags) ((RLSW.stateFlags & (flags)) == (flags)) #define SW_STATE_SCISSOR_TEST (1 << 0) #define SW_STATE_TEXTURE_2D (1 << 1) @@ -558,8 +627,11 @@ void swBindTexture(uint32_t id); #define SW_STATE_CULL_FACE (1 << 3) #define SW_STATE_BLEND (1 << 4) -/* === Internal Structs === */ - +//---------------------------------------------------------------------------------- +// Module Types and Structures Definition +//---------------------------------------------------------------------------------- +// Pixel data format type +// NOTE: Enum aligned with raylib PixelFormat typedef enum { SW_PIXELFORMAT_UNKNOWN = 0, SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE, // 8 bit per pixel (no alpha) @@ -578,49 +650,45 @@ typedef enum { } sw_pixelformat_t; typedef void (*sw_factor_f)( - float* SW_RESTRICT factor, - const float* SW_RESTRICT src, - const float* SW_RESTRICT dst + float *SW_RESTRICT factor, + const float *SW_RESTRICT src, + const float *SW_RESTRICT dst ); typedef float sw_matrix_t[4*4]; typedef uint16_t sw_half_t; typedef struct { + float position[4]; // Position coordinates + float texcoord[2]; // Texture coordinates + float color[4]; // Color value (RGBA) - float position[4]; // Position coordinates - float texcoord[2]; // Texture coordinates - float color[4]; // Color - - float homogeneous[4]; // Homogeneous coordinates - float screen[2]; // Screen coordinates - + float homogeneous[4]; // Homogeneous coordinates + float screen[2]; // Screen coordinates } sw_vertex_t; typedef struct { - // Dirty hack for copied data - // TODO: Rework copied image handling - + // TODO: Rework copied image handling union { - const void* cptr; //< NOTE: Is used for all data reads - void* ptr; //< WARNING: Should only be used to allocate and free data + const void *cptr; // NOTE: Is used for all data reads + void *ptr; // WARNING: Should only be used to allocate and free data } pixels; - int width, height; //< Dimensions of the texture - int wMinus1, hMinus1; //< Dimensions minus one - sw_pixelformat_t format; //< Pixel format (internal representation) + int width, height; // Dimensions of the texture + int wMinus1, hMinus1; // Dimensions minus one + sw_pixelformat_t format; // Pixel format (internal representation) - SWfilter minFilter; //< Minification filter - SWfilter magFilter; //< Magnification filter + SWfilter minFilter; // Minification filter + SWfilter magFilter; // Magnification filter - SWwrap sWrap; //< texcoord.x wrap mode - SWwrap tWrap; //< texcoord.y wrap mode + SWwrap sWrap; // texcoord.x wrap mode + SWwrap tWrap; // texcoord.y wrap mode - float tx; //< Texel width - float ty; //< Texel height + float tx; // Texel width + float ty; // Texel height - bool copy; //< Flag indicating whether memory has been allocated + bool copy; // Flag indicating whether memory has been allocated } sw_texture_t; @@ -633,28 +701,27 @@ typedef struct { } sw_framebuffer_t; typedef struct { - - sw_framebuffer_t framebuffer; + sw_framebuffer_t framebuffer; // Main framebuffer float clearColor[4]; // Color used to clear the screen float clearDepth; // Depth value used to clear the screen - int vpCenter[2]; // Represents the center of the viewport - int vpHalfSize[2]; // Represents the half dimensions of the viewport - int vpSize[2]; // Represents the dimensions of the viewport (minus one) - int vpMin[2]; // Represents the minimum renderable point of the viewport (top-left) - int vpMax[2]; // Represents the maximum renderable point of the viewport (bottom-right) + int vpCenter[2]; // Viewport center + int vpHalfSize[2]; // Viewport half dimensions + int vpSize[2]; // Viewport dimensions (minus one) + int vpMin[2]; // Viewport minimum renderable point (top-left) + int vpMax[2]; // Viewport maximum renderable point (bottom-right) - int scMin[2]; // Represents the minimum renderable point of the scissor rect (top-left) - int scMax[2]; // Represents the maximum renderable point of the scissor rect (bottom-right) - float scClipMin[2]; // Represents the minimum renderable point of the scissor rect in clip space - float scClipMax[2]; // Represents the maximum renderable point of the scissor rect in clip space + int scMin[2]; // Scissor rectangle minimum renderable point (top-left) + int scMax[2]; // Scissor rectangle maximum renderable point (bottom-right) + float scClipMin[2]; // Scissor rectangle minimum renderable point in clip space + float scClipMax[2]; // Scissor rectangle maximum renderable point in clip space - uint32_t currentTexture; + uint32_t currentTexture; // Current active texture id struct { - float* positions; - float* texcoords; - uint8_t* colors; + float *positions; + float *texcoords; + uint8_t *colors; } array; struct { @@ -678,7 +745,7 @@ typedef struct { uint32_t stackModelviewCounter; // Counter for matrix stack operations uint32_t stackTextureCounter; // Counter for matrix stack operations SWmatrix currentMatrixMode; // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION) - sw_matrix_t* currentMatrix; // Pointer to the currently used matrix according to the mode + sw_matrix_t *currentMatrix; // Pointer to the currently used matrix according to the mode sw_matrix_t matMVP; // Model view projection matrix, calculated and used internally bool isDirtyMVP; // Indicates if the MVP matrix should be rebuilt @@ -691,22 +758,24 @@ typedef struct { SWface cullFace; // Faces to cull SWerrcode errCode; // Last error code - sw_texture_t* loadedTextures; + sw_texture_t *loadedTextures; int loadedTextureCount; - uint32_t* freeTextureIds; + uint32_t *freeTextureIds; int freeTextureIdCount; uint32_t stateFlags; } sw_context_t; -/* === Global Data === */ - +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- static sw_context_t RLSW = { 0 }; -/* === Helper Functions === */ - +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- static inline void sw_matrix_id(sw_matrix_t dst) { dst[0] = 1, dst[1] = 0, dst[2] = 0, dst[3] = 0; @@ -715,32 +784,32 @@ static inline void sw_matrix_id(sw_matrix_t dst) dst[12] = 0, dst[13] = 0, dst[14] = 0, dst[15] = 1; } -static inline void sw_matrix_mul_rst(float* SW_RESTRICT dst, const float* SW_RESTRICT left, const float* SW_RESTRICT right) +static inline void sw_matrix_mul_rst(float *SW_RESTRICT dst, const float *SW_RESTRICT left, const float *SW_RESTRICT right) { - float l00 = left[0], l01 = left[1], l02 = left[2], l03 = left[3]; + float l00 = left[0], l01 = left[1], l02 = left[2], l03 = left[3]; float l10 = left[4], l11 = left[5], l12 = left[6], l13 = left[7]; float l20 = left[8], l21 = left[9], l22 = left[10], l23 = left[11]; float l30 = left[12], l31 = left[13], l32 = left[14], l33 = left[15]; - dst[0] = l00 * right[0] + l01 * right[4] + l02 * right[8] + l03 * right[12]; - dst[4] = l10 * right[0] + l11 * right[4] + l12 * right[8] + l13 * right[12]; - dst[8] = l20 * right[0] + l21 * right[4] + l22 * right[8] + l23 * right[12]; - dst[12] = l30 * right[0] + l31 * right[4] + l32 * right[8] + l33 * right[12]; + dst[0] = l00*right[0] + l01*right[4] + l02*right[8] + l03*right[12]; + dst[4] = l10*right[0] + l11*right[4] + l12*right[8] + l13*right[12]; + dst[8] = l20*right[0] + l21*right[4] + l22*right[8] + l23*right[12]; + dst[12] = l30*right[0] + l31*right[4] + l32*right[8] + l33*right[12]; - dst[1] = l00 * right[1] + l01 * right[5] + l02 * right[9] + l03 * right[13]; - dst[5] = l10 * right[1] + l11 * right[5] + l12 * right[9] + l13 * right[13]; - dst[9] = l20 * right[1] + l21 * right[5] + l22 * right[9] + l23 * right[13]; - dst[13] = l30 * right[1] + l31 * right[5] + l32 * right[9] + l33 * right[13]; + dst[1] = l00*right[1] + l01*right[5] + l02*right[9] + l03*right[13]; + dst[5] = l10*right[1] + l11*right[5] + l12*right[9] + l13*right[13]; + dst[9] = l20*right[1] + l21*right[5] + l22*right[9] + l23*right[13]; + dst[13] = l30*right[1] + l31*right[5] + l32*right[9] + l33*right[13]; - dst[2] = l00 * right[2] + l01 * right[6] + l02 * right[10] + l03 * right[14]; - dst[6] = l10 * right[2] + l11 * right[6] + l12 * right[10] + l13 * right[14]; - dst[10] = l20 * right[2] + l21 * right[6] + l22 * right[10] + l23 * right[14]; - dst[14] = l30 * right[2] + l31 * right[6] + l32 * right[10] + l33 * right[14]; + dst[2] = l00*right[2] + l01*right[6] + l02*right[10] + l03*right[14]; + dst[6] = l10*right[2] + l11*right[6] + l12*right[10] + l13*right[14]; + dst[10] = l20*right[2] + l21*right[6] + l22*right[10] + l23*right[14]; + dst[14] = l30*right[2] + l31*right[6] + l32*right[10] + l33*right[14]; - dst[3] = l00 * right[3] + l01 * right[7] + l02 * right[11] + l03 * right[15]; - dst[7] = l10 * right[3] + l11 * right[7] + l12 * right[11] + l13 * right[15]; - dst[11] = l20 * right[3] + l21 * right[7] + l22 * right[11] + l23 * right[15]; - dst[15] = l30 * right[3] + l31 * right[7] + l32 * right[11] + l33 * right[15]; + dst[3] = l00*right[3] + l01*right[7] + l02*right[11] + l03*right[15]; + dst[7] = l10*right[3] + l11*right[7] + l12*right[11] + l13*right[15]; + dst[11] = l20*right[3] + l21*right[7] + l22*right[11] + l23*right[15]; + dst[15] = l30*right[3] + l31*right[7] + l32*right[11] + l33*right[15]; } static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right) @@ -749,9 +818,7 @@ static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_mul_rst(result, left, right); - for (int i = 0; i < 16; i++) { - dst[i] = result[i]; - } + for (int i = 0; i < 16; i++) dst[i] = result[i]; } static inline float sw_saturate(float x) @@ -761,15 +828,11 @@ static inline float sw_saturate(float x) // Check if x < 0.0f // If sign bit is set (MSB), x is negative - if ((fb.u & 0x80000000) != 0) { - return 0.0f; - } + if ((fb.u & 0x80000000) != 0) return 0.0f; // Check if x > 1.0f // Works for positive floats: IEEE 754 ordering matches integer ordering - if (fb.u > 0x3F800000) { - return 1.0f; - } + if (fb.u > 0x3F800000) return 1.0f; // x is in [0.0f, 1.0f] return x; @@ -777,7 +840,7 @@ static inline float sw_saturate(float x) static inline float sw_fract(float x) { - return x - floorf(x); + return (x - floorf(x)); } static inline int sw_clampi(int v, int min, int max) @@ -788,68 +851,68 @@ static inline int sw_clampi(int v, int min, int max) } static inline void sw_lerp_vertex_PTCH( - sw_vertex_t* SW_RESTRICT out, - const sw_vertex_t* SW_RESTRICT a, - const sw_vertex_t* SW_RESTRICT b, + sw_vertex_t *SW_RESTRICT out, + const sw_vertex_t *SW_RESTRICT a, + const sw_vertex_t *SW_RESTRICT b, float t) { const float tInv = 1.0f - t; // Position interpolation (4 components) - out->position[0] = a->position[0] * tInv + b->position[0] * t; - out->position[1] = a->position[1] * tInv + b->position[1] * t; - out->position[2] = a->position[2] * tInv + b->position[2] * t; - out->position[3] = a->position[3] * tInv + b->position[3] * t; + out->position[0] = a->position[0]*tInv + b->position[0]*t; + out->position[1] = a->position[1]*tInv + b->position[1]*t; + out->position[2] = a->position[2]*tInv + b->position[2]*t; + out->position[3] = a->position[3]*tInv + b->position[3]*t; // Texture coordinate interpolation (2 components) - out->texcoord[0] = a->texcoord[0] * tInv + b->texcoord[0] * t; - out->texcoord[1] = a->texcoord[1] * tInv + b->texcoord[1] * t; + out->texcoord[0] = a->texcoord[0]*tInv + b->texcoord[0]*t; + out->texcoord[1] = a->texcoord[1]*tInv + b->texcoord[1]*t; // Color interpolation (4 components) - out->color[0] = a->color[0] * tInv + b->color[0] * t; - out->color[1] = a->color[1] * tInv + b->color[1] * t; - out->color[2] = a->color[2] * tInv + b->color[2] * t; - out->color[3] = a->color[3] * tInv + b->color[3] * t; + out->color[0] = a->color[0]*tInv + b->color[0]*t; + out->color[1] = a->color[1]*tInv + b->color[1]*t; + out->color[2] = a->color[2]*tInv + b->color[2]*t; + out->color[3] = a->color[3]*tInv + b->color[3]*t; // Homogeneous coordinate interpolation (4 components) - out->homogeneous[0] = a->homogeneous[0] * tInv + b->homogeneous[0] * t; - out->homogeneous[1] = a->homogeneous[1] * tInv + b->homogeneous[1] * t; - out->homogeneous[2] = a->homogeneous[2] * tInv + b->homogeneous[2] * t; - out->homogeneous[3] = a->homogeneous[3] * tInv + b->homogeneous[3] * t; + out->homogeneous[0] = a->homogeneous[0]*tInv + b->homogeneous[0]*t; + out->homogeneous[1] = a->homogeneous[1]*tInv + b->homogeneous[1]*t; + out->homogeneous[2] = a->homogeneous[2]*tInv + b->homogeneous[2]*t; + out->homogeneous[3] = a->homogeneous[3]*tInv + b->homogeneous[3]*t; } static inline void sw_get_vertex_grad_PTCH( - sw_vertex_t* SW_RESTRICT out, - const sw_vertex_t* SW_RESTRICT a, - const sw_vertex_t* SW_RESTRICT b, + sw_vertex_t *SW_RESTRICT out, + const sw_vertex_t *SW_RESTRICT a, + const sw_vertex_t *SW_RESTRICT b, float scale) { // Calculate gradients for Position - out->position[0] = (b->position[0] - a->position[0]) * scale; - out->position[1] = (b->position[1] - a->position[1]) * scale; - out->position[2] = (b->position[2] - a->position[2]) * scale; - out->position[3] = (b->position[3] - a->position[3]) * scale; + out->position[0] = (b->position[0] - a->position[0])*scale; + out->position[1] = (b->position[1] - a->position[1])*scale; + out->position[2] = (b->position[2] - a->position[2])*scale; + out->position[3] = (b->position[3] - a->position[3])*scale; // Calculate gradients for Texture coordinates - out->texcoord[0] = (b->texcoord[0] - a->texcoord[0]) * scale; - out->texcoord[1] = (b->texcoord[1] - a->texcoord[1]) * scale; + out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale; + out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale; // Calculate gradients for Color - out->color[0] = (b->color[0] - a->color[0]) * scale; - out->color[1] = (b->color[1] - a->color[1]) * scale; - out->color[2] = (b->color[2] - a->color[2]) * scale; - out->color[3] = (b->color[3] - a->color[3]) * scale; + out->color[0] = (b->color[0] - a->color[0])*scale; + out->color[1] = (b->color[1] - a->color[1])*scale; + out->color[2] = (b->color[2] - a->color[2])*scale; + out->color[3] = (b->color[3] - a->color[3])*scale; // Calculate gradients for Homogeneous coordinates - out->homogeneous[0] = (b->homogeneous[0] - a->homogeneous[0]) * scale; - out->homogeneous[1] = (b->homogeneous[1] - a->homogeneous[1]) * scale; - out->homogeneous[2] = (b->homogeneous[2] - a->homogeneous[2]) * scale; - out->homogeneous[3] = (b->homogeneous[3] - a->homogeneous[3]) * scale; + out->homogeneous[0] = (b->homogeneous[0] - a->homogeneous[0])*scale; + out->homogeneous[1] = (b->homogeneous[1] - a->homogeneous[1])*scale; + out->homogeneous[2] = (b->homogeneous[2] - a->homogeneous[2])*scale; + out->homogeneous[3] = (b->homogeneous[3] - a->homogeneous[3])*scale; } static inline void sw_add_vertex_grad_PTCH( - sw_vertex_t* SW_RESTRICT out, - const sw_vertex_t* SW_RESTRICT gradients) + sw_vertex_t *SW_RESTRICT out, + const sw_vertex_t *SW_RESTRICT gradients) { // Add gradients to Position out->position[0] += gradients->position[0]; @@ -874,8 +937,7 @@ static inline void sw_add_vertex_grad_PTCH( out->homogeneous[3] += gradients->homogeneous[3]; } -/* === Half Floating Point === */ - +// Half floating point management functions static inline uint32_t sw_f16_to_f32_ui(uint16_t h) { uint32_t s = (uint32_t)(h & 0x8000) << 16; @@ -885,20 +947,19 @@ static inline uint32_t sw_f16_to_f32_ui(uint16_t h) int32_t r = (em + (112 << 10)) << 13; // denormal: flush to zero - r = (em < (1 << 10)) ? 0 : r; + r = (em < (1 << 10))? 0 : r; // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 - r += (em >= (31 << 10)) ? (112 << 23) : 0; + r += (em >= (31 << 10))? (112 << 23) : 0; return s | r; } static inline float sw_f16_to_f32(sw_half_t y) { - union { float f; uint32_t i; } v = { - .i = sw_f16_to_f32_ui(y) - }; + union { float f; uint32_t i; } v = { .i = sw_f16_to_f32_ui(y) }; + return v.f; } @@ -907,17 +968,17 @@ static inline uint16_t sw_f16_from_f32_ui(uint32_t ui) int32_t s = (ui >> 16) & 0x8000; int32_t em = ui & 0x7fffffff; - // bias exponent and round to nearest; 112 is relative exponent bias (127-15) + // Bias exponent and round to nearest; 112 is relative exponent bias (127-15) int32_t h = (em - (112 << 23) + (1 << 12)) >> 13; - // underflow: flush to zero; 113 encodes exponent -14 - h = (em < (113 << 23)) ? 0 : h; + // Underflow: flush to zero; 113 encodes exponent -14 + h = (em < (113 << 23))? 0 : h; - // overflow: infinity; 143 encodes exponent 16 - h = (em >= (143 << 23)) ? 0x7c00 : h; + // Overflow: infinity; 143 encodes exponent 16 + h = (em >= (143 << 23))? 0x7c00 : h; // NaN; note that we convert all types of NaN to qNaN - h = (em > (255 << 23)) ? 0x7e00 : h; + h = (em > (255 << 23))? 0x7e00 : h; return (uint16_t)(s | h); } @@ -929,39 +990,39 @@ static inline sw_half_t sw_f16_from_f32(float i) return sw_f16_from_f32_ui(v.i); } -/* === Framebuffer Part === */ - +// Framebuffer management functions static inline bool sw_framebuffer_load(int w, int h) { - int size = w * h; + int size = w*h; - RLSW.framebuffer.color = SW_MALLOC(SW_COLOR_PIXEL_SIZE * size); + RLSW.framebuffer.color = SW_MALLOC(SW_COLOR_PIXEL_SIZE*size); if (RLSW.framebuffer.color == NULL) return false; - RLSW.framebuffer.depth = SW_MALLOC(SW_DEPTH_PIXEL_SIZE * size); + RLSW.framebuffer.depth = SW_MALLOC(SW_DEPTH_PIXEL_SIZE*size); if (RLSW.framebuffer.depth == NULL) return false; RLSW.framebuffer.width = w; RLSW.framebuffer.height = h; - RLSW.framebuffer.allocSz = w * h; + RLSW.framebuffer.allocSz = size; return true; } static inline bool sw_framebuffer_resize(int w, int h) { - int newSize = w * h; + int newSize = w*h; - if (newSize <= RLSW.framebuffer.allocSz) { + if (newSize <= RLSW.framebuffer.allocSz) + { RLSW.framebuffer.width = w; RLSW.framebuffer.height = h; return true; } - void* newColor = SW_REALLOC(RLSW.framebuffer.color, newSize); + void *newColor = SW_REALLOC(RLSW.framebuffer.color, newSize); if (newColor == NULL) return false; - void* newDepth = SW_REALLOC(RLSW.framebuffer.depth, newSize); + void *newDepth = SW_REALLOC(RLSW.framebuffer.depth, newSize); if (newDepth == NULL) return false; RLSW.framebuffer.color = newColor; @@ -974,199 +1035,201 @@ static inline bool sw_framebuffer_resize(int w, int h) return true; } -static inline void* sw_framebuffer_get_color_addr(const void* ptr, uint32_t offset) +static inline void *sw_framebuffer_get_color_addr(const void *ptr, uint32_t offset) { - return (uint8_t*)ptr + offset * SW_COLOR_PIXEL_SIZE; + return (uint8_t *)ptr + offset*SW_COLOR_PIXEL_SIZE; } -static inline void sw_framebuffer_inc_color_addr(void** ptr) +static inline void sw_framebuffer_inc_color_addr(void **ptr) { - *ptr = (void*)(((uint8_t*)*ptr) + SW_COLOR_PIXEL_SIZE); + *ptr = (void *)(((uint8_t *)*ptr) + SW_COLOR_PIXEL_SIZE); } -static inline void sw_framebuffer_inc_const_color_addr(const void** ptr) +static inline void sw_framebuffer_inc_const_color_addr(const void **ptr) { - *ptr = (const void*)(((const uint8_t*)*ptr) + SW_COLOR_PIXEL_SIZE); + *ptr = (const void *)(((const uint8_t *)*ptr) + SW_COLOR_PIXEL_SIZE); } -static inline void* sw_framebuffer_get_depth_addr(const void* ptr, uint32_t offset) +static inline void *sw_framebuffer_get_depth_addr(const void *ptr, uint32_t offset) { - return (uint8_t*)ptr + offset * SW_DEPTH_PIXEL_SIZE; + return (uint8_t *)ptr + offset*SW_DEPTH_PIXEL_SIZE; } -static inline void sw_framebuffer_inc_depth_addr(void** ptr) +static inline void sw_framebuffer_inc_depth_addr(void* *ptr) { - *ptr = (void*)(((uint8_t*)*ptr) + SW_DEPTH_PIXEL_SIZE); + *ptr = (void*)(((uint8_t *)*ptr) + SW_DEPTH_PIXEL_SIZE); } -#if (SW_COLOR_BUFFER_BITS == 8) // RGB - 332 - -static inline void sw_framebuffer_read_color(float dst[4], const void* src) +#if (SW_COLOR_BUFFER_BITS == 8) // RGB - (3:3:2) +static inline void sw_framebuffer_read_color(float dst[4], const void *src) { - uint8_t pixel = ((uint8_t*)src)[0]; + uint8_t pixel = ((uint8_t *)src)[0]; - dst[0] = ((pixel >> 5) & 0x07) * (1.0f / 7.0f); - dst[1] = ((pixel >> 2) & 0x07) * (1.0f / 7.0f); - dst[2] = (pixel & 0x03) * (1.0f / 3.0f); + dst[0] = ((pixel >> 5) & 0x07)*(1.0f/7.0f); + dst[1] = ((pixel >> 2) & 0x07)*(1.0f/7.0f); + dst[2] = (pixel & 0x03)*(1.0f/3.0f); dst[3] = 1.0f; } -static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void* src) +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src) { - uint8_t pixel = ((const uint8_t*)src)[0]; + uint8_t pixel = ((const uint8_t *)src)[0]; uint8_t r = (pixel >> 5) & 0x07; uint8_t g = (pixel >> 2) & 0x07; uint8_t b = pixel & 0x03; - dst[0] = (r * 255 + 3) / 7; - dst[1] = (g * 255 + 3) / 7; - dst[2] = (b * 255 + 1) / 3; + dst[0] = (r*255 + 3)/7; + dst[1] = (g*255 + 3)/7; + dst[2] = (b*255 + 1)/3; dst[3] = 255; } -static inline void sw_framebuffer_write_color(void* dst, const float color[3]) +static inline void sw_framebuffer_write_color(void *dst, const float color[3]) { - uint8_t r = ((uint8_t)(color[0] * UINT8_MAX) >> 5) & 0x07; - uint8_t g = ((uint8_t)(color[1] * UINT8_MAX) >> 5) & 0x07; - uint8_t b = ((uint8_t)(color[2] * UINT8_MAX) >> 6) & 0x03; + uint8_t r = ((uint8_t)(color[0]*UINT8_MAX) >> 5) & 0x07; + uint8_t g = ((uint8_t)(color[1]*UINT8_MAX) >> 5) & 0x07; + uint8_t b = ((uint8_t)(color[2]*UINT8_MAX) >> 6) & 0x03; - ((uint8_t*)dst)[0] = (r << 5) | (g << 2) | b; + ((uint8_t *)dst)[0] = (r << 5) | (g << 2) | b; } -static inline void sw_framebuffer_fill_color(void* ptr, int size, const float color[3]) +static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[3]) { - uint8_t r8 = (uint8_t)(color[0] * 7.0f + 0.5f); - uint8_t g8 = (uint8_t)(color[1] * 7.0f + 0.5f); - uint8_t b8 = (uint8_t)(color[2] * 3.0f + 0.5f); + uint8_t r8 = (uint8_t)(color[0]*7.0f + 0.5f); + uint8_t g8 = (uint8_t)(color[1]*7.0f + 0.5f); + uint8_t b8 = (uint8_t)(color[2]*3.0f + 0.5f); uint8_t packedColor = ((r8 & 0x07) << 5) | ((g8 & 0x07) << 2) | (b8 & 0x03); - uint8_t* p = (uint8_t*)ptr; + uint8_t *p = (uint8_t *)ptr; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - uint8_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; - for (int xCount = 0; xCount < wScissor; xCount++) { - *curPtr++ = packedColor; - } + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = packedColor; } } - else { - for (int i = 0; i < size; i++) { - *p++ = packedColor; - } + else + { + for (int i = 0; i < size; i++) *p++ = packedColor; } } -#elif (SW_COLOR_BUFFER_BITS == 16) // RGB - 565 +#elif (SW_COLOR_BUFFER_BITS == 16) // RGB - (5:6:5) -static inline void sw_framebuffer_read_color(float dst[4], const void* src) +static inline void sw_framebuffer_read_color(float dst[4], const void *src) { - uint16_t pixel = ((uint16_t*)src)[0]; + uint16_t pixel = ((uint16_t *)src)[0]; - dst[0] = ((pixel >> 11) & 0x1F) * (1.0f / 31.0f); - dst[1] = ((pixel >> 5) & 0x3F) * (1.0f / 63.0f); - dst[2] = (pixel & 0x1F) * (1.0f / 31.0f); + dst[0] = ((pixel >> 11) & 0x1F)*(1.0f/31.0f); + dst[1] = ((pixel >> 5) & 0x3F)*(1.0f/63.0f); + dst[2] = (pixel & 0x1F)*(1.0f/31.0f); dst[3] = 1.0f; } -static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void* src) +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src) { - uint16_t pixel = ((const uint16_t*)src)[0]; + uint16_t pixel = ((const uint16_t *)src)[0]; uint8_t r = (pixel >> 11) & 0x1F; uint8_t g = (pixel >> 5) & 0x3F; uint8_t b = pixel & 0x1F; - dst[0] = (r * 255 + 15) / 31; - dst[1] = (g * 255 + 31) / 63; - dst[2] = (b * 255 + 15) / 31; + dst[0] = (r*255 + 15)/31; + dst[1] = (g*255 + 31)/63; + dst[2] = (b*255 + 15)/31; dst[3] = 255; } -static inline void sw_framebuffer_write_color(void* dst, const float color[3]) +static inline void sw_framebuffer_write_color(void *dst, const float color[3]) { - uint8_t r = (uint8_t)(color[0] * 31.0f + 0.5f) & 0x1F; - uint8_t g = (uint8_t)(color[1] * 63.0f + 0.5f) & 0x3F; - uint8_t b = (uint8_t)(color[2] * 31.0f + 0.5f) & 0x1F; + uint8_t r = (uint8_t)(color[0]*31.0f + 0.5f) & 0x1F; + uint8_t g = (uint8_t)(color[1]*63.0f + 0.5f) & 0x3F; + uint8_t b = (uint8_t)(color[2]*31.0f + 0.5f) & 0x1F; - ((uint16_t*)dst)[0] = (r << 11) | (g << 5) | b; + ((uint16_t *)dst)[0] = (r << 11) | (g << 5) | b; } -static inline void sw_framebuffer_fill_color(void* ptr, int size, const float color[3]) +static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[3]) { - uint16_t r16 = (uint16_t)(color[0] * 31.0f + 0.5f); - uint16_t g_16 = (uint16_t)(color[1] * 63.0f + 0.5f); - uint16_t b_16 = (uint16_t)(color[2] * 31.0f + 0.5f); + uint16_t r16 = (uint16_t)(color[0]*31.0f + 0.5f); + uint16_t g_16 = (uint16_t)(color[1]*63.0f + 0.5f); + uint16_t b_16 = (uint16_t)(color[2]*31.0f + 0.5f); uint16_t packedColor = ((r16 & 0x1F) << 11) | ((g_16 & 0x3F) << 5) | (b_16 & 0x1F); - uint16_t* p = (uint16_t*)ptr; + uint16_t *p = (uint16_t *)ptr; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - uint16_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; - for (int xCount = 0; xCount < wScissor; xCount++) { - *curPtr++ = packedColor; - } + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint16_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = packedColor; } } - else { - for (int i = 0; i < size; i++) { - *p++ = packedColor; - } + else + { + for (int i = 0; i < size; i++) *p++ = packedColor; } } -#elif (SW_COLOR_BUFFER_BITS == 24) // RGB - 888 +#elif (SW_COLOR_BUFFER_BITS == 24) // RGB - (8:8:8) -static inline void sw_framebuffer_read_color(float dst[4], const void* src) +static inline void sw_framebuffer_read_color(float dst[4], const void *src) { - dst[0] = ((uint8_t*)src)[0] * (1.0f / 255.0f); - dst[1] = ((uint8_t*)src)[1] * (1.0f / 255.0f); - dst[2] = ((uint8_t*)src)[2] * (1.0f / 255.0f); + dst[0] = ((uint8_t *)src)[0]*(1.0f/255.0f); + dst[1] = ((uint8_t *)src)[1]*(1.0f/255.0f); + dst[2] = ((uint8_t *)src)[2]*(1.0f/255.0f); dst[3] = 1.0f; } -static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void* src) +static inline void sw_framebuffer_read_color8(uint8_t dst[4], const void *src) { - dst[0] = ((uint8_t*)src)[0]; - dst[1] = ((uint8_t*)src)[1]; - dst[2] = ((uint8_t*)src)[2]; + dst[0] = ((uint8_t *)src)[0]; + dst[1] = ((uint8_t *)src)[1]; + dst[2] = ((uint8_t *)src)[2]; dst[3] = 255; } -static inline void sw_framebuffer_write_color(void* dst, const float color[3]) +static inline void sw_framebuffer_write_color(void *dst, const float color[3]) { - ((uint8_t*)dst)[0] = (uint8_t)(color[0] * UINT8_MAX); - ((uint8_t*)dst)[1] = (uint8_t)(color[1] * UINT8_MAX); - ((uint8_t*)dst)[2] = (uint8_t)(color[2] * UINT8_MAX); + ((uint8_t *)dst)[0] = (uint8_t)(color[0]*UINT8_MAX); + ((uint8_t *)dst)[1] = (uint8_t)(color[1]*UINT8_MAX); + ((uint8_t *)dst)[2] = (uint8_t)(color[2]*UINT8_MAX); } -static inline void sw_framebuffer_fill_color(void* ptr, int size, const float color[3]) +static inline void sw_framebuffer_fill_color(void *ptr, int size, const float color[3]) { - uint8_t r = (uint8_t)(color[0] * 255.0f); - uint8_t g = (uint8_t)(color[1] * 255.0f); - uint8_t b = (uint8_t)(color[2] * 255.0f); + uint8_t r = (uint8_t)(color[0]*255.0f); + uint8_t g = (uint8_t)(color[1]*255.0f); + uint8_t b = (uint8_t)(color[2]*255.0f); - uint8_t* p = (uint8_t*)ptr; + uint8_t *p = (uint8_t *)ptr; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - uint8_t* curPtr = p + 3 * (y * RLSW.framebuffer.width + RLSW.scMin[0]); - for (int xCount = 0; xCount < wScissor; xCount++) { + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]); + for (int xCount = 0; xCount < wScissor; xCount++) + { *curPtr++ = r; *curPtr++ = g; *curPtr++ = b; } } } - else { - for (int i = 0; i < size; i++) { + else + { + for (int i = 0; i < size; i++) + { *p++ = r; *p++ = g; *p++ = b; @@ -1178,112 +1241,115 @@ static inline void sw_framebuffer_fill_color(void* ptr, int size, const float co #if (SW_DEPTH_BUFFER_BITS == 8) -static inline float sw_framebuffer_read_depth(const void* src) +static inline float sw_framebuffer_read_depth(const void *src) { - return (float)((uint8_t*)src)[0] * (1.0f / UINT8_MAX); + return (float)((uint8_t *)src)[0]*(1.0f/UINT8_MAX); } -static inline void sw_framebuffer_write_depth(void* dst, float depth) +static inline void sw_framebuffer_write_depth(void *dst, float depth) { - ((uint8_t*)dst)[0] = (uint8_t)(depth * UINT8_MAX); + ((uint8_t *)dst)[0] = (uint8_t)(depth*UINT8_MAX); } -static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) +static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value) { - uint8_t d8 = (uint8_t)(value * UINT8_MAX); - uint8_t* p = (uint8_t*)ptr; + uint8_t d8 = (uint8_t)(value*UINT8_MAX); + uint8_t *p = (uint8_t *)ptr; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - uint8_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; - for (int xCount = 0; xCount < wScissor; xCount++) { - *curPtr++ = d8; - } + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = d8; } } - else { - for (int i = 0; i < size; i++) { - *p++ = d8; - } + else + { + for (int i = 0; i < size; i++) *p++ = d8; } } #elif (SW_DEPTH_BUFFER_BITS == 16) -static inline float sw_framebuffer_read_depth(const void* src) +static inline float sw_framebuffer_read_depth(const void *src) { - return (float)((uint16_t*)src)[0] * (1.0f / UINT16_MAX); + return (float)((uint16_t *)src)[0]*(1.0f/UINT16_MAX); } -static inline void sw_framebuffer_write_depth(void* dst, float depth) +static inline void sw_framebuffer_write_depth(void *dst, float depth) { - ((uint16_t*)dst)[0] = (uint16_t)(depth * UINT16_MAX); + ((uint16_t *)dst)[0] = (uint16_t)(depth*UINT16_MAX); } -static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) +static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value) { - uint16_t d16 = (uint16_t)(value * UINT16_MAX); - uint16_t* p = (uint16_t*)ptr; + uint16_t d16 = (uint16_t)(value*UINT16_MAX); + uint16_t *p = (uint16_t *)ptr; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - uint16_t* curPtr = p + y * RLSW.framebuffer.width + RLSW.scMin[0]; - for (int xCount = 0; xCount < wScissor; xCount++) { - *curPtr++ = d16; - } + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint16_t *curPtr = p + y*RLSW.framebuffer.width + RLSW.scMin[0]; + for (int xCount = 0; xCount < wScissor; xCount++) *curPtr++ = d16; } } - else { - for (int i = 0; i < size; i++) { - *p++ = d16; - } + else + { + for (int i = 0; i < size; i++) *p++ = d16; } } #elif (SW_DEPTH_BUFFER_BITS == 24) -static inline float sw_framebuffer_read_depth(const void* src) +static inline float sw_framebuffer_read_depth(const void *src) { - uint32_t depth24 = (((uint8_t*)src)[0] << 16) | - (((uint8_t*)src)[1] << 8) | - ((uint8_t*)src)[2]; + uint32_t depth24 = (((uint8_t *)src)[0] << 16) | + (((uint8_t *)src)[1] << 8) | + ((uint8_t *)src)[2]; - return depth24 / (float)0xFFFFFF; + return depth24/(float)0xFFFFFF; } -static inline void sw_framebuffer_write_depth(void* dst, float depth) +static inline void sw_framebuffer_write_depth(void *dst, float depth) { - uint32_t depth24 = (uint32_t)(depth * 0xFFFFFF); + uint32_t depth24 = (uint32_t)(depth*0xFFFFFF); - ((uint8_t*)dst)[0] = (depth24 >> 16) & 0xFF; - ((uint8_t*)dst)[1] = (depth24 >> 8) & 0xFF; - ((uint8_t*)dst)[2] = depth24 & 0xFF; + ((uint8_t *)dst)[0] = (depth24 >> 16) & 0xFF; + ((uint8_t *)dst)[1] = (depth24 >> 8) & 0xFF; + ((uint8_t *)dst)[2] = depth24 & 0xFF; } -static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) +static inline void sw_framebuffer_fill_depth(void *ptr, int size, float value) { - uint32_t d32 = (uint32_t)(value * UINT32_MAX); + uint32_t d32 = (uint32_t)(value*UINT32_MAX); uint8_t d_byte0 = (uint8_t)((d32 >> 16) & 0xFF); uint8_t d_byte1 = (uint8_t)((d32 >> 8) & 0xFF); uint8_t d_byte2 = (uint8_t)(d32 & 0xFF); - uint8_t* p = (uint8_t*)ptr; + uint8_t *p = (uint8_t *)ptr; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - uint8_t* curPtr = p + 3 * (y * RLSW.framebuffer.width + RLSW.scMin[0]); - for (int xCount = 0; xCount < wScissor; xCount++) { + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + uint8_t *curPtr = p + 3*(y*RLSW.framebuffer.width + RLSW.scMin[0]); + for (int xCount = 0; xCount < wScissor; xCount++) + { *curPtr++ = d_byte0; *curPtr++ = d_byte1; *curPtr++ = d_byte2; } } } - else { - for (int i = 0; i < size; i++) { + else + { + for (int i = 0; i < size; i++) + { *p++ = d_byte0; *p++ = d_byte1; *p++ = d_byte2; @@ -1293,97 +1359,99 @@ static inline void sw_framebuffer_fill_depth(void* ptr, int size, float value) #endif // SW_DEPTH_BUFFER_BITS -static inline void sw_framebuffer_fill(void* colorPtr, void* depthPtr, int size, float color[4], float depth_value) +static inline void sw_framebuffer_fill(void *colorPtr, void *depthPtr, int size, float color[4], float depth_value) { #if (SW_COLOR_BUFFER_BITS == 8) // Calculate and pack 3:3:2 color // Scale color components to the max value for each bit depth and round - uint8_t r8 = (uint8_t)(color[0] * 7.0f + 0.5f); - uint8_t g8 = (uint8_t)(color[1] * 7.0f + 0.5f); - uint8_t b8 = (uint8_t)(color[2] * 3.0f + 0.5f); + uint8_t r8 = (uint8_t)(color[0]*7.0f + 0.5f); + uint8_t g8 = (uint8_t)(color[1]*7.0f + 0.5f); + uint8_t b8 = (uint8_t)(color[2]*3.0f + 0.5f); // Pack the components into a single byte uint8_t packedColor = ((r8 & 0x07) << 5) | ((g8 & 0x07) << 2) | (b8 & 0x03); - uint8_t* cptr = (uint8_t*)colorPtr; + uint8_t *cptr = (uint8_t *)colorPtr; #elif (SW_COLOR_BUFFER_BITS == 16) // Calculate and pack 5:6:5 color // Scale color components to the max value for each bit depth and round - uint16_t r16 = (uint16_t)(color[0] * 31.0f + 0.5f); - uint16_t g16 = (uint16_t)(color[1] * 63.0f + 0.5f); - uint16_t b16 = (uint16_t)(color[2] * 31.0f + 0.5f); + uint16_t r16 = (uint16_t)(color[0]*31.0f + 0.5f); + uint16_t g16 = (uint16_t)(color[1]*63.0f + 0.5f); + uint16_t b16 = (uint16_t)(color[2]*31.0f + 0.5f); // Pack the components into a 16-bit value uint16_t packedColor = ((r16 & 0x1F) << 11) | ((g16 & 0x3F) << 5) | (b16 & 0x1F); - uint16_t* cptr = (uint16_t*)colorPtr; + uint16_t *cptr = (uint16_t *)colorPtr; #elif (SW_COLOR_BUFFER_BITS == 24) // Calculate 8:8:8 color components - uint8_t r24 = (uint8_t)(color[0] * 255.0f); - uint8_t g24 = (uint8_t)(color[1] * 255.0f); - uint8_t b24 = (uint8_t)(color[2] * 255.0f); - uint8_t* cptr = (uint8_t*)colorPtr; + uint8_t r24 = (uint8_t)(color[0]*255.0f); + uint8_t g24 = (uint8_t)(color[1]*255.0f); + uint8_t b24 = (uint8_t)(color[2]*255.0f); + uint8_t *cptr = (uint8_t *)colorPtr; #endif #if (SW_DEPTH_BUFFER_BITS == 8) // Calculate 8-bit depth - uint8_t d8 = (uint8_t)(depth_value * UINT8_MAX); - uint8_t* dptr = (uint8_t*)depthPtr; + uint8_t d8 = (uint8_t)(depth_value*UINT8_MAX); + uint8_t *dptr = (uint8_t *)depthPtr; #elif (SW_DEPTH_BUFFER_BITS == 16) // Calculate 16-bit depth - uint16_t d16 = (uint16_t)(depth_value * UINT16_MAX); - uint16_t* dptr = (uint16_t*)depthPtr; + uint16_t d16 = (uint16_t)(depth_value*UINT16_MAX); + uint16_t *dptr = (uint16_t *)depthPtr; #elif (SW_DEPTH_BUFFER_BITS == 24) // Calculate 24-bit depth and pre-calculate bytes - uint32_t d32 = (uint32_t)(depth_value * UINT32_MAX); + uint32_t d32 = (uint32_t)(depth_value*UINT32_MAX); uint8_t dByte0 = (uint8_t)((d32 >> 16) & 0xFF); uint8_t dByte1 = (uint8_t)((d32 >> 8) & 0xFF); uint8_t dByte2 = (uint8_t)(d32 & 0xFF); - uint8_t* dptr = (uint8_t*)depthPtr; + uint8_t *dptr = (uint8_t *)depthPtr; #endif - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { int wScissor = RLSW.scMax[0] - RLSW.scMin[0] + 1; - for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) { - int rowStartIdx = y * RLSW.framebuffer.width + RLSW.scMin[0]; + for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++) + { + int rowStartIdx = y*RLSW.framebuffer.width + RLSW.scMin[0]; // Calculate starting pointers for the current row within the scissor rectangle -# if (SW_COLOR_BUFFER_BITS == 8) - uint8_t* curCPtr = cptr + rowStartIdx; -# elif (SW_COLOR_BUFFER_BITS == 16) - uint16_t* curCPtr = cptr + rowStartIdx; -# elif (SW_COLOR_BUFFER_BITS == 24) - uint8_t* curCPtr = cptr + 3 * rowStartIdx; -# endif + #if (SW_COLOR_BUFFER_BITS == 8) + uint8_t *curCPtr = cptr + rowStartIdx; + #elif (SW_COLOR_BUFFER_BITS == 16) + uint16_t *curCPtr = cptr + rowStartIdx; + #elif (SW_COLOR_BUFFER_BITS == 24) + uint8_t *curCPtr = cptr + 3*rowStartIdx; + #endif -# if (SW_DEPTH_BUFFER_BITS == 8) - uint8_t* curDPtr = dptr + rowStartIdx; -# elif (SW_DEPTH_BUFFER_BITS == 16) - uint16_t* curDPtr = dptr + rowStartIdx; -# elif (SW_DEPTH_BUFFER_BITS == 24) - uint8_t* curDPtr = dptr + 3 * rowStartIdx; -# endif + #if (SW_DEPTH_BUFFER_BITS == 8) + uint8_t *curDPtr = dptr + rowStartIdx; + #elif (SW_DEPTH_BUFFER_BITS == 16) + uint16_t *curDPtr = dptr + rowStartIdx; + #elif (SW_DEPTH_BUFFER_BITS == 24) + uint8_t *curDPtr = dptr + 3*rowStartIdx; + #endif // Fill the current row within the scissor rectangle for (int xCount = 0; xCount < wScissor; xCount++) { // Write color -# if (SW_COLOR_BUFFER_BITS == 8) + #if (SW_COLOR_BUFFER_BITS == 8) *curCPtr++ = packedColor; -# elif (SW_COLOR_BUFFER_BITS == 16) + #elif (SW_COLOR_BUFFER_BITS == 16) *curCPtr++ = packedColor; -# elif (SW_COLOR_BUFFER_BITS == 24) + #elif (SW_COLOR_BUFFER_BITS == 24) *curCPtr++ = r24; *curCPtr++ = g24; *curCPtr++ = b24; -# endif + #endif // Write depth -# if (SW_DEPTH_BUFFER_BITS == 8) + #if (SW_DEPTH_BUFFER_BITS == 8) *curDPtr++ = d8; -# elif (SW_DEPTH_BUFFER_BITS == 16) + #elif (SW_DEPTH_BUFFER_BITS == 16) *curDPtr++ = d16; -# elif (SW_DEPTH_BUFFER_BITS == 24) + #elif (SW_DEPTH_BUFFER_BITS == 24) *curDPtr++ = dByte0; *curDPtr++ = dByte1; *curDPtr++ = dByte2; -# endif + #endif } } return; @@ -1392,33 +1460,33 @@ static inline void sw_framebuffer_fill(void* colorPtr, void* depthPtr, int size, for (int i = 0; i < size; i++) { // Write color -# if (SW_COLOR_BUFFER_BITS == 8) + #if (SW_COLOR_BUFFER_BITS == 8) *cptr++ = packedColor; -# elif (SW_COLOR_BUFFER_BITS == 16) + #elif (SW_COLOR_BUFFER_BITS == 16) *cptr++ = packedColor; -# elif (SW_COLOR_BUFFER_BITS == 24) + #elif (SW_COLOR_BUFFER_BITS == 24) *cptr++ = r24; *cptr++ = g24; *cptr++ = b24; -# endif + #endif // Write depth -# if (SW_DEPTH_BUFFER_BITS == 8) + #if (SW_DEPTH_BUFFER_BITS == 8) *dptr++ = d8; -# elif (SW_DEPTH_BUFFER_BITS == 16) + #elif (SW_DEPTH_BUFFER_BITS == 16) *dptr++ = d16; -# elif (SW_DEPTH_BUFFER_BITS == 24) + #elif (SW_DEPTH_BUFFER_BITS == 24) *dptr++ = dByte0; *dptr++ = dByte1; *dptr++ = dByte2; -# endif + #endif } } #define DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(name, DST_PTR_T) \ -static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T* dst) \ +static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \ { \ - const void* src = RLSW.framebuffer.color; \ + const void *src = RLSW.framebuffer.color; \ \ for (int iy = y; iy < h; iy++) { \ for (int ix = x; ix < w; ix++) { \ @@ -1426,9 +1494,9 @@ static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST sw_framebuffer_read_color8(color, src); \ #define DEFINE_FRAMEBUFFER_COPY_F32_BEGIN(name, DST_PTR_T) \ -static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T* dst) \ +static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \ { \ - const void* src = RLSW.framebuffer.color; \ + const void *src = RLSW.framebuffer.color; \ \ for (int iy = y; iy < h; iy++) { \ for (int ix = x; ix < w; ix++) { \ @@ -1444,7 +1512,7 @@ static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(GRAYSCALE, uint8_t) { // NTSC grayscale conversion: Y = 0.299R + 0.587G + 0.114B - uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); *dst++ = gray; } DEFINE_FRAMEBUFFER_COPY_END() @@ -1452,7 +1520,7 @@ DEFINE_FRAMEBUFFER_COPY_END() DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(GRAYALPHA, uint8_t) { // Convert RGB to grayscale using NTSC formula - uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); dst[0] = gray; dst[1] = color[3]; // alpha @@ -1464,9 +1532,9 @@ DEFINE_FRAMEBUFFER_COPY_END() DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R5G6B5, uint16_t) { // Convert 8-bit RGB to 5:6:5 format - uint8_t r5 = (color[0] * 31 + 127) / 255; - uint8_t g6 = (color[1] * 63 + 127) / 255; - uint8_t b5 = (color[2] * 31 + 127) / 255; + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g6 = (color[1]*63 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; #if SW_GL_FRAMEBUFFER_COPY_BGRA uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; @@ -1496,9 +1564,9 @@ DEFINE_FRAMEBUFFER_COPY_END() DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R5G5B5A1, uint16_t) { - uint8_t r5 = (color[0] * 31 + 127) / 255; - uint8_t g5 = (color[1] * 31 + 127) / 255; - uint8_t b5 = (color[2] * 31 + 127) / 255; + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g5 = (color[1]*31 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; uint8_t a1 = color[3] >= 128 ? 1 : 0; #if SW_GL_FRAMEBUFFER_COPY_BGRA @@ -1513,10 +1581,10 @@ DEFINE_FRAMEBUFFER_COPY_END() DEFINE_FRAMEBUFFER_COPY_U32_BEGIN(R4G4B4A4, uint16_t) { - uint8_t r4 = (color[0] * 15 + 127) / 255; - uint8_t g4 = (color[1] * 15 + 127) / 255; - uint8_t b4 = (color[2] * 15 + 127) / 255; - uint8_t a4 = (color[3] * 15 + 127) / 255; + uint8_t r4 = (color[0]*15 + 127)/255; + uint8_t g4 = (color[1]*15 + 127)/255; + uint8_t b4 = (color[2]*15 + 127)/255; + uint8_t a4 = (color[3]*15 + 127)/255; #if SW_GL_FRAMEBUFFER_COPY_BGRA uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; @@ -1629,22 +1697,22 @@ DEFINE_FRAMEBUFFER_COPY_END() static inline void sw_framebuffer_blit_to_##name( \ int xDst, int yDst, int wDst, int hDst, \ int xSrc, int ySrc, int wSrc, int hSrc, \ - DST_PTR_T* dst) \ + DST_PTR_T *dst) \ { \ - const uint8_t* srcBase = RLSW.framebuffer.color; \ + const uint8_t *srcBase = RLSW.framebuffer.color; \ int fbWidth = RLSW.framebuffer.width; \ \ - uint32_t xScale = ((uint32_t)wSrc << 16) / (uint32_t)wDst; \ - uint32_t yScale = ((uint32_t)hSrc << 16) / (uint32_t)hDst; \ + uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \ + uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \ \ for (int dy = 0; dy < hDst; dy++) { \ - uint32_t yFix = ((uint32_t)ySrc << 16) + dy * yScale; \ + uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale; \ int sy = yFix >> 16; \ \ for (int dx = 0; dx < wDst; dx++) { \ - uint32_t xFix = dx * xScale; \ + uint32_t xFix = dx*xScale; \ int sx = xFix >> 16; \ - const void* srcPtr = sw_framebuffer_get_color_addr(srcBase, sy * fbWidth + sx); \ + const void *srcPtr = sw_framebuffer_get_color_addr(srcBase, sy*fbWidth + sx); \ uint8_t color[4]; \ sw_framebuffer_read_color8(color, srcPtr); \ @@ -1652,22 +1720,22 @@ static inline void sw_framebuffer_blit_to_##name( static inline void sw_framebuffer_blit_to_##name( \ int xDst, int yDst, int wDst, int hDst, \ int xSrc, int ySrc, int wSrc, int hSrc, \ - DST_PTR_T* dst) \ + DST_PTR_T *dst) \ { \ - const uint8_t* srcBase = RLSW.framebuffer.color; \ + const uint8_t *srcBase = RLSW.framebuffer.color; \ int fbWidth = RLSW.framebuffer.width; \ \ - uint32_t xScale = ((uint32_t)wSrc << 16) / (uint32_t)wDst; \ - uint32_t yScale = ((uint32_t)hSrc << 16) / (uint32_t)hDst; \ + uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst; \ + uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst; \ \ for (int dy = 0; dy < hDst; dy++) { \ - uint32_t yFix = ((uint32_t)ySrc << 16) + dy * yScale; \ + uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale; \ int sy = yFix >> 16; \ \ for (int dx = 0; dx < wDst; dx++) { \ - uint32_t xFix = dx * xScale; \ + uint32_t xFix = dx*xScale; \ int sx = xFix >> 16; \ - const void* srcPtr = sw_framebuffer_get_color_addr(srcBase, sy * fbWidth + sx); \ + const void *srcPtr = sw_framebuffer_get_color_addr(srcBase, sy*fbWidth + sx); \ float color[4]; \ sw_framebuffer_read_color(color, srcPtr); \ @@ -1678,14 +1746,14 @@ static inline void sw_framebuffer_blit_to_##name( DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(GRAYSCALE, uint8_t) { - uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); *dst++ = gray; } DEFINE_FRAMEBUFFER_BLIT_END() DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(GRAYALPHA, uint8_t) { - uint8_t gray = (uint8_t)((color[0] * 299 + color[1] * 587 + color[2] * 114 + 500) / 1000); + uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000); dst[0] = gray; dst[1] = color[3]; // alpha @@ -1696,9 +1764,9 @@ DEFINE_FRAMEBUFFER_BLIT_END() DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R5G6B5, uint16_t) { - uint8_t r5 = (color[0] * 31 + 127) / 255; - uint8_t g6 = (color[1] * 63 + 127) / 255; - uint8_t b5 = (color[2] * 31 + 127) / 255; + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g6 = (color[1]*63 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; #if SW_GL_FRAMEBUFFER_COPY_BGRA uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5; @@ -1728,9 +1796,9 @@ DEFINE_FRAMEBUFFER_BLIT_END() DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R5G5B5A1, uint16_t) { - uint8_t r5 = (color[0] * 31 + 127) / 255; - uint8_t g5 = (color[1] * 31 + 127) / 255; - uint8_t b5 = (color[2] * 31 + 127) / 255; + uint8_t r5 = (color[0]*31 + 127)/255; + uint8_t g5 = (color[1]*31 + 127)/255; + uint8_t b5 = (color[2]*31 + 127)/255; uint8_t a1 = color[3] >= 128 ? 1 : 0; #if SW_GL_FRAMEBUFFER_COPY_BGRA @@ -1745,10 +1813,10 @@ DEFINE_FRAMEBUFFER_BLIT_END() DEFINE_FRAMEBUFFER_BLIT_U32_BEGIN(R4G4B4A4, uint16_t) { - uint8_t r4 = (color[0] * 15 + 127) / 255; - uint8_t g4 = (color[1] * 15 + 127) / 255; - uint8_t b4 = (color[2] * 15 + 127) / 255; - uint8_t a4 = (color[3] * 15 + 127) / 255; + uint8_t r4 = (color[0]*15 + 127)/255; + uint8_t g4 = (color[1]*15 + 127)/255; + uint8_t b4 = (color[2]*15 + 127)/255; + uint8_t a4 = (color[3]*15 + 127)/255; #if SW_GL_FRAMEBUFFER_COPY_BGRA uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4; @@ -1857,49 +1925,52 @@ DEFINE_FRAMEBUFFER_BLIT_F32_BEGIN(R16G16B16A16, sw_half_t) } DEFINE_FRAMEBUFFER_BLIT_END() -/* === Pixel Format Part === */ +// Pixel format management functions static inline int sw_get_pixel_format(SWformat format, SWtype type) { int channels = 0; int bitsPerChannel = 8; // Default: 8 bits per channel - + // Determine the number of channels (format) - switch (format) { - case SW_LUMINANCE: channels = 1; break; - case SW_LUMINANCE_ALPHA: channels = 2; break; - case SW_RGB: channels = 3; break; - case SW_RGBA: channels = 4; break; - default: - return SW_PIXELFORMAT_UNKNOWN; + switch (format) + { + case SW_LUMINANCE: channels = 1; break; + case SW_LUMINANCE_ALPHA: channels = 2; break; + case SW_RGB: channels = 3; break; + case SW_RGBA: channels = 4; break; + default: return SW_PIXELFORMAT_UNKNOWN; } // Determine the depth of each channel (type) - switch (type) { - case SW_UNSIGNED_BYTE: bitsPerChannel = 8; break; - case SW_BYTE: bitsPerChannel = 8; break; - case SW_UNSIGNED_SHORT: bitsPerChannel = 16; break; - case SW_SHORT: bitsPerChannel = 16; break; - case SW_UNSIGNED_INT: bitsPerChannel = 32; break; - case SW_INT: bitsPerChannel = 32; break; - case SW_FLOAT: bitsPerChannel = 32; break; - default: - return SW_PIXELFORMAT_UNKNOWN; + switch (type) + { + case SW_UNSIGNED_BYTE: bitsPerChannel = 8; break; + case SW_BYTE: bitsPerChannel = 8; break; + case SW_UNSIGNED_SHORT: bitsPerChannel = 16; break; + case SW_SHORT: bitsPerChannel = 16; break; + case SW_UNSIGNED_INT: bitsPerChannel = 32; break; + case SW_INT: bitsPerChannel = 32; break; + case SW_FLOAT: bitsPerChannel = 32; break; + default: return SW_PIXELFORMAT_UNKNOWN; } // Map the format and type to the correct internal format - if (bitsPerChannel == 8) { + if (bitsPerChannel == 8) + { if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; if (channels == 2) return SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8; if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; } - else if (bitsPerChannel == 16) { + else if (bitsPerChannel == 16) + { if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R16; if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16; if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16; } - else if (bitsPerChannel == 32) { + else if (bitsPerChannel == 32) + { if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R32; if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32; if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; @@ -1912,29 +1983,30 @@ int sw_get_pixel_bytes(sw_pixelformat_t format) { int bpp = 0; - switch (format) { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 1; break; - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 2; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 4; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 3; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 4; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 4*3; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 4*4; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 2; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 2*3; break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 2*4; break; - default: break; + switch (format) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 1; break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 2; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 4*3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 4*4; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 2; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 2*3; break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 2*4; break; + default: break; } return bpp; } -static inline void sw_get_pixel_grayscale(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_grayscale(float *color, const void *pixels, uint32_t offset) { - float gray = (float)((uint8_t*)pixels)[offset] * (1.0f / 255); + float gray = (float)((uint8_t *)pixels)[offset]*(1.0f/255); color[0] = gray; color[1] = gray; @@ -1942,9 +2014,9 @@ static inline void sw_get_pixel_grayscale(float* color, const void* pixels, uint color[3] = 1.0f; } -static inline void sw_get_pixel_red_16(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_red_16(float *color, const void *pixels, uint32_t offset) { - float value = sw_f16_to_f32(((sw_half_t*)pixels)[offset]); + float value = sw_f16_to_f32(((sw_half_t *)pixels)[offset]); color[0] = value; color[1] = value; @@ -1952,9 +2024,9 @@ static inline void sw_get_pixel_red_16(float* color, const void* pixels, uint32_ color[3] = 1.0f; } -static inline void sw_get_pixel_red_32(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_red_32(float *color, const void *pixels, uint32_t offset) { - float value = ((float*)pixels)[offset]; + float value = ((float *)pixels)[offset]; color[0] = value; color[1] = value; @@ -1962,37 +2034,37 @@ static inline void sw_get_pixel_red_32(float* color, const void* pixels, uint32_ color[3] = 1.0f; } -static inline void sw_get_pixel_grayscale_alpha(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_grayscale_alpha(float *color, const void *pixels, uint32_t offset) { - const uint8_t* pixelData = (const uint8_t*)pixels + 2 * offset; + const uint8_t *pixelData = (const uint8_t *)pixels + 2*offset; - color[0] = color[1] = color[2] = (float)pixelData[0] * (1.0f / 255); - color[3] = (float)pixelData[1] * (1.0f / 255); + color[0] = color[1] = color[2] = (float)pixelData[0]*(1.0f/255); + color[3] = (float)pixelData[1]*(1.0f/255); } -static inline void sw_get_pixel_rgb_565(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgb_565(float *color, const void *pixels, uint32_t offset) { - uint16_t pixel = ((uint16_t*)pixels)[offset]; + uint16_t pixel = ((uint16_t *)pixels)[offset]; - color[0] = (float)((pixel & 0xF800) >> 11) / 31; - color[1] = (float)((pixel & 0x7E0) >> 5) / 63; - color[2] = (float)(pixel & 0x1F) / 31; + color[0] = (float)((pixel & 0xF800) >> 11)/31; + color[1] = (float)((pixel & 0x7E0) >> 5)/63; + color[2] = (float)(pixel & 0x1F)/31; color[3] = 1.0f; } -static inline void sw_get_pixel_rgb_888(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgb_888(float *color, const void *pixels, uint32_t offset) { - const uint8_t* pixel = (const uint8_t*)pixels + 3 * offset; + const uint8_t *pixel = (const uint8_t *)pixels + 3*offset; - color[0] = (float)pixel[0] * (1.0f / 255); - color[1] = (float)pixel[1] * (1.0f / 255); - color[2] = (float)pixel[2] * (1.0f / 255); + color[0] = (float)pixel[0]*(1.0f/255); + color[1] = (float)pixel[1]*(1.0f/255); + color[2] = (float)pixel[2]*(1.0f/255); color[3] = 1.0f; } -static inline void sw_get_pixel_rgb_161616(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgb_161616(float *color, const void *pixels, uint32_t offset) { - const sw_half_t *pixel = (sw_half_t*)pixels + 3 * offset; + const sw_half_t *pixel = (sw_half_t *)pixels + 3*offset; color[0] = sw_f16_to_f32(pixel[0]); color[1] = sw_f16_to_f32(pixel[1]); @@ -2000,9 +2072,9 @@ static inline void sw_get_pixel_rgb_161616(float* color, const void* pixels, uin color[3] = 1.0f; } -static inline void sw_get_pixel_rgb_323232(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgb_323232(float *color, const void *pixels, uint32_t offset) { - const float *pixel = (float*)pixels + 3 * offset; + const float *pixel = (float *)pixels + 3*offset; color[0] = pixel[0]; color[1] = pixel[1]; @@ -2010,39 +2082,39 @@ static inline void sw_get_pixel_rgb_323232(float* color, const void* pixels, uin color[3] = 1.0f; } -static inline void sw_get_pixel_rgba_5551(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgba_5551(float *color, const void *pixels, uint32_t offset) { - uint16_t pixel = ((uint16_t*)pixels)[offset]; + uint16_t pixel = ((uint16_t *)pixels)[offset]; - color[0] = (float)((pixel & 0xF800) >> 11) / 31; - color[1] = (float)((pixel & 0x7C0) >> 6) / 31; - color[2] = (float)((pixel & 0x3E) >> 1) / 31; + color[0] = (float)((pixel & 0xF800) >> 11)/31; + color[1] = (float)((pixel & 0x7C0) >> 6)/31; + color[2] = (float)((pixel & 0x3E) >> 1)/31; color[3] = (float)(pixel & 0x1); } -static inline void sw_get_pixel_rgba_4444(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgba_4444(float *color, const void *pixels, uint32_t offset) { - uint16_t pixel = ((uint16_t*)pixels)[offset]; + uint16_t pixel = ((uint16_t *)pixels)[offset]; - color[0] = (float)((pixel & 0xF000) >> 12) / 15; - color[1] = (float)((pixel & 0xF00) >> 8) / 15; - color[2] = (float)((pixel & 0xF0) >> 4) / 15; - color[3] = (float)(pixel & 0xF) / 15; + color[0] = (float)((pixel & 0xF000) >> 12)/15; + color[1] = (float)((pixel & 0xF00) >> 8)/15; + color[2] = (float)((pixel & 0xF0) >> 4)/15; + color[3] = (float)(pixel & 0xF)/15; } -static inline void sw_get_pixel_rgba_8888(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgba_8888(float *color, const void *pixels, uint32_t offset) { - const uint8_t *pixel = (uint8_t*)pixels + 4 * offset; + const uint8_t *pixel = (uint8_t *)pixels + 4*offset; - color[0] = (float)pixel[0] * (1.0f / 255); - color[1] = (float)pixel[1] * (1.0f / 255); - color[2] = (float)pixel[2] * (1.0f / 255); - color[3] = (float)pixel[3] * (1.0f / 255); + color[0] = (float)pixel[0]*(1.0f/255); + color[1] = (float)pixel[1]*(1.0f/255); + color[2] = (float)pixel[2]*(1.0f/255); + color[3] = (float)pixel[3]*(1.0f/255); } -static inline void sw_get_pixel_rgba_16161616(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgba_16161616(float *color, const void *pixels, uint32_t offset) { - const sw_half_t *pixel = (sw_half_t*)pixels + 4 * offset; + const sw_half_t *pixel = (sw_half_t *)pixels + 4*offset; color[0] = sw_f16_to_f32(pixel[0]); color[1] = sw_f16_to_f32(pixel[1]); @@ -2050,9 +2122,9 @@ static inline void sw_get_pixel_rgba_16161616(float* color, const void* pixels, color[3] = sw_f16_to_f32(pixel[3]); } -static inline void sw_get_pixel_rgba_32323232(float* color, const void* pixels, uint32_t offset) +static inline void sw_get_pixel_rgba_32323232(float *color, const void *pixels, uint32_t offset) { - const float *pixel = (float*)pixels + 4 * offset; + const float *pixel = (float *)pixels + 4*offset; color[0] = pixel[0]; color[1] = pixel[1]; @@ -2060,72 +2132,47 @@ static inline void sw_get_pixel_rgba_32323232(float* color, const void* pixels, color[3] = pixel[3]; } -static inline void sw_get_pixel(float* color, const void* pixels, uint32_t offset, sw_pixelformat_t format) +static inline void sw_get_pixel(float *color, const void *pixels, uint32_t offset, sw_pixelformat_t format) { - switch (format) { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: - sw_get_pixel_grayscale(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - sw_get_pixel_grayscale_alpha(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - sw_get_pixel_rgb_565(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: - sw_get_pixel_rgb_888(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - sw_get_pixel_rgba_5551(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: - sw_get_pixel_rgba_4444(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: - sw_get_pixel_rgba_8888(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - sw_get_pixel_red_32(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - sw_get_pixel_rgb_323232(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - sw_get_pixel_rgba_32323232(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - sw_get_pixel_red_16(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - sw_get_pixel_rgb_161616(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - sw_get_pixel_rgba_16161616(color, pixels, offset); - break; - case SW_PIXELFORMAT_UNKNOWN: - break; + switch (format) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_get_pixel_grayscale(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_get_pixel_grayscale_alpha(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_get_pixel_rgb_565(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_get_pixel_rgb_888(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_get_pixel_rgba_5551(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_get_pixel_rgba_4444(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_get_pixel_rgba_8888(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: sw_get_pixel_red_32(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: sw_get_pixel_rgb_323232(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: sw_get_pixel_rgba_32323232(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: sw_get_pixel_red_16(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: sw_get_pixel_rgb_161616(color, pixels, offset); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: sw_get_pixel_rgba_16161616(color, pixels, offset); break; + case SW_PIXELFORMAT_UNKNOWN: break; + default: break; } } -/* === Texture Sampling Part === */ +// Texture sampling functionality -static inline void sw_texture_sample_nearest(float* color, const sw_texture_t* tex, float u, float v) +static inline void sw_texture_sample_nearest(float *color, const sw_texture_t *tex, float u, float v) { - u = (tex->sWrap == SW_REPEAT) ? sw_fract(u) : sw_saturate(u); - v = (tex->tWrap == SW_REPEAT) ? sw_fract(v) : sw_saturate(v); + u = (tex->sWrap == SW_REPEAT)? sw_fract(u) : sw_saturate(u); + v = (tex->tWrap == SW_REPEAT)? sw_fract(v) : sw_saturate(v); - int x = u * tex->width, y = v * tex->height; + int x = u*tex->width, y = v*tex->height; - sw_get_pixel(color, tex->pixels.cptr, y * tex->width + x, tex->format); + sw_get_pixel(color, tex->pixels.cptr, y*tex->width + x, tex->format); } -static inline void sw_texture_sample_linear(float* color, const sw_texture_t* tex, float u, float v) +static inline void sw_texture_sample_linear(float *color, const sw_texture_t *tex, float u, float v) { - // REVIEW: With a bit more cleverness we could clearly reduce the - // number of operations here, but for now it works fine. + // TODO: REVIEW: With a bit more cleverness we could clearly reduce the + // number of operations here, but for now it works fine. - float xf = u * tex->width - 0.5f; - float yf = v * tex->height - 0.5f; + float xf = u*tex->width - 0.5f; + float yf = v*tex->height - 0.5f; int x0 = (int)floorf(xf); int y0 = (int)floorf(yf); @@ -2136,148 +2183,149 @@ static inline void sw_texture_sample_linear(float* color, const sw_texture_t* te int x1 = x0 + 1; int y1 = y0 + 1; - if (tex->sWrap == SW_CLAMP) { - x0 = x0 > tex->wMinus1 ? tex->wMinus1 : x0; - x1 = x1 > tex->wMinus1 ? tex->wMinus1 : x1; + if (tex->sWrap == SW_CLAMP) + { + x0 = (x0 > tex->wMinus1)? tex->wMinus1 : x0; + x1 = (x1 > tex->wMinus1)? tex->wMinus1 : x1; } - else { - x0 = (x0 % tex->width + tex->width) % tex->width; - x1 = (x1 % tex->width + tex->width) % tex->width; + else + { + x0 = (x0%tex->width + tex->width)%tex->width; + x1 = (x1%tex->width + tex->width)%tex->width; } - if (tex->tWrap == SW_CLAMP) { - y0 = y0 > tex->hMinus1 ? tex->hMinus1 : y0; - y1 = y1 > tex->hMinus1 ? tex->hMinus1 : y1; + if (tex->tWrap == SW_CLAMP) + { + y0 = (y0 > tex->hMinus1)? tex->hMinus1 : y0; + y1 = (y1 > tex->hMinus1)? tex->hMinus1 : y1; } - else { - y0 = (y0 % tex->height + tex->height) % tex->height; - y1 = (y1 % tex->height + tex->height) % tex->height; + else + { + y0 = (y0%tex->height + tex->height)%tex->height; + y1 = (y1%tex->height + tex->height)%tex->height; } float c00[4], c10[4], c01[4], c11[4]; - sw_get_pixel(c00, tex->pixels.cptr, y0 * tex->width + x0, tex->format); - sw_get_pixel(c10, tex->pixels.cptr, y0 * tex->width + x1, tex->format); - sw_get_pixel(c01, tex->pixels.cptr, y1 * tex->width + x0, tex->format); - sw_get_pixel(c11, tex->pixels.cptr, y1 * tex->width + x1, tex->format); + sw_get_pixel(c00, tex->pixels.cptr, y0*tex->width + x0, tex->format); + sw_get_pixel(c10, tex->pixels.cptr, y0*tex->width + x1, tex->format); + sw_get_pixel(c01, tex->pixels.cptr, y1*tex->width + x0, tex->format); + sw_get_pixel(c11, tex->pixels.cptr, y1*tex->width + x1, tex->format); - for (int i = 0; i < 4; i++) { - float t = c00[i] + fx * (c10[i] - c00[i]); - float b = c01[i] + fx * (c11[i] - c01[i]); - color[i] = t + fy * (b - t); + for (int i = 0; i < 4; i++) + { + float t = c00[i] + fx*(c10[i] - c00[i]); + float b = c01[i] + fx*(c11[i] - c01[i]); + color[i] = t + fy*(b - t); } } -static inline void sw_texture_sample(float* color, const sw_texture_t* tex, float u, float v, - float duDx, float duDy, float dvDx, float dvDy) +static inline void sw_texture_sample(float *color, const sw_texture_t *tex, float u, float v, float duDx, float duDy, float dvDx, float dvDy) { // Previous method: There is no need to compute the square root - // because using the squared value, the comparison remains `L2 > 1.0f * 1.0f` - //float du = sqrtf(duDx * duDx + duDy * duDy); - //float dv = sqrtf(dvDx * dvDx + dvDy * dvDy); - //float L = (du > dv) ? du : dv; + // because using the squared value, the comparison remains `L2 > 1.0f*1.0f` + //float du = sqrtf(duDx*duDx + duDy*duDy); + //float dv = sqrtf(dvDx*dvDx + dvDy*dvDy); + //float L = (du > dv)? du : dv; // Calculate the derivatives for each axis - float du2 = duDx * duDx + duDy * duDy; - float dv2 = dvDx * dvDx + dvDy * dvDy; - float L2 = (du2 > dv2) ? du2 : dv2; + float du2 = duDx*duDx + duDy*duDy; + float dv2 = dvDx*dvDx + dvDy*dvDy; + float L2 = (du2 > dv2)? du2 : dv2; - SWfilter filter = (L2 > 1.0f) - ? tex->minFilter : tex->magFilter; + SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter; - switch (filter) { - case SW_NEAREST: - sw_texture_sample_nearest(color, tex, u, v); - break; - case SW_LINEAR: - sw_texture_sample_linear(color, tex, u, v); - break; + switch (filter) + { + case SW_NEAREST: sw_texture_sample_nearest(color, tex, u, v); break; + case SW_LINEAR: sw_texture_sample_linear(color, tex, u, v); break; + default: break; } } -/* === Color Blending Functions === */ +// Color Blending functionality -static inline void sw_factor_zero(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_zero(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = factor[1] = factor[2] = factor[3] = 0.0f; } -static inline void sw_factor_one(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_one(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = factor[1] = factor[2] = factor[3] = 1.0f; } -static inline void sw_factor_src_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3]; } -static inline void sw_factor_one_minus_src_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_one_minus_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { - factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; + factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1]; factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3]; } -static inline void sw_factor_src_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = factor[1] = factor[2] = factor[3] = src[3]; } -static inline void sw_factor_one_minus_src_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_one_minus_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { - float inv_alpha = 1.0f - src[3]; - factor[0] = factor[1] = factor[2] = factor[3] = inv_alpha; + float invAlpha = 1.0f - src[3]; + factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; } -static inline void sw_factor_dst_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = factor[1] = factor[2] = factor[3] = dst[3]; } -static inline void sw_factor_one_minus_dst_alpha(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_one_minus_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { - float inv_alpha = 1.0f - dst[3]; - factor[0] = factor[1] = factor[2] = factor[3] = inv_alpha; + float invAlpha = 1.0f - dst[3]; + factor[0] = factor[1] = factor[2] = factor[3] = invAlpha; } -static inline void sw_factor_dst_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3]; } -static inline void sw_factor_one_minus_dst_color(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_one_minus_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { - factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; + factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1]; factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3]; } -static inline void sw_factor_src_alpha_saturate(float* SW_RESTRICT factor, const float* SW_RESTRICT src, const float* SW_RESTRICT dst) +static inline void sw_factor_src_alpha_saturate(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst) { factor[0] = factor[1] = factor[2] = 1.0f; - factor[3] = (src[3] < 1.0f) ? src[3] : 1.0f; + factor[3] = (src[3] < 1.0f)? src[3] : 1.0f; } -static inline void sw_blend_colors(float* SW_RESTRICT dst/*[4]*/, const float* SW_RESTRICT src/*[4]*/) +static inline void sw_blend_colors(float *SW_RESTRICT dst/*[4]*/, const float *SW_RESTRICT src/*[4]*/) { float srcFactor[4], dstFactor[4]; RLSW.srcFactorFunc(srcFactor, src, dst); RLSW.dstFactorFunc(dstFactor, src, dst); - dst[0] = srcFactor[0] * src[0] + dstFactor[0] * dst[0]; - dst[1] = srcFactor[1] * src[1] + dstFactor[1] * dst[1]; - dst[2] = srcFactor[2] * src[2] + dstFactor[2] * dst[2]; - dst[3] = srcFactor[3] * src[3] + dstFactor[3] * dst[3]; + dst[0] = srcFactor[0]*src[0] + dstFactor[0]*dst[0]; + dst[1] = srcFactor[1]*src[1] + dstFactor[1]*dst[1]; + dst[2] = srcFactor[2]*src[2] + dstFactor[2]*dst[2]; + dst[3] = srcFactor[3]*src[3] + dstFactor[3]*dst[3]; } -/* === Projection Helper Functions === */ +// Projection helper functions static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4]) { - screen[0] = RLSW.vpCenter[0] + ndc[0] * RLSW.vpHalfSize[0]; - screen[1] = RLSW.vpCenter[1] - ndc[1] * RLSW.vpHalfSize[1]; + screen[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalfSize[0]; + screen[1] = RLSW.vpCenter[1] - ndc[1]*RLSW.vpHalfSize[1]; } -/* === Polygon Clipping Part === */ +// Polygon Clipping management #define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \ static inline int sw_clip_##name( \ @@ -2311,7 +2359,7 @@ static inline int sw_clip_##name( return outputCount; \ } -// Frustum clip functions +// Frustum cliping functions #define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON) #define IS_INSIDE_PLANE_X_POS(h) ((h)[0] <= (h)[3]) @@ -2321,13 +2369,13 @@ static inline int sw_clip_##name( #define IS_INSIDE_PLANE_Z_POS(h) ((h)[2] <= (h)[3]) #define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3]) -#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3]) / ((hCurr)[3] - (hPrev)[3])) -#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0]) / (((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0]))) -#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0]) / (((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0]))) -#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1]) / (((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1]))) -#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1]) / (((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1]))) -#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2]) / (((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2]))) -#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2]) / (((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2]))) +#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3])/((hCurr)[3] - (hPrev)[3])) +#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0])/(((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0]))) +#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0])/(((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0]))) +#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1])/(((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1]))) +#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1])/(((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1]))) +#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2])/(((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2]))) +#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2])/(((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2]))) DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W) DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS) @@ -2339,15 +2387,15 @@ DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG) // Scissor clip functions -#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0]) * (hPrev)[3] - (hPrev)[0]) / (((hCurr)[0] - (RLSW.scClipMin[0]) * (hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0]) * (hPrev)[3]))) -#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0]) * (hPrev)[3] - (hPrev)[0]) / (((hCurr)[0] - (RLSW.scClipMax[0]) * (hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0]) * (hPrev)[3]))) -#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1]) * (hPrev)[3] - (hPrev)[1]) / (((hCurr)[1] - (RLSW.scClipMin[1]) * (hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1]) * (hPrev)[3]))) -#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1]) * (hPrev)[3] - (hPrev)[1]) / (((hCurr)[1] - (RLSW.scClipMax[1]) * (hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1]) * (hPrev)[3]))) +#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMin[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMax[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMin[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1])*(hPrev)[3]))) +#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMax[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1])*(hPrev)[3]))) -#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0]) * (h)[3]) -#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0]) * (h)[3]) -#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1]) * (h)[3]) -#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1]) * (h)[3]) +#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0])*(h)[3]) +#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0])*(h)[3]) +#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1])*(h)[3]) +#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1])*(h)[3]) DEFINE_CLIP_FUNC(scissor_x_min, IS_INSIDE_SCISSOR_X_MIN, COMPUTE_T_SCISSOR_X_MIN) DEFINE_CLIP_FUNC(scissor_x_max, IS_INSIDE_SCISSOR_X_MAX, COMPUTE_T_SCISSOR_X_MAX) @@ -2356,7 +2404,7 @@ DEFINE_CLIP_FUNC(scissor_y_max, IS_INSIDE_SCISSOR_Y_MAX, COMPUTE_T_SCISSOR_Y_MAX // Main clip function -static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) +static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int *vertexCounter) { static sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES]; @@ -2365,13 +2413,12 @@ static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VE #define CLIP_AGAINST_PLANE(FUNC_CLIP) \ { \ n = FUNC_CLIP(tmp, polygon, n); \ - if (n < 3) { \ + if (n < 3) \ + { \ *vertexCounter = 0; \ return false; \ } \ - for (int i = 0; i < n; i++) { \ - polygon[i] = tmp[i]; \ - } \ + for (int i = 0; i < n; i++) polygon[i] = tmp[i]; \ } CLIP_AGAINST_PLANE(sw_clip_w); @@ -2382,7 +2429,8 @@ static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VE CLIP_AGAINST_PLANE(sw_clip_z_pos); CLIP_AGAINST_PLANE(sw_clip_z_neg); - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { CLIP_AGAINST_PLANE(sw_clip_scissor_x_min); CLIP_AGAINST_PLANE(sw_clip_scissor_x_max); CLIP_AGAINST_PLANE(sw_clip_scissor_y_min); @@ -2394,7 +2442,7 @@ static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VE return (n >= 3); } -/* === Triangle Rendering Part === */ +// Triangle rendering logic static inline bool sw_triangle_face_culling(void) { @@ -2407,9 +2455,9 @@ static inline bool sw_triangle_face_culling(void) // even with negative w values. // Preload homogeneous coordinates into local variables - const float* h0 = RLSW.vertexBuffer[0].homogeneous; - const float* h1 = RLSW.vertexBuffer[1].homogeneous; - const float* h2 = RLSW.vertexBuffer[2].homogeneous; + const float *h0 = RLSW.vertexBuffer[0].homogeneous; + const float *h1 = RLSW.vertexBuffer[1].homogeneous; + const float *h2 = RLSW.vertexBuffer[2].homogeneous; // Compute a value proportional to the signed area in the projected 2D plane, // calculated directly using homogeneous coordinates BEFORE division by w. @@ -2418,14 +2466,14 @@ static inline bool sw_triangle_face_culling(void) // space and its relationship to the projected 2D winding order, even with // negative w values. // The determinant formula used here is: - // h0.x * (h1.y * h2.w - h2.y * h1.w) + - // h1.x * (h2.y * h0.w - h0.y * h2.w) + - // h2.x * (h0.y * h1.w - h1.y * h0.w) + // h0.x*(h1.y*h2.w - h2.y*h1.w) + + // h1.x*(h2.y*h0.w - h0.y*h2.w) + + // h2.x*(h0.y*h1.w - h1.y*h0.w) const float hSgnArea = - h0[0] * (h1[1] * h2[3] - h2[1] * h1[3]) + - h1[0] * (h2[1] * h0[3] - h0[1] * h2[3]) + - h2[0] * (h0[1] * h1[3] - h1[1] * h0[3]); + h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + + h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + + h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); // Discard the triangle if its winding order (determined by the sign // of the homogeneous area/determinant) matches the culled direction. @@ -2446,18 +2494,19 @@ static inline bool sw_triangle_face_culling(void) static inline void sw_triangle_clip_and_project(void) { - sw_vertex_t* polygon = RLSW.vertexBuffer; - int* vertexCounter = &RLSW.vertexCounter; - - if (sw_polygon_clip(polygon, vertexCounter)) { + sw_vertex_t *polygon = RLSW.vertexBuffer; + int *vertexCounter = &RLSW.vertexCounter; + if (sw_polygon_clip(polygon, vertexCounter)) + { // Transformation to screen space and normalization - for (int i = 0; i < *vertexCounter; i++) { + for (int i = 0; i < *vertexCounter; i++) + { sw_vertex_t *v = &polygon[i]; // Calculation of the reciprocal of W for normalization // as well as perspective-correct attributes - const float invW = 1.0f / v->homogeneous[3]; + const float invW = 1.0f/v->homogeneous[3]; v->homogeneous[3] = invW; // Division of XYZ coordinates by weight @@ -2474,7 +2523,7 @@ static inline void sw_triangle_clip_and_project(void) v->color[1] *= invW; v->color[2] *= invW; v->color[3] *= invW; - + // Transformation to screen space sw_project_ndc_to_screen(v->screen, v->homogeneous); } @@ -2482,69 +2531,59 @@ static inline void sw_triangle_clip_and_project(void) } #define DEFINE_TRIANGLE_RASTER_SCANLINE(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ -static inline void FUNC_NAME(const sw_texture_t* tex, const sw_vertex_t* start, \ - const sw_vertex_t* end, float duDy, float dvDy) \ +static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start, \ + const sw_vertex_t *end, float duDy, float dvDy) \ { \ /* Convert and center the screen coordinates */ \ - \ int xStart = (int)(start->screen[0] + 0.5f); \ int xEnd = (int)(end->screen[0] + 0.5f); \ int y = (int)start->screen[1]; \ \ /* Compute the inverse horizontal distance along the X axis */ \ - \ float dx = end->screen[0] - start->screen[0]; \ if (fabsf(dx) < 1e-6f) return; \ - float dxRcp = 1.0f / dx; \ + float dxRcp = 1.0f/dx; \ \ /* Compute the interpolation steps along the X axis */ \ + float dzDx = (end->homogeneous[2] - start->homogeneous[2])*dxRcp; \ + float dwDx = (end->homogeneous[3] - start->homogeneous[3])*dxRcp; \ \ - float dzDx = (end->homogeneous[2] - start->homogeneous[2]) * dxRcp; \ - float dwDx = (end->homogeneous[3] - start->homogeneous[3]) * dxRcp; \ + float dcDx[4] = { 0 }; \ + dcDx[0] = (end->color[0] - start->color[0])*dxRcp; \ + dcDx[1] = (end->color[1] - start->color[1])*dxRcp; \ + dcDx[2] = (end->color[2] - start->color[2])*dxRcp; \ + dcDx[3] = (end->color[3] - start->color[3])*dxRcp; \ \ - float dcDx[4]; \ - dcDx[0] = (end->color[0] - start->color[0]) * dxRcp; \ - dcDx[1] = (end->color[1] - start->color[1]) * dxRcp; \ - dcDx[2] = (end->color[2] - start->color[2]) * dxRcp; \ - dcDx[3] = (end->color[3] - start->color[3]) * dxRcp; \ - \ - float duDx, dvDx; \ + float duDx = 0.0f, dvDx = 0.0f; \ if (ENABLE_TEXTURE) { \ - duDx = (end->texcoord[0] - start->texcoord[0]) * dxRcp; \ - dvDx = (end->texcoord[1] - start->texcoord[1]) * dxRcp; \ + duDx = (end->texcoord[0] - start->texcoord[0])*dxRcp; \ + dvDx = (end->texcoord[1] - start->texcoord[1])*dxRcp; \ } \ \ - /* Initializing the interpolation starting values */ \ - \ + /* Initializing the interpolation starting values */ \ float z = start->homogeneous[2]; \ float w = start->homogeneous[3]; \ \ - float color[4]; \ + float color[4] = { 0 }; \ color[0] = start->color[0]; \ color[1] = start->color[1]; \ color[2] = start->color[2]; \ color[3] = start->color[3]; \ \ - float u, v; \ + float u = 0.0f, v = 0.0f; \ if (ENABLE_TEXTURE) { \ u = start->texcoord[0]; \ v = start->texcoord[1]; \ } \ \ /* Pre-calculate the starting pointers for the framebuffer row */ \ - \ - void* cptr = sw_framebuffer_get_color_addr( \ - RLSW.framebuffer.color, y * RLSW.framebuffer.width + xStart); \ - \ - void* dptr = sw_framebuffer_get_depth_addr( \ - RLSW.framebuffer.depth, y * RLSW.framebuffer.width + xStart); \ + void *cptr = sw_framebuffer_get_color_addr(RLSW.framebuffer.color, y*RLSW.framebuffer.width + xStart); \ + void *dptr = sw_framebuffer_get_depth_addr(RLSW.framebuffer.depth, y*RLSW.framebuffer.width + xStart); \ \ /* Scanline rasterization */ \ - \ for (int x = xStart; x < xEnd; x++) \ { \ /* Test and write depth */ \ - \ if (ENABLE_DEPTH_TEST) \ { \ /* TODO: Implement different depth funcs? */ \ @@ -2555,21 +2594,19 @@ static inline void FUNC_NAME(const sw_texture_t* tex, const sw_vertex_t* start, sw_framebuffer_write_depth(dptr, z); \ \ /* Pixel color computation */ \ - \ - float wRcp = 1.0f / w; \ - \ + float wRcp = 1.0f/w; \ float srcColor[4] = { \ - color[0] * wRcp, \ - color[1] * wRcp, \ - color[2] * wRcp, \ - color[3] * wRcp \ + color[0]*wRcp, \ + color[1]*wRcp, \ + color[2]*wRcp, \ + color[3]*wRcp \ }; \ \ if (ENABLE_TEXTURE) \ { \ float texColor[4]; \ - float s = u * wRcp; \ - float t = v * wRcp; \ + float s = u*wRcp; \ + float t = v*wRcp; \ sw_texture_sample(texColor, tex, s, t, duDx, duDy, dvDx, dvDy); \ srcColor[0] *= texColor[0]; \ srcColor[1] *= texColor[1]; \ @@ -2595,16 +2632,15 @@ static inline void FUNC_NAME(const sw_texture_t* tex, const sw_vertex_t* start, } \ \ /* Increment the interpolation parameter, UVs, and pointers */ \ - \ discard: \ - \ z += dzDx; \ w += dwDx; \ color[0] += dcDx[0]; \ color[1] += dcDx[1]; \ color[2] += dcDx[2]; \ color[3] += dcDx[3]; \ - if (ENABLE_TEXTURE) { \ + if (ENABLE_TEXTURE) \ + { \ u += duDx; \ v += dvDx; \ } \ @@ -2615,102 +2651,79 @@ static inline void FUNC_NAME(const sw_texture_t* tex, const sw_vertex_t* start, } #define DEFINE_TRIANGLE_RASTER(FUNC_NAME, FUNC_SCANLINE, ENABLE_TEXTURE) \ -static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1, const sw_vertex_t* v2, \ - const sw_texture_t* tex) \ +static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1, \ + const sw_vertex_t *v2, const sw_texture_t *tex) \ { \ /* Swap vertices by increasing y */ \ - \ - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } \ - if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t* tmp = v1; v1 = v2; v2 = tmp; } \ - if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t* tmp = v0; v0 = v1; v1 = tmp; } \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \ + if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } \ + if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \ \ /* Extracting coordinates from the sorted vertices */ \ - \ float x0 = v0->screen[0], y0 = v0->screen[1]; \ float x1 = v1->screen[0], y1 = v1->screen[1]; \ float x2 = v2->screen[0], y2 = v2->screen[1]; \ \ /* Compute height differences */ \ - \ float h02 = y2 - y0; \ float h01 = y1 - y0; \ float h12 = y2 - y1; \ \ - if (h02 < 1e-6f) { \ - return; \ - } \ + if (h02 < 1e-6f) return; \ \ /* Precompute the inverse values without additional checks */ \ - \ - float invH02 = 1.0f / h02; \ - float invH01 = (h01 > 1e-6f) ? 1.0f / h01 : 0.0f; \ - float invH12 = (h12 > 1e-6f) ? 1.0f / h12 : 0.0f; \ + float invH02 = 1.0f/h02; \ + float invH01 = (h01 > 1e-6f)? 1.0f/h01 : 0.0f; \ + float invH12 = (h12 > 1e-6f)? 1.0f/h12 : 0.0f; \ \ /* Pre-calculation of slopes */ \ - \ - float dx02 = (x2 - x0) * invH02; \ - float dx01 = (x1 - x0) * invH01; \ - float dx12 = (x2 - x1) * invH12; \ + float dx02 = (x2 - x0)*invH02; \ + float dx01 = (x1 - x0)*invH01; \ + float dx12 = (x2 - x1)*invH12; \ \ /* Y bounds (vertical clipping) */ \ - \ int yTop = (int)(y0 + 0.5f); \ int yMiddle = (int)(y1 + 0.5f); \ int yBottom = (int)(y2 + 0.5f); \ \ /* Compute gradients for each side of the triangle */ \ - \ sw_vertex_t vDy02, vDy01, vDy12; \ sw_get_vertex_grad_PTCH(&vDy02, v0, v2, invH02); \ sw_get_vertex_grad_PTCH(&vDy01, v0, v1, invH01); \ sw_get_vertex_grad_PTCH(&vDy12, v1, v2, invH12); \ \ /* Initializing scanline variables */ \ - \ sw_vertex_t vLeft = *v0; \ vLeft.screen[0] = x0; \ - \ sw_vertex_t vRight = *v0; \ vRight.screen[0] = x0; \ \ /* Scanline for the upper part of the triangle */ \ - \ for (int y = yTop; y < yMiddle; y++) \ { \ vLeft.screen[1] = vRight.screen[1] = y; \ \ - if (vLeft.screen[0] < vRight.screen[0]) { \ - FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ - } \ - else { \ - FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ - } \ + if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ + else FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ \ sw_add_vertex_grad_PTCH(&vLeft, &vDy02); \ vLeft.screen[0] += dx02; \ - \ sw_add_vertex_grad_PTCH(&vRight, &vDy01); \ vRight.screen[0] += dx01; \ } \ \ /* Scanline for the lower part of the triangle */ \ - \ vRight = *v1, vRight.screen[0] = x1; \ \ for (int y = yMiddle; y < yBottom; y++) \ { \ vLeft.screen[1] = vRight.screen[1] = y; \ \ - if (vLeft.screen[0] < vRight.screen[0]) { \ - FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ - } \ - else { \ - FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ - } \ + if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, vDy02.texcoord[0], vDy02.texcoord[1]); \ + else FUNC_SCANLINE(tex, &vRight, &vLeft, vDy02.texcoord[0], vDy02.texcoord[1]); \ \ sw_add_vertex_grad_PTCH(&vLeft, &vDy02); \ vLeft.screen[0] += dx02; \ - \ sw_add_vertex_grad_PTCH(&vRight, &vDy12); \ vRight.screen[0] += dx12; \ } \ @@ -2736,21 +2749,19 @@ DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND, sw_triangle_raster_sc static inline void sw_triangle_render(void) { - if (RLSW.stateFlags & SW_STATE_CULL_FACE) { - if (!sw_triangle_face_culling()) { - return; - } + if (RLSW.stateFlags & SW_STATE_CULL_FACE) + { + if (!sw_triangle_face_culling()) return; } sw_triangle_clip_and_project(); - if (RLSW.vertexCounter < 3) { - return; - } + if (RLSW.vertexCounter < 3) return; -# define TRIANGLE_RASTER(RASTER_FUNC) \ + #define TRIANGLE_RASTER(RASTER_FUNC) \ { \ - for (int i = 0; i < RLSW.vertexCounter - 2; i++) { \ + for (int i = 0; i < RLSW.vertexCounter - 2; i++) \ + { \ RASTER_FUNC( \ &RLSW.vertexBuffer[0], \ &RLSW.vertexBuffer[i + 1], \ @@ -2760,35 +2771,19 @@ static inline void sw_triangle_render(void) } \ } - if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - TRIANGLE_RASTER(sw_triangle_raster_DEPTH) - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX) - } - else { - TRIANGLE_RASTER(sw_triangle_raster) - } + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX) + else TRIANGLE_RASTER(sw_triangle_raster) -# undef TRIANGLE_RASTER + #undef TRIANGLE_RASTER } -/* === Quad Rendering Part === */ +// Quad rendering logic static inline bool sw_quad_face_culling(void) { @@ -2802,12 +2797,12 @@ static inline bool sw_quad_face_culling(void) // winding test on this first triangle. // Preload homogeneous coordinates into local variables - const float* h0 = RLSW.vertexBuffer[0].homogeneous; - const float* h1 = RLSW.vertexBuffer[1].homogeneous; - const float* h2 = RLSW.vertexBuffer[2].homogeneous; + const float *h0 = RLSW.vertexBuffer[0].homogeneous; + const float *h1 = RLSW.vertexBuffer[1].homogeneous; + const float *h2 = RLSW.vertexBuffer[2].homogeneous; // NOTE: h3 is not needed for this test - // const float* h3 = RLSW.vertexBuffer[3].homogeneous; + // const float *h3 = RLSW.vertexBuffer[3].homogeneous; // Compute a value proportional to the signed area of the triangle P0 P1 P2 // in the projected 2D plane, calculated directly using homogeneous coordinates @@ -2817,14 +2812,14 @@ static inline bool sw_quad_face_culling(void) // in homogeneous space and its relationship to the projected 2D winding order, // even with negative w values. // The determinant formula used here is: - // h0.x * (h1.y * h2.w - h2.y * h1.w) + - // h1.x * (h2.y * h0.w - h0.y * h2.w) + - // h2.x * (h0.y * h1.w - h1.y * h0.w) + // h0.x*(h1.y*h2.w - h2.y*h1.w) + + // h1.x*(h2.y*h0.w - h0.y*h2.w) + + // h2.x*(h0.y*h1.w - h1.y*h0.w) const float hSgnArea = - h0[0] * (h1[1] * h2[3] - h2[1] * h1[3]) + - h1[0] * (h2[1] * h0[3] - h0[1] * h2[3]) + - h2[0] * (h0[1] * h1[3] - h1[1] * h0[3]); + h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) + + h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) + + h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]); // Perform face culling based on the winding order determined by the sign // of the homogeneous area/determinant of triangle P0 P1 P2. @@ -2846,18 +2841,19 @@ static inline bool sw_quad_face_culling(void) static inline void sw_quad_clip_and_project(void) { - sw_vertex_t* polygon = RLSW.vertexBuffer; - int* vertexCounter = &RLSW.vertexCounter; - - if (sw_polygon_clip(polygon, vertexCounter)) { + sw_vertex_t *polygon = RLSW.vertexBuffer; + int *vertexCounter = &RLSW.vertexCounter; + if (sw_polygon_clip(polygon, vertexCounter)) + { // Transformation to screen space and normalization - for (int i = 0; i < *vertexCounter; i++) { + for (int i = 0; i < *vertexCounter; i++) + { sw_vertex_t *v = &polygon[i]; // Calculation of the reciprocal of W for normalization // as well as perspective-correct attributes - const float invW = 1.0f / v->homogeneous[3]; + const float invW = 1.0f/v->homogeneous[3]; v->homogeneous[3] = invW; // Division of XYZ coordinates by weight @@ -2874,7 +2870,7 @@ static inline void sw_quad_clip_and_project(void) v->color[1] *= invW; v->color[2] *= invW; v->color[3] *= invW; - + // Transformation to screen space sw_project_ndc_to_screen(v->screen, v->homogeneous); } @@ -2888,32 +2884,23 @@ static inline bool sw_quad_is_axis_aligned(void) for (int i = 0; i < 4; i++) { - if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) { - return false; - } + if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false; - const float* v0 = RLSW.vertexBuffer[i].position; - const float* v1 = RLSW.vertexBuffer[(i + 1) % 4].position; + const float *v0 = RLSW.vertexBuffer[i].position; + const float *v1 = RLSW.vertexBuffer[(i + 1)%4].position; float dx = v1[0] - v0[0]; float dy = v1[1] - v0[1]; - if (fabsf(dx) > 1e-6f && fabsf(dy) < 1e-6f) { - horizontal++; - } - else if (fabsf(dy) > 1e-6f && fabsf(dx) < 1e-6f) { - vertical++; - } - else { - // Diagonal edge -> not axis-aligned - return false; - } + if (fabsf(dx) > 1e-6f && fabsf(dy) < 1e-6f) horizontal++; + else if (fabsf(dy) > 1e-6f && fabsf(dx) < 1e-6f) vertical++; + else return false; // Diagonal edge -> not axis-aligned } - return (horizontal == 2 && vertical == 2); + return ((horizontal == 2) && (vertical == 2)); } -static inline void sw_quad_sort_cw(const sw_vertex_t** output) +static inline void sw_quad_sort_cw(const sw_vertex_t* *output) { // Sort 4 quad vertices into clockwise order with fixed layout: // @@ -2926,58 +2913,56 @@ static inline void sw_quad_sort_cw(const sw_vertex_t** output) // - v1: top-right (minimum Y row, maximum X) // - v2: bottom-right (maximum Y, maximum X) // - v3: bottom-left (maximum Y, minimum X) - - const sw_vertex_t* input = RLSW.vertexBuffer; + const sw_vertex_t *input = RLSW.vertexBuffer; // Separate vertices into top and bottom based on Y-coordinate - const sw_vertex_t* top[2] = {NULL, NULL}; - const sw_vertex_t* bottom[2] = {NULL, NULL}; + const sw_vertex_t *top[2] = {NULL, NULL}; + const sw_vertex_t *bottom[2] = {NULL, NULL}; int topCount = 0, bottomCount = 0; // Find minimum and maximum Y float minY = input[0].screen[1]; float maxY = input[0].screen[1]; - for (int i = 1; i < 4; i++) { + for (int i = 1; i < 4; i++) + { if (input[i].screen[1] < minY) minY = input[i].screen[1]; if (input[i].screen[1] > maxY) maxY = input[i].screen[1]; } // Separate vertices based on Y-coordinate - for (int i = 0; i < 4; i++) { - if (input[i].screen[1] == minY && topCount < 2) { - top[topCount++] = &input[i]; - } - else if (input[i].screen[1] == maxY && bottomCount < 2) { - bottom[bottomCount++] = &input[i]; - } + for (int i = 0; i < 4; i++) + { + if (input[i].screen[1] == minY && topCount < 2) top[topCount++] = &input[i]; + else if (input[i].screen[1] == maxY && bottomCount < 2) bottom[bottomCount++] = &input[i]; } // If we don't have enough top/bottom vertices (e.g., Y values are all different), // classify vertices as top or bottom based on whether they're closer to minY or maxY - for (int i = 0; i < 4; i++) { - if (topCount < 2 && &input[i] != top[0] && &input[i] != bottom[0] && &input[i] != bottom[1]) { - if (fabs(input[i].screen[1] - minY) <= fabs(input[i].screen[1] - maxY)) { - top[topCount++] = &input[i]; - } + for (int i = 0; i < 4; i++) + { + if ((topCount < 2) && (&input[i] != top[0]) && (&input[i] != bottom[0]) && (&input[i] != bottom[1])) + { + if (fabsf(input[i].screen[1] - minY) <= fabsf(input[i].screen[1] - maxY)) top[topCount++] = &input[i]; } - if (bottomCount < 2 && &input[i] != top[0] && &input[i] != top[1] && &input[i] != bottom[0]) { - if (fabs(input[i].screen[1] - maxY) < fabs(input[i].screen[1] - minY)) { - bottom[bottomCount++] = &input[i]; - } + if ((bottomCount < 2) && (&input[i] != top[0]) && (&input[i] != top[1]) && (&input[i] != bottom[0])) + { + if (fabsf(input[i].screen[1] - maxY) < fabsf(input[i].screen[1] - minY)) bottom[bottomCount++] = &input[i]; } } // Sort top vertices by X (left to right) - if (topCount == 2 && top[0]->screen[0] > top[1]->screen[0]) { - const sw_vertex_t* temp = top[0]; + if ((topCount == 2) && (top[0]->screen[0] > top[1]->screen[0])) + { + const sw_vertex_t *temp = top[0]; top[0] = top[1]; top[1] = temp; } // Sort bottom vertices by X (left to right) - if (bottomCount == 2 && bottom[0]->screen[0] > bottom[1]->screen[0]) { - const sw_vertex_t* temp = bottom[0]; + if (bottomCount == 2 && bottom[0]->screen[0] > bottom[1]->screen[0]) + { + const sw_vertex_t *temp = bottom[0]; bottom[0] = bottom[1]; bottom[1] = temp; } @@ -2989,21 +2974,21 @@ static inline void sw_quad_sort_cw(const sw_vertex_t** output) output[3] = bottom[0]; // v3: bottom-left } -// REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, still appear perfectly aligned from a certain point of view? -// Because in that case, we would still need to perform perspective division for textures and colors... +// TODO: REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth, +// still appear perfectly aligned from a certain point of view? +// Because in that case, we would still need to perform perspective division for textures and colors... #define DEFINE_QUAD_RASTER_AXIS_ALIGNED(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ static inline void FUNC_NAME(void) \ { \ - const sw_vertex_t* sortedVerts[4]; \ + const sw_vertex_t *sortedVerts[4]; \ sw_quad_sort_cw(sortedVerts); \ \ - const sw_vertex_t* v0 = sortedVerts[0]; \ - const sw_vertex_t* v1 = sortedVerts[1]; \ - const sw_vertex_t* v2 = sortedVerts[2]; \ - const sw_vertex_t* v3 = sortedVerts[3]; \ + const sw_vertex_t *v0 = sortedVerts[0]; \ + const sw_vertex_t *v1 = sortedVerts[1]; \ + const sw_vertex_t *v2 = sortedVerts[2]; \ + const sw_vertex_t *v3 = sortedVerts[3]; \ \ /* Screen bounds (axis-aligned) */ \ - \ int xMin = (int)(v0->screen[0] + 0.5f); \ int yMin = (int)(v0->screen[1] + 0.5f); \ int xMax = (int)(v2->screen[0] + 0.5f); \ @@ -3014,42 +2999,38 @@ static inline void FUNC_NAME(void) \ if (width == 0 || height == 0) return; \ \ - float wRcp = (width > 0.0f) ? 1.0f / width : 0.0f; \ - float hRcp = (height > 0.0f) ? 1.0f / height : 0.0f; \ + float wRcp = (width > 0.0f)? 1.0f/width : 0.0f; \ + float hRcp = (height > 0.0f)? 1.0f/height : 0.0f; \ \ /* Calculation of vertex gradients in X and Y */ \ - \ float tcDx[2], tcDy[2]; \ if (ENABLE_TEXTURE) { \ - tcDx[0] = (v1->texcoord[0] - v0->texcoord[0]) * wRcp; \ - tcDx[1] = (v1->texcoord[1] - v0->texcoord[1]) * wRcp; \ - tcDy[0] = (v3->texcoord[0] - v0->texcoord[0]) * hRcp; \ - tcDy[1] = (v3->texcoord[1] - v0->texcoord[1]) * hRcp; \ + tcDx[0] = (v1->texcoord[0] - v0->texcoord[0])*wRcp; \ + tcDx[1] = (v1->texcoord[1] - v0->texcoord[1])*wRcp; \ + tcDy[0] = (v3->texcoord[0] - v0->texcoord[0])*hRcp; \ + tcDy[1] = (v3->texcoord[1] - v0->texcoord[1])*hRcp; \ } \ \ float cDx[4], cDy[4]; \ - cDx[0] = (v1->color[0] - v0->color[0]) * wRcp; \ - cDx[1] = (v1->color[1] - v0->color[1]) * wRcp; \ - cDx[2] = (v1->color[2] - v0->color[2]) * wRcp; \ - cDx[3] = (v1->color[3] - v0->color[3]) * wRcp; \ - cDy[0] = (v3->color[0] - v0->color[0]) * hRcp; \ - cDy[1] = (v3->color[1] - v0->color[1]) * hRcp; \ - cDy[2] = (v3->color[2] - v0->color[2]) * hRcp; \ - cDy[3] = (v3->color[3] - v0->color[3]) * hRcp; \ + cDx[0] = (v1->color[0] - v0->color[0])*wRcp; \ + cDx[1] = (v1->color[1] - v0->color[1])*wRcp; \ + cDx[2] = (v1->color[2] - v0->color[2])*wRcp; \ + cDx[3] = (v1->color[3] - v0->color[3])*wRcp; \ + cDy[0] = (v3->color[0] - v0->color[0])*hRcp; \ + cDy[1] = (v3->color[1] - v0->color[1])*hRcp; \ + cDy[2] = (v3->color[2] - v0->color[2])*hRcp; \ + cDy[3] = (v3->color[3] - v0->color[3])*hRcp; \ \ float zDx, zDy; \ - zDx = (v1->homogeneous[2] - v0->homogeneous[2]) * wRcp; \ - zDy = (v3->homogeneous[2] - v0->homogeneous[2]) * hRcp; \ + zDx = (v1->homogeneous[2] - v0->homogeneous[2])*wRcp; \ + zDy = (v3->homogeneous[2] - v0->homogeneous[2])*hRcp; \ \ /* Start of quad rasterization */ \ + const sw_texture_t *tex; \ + if (ENABLE_TEXTURE) tex = &RLSW.loadedTextures[RLSW.currentTexture]; \ \ - const sw_texture_t* tex; \ - if (ENABLE_TEXTURE) { \ - tex = &RLSW.loadedTextures[RLSW.currentTexture]; \ - } \ - \ - void* cDstBase = RLSW.framebuffer.color; \ - void* dDstBase = RLSW.framebuffer.depth; \ + void *cDstBase = RLSW.framebuffer.color; \ + void *dDstBase = RLSW.framebuffer.depth; \ int wDst = RLSW.framebuffer.width; \ \ float zScanline = v0->homogeneous[2]; \ @@ -3065,8 +3046,8 @@ static inline void FUNC_NAME(void) \ for (int y = yMin; y < yMax; y++) \ { \ - void* cptr = sw_framebuffer_get_color_addr(cDstBase, y * wDst + xMin); \ - void* dptr = sw_framebuffer_get_depth_addr(dDstBase, y * wDst + xMin); \ + void *cptr = sw_framebuffer_get_color_addr(cDstBase, y*wDst + xMin); \ + void *dptr = sw_framebuffer_get_depth_addr(dDstBase, y*wDst + xMin); \ \ float z = zScanline; \ float u = uScanline; \ @@ -3080,11 +3061,9 @@ static inline void FUNC_NAME(void) }; \ \ /* Scanline rasterization */ \ - \ for (int x = xMin; x < xMax; x++) \ { \ /* Test and write depth */ \ - \ if (ENABLE_DEPTH_TEST) \ { \ /* TODO: Implement different depth funcs? */ \ @@ -3095,7 +3074,6 @@ static inline void FUNC_NAME(void) sw_framebuffer_write_depth(dptr, z); \ \ /* Pixel color computation */ \ - \ float srcColor[4] = { \ color[0], \ color[1], \ @@ -3126,21 +3104,17 @@ static inline void FUNC_NAME(void) \ sw_framebuffer_write_color(cptr, dstColor); \ } \ - else \ - { \ - sw_framebuffer_write_color(cptr, srcColor); \ - } \ + else sw_framebuffer_write_color(cptr, srcColor); \ \ discard: \ - \ z += zDx; \ - \ color[0] += cDx[0]; \ color[1] += cDx[1]; \ color[2] += cDx[2]; \ color[3] += cDx[3]; \ \ - if (ENABLE_TEXTURE) { \ + if (ENABLE_TEXTURE) \ + { \ u += tcDx[0]; \ v += tcDx[1]; \ } \ @@ -3150,13 +3124,13 @@ static inline void FUNC_NAME(void) } \ \ zScanline += zDy; \ - \ colorScanline[0] += cDy[0]; \ colorScanline[1] += cDy[1]; \ colorScanline[2] += cDy[2]; \ colorScanline[3] += cDy[3]; \ \ - if (ENABLE_TEXTURE) { \ + if (ENABLE_TEXTURE) \ + { \ uScanline += tcDy[0]; \ vScanline += tcDy[1]; \ } \ @@ -3174,50 +3148,32 @@ DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND, 1, static inline void sw_quad_render(void) { - if (RLSW.stateFlags & SW_STATE_CULL_FACE) { - if (!sw_quad_face_culling()) { - return; - } + if (RLSW.stateFlags & SW_STATE_CULL_FACE) + { + if (!sw_quad_face_culling()) return; } sw_quad_clip_and_project(); - if (RLSW.vertexCounter < 3) { - return; - } + if (RLSW.vertexCounter < 3) return; if (RLSW.vertexCounter == 4 && sw_quad_is_axis_aligned()) { - if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND(); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_quad_raster_axis_aligned_DEPTH_BLEND(); - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) { - sw_quad_raster_axis_aligned_TEX_BLEND(); - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) { - sw_quad_raster_axis_aligned_TEX_DEPTH(); - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - sw_quad_raster_axis_aligned_BLEND(); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - sw_quad_raster_axis_aligned_DEPTH(); - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) { - sw_quad_raster_axis_aligned_TEX(); - } - else { - sw_quad_raster_axis_aligned(); - } + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_DEPTH_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_TEX_DEPTH(); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_quad_raster_axis_aligned_BLEND(); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_DEPTH(); + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) sw_quad_raster_axis_aligned_TEX(); + else sw_quad_raster_axis_aligned(); return; } -# define TRIANGLE_RASTER(RASTER_FUNC) \ + #define TRIANGLE_RASTER(RASTER_FUNC) \ { \ - for (int i = 0; i < RLSW.vertexCounter - 2; i++) { \ + for (int i = 0; i < RLSW.vertexCounter - 2; i++) \ + { \ RASTER_FUNC( \ &RLSW.vertexBuffer[0], \ &RLSW.vertexBuffer[i + 1], \ @@ -3227,51 +3183,38 @@ static inline void sw_quad_render(void) } \ } - if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - TRIANGLE_RASTER(sw_triangle_raster_BLEND) - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - TRIANGLE_RASTER(sw_triangle_raster_DEPTH) - } - else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) { - TRIANGLE_RASTER(sw_triangle_raster_TEX) - } - else { - TRIANGLE_RASTER(sw_triangle_raster) - } + if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND) + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH) + else if (SW_STATE_CHECK(SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX) + else TRIANGLE_RASTER(sw_triangle_raster) -# undef TRIANGLE_RASTER + #undef TRIANGLE_RASTER } -/* === Line Rendering Part === */ +// Line rendering logic -static inline bool sw_line_clip_coord(float q, float p, float* t0, float* t1) +static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1) { - if (fabsf(p) < SW_CLIP_EPSILON) { + if (fabsf(p) < SW_CLIP_EPSILON) + { // Check if the line is entirely outside the window if (q < -SW_CLIP_EPSILON) return 0; // Completely outside - return 1; // Completely inside or on the edges + return 1; // Completely inside or on the edges } - const float r = q / p; + const float r = q/p; - if (p < 0) { + if (p < 0) + { if (r > *t1) return 0; if (r > *t0) *t0 = r; } - else { + else + { if (r < *t0) return 0; if (r < *t1) *t1 = r; } @@ -3279,12 +3222,13 @@ static inline bool sw_line_clip_coord(float q, float p, float* t0, float* t1) return 1; } -static inline bool sw_line_clip(sw_vertex_t* v0, sw_vertex_t* v1) +static inline bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1) { float t0 = 0.0f, t1 = 1.0f; float dH[4], dC[4]; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) + { dH[i] = v1->homogeneous[i] - v0->homogeneous[i]; dC[i] = v1->color[i] - v0->color[i]; } @@ -3298,40 +3242,45 @@ static inline bool sw_line_clip(sw_vertex_t* v0, sw_vertex_t* v1) if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false; // Clipping Scissor - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { - if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0] * v0->homogeneous[3], RLSW.scClipMin[0] * dH[3] - dH[0], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[0] * v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0] * dH[3], &t0, &t1)) return false; - if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1] * v0->homogeneous[3], RLSW.scClipMin[1] * dH[3] - dH[1], &t0, &t1)) return false; - if (!sw_line_clip_coord(RLSW.scClipMax[1] * v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1] * dH[3], &t0, &t1)) return false; + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { + if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0]*v0->homogeneous[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false; + if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1]*v0->homogeneous[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false; + if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false; } // Interpolation of new coordinates - if (t1 < 1.0f) { - for (int i = 0; i < 4; i++) { - v1->homogeneous[i] = v0->homogeneous[i] + t1 * dH[i]; - v1->color[i] = v0->color[i] + t1 * dC[i]; + if (t1 < 1.0f) + { + for (int i = 0; i < 4; i++) + { + v1->homogeneous[i] = v0->homogeneous[i] + t1*dH[i]; + v1->color[i] = v0->color[i] + t1*dC[i]; } } - if (t0 > 0.0f) { - for (int i = 0; i < 4; i++) { - v0->homogeneous[i] += t0 * dH[i]; - v0->color[i] += t0 * dC[i]; + + if (t0 > 0.0f) + { + for (int i = 0; i < 4; i++) + { + v0->homogeneous[i] += t0*dH[i]; + v0->color[i] += t0*dC[i]; } } return true; } -static inline bool sw_line_clip_and_project(sw_vertex_t* v0, sw_vertex_t* v1) +static inline bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1) { - if (!sw_line_clip(v0, v1)) { - return false; - } + if (!sw_line_clip(v0, v1)) return false; // Convert homogeneous coordinates to NDC - v0->homogeneous[3] = 1.0f / v0->homogeneous[3]; - v1->homogeneous[3] = 1.0f / v1->homogeneous[3]; - for (int i = 0; i < 3; i++) { + v0->homogeneous[3] = 1.0f/v0->homogeneous[3]; + v1->homogeneous[3] = 1.0f/v1->homogeneous[3]; + for (int i = 0; i < 3; i++) + { v0->homogeneous[i] *= v0->homogeneous[3]; v1->homogeneous[i] *= v1->homogeneous[3]; } @@ -3344,7 +3293,7 @@ static inline bool sw_line_clip_and_project(sw_vertex_t* v0, sw_vertex_t* v1) } #define DEFINE_LINE_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \ -static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ +static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1) \ { \ int x1 = (int)(v0->screen[0] + 0.5f); \ int y1 = (int)(v0->screen[1] + 0.5f); \ @@ -3355,8 +3304,8 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ int dy = y2 - y1; \ \ /* Handling of lines that are more horizontal or vertical */ \ - \ - if (dx == 0 && dy == 0) { \ + if (dx == 0 && dy == 0) \ + { \ /* TODO: A point should be rendered here */ \ return; \ } \ @@ -3364,36 +3313,33 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ bool yLonger = (abs(dy) > abs(dx)); \ int longLen, shortLen; \ \ - if (yLonger) { \ + if (yLonger) \ + { \ longLen = dy; \ shortLen = dx; \ } \ - else { \ + else \ + { \ longLen = dx; \ shortLen = dy; \ } \ \ /* Handling of traversal direction */ \ - \ - int sgnInc = (longLen < 0) ? -1 : 1; \ + int sgnInc = (longLen < 0)? -1 : 1; \ int abslongLen = abs(longLen); \ \ /* Calculation of the increment step for the shorter coordinate */ \ - \ int decInc = 0; \ - if (abslongLen != 0) { \ - decInc = (shortLen << 16) / abslongLen; \ - } \ + if (abslongLen != 0) decInc = (shortLen << 16)/abslongLen; \ \ - float longLenRcp = (abslongLen != 0) ? (1.0f / abslongLen) : 0.0f; \ + float longLenRcp = (abslongLen != 0)? (1.0f/abslongLen) : 0.0f; \ \ /* Calculation of interpolation steps */ \ - \ - const float zStep = (v1->homogeneous[2] - v0->homogeneous[2]) * longLenRcp; \ - const float rStep = (v1->color[0] - v0->color[0]) * longLenRcp; \ - const float gStep = (v1->color[1] - v0->color[1]) * longLenRcp; \ - const float bStep = (v1->color[2] - v0->color[2]) * longLenRcp; \ - const float aStep = (v1->color[3] - v0->color[3]) * longLenRcp; \ + const float zStep = (v1->homogeneous[2] - v0->homogeneous[2])*longLenRcp; \ + const float rStep = (v1->color[0] - v0->color[0])*longLenRcp; \ + const float gStep = (v1->color[1] - v0->color[1])*longLenRcp; \ + const float bStep = (v1->color[2] - v0->color[2])*longLenRcp; \ + const float aStep = (v1->color[3] - v0->color[3])*longLenRcp; \ \ float z = v0->homogeneous[2]; \ \ @@ -3405,27 +3351,26 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ }; \ \ const int fbWidth = RLSW.framebuffer.width; \ - void* cBuffer = RLSW.framebuffer.color; \ - void* dBuffer = RLSW.framebuffer.depth; \ + void *cBuffer = RLSW.framebuffer.color; \ + void *dBuffer = RLSW.framebuffer.depth; \ \ int j = 0; \ - \ if (yLonger) \ { \ for (int i = 0; i != longLen; i += sgnInc) \ { \ - int offset = (y1 + i) * fbWidth + (x1 + (j >> 16)); \ + int offset = (y1 + i)*fbWidth + (x1 + (j >> 16)); \ + void *dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ \ - void* dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ - \ - if (ENABLE_DEPTH_TEST) { \ + if (ENABLE_DEPTH_TEST) \ + { \ float depth = sw_framebuffer_read_depth(dptr); \ if (z > depth) goto discardA; \ } \ \ sw_framebuffer_write_depth(dptr, z); \ \ - void* cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ + void *cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ \ if (ENABLE_COLOR_BLEND) \ { \ @@ -3435,10 +3380,7 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ sw_blend_colors(dstColor, color); \ sw_framebuffer_write_color(cptr, dstColor); \ } \ - else \ - { \ - sw_framebuffer_write_color(cptr, color); \ - } \ + else sw_framebuffer_write_color(cptr, color); \ \ discardA: \ j += decInc; \ @@ -3453,18 +3395,18 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ { \ for (int i = 0; i != longLen; i += sgnInc) \ { \ - int offset = (y1 + (j >> 16)) * fbWidth + (x1 + i); \ + int offset = (y1 + (j >> 16))*fbWidth + (x1 + i); \ + void *dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ \ - void* dptr = sw_framebuffer_get_depth_addr(dBuffer, offset); \ - \ - if (ENABLE_DEPTH_TEST) { \ + if (ENABLE_DEPTH_TEST) \ + { \ float depth = sw_framebuffer_read_depth(dptr); \ if (z > depth) goto discardB; \ } \ \ sw_framebuffer_write_depth(dptr, z); \ \ - void* cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ + void *cptr = sw_framebuffer_get_color_addr(cBuffer, offset); \ \ if (ENABLE_COLOR_BLEND) \ { \ @@ -3474,10 +3416,7 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ sw_blend_colors(dstColor, color); \ sw_framebuffer_write_color(cptr, dstColor); \ } \ - else \ - { \ - sw_framebuffer_write_color(cptr, color); \ - } \ + else sw_framebuffer_write_color(cptr, color); \ \ discardB: \ j += decInc; \ @@ -3491,7 +3430,7 @@ static inline void FUNC_NAME(const sw_vertex_t* v0, const sw_vertex_t* v1) \ } #define DEFINE_LINE_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ -void FUNC_NAME(const sw_vertex_t* v1, const sw_vertex_t* v2) \ +void FUNC_NAME(const sw_vertex_t *v1, const sw_vertex_t *v2) \ { \ sw_vertex_t tv1, tv2; \ \ @@ -3505,10 +3444,12 @@ void FUNC_NAME(const sw_vertex_t* v1, const sw_vertex_t* v2) \ \ RASTER_FUNC(v1, v2); \ \ - if (dx != 0 && abs(dy / dx) < 1) { \ - int wy = (int)((RLSW.lineWidth - 1.0f) * abs(dx) / sqrtf(dx * dx + dy * dy)); \ - wy >>= 1; /* Division by 2 via bit shift */ \ - for (int i = 1; i <= wy; i++) { \ + if (dx != 0 && abs(dy/dx) < 1) \ + { \ + int wy = (int)((RLSW.lineWidth - 1.0f)*abs(dx)/sqrtf(dx*dx + dy*dy)); \ + wy >>= 1; \ + for (int i = 1; i <= wy; i++) \ + { \ tv1 = *v1, tv2 = *v2; \ tv1.screen[1] -= i; \ tv2.screen[1] -= i; \ @@ -3519,10 +3460,12 @@ void FUNC_NAME(const sw_vertex_t* v1, const sw_vertex_t* v2) \ RASTER_FUNC(&tv1, &tv2); \ } \ } \ - else if (dy != 0) { \ - int wx = (int)((RLSW.lineWidth - 1.0f) * abs(dy) / sqrtf(dx * dx + dy * dy)); \ - wx >>= 1; /* Division by 2 via bit shift */ \ - for (int i = 1; i <= wx; i++) { \ + else if (dy != 0) \ + { \ + int wx = (int)((RLSW.lineWidth - 1.0f)*abs(dy)/sqrtf(dx*dx + dy*dy)); \ + wx >>= 1; \ + for (int i = 1; i <= wx; i++) \ + { \ tv1 = *v1, tv2 = *v2; \ tv1.screen[0] -= i; \ tv2.screen[0] -= i; \ @@ -3545,53 +3488,38 @@ DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH, sw_line_raster_DEPTH) DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_BLEND, sw_line_raster_BLEND) DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH_BLEND, sw_line_raster_DEPTH_BLEND) -static inline void sw_line_render(sw_vertex_t* vertices) +static inline void sw_line_render(sw_vertex_t *vertices) { - if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) { - return; - } + if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) return; - if (RLSW.lineWidth >= 2.0f) { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_line_thick_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - sw_line_thick_raster_BLEND(&vertices[0], &vertices[1]); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - sw_line_thick_raster_DEPTH(&vertices[0], &vertices[1]); - } - else { - sw_line_thick_raster(&vertices[0], &vertices[1]); - } + if (RLSW.lineWidth >= 2.0f) + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_thick_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_thick_raster_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_thick_raster_DEPTH(&vertices[0], &vertices[1]); + else sw_line_thick_raster(&vertices[0], &vertices[1]); } - else { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_line_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - sw_line_raster_BLEND(&vertices[0], &vertices[1]); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - sw_line_raster_DEPTH(&vertices[0], &vertices[1]); - } - else { - sw_line_raster(&vertices[0], &vertices[1]); - } + else + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_raster_DEPTH_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_raster_BLEND(&vertices[0], &vertices[1]); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_raster_DEPTH(&vertices[0], &vertices[1]); + else sw_line_raster(&vertices[0], &vertices[1]); } } -/* === Point Rendering Part === */ +// Point rendering logic -static inline bool sw_point_clip_and_project(sw_vertex_t* v) +static inline bool sw_point_clip_and_project(sw_vertex_t *v) { - if (v->homogeneous[3] != 1.0f) { - for (int_fast8_t i = 0; i < 3; i++) { - if (v->homogeneous[i] < -v->homogeneous[3] || v->homogeneous[i] > v->homogeneous[3]) { - return false; - } + if (v->homogeneous[3] != 1.0f) + { + for (int_fast8_t i = 0; i < 3; i++) + { + if ((v->homogeneous[i] < -v->homogeneous[3]) || (v->homogeneous[i] > v->homogeneous[3])) return false; } - v->homogeneous[3] = 1.0f / v->homogeneous[3]; + + v->homogeneous[3] = 1.0f/v->homogeneous[3]; v->homogeneous[0] *= v->homogeneous[3]; v->homogeneous[1] *= v->homogeneous[3]; v->homogeneous[2] *= v->homogeneous[3]; @@ -3599,24 +3527,23 @@ static inline bool sw_point_clip_and_project(sw_vertex_t* v) sw_project_ndc_to_screen(v->screen, v->homogeneous); - const int *min, *max; + const int *min = NULL, *max = NULL; - if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) { + if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST) + { min = RLSW.scMin; max = RLSW.scMax; } - else { + else + { min = RLSW.vpMin; max = RLSW.vpMax; } - bool insideX = v->screen[0] - RLSW.pointRadius < max[0] - && v->screen[0] + RLSW.pointRadius > min[0]; + bool insideX = (v->screen[0] - RLSW.pointRadius < max[0]) && (v->screen[0] + RLSW.pointRadius > min[0]); + bool insideY = (v->screen[1] - RLSW.pointRadius < max[1]) && (v->screen[1] + RLSW.pointRadius > min[1]); - bool insideY = v->screen[1] - RLSW.pointRadius < max[1] - && v->screen[1] + RLSW.pointRadius > min[1]; - - return insideX && insideY; + return (insideX && insideY); } #define DEFINE_POINT_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND, CHECK_BOUNDS) \ @@ -3633,11 +3560,9 @@ static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \ if (y < RLSW.scMin[1] || y >= RLSW.scMax[1]) return; \ } \ \ - int offset = y * RLSW.framebuffer.width + x; \ + int offset = y*RLSW.framebuffer.width + x; \ \ - void* dptr = sw_framebuffer_get_depth_addr( \ - RLSW.framebuffer.depth, offset \ - ); \ + void *dptr = sw_framebuffer_get_depth_addr(RLSW.framebuffer.depth, offset); \ \ if (ENABLE_DEPTH_TEST) \ { \ @@ -3647,9 +3572,7 @@ static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \ \ sw_framebuffer_write_depth(dptr, z); \ \ - void* cptr = sw_framebuffer_get_color_addr( \ - RLSW.framebuffer.color, offset \ - ); \ + void *cptr = sw_framebuffer_get_color_addr(RLSW.framebuffer.color, offset); \ \ if (ENABLE_COLOR_BLEND) \ { \ @@ -3659,41 +3582,40 @@ static inline void FUNC_NAME(int x, int y, float z, const float color[4]) \ sw_blend_colors(dstColor, color); \ sw_framebuffer_write_color(cptr, dstColor); \ } \ - else \ - { \ - sw_framebuffer_write_color(cptr, color); \ - } \ + else sw_framebuffer_write_color(cptr, color); \ } #define DEFINE_POINT_THICK_RASTER(FUNC_NAME, RASTER_FUNC) \ -static inline void FUNC_NAME(sw_vertex_t* v) \ +static inline void FUNC_NAME(sw_vertex_t *v) \ { \ int cx = v->screen[0]; \ int cy = v->screen[1]; \ float cz = v->homogeneous[2]; \ int radius = RLSW.pointRadius; \ - const float* color = v->color; \ + const float *color = v->color; \ \ int x = 0; \ int y = radius; \ - int d = 3 - 2 * radius; \ + int d = 3 - 2*radius; \ \ - while (x <= y) { \ - for (int i = -x; i <= x; i++) { \ + while (x <= y) \ + { \ + for (int i = -x; i <= x; i++) \ + { \ RASTER_FUNC(cx + i, cy + y, cz, color); \ RASTER_FUNC(cx + i, cy - y, cz, color); \ } \ - for (int i = -y; i <= y; i++) { \ + for (int i = -y; i <= y; i++) \ + { \ RASTER_FUNC(cx + i, cy + x, cz, color); \ RASTER_FUNC(cx + i, cy - x, cz, color); \ } \ - if (d > 0) { \ + if (d > 0) \ + { \ y--; \ - d = d + 4 * (x - y) + 10; \ - } \ - else { \ - d = d + 4 * x + 6; \ + d = d + 4*(x - y) + 10; \ } \ + else d = d + 4*x + 6; \ x++; \ } \ } @@ -3723,114 +3645,65 @@ DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_SCISSOR, sw_point_raster_D DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND_SCISSOR, sw_point_raster_BLEND_CHECK_SCISSOR) DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND_SCISSOR, sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR) -static inline void sw_point_render(sw_vertex_t* v) +static inline void sw_point_render(sw_vertex_t *v) { - if (!sw_point_clip_and_project(v)) { - return; - } + if (!sw_point_clip_and_project(v)) return; - if (RLSW.pointRadius >= 1.0f) { - if (SW_STATE_CHECK(SW_STATE_SCISSOR_TEST)) { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_point_thick_raster_DEPTH_BLEND_SCISSOR(v); - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - sw_point_thick_raster_BLEND_SCISSOR(v); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - sw_point_thick_raster_DEPTH_SCISSOR(v); - } - else { - sw_point_thick_raster_SCISSOR(v); - } + if (RLSW.pointRadius >= 1.0f) + { + if (SW_STATE_CHECK(SW_STATE_SCISSOR_TEST)) + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND_SCISSOR(v); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND_SCISSOR(v); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH_SCISSOR(v); + else sw_point_thick_raster_SCISSOR(v); } - else { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_point_thick_raster_DEPTH_BLEND(v); - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - sw_point_thick_raster_BLEND(v); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - sw_point_thick_raster_DEPTH(v); - } - else { - sw_point_thick_raster(v); - } + else + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND(v); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND(v); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH(v); + else sw_point_thick_raster(v); } } - else { - if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) { - sw_point_raster_DEPTH_BLEND( - v->screen[0], v->screen[1], - v->homogeneous[2], v->color - ); - } - else if (SW_STATE_CHECK(SW_STATE_BLEND)) { - sw_point_raster_BLEND( - v->screen[0], v->screen[1], - v->homogeneous[2], v->color - ); - } - else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) { - sw_point_raster_DEPTH( - v->screen[0], v->screen[1], - v->homogeneous[2], v->color - ); - } - else { - sw_point_raster( - v->screen[0], v->screen[1], - v->homogeneous[2], v->color - ); - } + else + { + if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_raster_DEPTH_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_raster_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_raster_DEPTH(v->screen[0], v->screen[1], v->homogeneous[2], v->color); + else sw_point_raster(v->screen[0], v->screen[1], v->homogeneous[2], v->color); } } -/* === Polygon Modes Rendering Part === */ +// Polygon modes mendering logic static inline void sw_poly_point_render(void) { - for (int i = 0; i < RLSW.vertexCounter; i++) { - sw_point_render(&RLSW.vertexBuffer[i]); - } + for (int i = 0; i < RLSW.vertexCounter; i++) sw_point_render(&RLSW.vertexBuffer[i]); } static inline void sw_poly_line_render(void) { - const sw_vertex_t* vertices = RLSW.vertexBuffer; + const sw_vertex_t *vertices = RLSW.vertexBuffer; int cm1 = RLSW.vertexCounter - 1; - for (int i = 0; i < cm1; i++) { - sw_line_render((sw_vertex_t[2]){ - vertices[i], vertices[i + 1] - }); - } + for (int i = 0; i < cm1; i++) sw_line_render((sw_vertex_t[2]){ vertices[i], vertices[i + 1] }); - sw_line_render((sw_vertex_t[2]){ - vertices[cm1], vertices[0] - }); + sw_line_render((sw_vertex_t[2]){ vertices[cm1], vertices[0] }); } static inline void sw_poly_fill_render(void) { - switch (RLSW.drawMode) { - case SW_POINTS: - sw_point_render(&RLSW.vertexBuffer[0]); - break; - case SW_LINES: - sw_line_render(RLSW.vertexBuffer); - break; - case SW_TRIANGLES: - sw_triangle_render(); - break; - case SW_QUADS: - sw_quad_render(); - break; + switch (RLSW.drawMode) + { + case SW_POINTS: sw_point_render(&RLSW.vertexBuffer[0]); break; + case SW_LINES: sw_line_render(RLSW.vertexBuffer); break; + case SW_TRIANGLES: sw_triangle_render(); break; + case SW_QUADS: sw_quad_render(); break; } } -/* === Some Validity Check Helper === */ +// Validity check helper functions static inline bool sw_is_texture_valid(uint32_t id) { @@ -3857,15 +3730,13 @@ static inline bool sw_is_draw_mode_valid(int mode) { bool result = false; - switch (mode) { - case SW_POINTS: - case SW_LINES: - case SW_TRIANGLES: - case SW_QUADS: - result = true; - break; - default: - break; + switch (mode) + { + case SW_POINTS: + case SW_LINES: + case SW_TRIANGLES: + case SW_QUADS: result = true; break; + default: break; } return result; @@ -3875,14 +3746,12 @@ static inline bool sw_is_poly_mode_valid(int mode) { bool result = false; - switch (mode) { - case SW_POINT: - case SW_LINE: - case SW_FILL: - result = true; - break; - default: - break; + switch (mode) + { + case SW_POINT: + case SW_LINE: + case SW_FILL: result = true; break; + default: break; } return result; @@ -3897,22 +3766,20 @@ static inline bool sw_is_blend_src_factor_valid(int blend) { bool result = false; - switch (blend) { - case SW_ZERO: - case SW_ONE: - case SW_SRC_COLOR: - case SW_ONE_MINUS_SRC_COLOR: - case SW_SRC_ALPHA: - case SW_ONE_MINUS_SRC_ALPHA: - case SW_DST_ALPHA: - case SW_ONE_MINUS_DST_ALPHA: - case SW_DST_COLOR: - case SW_ONE_MINUS_DST_COLOR: - case SW_SRC_ALPHA_SATURATE: - result = true; - break; - default: - break; + switch (blend) + { + case SW_ZERO: + case SW_ONE: + case SW_SRC_COLOR: + case SW_ONE_MINUS_SRC_COLOR: + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_DST_COLOR: + case SW_ONE_MINUS_DST_COLOR: + case SW_SRC_ALPHA_SATURATE: result = true; break; + default: break; } return result; @@ -3922,41 +3789,38 @@ static inline bool sw_is_blend_dst_factor_valid(int blend) { bool result = false; - switch (blend) { - case SW_ZERO: - case SW_ONE: - case SW_SRC_COLOR: - case SW_ONE_MINUS_SRC_COLOR: - case SW_SRC_ALPHA: - case SW_ONE_MINUS_SRC_ALPHA: - case SW_DST_ALPHA: - case SW_ONE_MINUS_DST_ALPHA: - case SW_DST_COLOR: - case SW_ONE_MINUS_DST_COLOR: - result = true; - break; - default: - break; + switch (blend) + { + case SW_ZERO: + case SW_ONE: + case SW_SRC_COLOR: + case SW_ONE_MINUS_SRC_COLOR: + case SW_SRC_ALPHA: + case SW_ONE_MINUS_SRC_ALPHA: + case SW_DST_ALPHA: + case SW_ONE_MINUS_DST_ALPHA: + case SW_DST_COLOR: + case SW_ONE_MINUS_DST_COLOR: result = true; break; + default: break; } return result; } -/* === Public Implementation === */ - +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- bool swInit(int w, int h) { - if (!sw_framebuffer_load(w, h)) { - swClose(); return false; - } + if (!sw_framebuffer_load(w, h)) { swClose(); return false; } swViewport(0, 0, w, h); swScissor(0, 0, w, h); - RLSW.loadedTextures = SW_MALLOC(SW_MAX_TEXTURES * sizeof(sw_texture_t)); + RLSW.loadedTextures = SW_MALLOC(SW_MAX_TEXTURES*sizeof(sw_texture_t)); if (RLSW.loadedTextures == NULL) { swClose(); return false; } - RLSW.freeTextureIds = SW_MALLOC(SW_MAX_TEXTURES * sizeof(uint32_t)); + RLSW.freeTextureIds = SW_MALLOC(SW_MAX_TEXTURES*sizeof(uint32_t)); if (RLSW.loadedTextures == NULL) { swClose(); return false; } RLSW.clearColor[0] = 0.0f; @@ -3995,8 +3859,7 @@ bool swInit(int w, int h) RLSW.polyMode = SW_FILL; RLSW.cullFace = SW_BACK; - static const float defTex[3*2*2] = - { + static const float defTex[3*2*2] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, @@ -4024,11 +3887,10 @@ bool swInit(int w, int h) void swClose(void) { - for (int i = 1; i < RLSW.loadedTextureCount; i++) { - sw_texture_t* texture = &RLSW.loadedTextures[i]; - if (sw_is_texture_valid(i) && texture->copy) { - SW_FREE(texture->pixels.ptr); - } + for (int i = 1; i < RLSW.loadedTextureCount; i++) + { + sw_texture_t *texture = &RLSW.loadedTextures[i]; + if (sw_is_texture_valid(i) && texture->copy) SW_FREE(texture->pixels.ptr); } SW_FREE(RLSW.framebuffer.color); @@ -4044,147 +3906,89 @@ bool swResizeFramebuffer(int w, int h) return sw_framebuffer_resize(w, h); } -void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void* pixels) +void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels) { sw_pixelformat_t pFormat = sw_get_pixel_format(format, type); - if (w <= 0) { + if (w <= 0) + { RLSW.errCode = SW_INVALID_VALUE; return; } - if (h <= 0) { + if (h <= 0) + { RLSW.errCode = SW_INVALID_VALUE; return; } - if (w > RLSW.framebuffer.width) - w = RLSW.framebuffer.width; - - if (h > RLSW.framebuffer.height) - h = RLSW.framebuffer.height; + if (w > RLSW.framebuffer.width) w = RLSW.framebuffer.width; + if (h > RLSW.framebuffer.height) h = RLSW.framebuffer.height; x = sw_clampi(x, 0, w); y = sw_clampi(y, 0, h); - switch (pFormat) { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: - sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - sw_framebuffer_copy_to_R5G6B5(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: - sw_framebuffer_copy_to_R8G8B8(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: - sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: - sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - sw_framebuffer_copy_to_R32(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - sw_framebuffer_copy_to_R32G32B32(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - sw_framebuffer_copy_to_R32G32B32A32(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - sw_framebuffer_copy_to_R16(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - sw_framebuffer_copy_to_R16G16B16(x, y, w, h, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - sw_framebuffer_copy_to_R16G16B16A16(x, y, w, h, pixels); - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (pFormat) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_copy_to_R5G6B5(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_copy_to_R8G8B8(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: sw_framebuffer_copy_to_R32(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: sw_framebuffer_copy_to_R32G32B32(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: sw_framebuffer_copy_to_R32G32B32A32(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: sw_framebuffer_copy_to_R16(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: sw_framebuffer_copy_to_R16G16B16(x, y, w, h, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: sw_framebuffer_copy_to_R16G16B16A16(x, y, w, h, pixels); break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } -void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, - int xSrc, int ySrc, int wSrc, int hSrc, - SWformat format, SWtype type, void* pixels) +void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels) { sw_pixelformat_t pFormat = sw_get_pixel_format(format, type); - if (wSrc <= 0) { + if (wSrc <= 0) + { RLSW.errCode = SW_INVALID_VALUE; return; } - if (hSrc <= 0) { + if (hSrc <= 0) + { RLSW.errCode = SW_INVALID_VALUE; return; } - if (wSrc > RLSW.framebuffer.width) - wSrc = RLSW.framebuffer.width; - - if (hSrc > RLSW.framebuffer.height) - hSrc = RLSW.framebuffer.height; + if (wSrc > RLSW.framebuffer.width) wSrc = RLSW.framebuffer.width; + if (hSrc > RLSW.framebuffer.height) hSrc = RLSW.framebuffer.height; xSrc = sw_clampi(xSrc, 0, wSrc); ySrc = sw_clampi(ySrc, 0, hSrc); - switch (pFormat) { - case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: - sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: - sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: - sw_framebuffer_blit_to_R5G6B5(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: - sw_framebuffer_blit_to_R8G8B8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: - sw_framebuffer_blit_to_R5G5B5A1(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: - sw_framebuffer_blit_to_R4G4B4A4(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: - sw_framebuffer_blit_to_R8G8B8A8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32: - sw_framebuffer_blit_to_R32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: - sw_framebuffer_blit_to_R32G32B32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: - sw_framebuffer_blit_to_R32G32B32A32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16: - sw_framebuffer_blit_to_R16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: - sw_framebuffer_blit_to_R16G16B16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: - sw_framebuffer_blit_to_R16G16B16A16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (pFormat) + { + case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_blit_to_R5G6B5(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_blit_to_R8G8B8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_blit_to_R5G5B5A1(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_blit_to_R4G4B4A4(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_blit_to_R8G8B8A8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32: sw_framebuffer_blit_to_R32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32: sw_framebuffer_blit_to_R32G32B32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: sw_framebuffer_blit_to_R32G32B32A32(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16: sw_framebuffer_blit_to_R16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16: sw_framebuffer_blit_to_R16G16B16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: sw_framebuffer_blit_to_R16G16B16A16(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, pixels); break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } -void* swGetColorBuffer(int* w, int* h) +void *swGetColorBuffer(int *w, int *h) { if (w) *w = RLSW.framebuffer.width; if (h) *h = RLSW.framebuffer.height; @@ -4194,136 +3998,102 @@ void* swGetColorBuffer(int* w, int* h) void swEnable(SWstate state) { - switch (state) { - case SW_SCISSOR_TEST: - RLSW.stateFlags |= SW_STATE_SCISSOR_TEST; - break; - case SW_TEXTURE_2D: - RLSW.stateFlags |= SW_STATE_TEXTURE_2D; - break; - case SW_DEPTH_TEST: - RLSW.stateFlags |= SW_STATE_DEPTH_TEST; - break; - case SW_CULL_FACE: - RLSW.stateFlags |= SW_STATE_CULL_FACE; - break; - case SW_BLEND: - RLSW.stateFlags |= SW_STATE_BLEND; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (state) + { + case SW_SCISSOR_TEST: RLSW.stateFlags |= SW_STATE_SCISSOR_TEST; break; + case SW_TEXTURE_2D: RLSW.stateFlags |= SW_STATE_TEXTURE_2D; break; + case SW_DEPTH_TEST: RLSW.stateFlags |= SW_STATE_DEPTH_TEST; break; + case SW_CULL_FACE: RLSW.stateFlags |= SW_STATE_CULL_FACE; break; + case SW_BLEND: RLSW.stateFlags |= SW_STATE_BLEND; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } void swDisable(SWstate state) { - switch (state) { - case SW_SCISSOR_TEST: - RLSW.stateFlags &= ~SW_STATE_SCISSOR_TEST; - break; - case SW_TEXTURE_2D: - RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; - break; - case SW_DEPTH_TEST: - RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; - break; - case SW_CULL_FACE: - RLSW.stateFlags &= ~SW_STATE_CULL_FACE; - break; - case SW_BLEND: - RLSW.stateFlags &= ~SW_STATE_BLEND; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (state) + { + case SW_SCISSOR_TEST: RLSW.stateFlags &= ~SW_STATE_SCISSOR_TEST; break; + case SW_TEXTURE_2D: RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; break; + case SW_DEPTH_TEST: RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; break; + case SW_CULL_FACE: RLSW.stateFlags &= ~SW_STATE_CULL_FACE; break; + case SW_BLEND: RLSW.stateFlags &= ~SW_STATE_BLEND; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } -void swGetIntegerv(SWget name, int* v) +void swGetIntegerv(SWget name, int *v) { - switch (name) { - case SW_MODELVIEW_STACK_DEPTH: - *v = SW_MODELVIEW_STACK_DEPTH; - break; - case SW_PROJECTION_STACK_DEPTH: - *v = SW_PROJECTION_STACK_DEPTH; - break; - case SW_TEXTURE_STACK_DEPTH: - *v = SW_TEXTURE_STACK_DEPTH; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (name) + { + case SW_MODELVIEW_STACK_DEPTH: *v = SW_MODELVIEW_STACK_DEPTH; break; + case SW_PROJECTION_STACK_DEPTH: *v = SW_PROJECTION_STACK_DEPTH; break; + case SW_TEXTURE_STACK_DEPTH: *v = SW_TEXTURE_STACK_DEPTH; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } -void swGetFloatv(SWget name, float* v) +void swGetFloatv(SWget name, float *v) { - switch (name) { - case SW_COLOR_CLEAR_VALUE: - v[0] = RLSW.clearColor[0]; - v[1] = RLSW.clearColor[1]; - v[2] = RLSW.clearColor[2]; - v[3] = RLSW.clearColor[3]; - break; - case SW_CURRENT_COLOR: - v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[0]; - v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[1]; - v[2] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[2]; - v[3] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[3]; - break; - case SW_CURRENT_TEXTURE_COORDS: - v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[0]; - v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[1]; - break; - case SW_POINT_SIZE: - v[0] = 2.0f * RLSW.pointRadius; - break; - case SW_LINE_WIDTH: - v[0] = RLSW.lineWidth; - break; - case SW_MODELVIEW_MATRIX: - for (int i = 0; i < 16; i++) { - v[i] = RLSW.stackModelview[RLSW.stackModelviewCounter - 1][i]; - } - break; - case SW_PROJECTION_MATRIX: - for (int i = 0; i < 16; i++) { - v[i] = RLSW.stackProjection[RLSW.stackProjectionCounter - 1][i]; - } - break; - case SW_TEXTURE_MATRIX: - for (int i = 0; i < 16; i++) { - v[i] = RLSW.stackTexture[RLSW.stackTextureCounter - 1][i]; - } - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (name) + { + case SW_COLOR_CLEAR_VALUE: + { + v[0] = RLSW.clearColor[0]; + v[1] = RLSW.clearColor[1]; + v[2] = RLSW.clearColor[2]; + v[3] = RLSW.clearColor[3]; + } break; + case SW_CURRENT_COLOR: + { + v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[0]; + v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[1]; + v[2] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[2]; + v[3] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[3]; + } break; + case SW_CURRENT_TEXTURE_COORDS: + { + v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[0]; + v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[1]; + } break; + case SW_POINT_SIZE: + { + v[0] = 2.0f*RLSW.pointRadius; + } break; + case SW_LINE_WIDTH: + { + v[0] = RLSW.lineWidth; + } break; + case SW_MODELVIEW_MATRIX: + { + for (int i = 0; i < 16; i++) v[i] = RLSW.stackModelview[RLSW.stackModelviewCounter - 1][i]; + + } break; + case SW_PROJECTION_MATRIX: + { + for (int i = 0; i < 16; i++) v[i] = RLSW.stackProjection[RLSW.stackProjectionCounter - 1][i]; + + } break; + case SW_TEXTURE_MATRIX: + { + for (int i = 0; i < 16; i++) v[i] = RLSW.stackTexture[RLSW.stackTextureCounter - 1][i]; + + } break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } -const char* swGetString(SWget name) +const char *swGetString(SWget name) { - const char* result = NULL; + const char *result = NULL; - switch (name) { - case SW_VENDOR: - result = "RLSW Header"; - break; - case SW_RENDERER: - result = "RLSW Software Renderer"; - break; - case SW_VERSION: - result = "RLSW 1.0"; - break; - case SW_EXTENSIONS: - result = "None"; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - break; + switch (name) + { + case SW_VENDOR: result = "RLSW Header"; break; + case SW_RENDERER: result = "RLSW Software Renderer"; break; + case SW_VERSION: result = "RLSW 1.0"; break; + case SW_EXTENSIONS: result = "None"; break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } return result; @@ -4338,7 +4108,8 @@ SWerrcode swGetError(void) void swViewport(int x, int y, int width, int height) { - if (width < 0 || height < 0) { + if ((width < 0) || (height < 0)) + { RLSW.errCode = SW_INVALID_VALUE; return; } @@ -4346,8 +4117,8 @@ void swViewport(int x, int y, int width, int height) RLSW.vpSize[0] = width; RLSW.vpSize[1] = height; - RLSW.vpHalfSize[0] = (int)(width / 2.0f + 0.5f); - RLSW.vpHalfSize[1] = (int)(height / 2.0f + 0.5f); + RLSW.vpHalfSize[0] = (int)(width/2.0f + 0.5f); + RLSW.vpHalfSize[1] = (int)(height/2.0f + 0.5f); RLSW.vpCenter[0] = x + RLSW.vpHalfSize[0]; RLSW.vpCenter[1] = y + RLSW.vpHalfSize[1]; @@ -4360,7 +4131,8 @@ void swViewport(int x, int y, int width, int height) void swScissor(int x, int y, int width, int height) { - if (width < 0 || height < 0) { + if ((width < 0) || (height < 0)) + { RLSW.errCode = SW_INVALID_VALUE; return; } @@ -4370,10 +4142,10 @@ void swScissor(int x, int y, int width, int height) RLSW.scMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1); RLSW.scMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1); - RLSW.scClipMin[0] = (2.0f * (float)RLSW.scMin[0] / (float)RLSW.vpSize[0]) - 1.0f; - RLSW.scClipMax[0] = (2.0f * (float)RLSW.scMax[0] / (float)RLSW.vpSize[0]) - 1.0f; - RLSW.scClipMax[1] = 1.0f - (2.0f * (float)RLSW.scMin[1] / (float)RLSW.vpSize[1]); - RLSW.scClipMin[1] = 1.0f - (2.0f * (float)RLSW.scMax[1] / (float)RLSW.vpSize[1]); + RLSW.scClipMin[0] = (2.0f*(float)RLSW.scMin[0]/(float)RLSW.vpSize[0]) - 1.0f; + RLSW.scClipMax[0] = (2.0f*(float)RLSW.scMax[0]/(float)RLSW.vpSize[0]) - 1.0f; + RLSW.scClipMax[1] = 1.0f - (2.0f*(float)RLSW.scMin[1]/(float)RLSW.vpSize[1]); + RLSW.scClipMin[1] = 1.0f - (2.0f*(float)RLSW.scMax[1]/(float)RLSW.vpSize[1]); } void swClearColor(float r, float g, float b, float a) @@ -4386,126 +4158,92 @@ void swClearColor(float r, float g, float b, float a) void swClear(uint32_t bitmask) { - int size = RLSW.framebuffer.width * RLSW.framebuffer.height; + int size = RLSW.framebuffer.width*RLSW.framebuffer.height; - if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) { - sw_framebuffer_fill( - RLSW.framebuffer.color, RLSW.framebuffer.depth, - size, RLSW.clearColor, RLSW.clearDepth - ); + if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) + { + sw_framebuffer_fill(RLSW.framebuffer.color, RLSW.framebuffer.depth,size, RLSW.clearColor, RLSW.clearDepth); } - else if (bitmask & (SW_COLOR_BUFFER_BIT)) { + else if (bitmask & (SW_COLOR_BUFFER_BIT)) + { sw_framebuffer_fill_color(RLSW.framebuffer.color, size, RLSW.clearColor); } - else if (bitmask & SW_DEPTH_BUFFER_BIT) { + else if (bitmask & SW_DEPTH_BUFFER_BIT) + { sw_framebuffer_fill_depth(RLSW.framebuffer.depth, size, RLSW.clearDepth); } } void swBlendFunc(SWfactor sfactor, SWfactor dfactor) { - if (!sw_is_blend_src_factor_valid(sfactor) - || !sw_is_blend_dst_factor_valid(dfactor)) { + if (!sw_is_blend_src_factor_valid(sfactor) || + !sw_is_blend_dst_factor_valid(dfactor)) + { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.srcFactor = sfactor; RLSW.dstFactor = dfactor; - switch (sfactor) { - case SW_ZERO: - RLSW.srcFactorFunc = sw_factor_zero; - break; - case SW_ONE: - RLSW.srcFactorFunc = sw_factor_one; - break; - case SW_SRC_COLOR: - RLSW.srcFactorFunc = sw_factor_src_color; - break; - case SW_ONE_MINUS_SRC_COLOR: - RLSW.srcFactorFunc = sw_factor_one_minus_src_color; - break; - case SW_SRC_ALPHA: - RLSW.srcFactorFunc = sw_factor_src_alpha; - break; - case SW_ONE_MINUS_SRC_ALPHA: - RLSW.srcFactorFunc = sw_factor_one_minus_src_alpha; - break; - case SW_DST_ALPHA: - RLSW.srcFactorFunc = sw_factor_dst_alpha; - break; - case SW_ONE_MINUS_DST_ALPHA: - RLSW.srcFactorFunc = sw_factor_one_minus_dst_alpha; - break; - case SW_DST_COLOR: - RLSW.srcFactorFunc = sw_factor_dst_color; - break; - case SW_ONE_MINUS_DST_COLOR: - RLSW.srcFactorFunc = sw_factor_one_minus_dst_color; - break; - case SW_SRC_ALPHA_SATURATE: - RLSW.srcFactorFunc = sw_factor_src_alpha_saturate; - break; + switch (sfactor) + { + case SW_ZERO: RLSW.srcFactorFunc = sw_factor_zero; break; + case SW_ONE: RLSW.srcFactorFunc = sw_factor_one; break; + case SW_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_src_color; break; + case SW_ONE_MINUS_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_src_color; break; + case SW_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_src_alpha; break; + case SW_ONE_MINUS_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_src_alpha; break; + case SW_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_dst_alpha; break; + case SW_ONE_MINUS_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_dst_alpha; break; + case SW_DST_COLOR: RLSW.srcFactorFunc = sw_factor_dst_color; break; + case SW_ONE_MINUS_DST_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_dst_color; break; + case SW_SRC_ALPHA_SATURATE: RLSW.srcFactorFunc = sw_factor_src_alpha_saturate; break; + default: break; } - switch (dfactor) { - case SW_ZERO: - RLSW.dstFactorFunc = sw_factor_zero; - break; - case SW_ONE: - RLSW.dstFactorFunc = sw_factor_one; - break; - case SW_SRC_COLOR: - RLSW.dstFactorFunc = sw_factor_src_color; - break; - case SW_ONE_MINUS_SRC_COLOR: - RLSW.dstFactorFunc = sw_factor_one_minus_src_color; - break; - case SW_SRC_ALPHA: - RLSW.dstFactorFunc = sw_factor_src_alpha; - break; - case SW_ONE_MINUS_SRC_ALPHA: - RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; - break; - case SW_DST_ALPHA: - RLSW.dstFactorFunc = sw_factor_dst_alpha; - break; - case SW_ONE_MINUS_DST_ALPHA: - RLSW.dstFactorFunc = sw_factor_one_minus_dst_alpha; - break; - case SW_DST_COLOR: - RLSW.dstFactorFunc = sw_factor_dst_color; - break; - case SW_ONE_MINUS_DST_COLOR: - RLSW.dstFactorFunc = sw_factor_one_minus_dst_color; - break; - case SW_SRC_ALPHA_SATURATE: - // NOTE: Should never be reached - break; + switch (dfactor) + { + case SW_ZERO: RLSW.dstFactorFunc = sw_factor_zero; break; + case SW_ONE: RLSW.dstFactorFunc = sw_factor_one; break; + case SW_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_src_color; break; + case SW_ONE_MINUS_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_src_color; break; + case SW_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_src_alpha; break; + case SW_ONE_MINUS_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; break; + case SW_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_dst_alpha; break; + case SW_ONE_MINUS_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_dst_alpha; break; + case SW_DST_COLOR: RLSW.dstFactorFunc = sw_factor_dst_color; break; + case SW_ONE_MINUS_DST_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_dst_color; break; + case SW_SRC_ALPHA_SATURATE: break; + default: break; } } void swPolygonMode(SWpoly mode) { - if (!sw_is_poly_mode_valid(mode)) { + if (!sw_is_poly_mode_valid(mode)) + { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.polyMode = mode; } void swCullFace(SWface face) { - if (!sw_is_face_valid(face)) { + if (!sw_is_face_valid(face)) + { RLSW.errCode = SW_INVALID_ENUM; return; } + RLSW.cullFace = face; } void swPointSize(float size) { - RLSW.pointRadius = floorf(size * 0.5f); + RLSW.pointRadius = floorf(size*0.5f); } void swLineWidth(float width) @@ -4515,19 +4253,12 @@ void swLineWidth(float width) void swMatrixMode(SWmatrix mode) { - switch (mode) { - case SW_PROJECTION: - RLSW.currentMatrix = &RLSW.stackProjection[RLSW.stackProjectionCounter - 1]; - break; - case SW_MODELVIEW: - RLSW.currentMatrix = &RLSW.stackModelview[RLSW.stackModelviewCounter - 1]; - break; - case SW_TEXTURE: - RLSW.currentMatrix = &RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - break; - default: - RLSW.errCode = SW_INVALID_ENUM; - return; + switch (mode) + { + case SW_PROJECTION: RLSW.currentMatrix = &RLSW.stackProjection[RLSW.stackProjectionCounter - 1]; break; + case SW_MODELVIEW: RLSW.currentMatrix = &RLSW.stackModelview[RLSW.stackModelviewCounter - 1]; break; + case SW_TEXTURE: RLSW.currentMatrix = &RLSW.stackTexture[RLSW.stackTextureCounter - 1]; break; + default: RLSW.errCode = SW_INVALID_ENUM; return; } RLSW.currentMatrixMode = mode; @@ -4535,10 +4266,12 @@ void swMatrixMode(SWmatrix mode) void swPushMatrix(void) { - switch (RLSW.currentMatrixMode) { - case SW_PROJECTION: + switch (RLSW.currentMatrixMode) + { + case SW_PROJECTION: { - if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE) { + if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE) + { RLSW.errCode = SW_STACK_OVERFLOW; return; } @@ -4546,16 +4279,17 @@ void swPushMatrix(void) int iOld = RLSW.stackProjectionCounter - 1; int iNew = RLSW.stackProjectionCounter++; - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { RLSW.stackProjection[iNew][i] = RLSW.stackProjection[iOld][i]; } RLSW.currentMatrix = &RLSW.stackProjection[iNew]; - } - break; - case SW_MODELVIEW: + } break; + case SW_MODELVIEW: { - if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE) { + if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE) + { RLSW.errCode = SW_STACK_OVERFLOW; return; } @@ -4563,16 +4297,17 @@ void swPushMatrix(void) int iOld = RLSW.stackModelviewCounter - 1; int iNew = RLSW.stackModelviewCounter++; - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { RLSW.stackModelview[iNew][i] = RLSW.stackModelview[iOld][i]; } RLSW.currentMatrix = &RLSW.stackModelview[iNew]; - } - break; - case SW_TEXTURE: + } break; + case SW_TEXTURE: { - if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE) { + if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE) + { RLSW.errCode = SW_STACK_OVERFLOW; return; } @@ -4580,60 +4315,61 @@ void swPushMatrix(void) int iOld = RLSW.stackTextureCounter - 1; int iNew = RLSW.stackTextureCounter++; - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) + { RLSW.stackTexture[iNew][i] = RLSW.stackTexture[iOld][i]; } RLSW.currentMatrix = &RLSW.stackTexture[iNew]; - } - break; + } break; + default: break; } } void swPopMatrix(void) { - switch (RLSW.currentMatrixMode) { - case SW_PROJECTION: + switch (RLSW.currentMatrixMode) + { + case SW_PROJECTION: { - if (RLSW.stackProjectionCounter <= 0) { + if (RLSW.stackProjectionCounter <= 0) + { RLSW.errCode = SW_STACK_UNDERFLOW; return; } RLSW.currentMatrix = &RLSW.stackProjection[--RLSW.stackProjectionCounter]; RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed - } - break; - case SW_MODELVIEW: + } break; + case SW_MODELVIEW: { - if (RLSW.stackModelviewCounter <= 0) { + if (RLSW.stackModelviewCounter <= 0) + { RLSW.errCode = SW_STACK_UNDERFLOW; return; } RLSW.currentMatrix = &RLSW.stackModelview[--RLSW.stackModelviewCounter]; RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed - } - break; - case SW_TEXTURE: + } break; + case SW_TEXTURE: { - if (RLSW.stackTextureCounter <= 0) { + if (RLSW.stackTextureCounter <= 0) + { RLSW.errCode = SW_STACK_UNDERFLOW; return; } RLSW.currentMatrix = &RLSW.stackTexture[--RLSW.stackTextureCounter]; - } - break; + } break; + default: break; } } void swLoadIdentity(void) { sw_matrix_id(*RLSW.currentMatrix); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } void swTranslatef(float x, float y, float z) @@ -4647,9 +4383,7 @@ void swTranslatef(float x, float y, float z) sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } void swRotatef(float angle, float x, float y, float z) @@ -4658,7 +4392,8 @@ void swRotatef(float angle, float x, float y, float z) float lengthSq = x*x + y*y + z*z; - if (lengthSq != 1.0f && lengthSq != 0.0f) { + if (lengthSq != 1.0f && lengthSq != 0.0f) + { float invLength = 1.0f/sqrtf(lengthSq); x *= invLength; y *= invLength; @@ -4693,9 +4428,7 @@ void swRotatef(float angle, float x, float y, float z) sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } void swScalef(float x, float y, float z) @@ -4709,18 +4442,14 @@ void swScalef(float x, float y, float z) sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } -void swMultMatrixf(const float* mat) +void swMultMatrixf(const float *mat) { sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } void swFrustum(double left, double right, double bottom, double top, double znear, double zfar) @@ -4731,19 +4460,19 @@ void swFrustum(double left, double right, double bottom, double top, double znea double tb = top - bottom; double fn = zfar - znear; - mat[0] = (znear*2.0)/rl; + mat[0] = (float)(znear*2.0)/rl; mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; - mat[5] = (znear*2.0)/tb; + mat[5] = (float)(znear*2.0)/tb; mat[6] = 0.0f; mat[7] = 0.0f; - mat[8] = (right + left)/rl; - mat[9] = (top + bottom)/tb; - mat[10] = -(zfar + znear)/fn; + mat[8] = (float)(right + left)/rl; + mat[9] = (float)(top + bottom)/tb; + mat[10] = -(float)(zfar + znear)/fn; mat[11] = -1.0f; mat[12] = 0.0f; @@ -4753,9 +4482,7 @@ void swFrustum(double left, double right, double bottom, double top, double znea sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } void swOrtho(double left, double right, double bottom, double top, double znear, double zfar) @@ -4766,72 +4493,60 @@ void swOrtho(double left, double right, double bottom, double top, double znear, double tb = top - bottom; double fn = zfar - znear; - mat[0] = 2.0f/rl; + mat[0] = 2.0f/(float)rl; mat[1] = 0.0f; mat[2] = 0.0f; mat[3] = 0.0f; mat[4] = 0.0f; - mat[5] = 2.0f/tb; + mat[5] = 2.0f/(float)tb; mat[6] = 0.0f; mat[7] = 0.0f; mat[8] = 0.0f; mat[9] = 0.0f; - mat[10] = -2.0f/fn; + mat[10] = -2.0f/(float)fn; mat[11] = 0.0f; - mat[12] = -(left + right)/rl; - mat[13] = -(top + bottom)/tb; - mat[14] = -(zfar + znear)/fn; + mat[12] = -(float)(left + right)/rl; + mat[13] = -(float)(top + bottom)/tb; + mat[14] = -(float)(zfar + znear)/fn; mat[15] = 1.0f; sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat); - if (RLSW.currentMatrixMode != SW_TEXTURE) { - RLSW.isDirtyMVP = true; - } + if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true; } void swBegin(SWdraw mode) { - /* --- Check if the draw mode is valid --- */ - - if (!sw_is_draw_mode_valid(mode)) { + // Check if the draw mode is valid + if (!sw_is_draw_mode_valid(mode)) + { RLSW.errCode = SW_INVALID_ENUM; return; } - /* --- Recalculate the MVP if this is needed --- */ - - if (RLSW.isDirtyMVP) { - sw_matrix_mul_rst( - RLSW.matMVP, + // Recalculate the MVP if this is needed + if (RLSW.isDirtyMVP) + { + sw_matrix_mul_rst(RLSW.matMVP, RLSW.stackModelview[RLSW.stackModelviewCounter - 1], - RLSW.stackProjection[RLSW.stackProjectionCounter - 1] - ); + RLSW.stackProjection[RLSW.stackProjectionCounter - 1]); + RLSW.isDirtyMVP = false; } - /* --- Obtaining the number of vertices needed for this primitive --- */ - - switch (mode) { - case SW_POINTS: - RLSW.reqVertices = 1; - break; - case SW_LINES: - RLSW.reqVertices = 2; - break; - case SW_TRIANGLES: - RLSW.reqVertices = 3; - break; - case SW_QUADS: - RLSW.reqVertices = 4; - break; + // Obtain the number of vertices needed for this primitive + switch (mode) + { + case SW_POINTS: RLSW.reqVertices = 1; break; + case SW_LINES: RLSW.reqVertices = 2; break; + case SW_TRIANGLES: RLSW.reqVertices = 3; break; + case SW_QUADS: RLSW.reqVertices = 4; break; } - - /* --- Initialize some values --- */ + // Initialize required values RLSW.vertexCounter = 0; RLSW.drawMode = mode; } @@ -4853,7 +4568,7 @@ void swVertex2f(float x, float y) swVertex4fv(v); } -void swVertex2fv(const float* v) +void swVertex2fv(const float *v) { const float v4[4] = { v[0], v[1], 0.0f, 1.0f }; swVertex4fv(v4); @@ -4871,7 +4586,7 @@ void swVertex3f(float x, float y, float z) swVertex4fv(v); } -void swVertex3fv(const float* v) +void swVertex3fv(const float *v) { const float v4[4] = { v[0], v[1], v[2], 1.0f }; swVertex4fv(v4); @@ -4889,49 +4604,34 @@ void swVertex4f(float x, float y, float z, float w) swVertex4fv(v); } -void swVertex4fv(const float* v) +void swVertex4fv(const float *v) { - /* --- Copy the position in the current vertex --- */ + // Copy the position in the current vertex + sw_vertex_t *vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; + for (int i = 0; i < 4; i++) vertex->position[i] = v[i]; - sw_vertex_t* vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++]; + // Copy additonal vertex data + for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.current.texcoord[i]; + for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.current.color[i]; - for (int i = 0; i < 4; i++) { - vertex->position[i] = v[i]; - } + // Calculation of homogeneous coordinates + const float *m = RLSW.matMVP; + vertex->homogeneous[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3]; + vertex->homogeneous[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3]; + vertex->homogeneous[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3]; + vertex->homogeneous[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3]; - /* --- Copy additonal vertex data --- */ - - for (int i = 0; i < 2; i++) { - vertex->texcoord[i] = RLSW.current.texcoord[i]; - } - - for (int i = 0; i < 4; i++) { - vertex->color[i] = RLSW.current.color[i]; - } - - /* --- Calculation of homogeneous coordinates --- */ - - const float* m = RLSW.matMVP; - - vertex->homogeneous[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3]; - vertex->homogeneous[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3]; - vertex->homogeneous[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3]; - vertex->homogeneous[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3]; - - /* --- Immediate rendering of the primitive if the required number is reached --- */ - - if (RLSW.vertexCounter == RLSW.reqVertices) { - switch (RLSW.polyMode) { - case SW_FILL: - sw_poly_fill_render(); - break; - case SW_LINE: - sw_poly_line_render(); - break; - case SW_POINT: - sw_poly_point_render(); - break; + // Immediate rendering of the primitive if the required number is reached + if (RLSW.vertexCounter == RLSW.reqVertices) + { + switch (RLSW.polyMode) + { + case SW_FILL: sw_poly_fill_render(); break; + case SW_LINE: sw_poly_line_render(); break; + case SW_POINT: sw_poly_point_render(); break; + default: break; } + RLSW.vertexCounter = 0; } } @@ -4939,20 +4639,20 @@ void swVertex4fv(const float* v) void swColor3ub(uint8_t r, uint8_t g, uint8_t b) { float cv[4]; - cv[0] = (float)r / 255; - cv[1] = (float)g / 255; - cv[2] = (float)b / 255; + cv[0] = (float)r/255; + cv[1] = (float)g/255; + cv[2] = (float)b/255; cv[3] = 1.0f; swColor4fv(cv); } -void swColor3ubv(const uint8_t* v) +void swColor3ubv(const uint8_t *v) { float cv[4]; - cv[0] = (float)v[0] / 255; - cv[1] = (float)v[1] / 255; - cv[2] = (float)v[2] / 255; + cv[0] = (float)v[0]/255; + cv[1] = (float)v[1]/255; + cv[2] = (float)v[2]/255; cv[3] = 1.0f; swColor4fv(cv); @@ -4969,7 +4669,7 @@ void swColor3f(float r, float g, float b) swColor4fv(cv); } -void swColor3fv(const float* v) +void swColor3fv(const float *v) { float cv[4]; cv[0] = v[0]; @@ -4983,21 +4683,21 @@ void swColor3fv(const float* v) void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { float cv[4]; - cv[0] = (float)r / 255; - cv[1] = (float)g / 255; - cv[2] = (float)b / 255; - cv[3] = (float)a / 255; + cv[0] = (float)r/255; + cv[1] = (float)g/255; + cv[2] = (float)b/255; + cv[3] = (float)a/255; swColor4fv(cv); } -void swColor4ubv(const uint8_t* v) +void swColor4ubv(const uint8_t *v) { float cv[4]; - cv[0] = (float)v[0] / 255; - cv[1] = (float)v[1] / 255; - cv[2] = (float)v[2] / 255; - cv[3] = (float)v[3] / 255; + cv[0] = (float)v[0]/255; + cv[1] = (float)v[1]/255; + cv[2] = (float)v[2]/255; + cv[3] = (float)v[3]/255; swColor4fv(cv); } @@ -5013,49 +4713,42 @@ void swColor4f(float r, float g, float b, float a) swColor4fv(cv); } -void swColor4fv(const float* v) +void swColor4fv(const float *v) { - for (int i = 0; i < 4; i++) { - RLSW.current.color[i] = v[i]; - } + for (int i = 0; i < 4; i++) RLSW.current.color[i] = v[i]; } void swTexCoord2f(float u, float v) { - const float* m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - RLSW.current.texcoord[0] = m[0] * u + m[4] * v + m[12]; - RLSW.current.texcoord[1] = m[1] * u + m[5] * v + m[13]; + RLSW.current.texcoord[0] = m[0]*u + m[4]*v + m[12]; + RLSW.current.texcoord[1] = m[1]*u + m[5]*v + m[13]; } -void swTexCoord2fv(const float* v) +void swTexCoord2fv(const float *v) { - const float* m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; + const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1]; - RLSW.current.texcoord[0] = m[0] * v[0] + m[4] * v[1] + m[12]; - RLSW.current.texcoord[1] = m[1] * v[0] + m[5] * v[1] + m[13]; + RLSW.current.texcoord[0] = m[0]*v[0] + m[4]*v[1] + m[12]; + RLSW.current.texcoord[1] = m[1]*v[0] + m[5]*v[1] + m[13]; } void swBindArray(SWarray type, void *buffer) { - switch (type) { - case SW_VERTEX_ARRAY: - RLSW.array.positions = buffer; - break; - case SW_TEXTURE_COORD_ARRAY: - RLSW.array.texcoords = buffer; - break; - case SW_COLOR_ARRAY: - RLSW.array.colors = buffer; - break; - default: - break; + switch (type) + { + case SW_VERTEX_ARRAY: RLSW.array.positions = buffer; break; + case SW_TEXTURE_COORD_ARRAY: RLSW.array.texcoords = buffer; break; + case SW_COLOR_ARRAY: RLSW.array.colors = buffer; break; + default: break; } } void swDrawArrays(SWdraw mode, int offset, int count) { - if (RLSW.array.positions == 0) { + if (RLSW.array.positions == 0) + { RLSW.errCode = SW_INVALID_OPERATION; return; } @@ -5065,98 +4758,105 @@ void swDrawArrays(SWdraw mode, int offset, int count) swTexCoord2f(0.0f, 0.0f); swColor4f(1.0f, 1.0f, 1.0f, 1.0f); - for (int i = offset; i < count; i++) { - if (RLSW.array.texcoords) swTexCoord2fv(RLSW.array.texcoords + 2 * i); - if (RLSW.array.colors) swColor4ubv(RLSW.array.colors + 4 * i); - swVertex3fv(RLSW.array.positions + 3 * i); + for (int i = offset; i < count; i++) + { + if (RLSW.array.texcoords) swTexCoord2fv(RLSW.array.texcoords + 2*i); + if (RLSW.array.colors) swColor4ubv(RLSW.array.colors + 4*i); + swVertex3fv(RLSW.array.positions + 3*i); } } swEnd(); } -void swGenTextures(int count, uint32_t* textures) +void swGenTextures(int count, uint32_t *textures) { - if (count == 0 || textures == NULL) { - return; - } + if ((count == 0) || (textures == NULL)) return; - for (int i = 0; i < count; i++) { - if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) { - RLSW.errCode = SW_STACK_OVERFLOW; //< Out of memory, not really stack overflow + for (int i = 0; i < count; i++) + { + if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES) + { + RLSW.errCode = SW_STACK_OVERFLOW; // WARNING: Out of memory, not really stack overflow return; } + uint32_t id = 0; - if (RLSW.freeTextureIdCount > 0) { - id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; - } - else { - id = RLSW.loadedTextureCount++; - } + if (RLSW.freeTextureIdCount > 0) id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount]; + else id = RLSW.loadedTextureCount++; + RLSW.loadedTextures[id] = RLSW.loadedTextures[0]; textures[i] = id; } } -void swDeleteTextures(int count, uint32_t* textures) +void swDeleteTextures(int count, uint32_t *textures) { - if (count == 0 || textures == NULL) { - return; - } + if ((count == 0) || (textures == NULL)) return; - for (int i = 0; i < count; i++) { - if (!sw_is_texture_valid(textures[i])) { + for (int i = 0; i < count; i++) + { + if (!sw_is_texture_valid(textures[i])) + { RLSW.errCode = SW_INVALID_VALUE; continue; } - if (RLSW.loadedTextures[textures[i]].copy) { + + if (RLSW.loadedTextures[textures[i]].copy) + { SW_FREE(RLSW.loadedTextures[textures[i]].pixels.ptr); } + RLSW.loadedTextures[textures[i]].pixels.cptr = NULL; RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = textures[i]; } } -void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void* data) +void swTexImage2D(int width, int height, SWformat format, SWtype type, bool copy, const void *data) { uint32_t id = RLSW.currentTexture; - if (!sw_is_texture_valid(id)) { + if (!sw_is_texture_valid(id)) + { RLSW.errCode = SW_INVALID_VALUE; return; } int pixelFormat = sw_get_pixel_format(format, type); - if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN) { + if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN) + { RLSW.errCode = SW_INVALID_ENUM; return; } - sw_texture_t* texture = &RLSW.loadedTextures[id]; + sw_texture_t *texture = &RLSW.loadedTextures[id]; - if (copy) { + if (copy) + { int bytes = sw_get_pixel_bytes(pixelFormat); - int size = bytes * width * height; + int size = bytes*width*height; texture->pixels.ptr = SW_MALLOC(size); - if (texture->pixels.ptr == NULL) { - RLSW.errCode = SW_STACK_OVERFLOW; //< Out of memory... + + if (texture->pixels.ptr == NULL) + { + RLSW.errCode = SW_STACK_OVERFLOW; // WARING: Out of memory... return; } - for (int i = 0; i < size; i++) { - ((uint8_t*)texture->pixels.ptr)[i] = ((uint8_t*)data)[i]; + + for (int i = 0; i < size; i++) + { + ((uint8_t *)texture->pixels.ptr)[i] = ((uint8_t *)data)[i]; } } - else { - texture->pixels.cptr = data; - } + else texture->pixels.cptr = data; texture->width = width; texture->height = height; texture->wMinus1 = width - 1; texture->hMinus1 = height - 1; texture->format = pixelFormat; - texture->tx = 1.0f / width; - texture->ty = 1.0f / height; + texture->tx = 1.0f/width; + texture->ty = 1.0f/height; texture->copy = copy; } @@ -5164,62 +4864,70 @@ void swTexParameteri(int param, int value) { uint32_t id = RLSW.currentTexture; - if (!sw_is_texture_valid(id)) { + if (!sw_is_texture_valid(id)) + { RLSW.errCode = SW_INVALID_VALUE; return; } - sw_texture_t* texture = &RLSW.loadedTextures[id]; + sw_texture_t *texture = &RLSW.loadedTextures[id]; - switch (param) { + switch (param) + { + case SW_TEXTURE_MIN_FILTER: + { + if (!sw_is_texture_filter_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } - case SW_TEXTURE_MIN_FILTER: - if (!sw_is_texture_filter_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->minFilter = value; - break; + texture->minFilter = value; + } break; + case SW_TEXTURE_MAG_FILTER: + { + if (!sw_is_texture_filter_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } - case SW_TEXTURE_MAG_FILTER: - if (!sw_is_texture_filter_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->magFilter = value; - break; + texture->magFilter = value; + } break; + case SW_TEXTURE_WRAP_S: + { + if (!sw_is_texture_wrap_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } - case SW_TEXTURE_WRAP_S: - if (!sw_is_texture_wrap_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->sWrap = value; - break; - - case SW_TEXTURE_WRAP_T: - if (!sw_is_texture_wrap_valid(value)) { - RLSW.errCode = SW_INVALID_ENUM; - return; - } - texture->tWrap = value; - break; - - default: - RLSW.errCode = SW_INVALID_ENUM; - return; + texture->sWrap = value; + } break; + case SW_TEXTURE_WRAP_T: + { + if (!sw_is_texture_wrap_valid(value)) + { + RLSW.errCode = SW_INVALID_ENUM; + return; + } + texture->tWrap = value; + } break; + default: RLSW.errCode = SW_INVALID_ENUM; break; } } void swBindTexture(uint32_t id) { - if (id >= SW_MAX_TEXTURES) { + if (id >= SW_MAX_TEXTURES) + { RLSW.errCode = SW_INVALID_VALUE; return; } - if (RLSW.loadedTextures[id].pixels.cptr == NULL) { + if (RLSW.loadedTextures[id].pixels.cptr == NULL) + { RLSW.errCode = SW_INVALID_OPERATION; return; } @@ -5227,4 +4935,4 @@ void swBindTexture(uint32_t id) RLSW.currentTexture = id; } -#endif // RLSW_IMPL +#endif // RLSW_IMPLEMENTATION diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 00a6ccc47..6655db460 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1233,7 +1233,7 @@ void SwapScreenBuffer(void) { #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // NOTE: We use a preprocessor condition here because `rlCopyFramebuffer` is only declared for software rendering - SDL_Surface* surface = SDL_GetWindowSurface(platform.window); + SDL_Surface *surface = SDL_GetWindowSurface(platform.window); rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels); SDL_UpdateWindowSurface(platform.window); #else diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index fe0ac5fd8..e067fd82a 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -818,14 +818,14 @@ void SwapScreenBuffer(void) platform.prevBO = bo; #else // Software rendering buffer swap - if ((-1 == platform.fd) || !platform.connector || (platform.modeIndex < 0)) + if ((platform.fd == -1) || !platform.connector || (platform.modeIndex < 0)) { TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); return; } // Get the software rendered color buffer - int bufferWidth, bufferHeight; + int bufferWidth = 0, bufferHeight = 0; void *colorBuffer = swGetColorBuffer(&bufferWidth, &bufferHeight); if (!colorBuffer) { @@ -849,7 +849,7 @@ void SwapScreenBuffer(void) #endif // Create a dumb buffer for software rendering - struct drm_mode_create_dumb creq = {0}; + struct drm_mode_create_dumb creq = { 0 }; creq.width = width; creq.height = height; creq.bpp = bpp; @@ -863,28 +863,25 @@ void SwapScreenBuffer(void) // Create framebuffer with the correct format uint32_t fb = 0; - result = drmModeAddFB(platform.fd, - width, height, - depth, bpp, creq.pitch, - creq.handle, &fb); + result = drmModeAddFB(platform.fd, width, height, depth, bpp, creq.pitch, creq.handle, &fb); if (result != 0) { TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d (%s)", result, strerror(errno)); - struct drm_mode_destroy_dumb dreq = {0}; + struct drm_mode_destroy_dumb dreq = { 0 }; dreq.handle = creq.handle; drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); return; } // Map the dumb buffer to copy our software rendered buffer - struct drm_mode_map_dumb mreq = {0}; + struct drm_mode_map_dumb mreq = { 0 }; mreq.handle = creq.handle; result = drmIoctl(platform.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); if (result != 0) { TRACELOG(LOG_ERROR, "DISPLAY: Failed to map dumb buffer: %s", strerror(errno)); drmModeRmFB(platform.fd, fb); - struct drm_mode_destroy_dumb dreq = {0}; + struct drm_mode_destroy_dumb dreq = { 0 }; dreq.handle = creq.handle; drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); return; @@ -896,7 +893,7 @@ void SwapScreenBuffer(void) { TRACELOG(LOG_ERROR, "DISPLAY: Failed to mmap dumb buffer: %s", strerror(errno)); drmModeRmFB(platform.fd, fb); - struct drm_mode_destroy_dumb dreq = {0}; + struct drm_mode_destroy_dumb dreq = { 0 }; dreq.handle = creq.handle; drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); return; @@ -919,10 +916,7 @@ void SwapScreenBuffer(void) // Find a CRTC compatible with the connector uint32_t crtcId = 0; - if (platform.crtc) - { - crtcId = platform.crtc->crtc_id; - } + if (platform.crtc) crtcId = platform.crtc->crtc_id; else { // Find a CRTC that's compatible with this connector @@ -939,10 +933,7 @@ void SwapScreenBuffer(void) // Check which CRTCs are compatible with this connector drmModeEncoder *encoder = NULL; - if (platform.connector->encoder_id) - { - encoder = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); - } + if (platform.connector->encoder_id) encoder = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (encoder && encoder->crtc_id) { @@ -962,6 +953,7 @@ void SwapScreenBuffer(void) platform.crtc = crtc; break; } + if (crtc) drmModeFreeCrtc(crtc); } } @@ -981,9 +973,7 @@ void SwapScreenBuffer(void) } // Set CRTC with better error handling - result = drmModeSetCrtc(platform.fd, crtcId, fb, 0, 0, - &platform.connector->connector_id, 1, - mode); + result = drmModeSetCrtc(platform.fd, crtcId, fb, 0, 0, &platform.connector->connector_id, 1, mode); if (result != 0) { TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d (%s)", result, strerror(errno)); @@ -1001,10 +991,7 @@ void SwapScreenBuffer(void) if (platform.prevFB) { result = drmModeRmFB(platform.fd, platform.prevFB); - if (result != 0) - { - TRACELOG(LOG_WARNING, "DISPLAY: drmModeRmFB() failed with result: %d", result); - } + if (result != 0) TRACELOG(LOG_WARNING, "DISPLAY: drmModeRmFB() failed with result: %d", result); } platform.prevFB = fb; @@ -1249,7 +1236,7 @@ int InitPlatform(void) // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output // WARNING: Accept CONNECTED, UNKNOWN and even those without encoder_id connectors for software mode - if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0)//(con->encoder_id)) + if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0)) { #if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) // For hardware rendering, we need an encoder_id @@ -1259,10 +1246,7 @@ int InitPlatform(void) platform.connector = con; break; } - else - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i); - } + else TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i); #else // For software rendering, we can accept even without encoder_id TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i suitable for software rendering", i); diff --git a/src/rlgl.h b/src/rlgl.h index 5e8cf2bfa..ecd3795a9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -776,8 +776,8 @@ RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attac RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) -RLAPI void rlCopyFramebuffer(int x, int y, int w, int h, int format, void* pixels); -RLAPI void rlResizeFramebuffer(int width, int height); +RLAPI void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels); // Copy framebuffer pixel data to internal buffer +RLAPI void rlResizeFramebuffer(int width, int height); // Resize internal framebuffer #endif // Shaders management @@ -846,7 +846,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_11) #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - #define RLSW_IMPL + #define RLSW_IMPLEMENTATION #define SW_MALLOC(sz) RL_MALLOC(sz) #define SW_REALLOC(ptr, newSz) RL_REALLOC(ptr, newSz) #define SW_FREE(ptr) RL_FREE(ptr) @@ -2357,9 +2357,10 @@ void rlglInit(int width, int height) #endif #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - if (!swInit(width, height)) + int result = swInit(width, height); // Initialize software renderer backend + if (result == 0) { - TRACELOG(RL_LOG_ERROR, "RLGL: Software renderer initialization failed!"); + TRACELOG(RL_LOG_ERROR, "RLSW: Software renderer initialization failed!"); exit(-1); } #endif @@ -2385,14 +2386,14 @@ void rlglClose(void) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) rlUnloadRenderBatch(RLGL.defaultBatch); - rlUnloadShaderDefault(); // Unload default shader + rlUnloadShaderDefault(); // Unload default shader glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId); #endif #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) - swClose(); + swClose(); // Unload sofware renderer resources #endif } @@ -2703,6 +2704,7 @@ void *rlGetProcAddress(const char *procName) int rlGetVersion(void) { int glVersion = 0; + #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) glVersion = RL_OPENGL_11_SOFTWARE; #elif defined(GRAPHICS_API_OPENGL_11) @@ -3747,13 +3749,15 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) } #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE) -void rlCopyFramebuffer(int x, int y, int w, int h, int format, void* pixels) +// Copy framebuffer pixel data to internal buffer +void rlCopyFramebuffer(int x, int y, int width, int height, int format, void* pixels) { unsigned int glInternalFormat, glFormat, glType; - rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - swCopyFramebuffer(x, y, w, h, glFormat, glType, pixels); + rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); // Get OpenGL texture format + swCopyFramebuffer(x, y, width, height, glFormat, glType, pixels); } +// Resize internal framebuffer void rlResizeFramebuffer(int width, int height) { swResizeFramebuffer(width, height); From 37149082e85cfa40550bed7c9b3d53638c71994f Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 29 Sep 2025 18:05:16 +0200 Subject: [PATCH 16/31] Avoid warnings --- src/rtext.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index edfe33c8b..ce9f381c4 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1452,7 +1452,7 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint) char **LoadTextLines(const char *text, int *count) { int lineCount = 1; - int textSize = strlen(text); + int textSize = (int)strlen(text); // Text pass to get required line count for (int i = 0; i < textSize; i++) @@ -1679,7 +1679,7 @@ char *GetTextBetween(const char *text, const char *begin, const char *end) if (beginIndex > -1) { - int beginLen = strlen(begin); + int beginLen = (int)strlen(begin); int endIndex = TextFindIndex(text + beginIndex + beginLen, end); if (endIndex > -1) @@ -1758,15 +1758,15 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c if (beginIndex > -1) { - int beginLen = strlen(begin); + int beginLen = (int)strlen(begin); int endIndex = TextFindIndex(text + beginIndex + beginLen, end); if (endIndex > -1) { endIndex += (beginIndex + beginLen); - int textLen = strlen(text); - int replaceLen = (replacement == NULL)? 0 : strlen(replacement); + int textLen = (int)strlen(text); + int replaceLen = (replacement == NULL)? 0 : (int)strlen(replacement); int toreplaceLen = endIndex - beginIndex - beginLen; result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char)); From 56e32f2c3ed7add455e8c90d158df43b3690a900 Mon Sep 17 00:00:00 2001 From: zero <103244351+zerohorsepower@users.noreply.github.com> Date: Tue, 30 Sep 2025 06:59:44 -0300 Subject: [PATCH 17/31] ADDED: example: `shapes_bullet_hell` (#5218) * add shapes_bullet_hell example * adjust example count --- examples/Makefile | 1 + examples/Makefile.Web | 4 + examples/README.md | 5 +- examples/examples_list.txt | 1 + examples/shapes/shapes_bullet_hell.c | 286 +++++++++++++++++++++++++ examples/shapes/shapes_bullet_hell.png | Bin 0 -> 38660 bytes 6 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 examples/shapes/shapes_bullet_hell.c create mode 100644 examples/shapes/shapes_bullet_hell.png diff --git a/examples/Makefile b/examples/Makefile index 36faed3b3..de02ae085 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -541,6 +541,7 @@ CORE = \ SHAPES = \ shapes/shapes_basic_shapes \ shapes/shapes_bouncing_ball \ + shapes/shapes_bullet_hell \ shapes/shapes_circle_sector_drawing \ shapes/shapes_collision_area \ shapes/shapes_colors_palette \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index b1b1f0779..8c9707274 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -541,6 +541,7 @@ CORE = \ SHAPES = \ shapes/shapes_basic_shapes \ shapes/shapes_bouncing_ball \ + shapes/shapes_bullet_hell \ shapes/shapes_circle_sector_drawing \ shapes/shapes_collision_area \ shapes/shapes_colors_palette \ @@ -807,6 +808,9 @@ shapes/shapes_basic_shapes: shapes/shapes_basic_shapes.c shapes/shapes_bouncing_ball: shapes/shapes_bouncing_ball.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +shapes/shapes_bullet_hell: shapes/shapes_bullet_hell.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + shapes/shapes_circle_sector_drawing: shapes/shapes_circle_sector_drawing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/README.md b/examples/README.md index 48e83c852..92ea9aa07 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 164] +## EXAMPLES COLLECTION [TOTAL: 165] ### category: core [38] @@ -64,7 +64,7 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c | [core_undo_redo](core/core_undo_redo.c) | core_undo_redo | ⭐⭐⭐☆ | 5.5 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) | | [core_input_actions](core/core_input_actions.c) | core_input_actions | ⭐⭐☆☆ | 5.5 | 5.6 | [Jett](https://github.com/JettMonstersGoBoom) | -### category: shapes [20] +### category: shapes [21] Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module. @@ -72,6 +72,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( |-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------| | [shapes_basic_shapes](shapes/shapes_basic_shapes.c) | shapes_basic_shapes | ⭐☆☆☆ | 1.0 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_bouncing_ball](shapes/shapes_bouncing_ball.c) | shapes_bouncing_ball | ⭐☆☆☆ | 2.5 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | +| [shapes_bullet_hell](shapes/shapes_bullet_hell.c) | shapes_bullet_hell | ⭐☆☆☆ | 5.6 | 5.6 | [Zero](https://github.com/zerohorsepower) | | [shapes_colors_palette](shapes/shapes_colors_palette.c) | shapes_colors_palette | ⭐⭐☆☆ | 1.0 | 2.5 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_logo_raylib](shapes/shapes_logo_raylib.c) | shapes_logo_raylib | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_logo_raylib_anim](shapes/shapes_logo_raylib_anim.c) | shapes_logo_raylib_anim | ⭐⭐☆☆ | 2.5 | 4.0 | [Ramon Santamaria](https://github.com/raysan5) | diff --git a/examples/examples_list.txt b/examples/examples_list.txt index b5682123f..6af9bb6eb 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -47,6 +47,7 @@ core;core_undo_redo;★★★☆;5.5;5.6;2025;2025;"Ramon Santamaria";@raysan5 core;core_input_actions;★★☆☆;5.5;5.6;2025;2025;"Jett";@JettMonstersGoBoom shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_bouncing_ball;★☆☆☆;2.5;2.5;2013;2025;"Ramon Santamaria";@raysan5 +shapes;shapes_bullet_hell;★☆☆☆;5.6;5.6;2025;2025;"Zero";@zerohorsepower shapes;shapes_colors_palette;★★☆☆;1.0;2.5;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 shapes;shapes_logo_raylib_anim;★★☆☆;2.5;4.0;2014;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/shapes/shapes_bullet_hell.c b/examples/shapes/shapes_bullet_hell.c new file mode 100644 index 000000000..011be9e84 --- /dev/null +++ b/examples/shapes/shapes_bullet_hell.c @@ -0,0 +1,286 @@ +/******************************************************************************************* +* +* raylib [shapes] example - bullet hell +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Zero (@zerohorsepower) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Zero (@zerohorsepower) +* +********************************************************************************************/ + +#include "raylib.h" +#include // Required for: malloc(), free() +#include // Required for: cosf(), sinf() + +#define MAX_BULLETS 500000 // Max bullets that 800x450 can keep on minimum settings is 130.000 bullets + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct Bullet { + Vector2 position; + Vector2 acceleration; // the amount of pixels to be incremented to position every frame + bool disabled; // skip processing and draw case out of screen + Color color; +} Bullet; + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bullet hell"); + + // Bullet + Bullet *bullets = (Bullet *)malloc(MAX_BULLETS*sizeof(Bullet)); // Bullets array + int bulletCount = 0; + int bulletDisabledCount = 0; // Used to calculate how many bullets are on screen + int bulletRadius = 10; + float bulletSpeed = 3.0f; + int bulletRows = 6; + Color bulletColor[2] = { RED, BLUE }; + + // Spawner + float baseDirection = 0; + int angleIncrement = 5; // After spawn all bullet rows, increment this value on the baseDirection for next the frame + float spawnCooldown = 2; + float spawnCooldownTimer = spawnCooldown; + + // Magic circle + float magicCircleRotation = 0; + + // Used on performance drawing + RenderTexture bulletTexture = LoadRenderTexture(24, 24); + + // Draw circle to bullet texture, then draw bullet using DrawTexture() + // This is being done to improve the performance, since DrawCircle() is slow + BeginDrawing(); + BeginTextureMode(bulletTexture); + DrawCircle(12, 12, bulletRadius, WHITE); + DrawCircleLines(12, 12, bulletRadius, BLACK); + EndTextureMode(); + EndDrawing(); + + bool drawInPerformanceMode = true; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + // Reset the bullet index + // New bullets will replace the old ones that are already disabled due to out-of-screen + if (bulletCount >= MAX_BULLETS) + { + bulletCount = 0; + bulletDisabledCount = 0; + } + + spawnCooldownTimer--; + if (spawnCooldownTimer < 0) + { + spawnCooldownTimer = spawnCooldown; + + // Spawn bullets + float degreesPerRow = 360.0f / bulletRows; + for (int row = 0; row < bulletRows; row++) + { + + if (bulletCount < MAX_BULLETS) + { + + bullets[bulletCount].position = (Vector2){(float) screenWidth/2, (float) screenHeight/2}; + bullets[bulletCount].disabled = false; + bullets[bulletCount].color = bulletColor[row % 2]; + + float bulletDirection = baseDirection + (degreesPerRow * row); + + // bullet speed * bullet direction, this will determine how much pixels will be incremented/decremented + // from the bullet position every frame. Since the bullets doesn't change its direction and speed, + // only need to calculate it at the spawning time. + // 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise. + // Case you want it to be anti-clockwise, add "* -1" at the y acceleration + bullets[bulletCount].acceleration = (Vector2){ + bulletSpeed * cosf(bulletDirection * DEG2RAD), + bulletSpeed * sinf(bulletDirection * DEG2RAD) + }; + + bulletCount++; + } + } + + baseDirection += angleIncrement; + + } + + + // Update bullets position based on its acceleration + for (int i = 0; i < bulletCount; i++) + { + + // Only update bullet if inside the screen + if (!bullets[i].disabled) + { + + bullets[i].position.x += bullets[i].acceleration.x; + bullets[i].position.y += bullets[i].acceleration.y; + + // Disable bullet if out of screen + if + ( + bullets[i].position.x < -bulletRadius*2 || + bullets[i].position.x > screenWidth + bulletRadius*2 || + bullets[i].position.y < -bulletRadius*2 || + bullets[i].position.y > screenHeight + bulletRadius*2 + ) + { + bullets[i].disabled = true; + bulletDisabledCount++; + } + } + } + + // Input + if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) && bulletRows < 359) bulletRows++; + if ((IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) && bulletRows > 1) bulletRows--; + if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) bulletSpeed += 0.25f; + if ((IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) && bulletSpeed > 0.50f) bulletSpeed -= 0.25f; + if (IsKeyPressed(KEY_Z) && spawnCooldown > 1) spawnCooldown--; + if (IsKeyPressed(KEY_X)) spawnCooldown++; + if (IsKeyPressed(KEY_ENTER)) drawInPerformanceMode = !drawInPerformanceMode; + + if (IsKeyDown(KEY_SPACE)) + { + angleIncrement += 1; + angleIncrement %= 360; + } + + if (IsKeyPressed(KEY_C)) + { + bulletCount = 0; + bulletDisabledCount = 0; + } + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw magic circle + magicCircleRotation++; + DrawRectanglePro( + (Rectangle) { (float) screenWidth/2, (float) screenHeight/2, 120, 120 }, + (Vector2) { 60, 60 }, + magicCircleRotation, + PURPLE + ); + DrawRectanglePro( + (Rectangle) { (float) screenWidth/2, (float) screenHeight/2, 120, 120 }, + (Vector2) { 60, 60 }, + magicCircleRotation + 45, + PURPLE + ); + DrawCircleLines(screenWidth/2, screenHeight/2, 70, BLACK); + DrawCircleLines(screenWidth/2, screenHeight/2, 50, BLACK); + DrawCircleLines(screenWidth/2, screenHeight/2, 30, BLACK); + + + // Draw bullets + // DrawInPerformanceMode = draw bullets using DrawTexture, DrawCircle is vary slow + if (drawInPerformanceMode) + { + for (int i = 0; i < bulletCount; i++) + { + // Do not draw disabled bullets (out of screen) + if (!bullets[i].disabled) + { + DrawTexture( + bulletTexture.texture, + bullets[i].position.x - bulletTexture.texture.width*0.5f, + bullets[i].position.y - bulletTexture.texture.height*0.5f, + bullets[i].color + ); + } + } + } else { + + for (int i = 0; i < bulletCount; i++) + { + // Do not draw disabled bullets (out of screen) + if (!bullets[i].disabled) + { + DrawCircleV(bullets[i].position, bulletRadius, bullets[i].color); + DrawCircleLinesV(bullets[i].position, bulletRadius, BLACK); + } + } + } + + // Draw UI + DrawRectangle(10, 10, 280, 150, (Color){0,0, 0, 200 }); + DrawText("Controls:", 20, 20, 10, LIGHTGRAY); + DrawText("- Right/Left or A/D: Change rows number", 40, 40, 10, LIGHTGRAY); + DrawText("- Up/Down or W/S: Change bullet speed", 40, 60, 10, LIGHTGRAY); + DrawText("- Z or X: Change spawn cooldown", 40, 80, 10, LIGHTGRAY); + DrawText("- Space (Hold): Change the angle increment", 40, 100, 10, LIGHTGRAY); + DrawText("- Enter: Switch draw method (Performance)", 40, 120, 10, LIGHTGRAY); + DrawText("- C: Clear bullets", 40, 140, 10, LIGHTGRAY); + + DrawRectangle(610, 10, 170, 30, (Color){0,0, 0, 200 }); + if (drawInPerformanceMode) + { + DrawText("Draw method: DrawTexture(*)", 620, 20, 10, GREEN); + } else { + DrawText("Draw method: DrawCircle(*)", 620, 20, 10, RED); + } + + + DrawRectangle(135, 410, 530, 30, (Color){0,0, 0, 200 }); + DrawText( + TextFormat( + "[ FPS: %d, Bullets: %d, Rows: %d, Bullet speed: %.2f, Angle increment per frame: %d, Cooldown: %.0f ]", + GetFPS(), bulletCount - bulletDisabledCount, bulletRows, bulletSpeed, angleIncrement, spawnCooldown + ), + 155, + 420, + 10, + GREEN + ); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + UnloadRenderTexture(bulletTexture); + + free(bullets); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_bullet_hell.png b/examples/shapes/shapes_bullet_hell.png new file mode 100644 index 0000000000000000000000000000000000000000..cf8e5d8f638b7ef0ef5d4269cbdae98a6c2d945a GIT binary patch literal 38660 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 Sv)Ds0@bmQ!@VY|dWc&|@=gc?& literal 0 HcmV?d00001 From 5d9352a0a195a3275d07a54b0fe9e2c1fa619168 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Sep 2025 18:27:59 +0200 Subject: [PATCH 18/31] Update shapes_bullet_hell.c --- examples/shapes/shapes_bullet_hell.c | 140 ++++++++++----------------- 1 file changed, 51 insertions(+), 89 deletions(-) diff --git a/examples/shapes/shapes_bullet_hell.c b/examples/shapes/shapes_bullet_hell.c index 011be9e84..2635ee02c 100644 --- a/examples/shapes/shapes_bullet_hell.c +++ b/examples/shapes/shapes_bullet_hell.c @@ -16,26 +16,22 @@ ********************************************************************************************/ #include "raylib.h" -#include // Required for: malloc(), free() -#include // Required for: cosf(), sinf() -#define MAX_BULLETS 500000 // Max bullets that 800x450 can keep on minimum settings is 130.000 bullets +#include // Required for: calloc(), free() +#include // Required for: cosf(), sinf() + +#define MAX_BULLETS 500000 // Max bullets to be processed //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct Bullet { - Vector2 position; - Vector2 acceleration; // the amount of pixels to be incremented to position every frame - bool disabled; // skip processing and draw case out of screen - Color color; + Vector2 position; // Bullet position on screen + Vector2 acceleration; // Amount of pixels to be incremented to position every frame + bool disabled; // Skip processing and draw case out of screen + Color color; // Bullet color } Bullet; -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- - - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -48,8 +44,8 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bullet hell"); - // Bullet - Bullet *bullets = (Bullet *)malloc(MAX_BULLETS*sizeof(Bullet)); // Bullets array + // Bullets definition + Bullet *bullets = (Bullet *)RL_CALLOC(MAX_BULLETS, sizeof(Bullet)); // Bullets array int bulletCount = 0; int bulletDisabledCount = 0; // Used to calculate how many bullets are on screen int bulletRadius = 10; @@ -57,7 +53,7 @@ int main(void) int bulletRows = 6; Color bulletColor[2] = { RED, BLUE }; - // Spawner + // Spawner variables float baseDirection = 0; int angleIncrement = 5; // After spawn all bullet rows, increment this value on the baseDirection for next the frame float spawnCooldown = 2; @@ -70,15 +66,13 @@ int main(void) RenderTexture bulletTexture = LoadRenderTexture(24, 24); // Draw circle to bullet texture, then draw bullet using DrawTexture() - // This is being done to improve the performance, since DrawCircle() is slow - BeginDrawing(); + // NOTE: This is done to improve the performance, since DrawCircle() is very slow BeginTextureMode(bulletTexture); - DrawCircle(12, 12, bulletRadius, WHITE); - DrawCircleLines(12, 12, bulletRadius, BLACK); + DrawCircle(12, 12, bulletRadius, WHITE); + DrawCircleLines(12, 12, bulletRadius, BLACK); EndTextureMode(); - EndDrawing(); - bool drawInPerformanceMode = true; + bool drawInPerformanceMode = true; // Switch between DrawCircle() and DrawTexture() SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -88,7 +82,6 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - // Reset the bullet index // New bullets will replace the old ones that are already disabled due to out-of-screen if (bulletCount >= MAX_BULLETS) @@ -103,27 +96,25 @@ int main(void) spawnCooldownTimer = spawnCooldown; // Spawn bullets - float degreesPerRow = 360.0f / bulletRows; + float degreesPerRow = 360.0f/bulletRows; for (int row = 0; row < bulletRows; row++) { - if (bulletCount < MAX_BULLETS) { - bullets[bulletCount].position = (Vector2){(float) screenWidth/2, (float) screenHeight/2}; bullets[bulletCount].disabled = false; - bullets[bulletCount].color = bulletColor[row % 2]; + bullets[bulletCount].color = bulletColor[row%2]; - float bulletDirection = baseDirection + (degreesPerRow * row); + float bulletDirection = baseDirection + (degreesPerRow*row); - // bullet speed * bullet direction, this will determine how much pixels will be incremented/decremented + // Bullet speed * bullet direction, this will determine how much pixels will be incremented/decremented // from the bullet position every frame. Since the bullets doesn't change its direction and speed, - // only need to calculate it at the spawning time. - // 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise. + // only need to calculate it at the spawning time + // 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise // Case you want it to be anti-clockwise, add "* -1" at the y acceleration bullets[bulletCount].acceleration = (Vector2){ - bulletSpeed * cosf(bulletDirection * DEG2RAD), - bulletSpeed * sinf(bulletDirection * DEG2RAD) + bulletSpeed*cosf(bulletDirection*DEG2RAD), + bulletSpeed*sinf(bulletDirection*DEG2RAD) }; bulletCount++; @@ -131,29 +122,22 @@ int main(void) } baseDirection += angleIncrement; - } - // Update bullets position based on its acceleration for (int i = 0; i < bulletCount; i++) { - // Only update bullet if inside the screen if (!bullets[i].disabled) { - bullets[i].position.x += bullets[i].acceleration.x; bullets[i].position.y += bullets[i].acceleration.y; // Disable bullet if out of screen - if - ( - bullets[i].position.x < -bulletRadius*2 || - bullets[i].position.x > screenWidth + bulletRadius*2 || - bullets[i].position.y < -bulletRadius*2 || - bullets[i].position.y > screenHeight + bulletRadius*2 - ) + if ((bullets[i].position.x < -bulletRadius*2) || + (bullets[i].position.x > screenWidth + bulletRadius*2) || + (bullets[i].position.y < -bulletRadius*2) || + (bullets[i].position.y > screenHeight + bulletRadius*2)) { bullets[i].disabled = true; bulletDisabledCount++; @@ -161,12 +145,12 @@ int main(void) } } - // Input - if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) && bulletRows < 359) bulletRows++; - if ((IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) && bulletRows > 1) bulletRows--; + // Input logic + if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) && (bulletRows < 359)) bulletRows++; + if ((IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) && (bulletRows > 1)) bulletRows--; if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) bulletSpeed += 0.25f; - if ((IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) && bulletSpeed > 0.50f) bulletSpeed -= 0.25f; - if (IsKeyPressed(KEY_Z) && spawnCooldown > 1) spawnCooldown--; + if ((IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) && (bulletSpeed > 0.50f)) bulletSpeed -= 0.25f; + if (IsKeyPressed(KEY_Z) && (spawnCooldown > 1)) spawnCooldown--; if (IsKeyPressed(KEY_X)) spawnCooldown++; if (IsKeyPressed(KEY_ENTER)) drawInPerformanceMode = !drawInPerformanceMode; @@ -181,51 +165,42 @@ int main(void) bulletCount = 0; bulletDisabledCount = 0; } + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); // Draw magic circle magicCircleRotation++; - DrawRectanglePro( - (Rectangle) { (float) screenWidth/2, (float) screenHeight/2, 120, 120 }, - (Vector2) { 60, 60 }, - magicCircleRotation, - PURPLE - ); - DrawRectanglePro( - (Rectangle) { (float) screenWidth/2, (float) screenHeight/2, 120, 120 }, - (Vector2) { 60, 60 }, - magicCircleRotation + 45, - PURPLE - ); + DrawRectanglePro((Rectangle){ (float)screenWidth/2, (float)screenHeight/2, 120, 120 }, + (Vector2){ 60.0f, 60.0f }, magicCircleRotation, PURPLE); + DrawRectanglePro((Rectangle){ (float)screenWidth/2, (float)screenHeight/2, 120, 120 }, + (Vector2){ 60.0f, 60.0f }, magicCircleRotation + 45, PURPLE); DrawCircleLines(screenWidth/2, screenHeight/2, 70, BLACK); DrawCircleLines(screenWidth/2, screenHeight/2, 50, BLACK); DrawCircleLines(screenWidth/2, screenHeight/2, 30, BLACK); - // Draw bullets - // DrawInPerformanceMode = draw bullets using DrawTexture, DrawCircle is vary slow if (drawInPerformanceMode) { + // Draw bullets using pre-rendered texture containing circle for (int i = 0; i < bulletCount; i++) { // Do not draw disabled bullets (out of screen) if (!bullets[i].disabled) { - DrawTexture( - bulletTexture.texture, + DrawTexture(bulletTexture.texture, bullets[i].position.x - bulletTexture.texture.width*0.5f, bullets[i].position.y - bulletTexture.texture.height*0.5f, - bullets[i].color - ); + bullets[i].color); } } - } else { - + } + else + { + // Draw bullets using DrawCircle(), less performant for (int i = 0; i < bulletCount; i++) { // Do not draw disabled bullets (out of screen) @@ -248,25 +223,13 @@ int main(void) DrawText("- C: Clear bullets", 40, 140, 10, LIGHTGRAY); DrawRectangle(610, 10, 170, 30, (Color){0,0, 0, 200 }); - if (drawInPerformanceMode) - { - DrawText("Draw method: DrawTexture(*)", 620, 20, 10, GREEN); - } else { - DrawText("Draw method: DrawCircle(*)", 620, 20, 10, RED); - } - + if (drawInPerformanceMode) DrawText("Draw method: DrawTexture(*)", 620, 20, 10, GREEN); + else DrawText("Draw method: DrawCircle(*)", 620, 20, 10, RED); DrawRectangle(135, 410, 530, 30, (Color){0,0, 0, 200 }); - DrawText( - TextFormat( - "[ FPS: %d, Bullets: %d, Rows: %d, Bullet speed: %.2f, Angle increment per frame: %d, Cooldown: %.0f ]", - GetFPS(), bulletCount - bulletDisabledCount, bulletRows, bulletSpeed, angleIncrement, spawnCooldown - ), - 155, - 420, - 10, - GREEN - ); + DrawText(TextFormat("[ FPS: %d, Bullets: %d, Rows: %d, Bullet speed: %.2f, Angle increment per frame: %d, Cooldown: %.0f ]", + GetFPS(), bulletCount - bulletDisabledCount, bulletRows, bulletSpeed, angleIncrement, spawnCooldown), + 155, 420, 10, GREEN); EndDrawing(); //---------------------------------------------------------------------------------- @@ -274,10 +237,9 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + UnloadRenderTexture(bulletTexture); // Unload bullet texture - UnloadRenderTexture(bulletTexture); - - free(bullets); + RL_FREE(bullets); // Free bullets array data CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From 0e2e8ce22527c79f25845c06c31aa10e0de1e718 Mon Sep 17 00:00:00 2001 From: Jopestpe <47086979+Jopestpe@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:32:25 -0300 Subject: [PATCH 19/31] [examples] Update `shapes_bouncing_ball` with gravity (#5217) * [examples] Add shapes_bouncing_ball with gravity * gravity could be enabled/disabled --- examples/shapes/shapes_bouncing_ball.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/shapes/shapes_bouncing_ball.c b/examples/shapes/shapes_bouncing_ball.c index 187cd41af..73dca0011 100644 --- a/examples/shapes/shapes_bouncing_ball.c +++ b/examples/shapes/shapes_bouncing_ball.c @@ -4,8 +4,10 @@ * * Example complexity rating: [★☆☆☆] 1/4 * -* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* Example originally created with raylib 2.5, last time updated with raylib 5.6 * +* Example contributed by Jopestpe (@jopestpe) +* * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * @@ -31,7 +33,9 @@ int main(void) Vector2 ballPosition = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; Vector2 ballSpeed = { 5.0f, 4.0f }; int ballRadius = 20; + float gravity = 0.2f; + bool useGravity = true; bool pause = 0; int framesCounter = 0; @@ -43,16 +47,19 @@ int main(void) { // Update //----------------------------------------------------- + if (IsKeyPressed(KEY_G)) useGravity = !useGravity; if (IsKeyPressed(KEY_SPACE)) pause = !pause; - + if (!pause) { ballPosition.x += ballSpeed.x; ballPosition.y += ballSpeed.y; + if (useGravity) ballSpeed.y += gravity; + // Check walls collision for bouncing if ((ballPosition.x >= (GetScreenWidth() - ballRadius)) || (ballPosition.x <= ballRadius)) ballSpeed.x *= -1.0f; - if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -1.0f; + if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -0.95f; } else framesCounter++; //----------------------------------------------------- @@ -65,12 +72,15 @@ int main(void) DrawCircleV(ballPosition, (float)ballRadius, MAROON); DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY); + + if (useGravity) DrawText("GRAVITY: ON (Press G to disable)", 10, GetScreenHeight() - 50, 20, DARKGREEN); + else DrawText("GRAVITY: OFF (Press G to enable)", 10, GetScreenHeight() - 50, 20, RED); // On pause, we draw a blinking message if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY); DrawFPS(10, 10); - + EndDrawing(); //----------------------------------------------------- } From a553fbd0c76c29d4d2581d4959da5a75c3041e9c Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Tue, 30 Sep 2025 17:34:06 +0100 Subject: [PATCH 20/31] [examples] Fix `shaders_ascii_rendering` (#5219) * Add ascii post processing * Fix broken characters and add more comments * Rename example * Refactored as requested --- .../resources/shaders/glsl100/ascii.fs | 59 +++++++++++------- .../resources/shaders/glsl120/ascii.fs | 57 ++++++++++------- .../resources/shaders/glsl330/ascii.fs | 44 ++++++++----- examples/shaders/shaders_ascii_rendering.c | 44 ++++++++----- examples/shaders/shaders_ascii_rendering.png | Bin 45176 -> 43045 bytes 5 files changed, 125 insertions(+), 79 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/ascii.fs b/examples/shaders/resources/shaders/glsl100/ascii.fs index a4a930c3c..54e20bf66 100644 --- a/examples/shaders/resources/shaders/glsl100/ascii.fs +++ b/examples/shaders/resources/shaders/glsl100/ascii.fs @@ -10,58 +10,71 @@ varying vec4 finalColor; uniform sampler2D texture0; uniform vec2 resolution; + +// Fontsize less then 9 may be not complete uniform float fontSize; -float greyScale(in vec3 col) { +float GreyScale(in vec3 col) +{ return dot(col, vec3(0.2126, 0.7152, 0.0722)); } -float character(float n, vec2 p) +float GetCharacter(float n, vec2 p) { - p = floor(p * vec2(4.0, -4.0) + 2.5); - - // Check if the coordinate is inside the 5x5 grid (0 to 4). - if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { - - if (int(mod(n / exp2(p.x + 5.0 * p.y), 2.0)) == 1) { - return 1.0; // The bit is on, so draw this part of the character. + p = floor(p*vec2(-4.0, 4.0) + 2.5); + + // Check if the calculated coordinate is inside the 5x5 grid (from 0.0 to 4.0) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + float a = floor(p.x + 0.5) + 5.0*floor(p.y + 0.5); + + // This checked if the 'a'-th bit of 'n' was set + float shiftedN = floor(n/pow(2.0, a)); + + if (mod(shiftedN, 2.0) == 1.0) + { + return 1.0; // The bit is on } } - return 0.0; // The bit is off, or we are outside the grid. + return 0.0; // The bit is off, or we are outside the grid } // ----------------------------------------------------------------------------- // Main shader logic // ----------------------------------------------------------------------------- + void main() { - vec2 charPixelSize = vec2(fontSize, fontSize * 1.8); - vec2 uvCellSize = charPixelSize / resolution; + vec2 charPixelSize = vec2(fontSize, fontSize); + vec2 uvCellSize = charPixelSize/resolution; - vec2 cellUV = floor(fragTexCoord / uvCellSize) * uvCellSize; + // The cell size is based on the fontSize set by application + vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize; vec3 cellColor = texture2D(texture0, cellUV).rgb; - float gray = greyScale(cellColor); - float n = 4096; + // Gray is used to define what character will be selected to draw + float gray = GreyScale(cellColor); + + float n = 4096.0; - // limited character set + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ if (gray > 0.2) n = 65600.0; // : - if (gray > 0.3) n = 163153.0; // * + if (gray > 0.3) n = 18725316.0; // v if (gray > 0.4) n = 15255086.0; // o if (gray > 0.5) n = 13121101.0; // & if (gray > 0.6) n = 15252014.0; // 8 if (gray > 0.7) n = 13195790.0; // @ if (gray > 0.8) n = 11512810.0; // # - vec2 localUV = (fragTexCoord - cellUV) / uvCellSize; // Range [0.0, 1.0] + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] - vec2 p = localUV * 2.0 - 1.0; + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] - float charShape = character(n, p); + // cellColor and charShape will define the color of the char + vec3 color = cellColor*GetCharacter(n, p); - vec3 final_col = cellColor * charShape; - - gl_FragColor = vec4(final_col, 1.0); + gl_FragColor = vec4(color, 1.0); } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl120/ascii.fs b/examples/shaders/resources/shaders/glsl120/ascii.fs index 3521c8b33..09c572ae5 100644 --- a/examples/shaders/resources/shaders/glsl120/ascii.fs +++ b/examples/shaders/resources/shaders/glsl120/ascii.fs @@ -8,58 +8,71 @@ varying vec4 finalColor; uniform sampler2D texture0; uniform vec2 resolution; + +// Fontsize less then 9 may be not complete uniform float fontSize; -float greyScale(in vec3 col) { +float GreyScale(in vec3 col) +{ return dot(col, vec3(0.2126, 0.7152, 0.0722)); } -float character(float n, vec2 p) +float GetCharacter(float n, vec2 p) { - p = floor(p * vec2(4.0, -4.0) + 2.5); - - // Check if the coordinate is inside the 5x5 grid (0 to 4). - if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { - - if (int(mod(n / exp2(p.x + 5.0 * p.y), 2.0)) == 1) { - return 1.0; // The bit is on, so draw this part of the character. + p = floor(p*vec2(-4.0, 4.0) + 2.5); + + // Check if the calculated coordinate is inside the 5x5 grid (from 0.0 to 4.0) + if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) + { + float a = floor(p.x + 0.5) + 5.0*floor(p.y + 0.5); + + // This checked if the 'a'-th bit of 'n' was set + float shiftedN = floor(n/pow(2.0, a)); + + if (mod(shiftedN, 2.0) == 1.0) + { + return 1.0; // The bit is on } } - return 0.0; // The bit is off, or we are outside the grid. + return 0.0; // The bit is off, or we are outside the grid } // ----------------------------------------------------------------------------- // Main shader logic // ----------------------------------------------------------------------------- + void main() { - vec2 charPixelSize = vec2(fontSize, fontSize * 1.8); + vec2 charPixelSize = vec2(fontSize, fontSize); vec2 uvCellSize = charPixelSize / resolution; - vec2 cellUV = floor(fragTexCoord / uvCellSize) * uvCellSize; + // The cell size is based on the fontSize set by application + vec2 cellUV = floor(fragTexCoord / uvCellSize)*uvCellSize; vec3 cellColor = texture2D(texture0, cellUV).rgb; - float gray = greyScale(cellColor); - float n = 4096; + // Gray is used to define what character will be selected to draw + float gray = GreyScale(cellColor); + + float n = 4096.0; - // limited character set + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ if (gray > 0.2) n = 65600.0; // : - if (gray > 0.3) n = 163153.0; // * + if (gray > 0.3) n = 18725316.0; // v if (gray > 0.4) n = 15255086.0; // o if (gray > 0.5) n = 13121101.0; // & if (gray > 0.6) n = 15252014.0; // 8 if (gray > 0.7) n = 13195790.0; // @ if (gray > 0.8) n = 11512810.0; // # - vec2 localUV = (fragTexCoord - cellUV) / uvCellSize; // Range [0.0, 1.0] + vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] - vec2 p = localUV * 2.0 - 1.0; + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] - float charShape = character(n, p); + // cellColor and charShape will define the color of the char + vec3 color = cellColor*GetCharacter(n, p); - vec3 final_col = cellColor * charShape; - - gl_FragColor = vec4(final_col, 1.0); + gl_FragColor = vec4(color, 1.0); } \ No newline at end of file diff --git a/examples/shaders/resources/shaders/glsl330/ascii.fs b/examples/shaders/resources/shaders/glsl330/ascii.fs index 014966835..3f73bf288 100644 --- a/examples/shaders/resources/shaders/glsl330/ascii.fs +++ b/examples/shaders/resources/shaders/glsl330/ascii.fs @@ -8,6 +8,8 @@ out vec4 finalColor; uniform sampler2D texture0; uniform vec2 resolution; + +// Fontsize less then 9 may be not complete uniform float fontSize; float GreyScale(in vec3 col) @@ -15,16 +17,17 @@ float GreyScale(in vec3 col) return dot(col, vec3(0.2126, 0.7152, 0.0722)); } -float GetCharacter(float n, vec2 p) +float GetCharacter(int n, vec2 p) { - p = floor(p*vec2(4.0, -4.0) + 2.5); + p = floor(p*vec2(-4.0, 4.0) + 2.5); // Check if the coordinate is inside the 5x5 grid (0 to 4) if (clamp(p.x, 0.0, 4.0) == p.x && clamp(p.y, 0.0, 4.0) == p.y) { - if (int(mod(n/exp2(p.x + 5.0 * p.y), 2.0)) == 1) + int a = int(round(p.x) + 5.0*round(p.y)); + if (((n >> a) & 1) == 1) { - return 1.0; // The bit is on, so draw this part of the character + return 1.0; } } @@ -34,30 +37,37 @@ float GetCharacter(float n, vec2 p) // ----------------------------------------------------------------------------- // Main shader logic // ----------------------------------------------------------------------------- + void main() { - vec2 charPixelSize = vec2(fontSize, fontSize*1.8); + vec2 charPixelSize = vec2(fontSize, fontSize); vec2 uvCellSize = charPixelSize/resolution; + + // The cell size is based on the fontSize set by application vec2 cellUV = floor(fragTexCoord/uvCellSize)*uvCellSize; vec3 cellColor = texture(texture0, cellUV).rgb; + + // Gray is used to define what character will be selected to draw float gray = GreyScale(cellColor); - float n = 4096; + int n = 4096; - // Limited character set - if (gray > 0.2) n = 65600.0; // : - if (gray > 0.3) n = 163153.0; // * - if (gray > 0.4) n = 15255086.0; // o - if (gray > 0.5) n = 13121101.0; // & - if (gray > 0.6) n = 15252014.0; // 8 - if (gray > 0.7) n = 13195790.0; // @ - if (gray > 0.8) n = 11512810.0; // # + // Character set from https://www.shadertoy.com/view/lssGDj + // Create new bitmaps https://thrill-project.com/archiv/coding/bitmap/ + if (gray > 0.2) n = 65600; // : + if (gray > 0.3) n = 18725316; // v + if (gray > 0.4) n = 15255086; // o + if (gray > 0.5) n = 13121101; // & + if (gray > 0.6) n = 15252014; // 8 + if (gray > 0.7) n = 13195790; // @ + if (gray > 0.8) n = 11512810; // # vec2 localUV = (fragTexCoord - cellUV)/uvCellSize; // Range [0.0, 1.0] - vec2 p = localUV*2.0 - 1.0; - float charShape = GetCharacter(n, p); + vec2 p = localUV*2.0 - 1.0; // Range [-1.0, 1.0] - finalColor = vec4(cellColor*charShape, 1.0); + vec3 color = cellColor*GetCharacter(n, p); + + finalColor = vec4(color, 1.0); } \ No newline at end of file diff --git a/examples/shaders/shaders_ascii_rendering.c b/examples/shaders/shaders_ascii_rendering.c index 5444caeee..23ba1f549 100644 --- a/examples/shaders/shaders_ascii_rendering.c +++ b/examples/shaders/shaders_ascii_rendering.c @@ -48,14 +48,14 @@ int main(void) int fontSizeLoc = GetShaderLocation(shader, "fontSize"); // Set the character size for the ASCII effect - float fontSize = 4.0f; + // Fontsize should be 9 or more + float fontSize = 9.0f; // Send the updated values to the shader float resolution[2] = { (float)screenWidth, (float)screenHeight }; SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2); - SetShaderValue(shader, fontSizeLoc, &fontSize, SHADER_UNIFORM_FLOAT); - Vector2 circlePos = (Vector2){40.0f, (float)screenHeight * 0.5f}; + Vector2 circlePos = (Vector2){40.0f, (float)screenHeight*0.5f}; float circleSpeed = 1.0f; // RenderTexture to apply the postprocessing later @@ -67,37 +67,47 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { + // Update //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_LEFT) && fontSize > 9.0) fontSize -= 1; // Reduce fontSize + + if (IsKeyPressed(KEY_RIGHT) && fontSize < 15.0) fontSize += 1; // Increase fontSize + + if (circlePos.x > 200.0f || circlePos.x < 40.0f) circleSpeed *= -1; // Revert speed + circlePos.x += circleSpeed; - - if ((circlePos.x > 200.0f) || (circlePos.x < 40.0f)) circleSpeed *= -1; - //---------------------------------------------------------------------------------- + + // Set fontsize for the shader + SetShaderValue(shader, fontSizeLoc, &fontSize, SHADER_UNIFORM_FLOAT); // Draw //---------------------------------------------------------------------------------- - // Draw our scene to a render texture first + BeginTextureMode(target); - ClearBackground(WHITE); - + ClearBackground(WHITE); // The background of the scene itself + + // Samples Using custom shader DrawTexture(fudesumi, 500, -30, WHITE); DrawTextureV(raysan, circlePos, WHITE); - + EndTextureMode(); - BeginDrawing(); + ClearBackground(RAYWHITE); BeginShaderMode(shader); - // Draw the render texture containing scene - // The shader will process every pixel on the screen - DrawTextureRec(target.texture, - (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, - (Vector2){ 0, 0 }, WHITE); + + // Draw the scene texture (that we rendered earlier) to the screen + // The shader will process every pixel of this texture + DrawTextureRec(target.texture, + (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, + (Vector2){ 0, 0 }, + WHITE); EndShaderMode(); DrawRectangle(0, 0, screenWidth, 40, BLACK); - DrawText("Ascii effect", 120, 10, 20, LIGHTGRAY); + DrawText(TextFormat("Ascii effect - FontSize:%2.0f - [Left] -1 [Right] +1 ", fontSize), 120, 10, 20, LIGHTGRAY); DrawFPS(10, 10); EndDrawing(); diff --git a/examples/shaders/shaders_ascii_rendering.png b/examples/shaders/shaders_ascii_rendering.png index eee5cc90b620c14cc111bb9a8348765cb77e827d..59ecc83954129a13030476092ef346d16e943869 100644 GIT binary patch literal 43045 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 J3RE9F`#i6Q_1 literal 45176 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 ntY^P`$Sott Date: Tue, 30 Sep 2025 12:35:15 -0400 Subject: [PATCH 21/31] [examples] Add `core_delta_time` (#5216) * delta example * clarification * made it more clear what delta time is * more explanation * vector2 positions, removed all warnings * removed keys, moved global to local, conventions & comments, show target fps * new png * remove more periods * another period * remove note about conventions, remove period --- examples/core/core_delta_time.c | 131 ++++++++++++++++++++++++++++++ examples/core/core_delta_time.png | Bin 0 -> 16554 bytes 2 files changed, 131 insertions(+) create mode 100644 examples/core/core_delta_time.c create mode 100644 examples/core/core_delta_time.png diff --git a/examples/core/core_delta_time.c b/examples/core/core_delta_time.c new file mode 100644 index 000000000..aeedd7fff --- /dev/null +++ b/examples/core/core_delta_time.c @@ -0,0 +1,131 @@ +/******************************************************************************************* +* +* raylib [core] example - delta time +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5 +* +* Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Robin (@RobinsAviary) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - delta time"); + + int currentFps = 60; + + // Store the position for the both of the circles + Vector2 deltaCircle = {0, screenHeight / 3.0f}; + Vector2 frameCircle = {0, screenHeight * (2.0f/3.0f)}; + + // The speed applied to both circles + const float speed = 10.0; + const float circleRadius = 32; + + SetTargetFPS(currentFps); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------- + if (IsKeyPressed(KEY_R)) // Reset both circles' positions when you press R + { + deltaCircle.x = 0; + frameCircle.x = 0; + } + + // Adjust the FPS target based on the mouse wheel + float mouseWheel = GetMouseWheelMove(); + if (mouseWheel != 0) + { + currentFps += (int)mouseWheel; + SetTargetFPS(currentFps); + } + + // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time) + // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS + + // Multiply by 6.0 (an arbitrary value) in order to make the speed visually closer to the other circle (at 60 fps), for comparison + deltaCircle.x += GetFrameTime() * 6.0f * speed; + // This circle can move faster or slower visually depending on the FPS + frameCircle.x += .1f * speed; + + // If either circle is off the screen, reset it back to the start + if (deltaCircle.x > screenWidth) + { + deltaCircle.x = 0; + } + + if (frameCircle.x > screenWidth) + { + frameCircle.x = 0; + } + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw both circles to the screen + DrawCircleV(deltaCircle, circleRadius, RED); + DrawCircleV(frameCircle, circleRadius, BLUE); + + // Determine what help text to show depending on the current FPS target + const char* fpsText; + + if (currentFps <= 0) + { + if (currentFps < 0) + { + // Clamp values below 0 + currentFps = 0; + } + + // Special text for when the FPS target is set to 0 or less, which makes it unlimited + fpsText = TextFormat("fps: unlimited (%i)", GetFPS()); + } + else + { + fpsText = TextFormat("fps: %i (target: %i)", GetFPS(), currentFps); + } + + // Draw the help text + DrawText(fpsText, 10, 10, 20, DARKGRAY); + DrawText(TextFormat("frame time: %02.02f ms", GetFrameTime()), 10, 30, 20, DARKGRAY); + DrawText("use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, DARKGRAY); + + // Draw the text above the circles + DrawText("x += GetFrameTime() * speed", 10, 90, 20, RED); + DrawText("x += speed", 10, 240, 20, BLUE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_delta_time.png b/examples/core/core_delta_time.png new file mode 100644 index 0000000000000000000000000000000000000000..cf8aa7c54e50ac477f8ea1011e0b61f1cedcb8ad GIT binary patch literal 16554 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 Date: Tue, 30 Sep 2025 18:39:27 +0200 Subject: [PATCH 22/31] Update shaders_ascii_rendering.c --- examples/shaders/shaders_ascii_rendering.c | 34 +++++++++------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/examples/shaders/shaders_ascii_rendering.c b/examples/shaders/shaders_ascii_rendering.c index 23ba1f549..33fb052f7 100644 --- a/examples/shaders/shaders_ascii_rendering.c +++ b/examples/shaders/shaders_ascii_rendering.c @@ -67,58 +67,52 @@ int main(void) // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { - // Update //---------------------------------------------------------------------------------- - if (IsKeyPressed(KEY_LEFT) && fontSize > 9.0) fontSize -= 1; // Reduce fontSize - - if (IsKeyPressed(KEY_RIGHT) && fontSize < 15.0) fontSize += 1; // Increase fontSize - - if (circlePos.x > 200.0f || circlePos.x < 40.0f) circleSpeed *= -1; // Revert speed - circlePos.x += circleSpeed; + if ((circlePos.x > 200.0f) || (circlePos.x < 40.0f)) circleSpeed *= -1; // Revert speed + + if (IsKeyPressed(KEY_LEFT) && (fontSize > 9.0)) fontSize -= 1; // Reduce fontSize + if (IsKeyPressed(KEY_RIGHT) && (fontSize < 15.0)) fontSize += 1; // Increase fontSize // Set fontsize for the shader SetShaderValue(shader, fontSizeLoc, &fontSize, SHADER_UNIFORM_FLOAT); // Draw //---------------------------------------------------------------------------------- - BeginTextureMode(target); - ClearBackground(WHITE); // The background of the scene itself + ClearBackground(WHITE); - // Samples Using custom shader + // Draw scene in our render texture DrawTexture(fudesumi, 500, -30, WHITE); DrawTextureV(raysan, circlePos, WHITE); - EndTextureMode(); + BeginDrawing(); - ClearBackground(RAYWHITE); BeginShaderMode(shader); - // Draw the scene texture (that we rendered earlier) to the screen // The shader will process every pixel of this texture DrawTextureRec(target.texture, - (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, - (Vector2){ 0, 0 }, - WHITE); + (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, + (Vector2){ 0, 0 }, WHITE); EndShaderMode(); DrawRectangle(0, 0, screenWidth, 40, BLACK); DrawText(TextFormat("Ascii effect - FontSize:%2.0f - [Left] -1 [Right] +1 ", fontSize), 120, 10, 20, LIGHTGRAY); DrawFPS(10, 10); - EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- - UnloadShader(shader); // Unload shader - UnloadTexture(fudesumi); // Unload texture - UnloadTexture(raysan); // Unload texture + UnloadRenderTexture(target); // Unload render texture + + UnloadShader(shader); // Unload shader + UnloadTexture(fudesumi); // Unload texture + UnloadTexture(raysan); // Unload texture CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- From 8932ba0b0af037ffb27733a77e5a44e80915d66e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Sep 2025 18:48:42 +0200 Subject: [PATCH 23/31] Update core_delta_time.c --- examples/core/core_delta_time.c | 81 +++++++++++++-------------------- 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/examples/core/core_delta_time.c b/examples/core/core_delta_time.c index aeedd7fff..86bc969b3 100644 --- a/examples/core/core_delta_time.c +++ b/examples/core/core_delta_time.c @@ -32,12 +32,12 @@ int main(void) int currentFps = 60; // Store the position for the both of the circles - Vector2 deltaCircle = {0, screenHeight / 3.0f}; - Vector2 frameCircle = {0, screenHeight * (2.0f/3.0f)}; + Vector2 deltaCircle = { 0, (float)screenHeight/3.0f }; + Vector2 frameCircle = { 0, (float)screenHeight*(2.0f/3.0f) }; // The speed applied to both circles - const float speed = 10.0; - const float circleRadius = 32; + const float speed = 10.0f; + const float circleRadius = 32.0f; SetTargetFPS(currentFps); //-------------------------------------------------------------------------------------- @@ -46,77 +46,58 @@ int main(void) while (!WindowShouldClose()) // Detect window close button or ESC key { // Update - //---------------------------------------------- - if (IsKeyPressed(KEY_R)) // Reset both circles' positions when you press R - { - deltaCircle.x = 0; - frameCircle.x = 0; - } - + //---------------------------------------------------------------------------------- // Adjust the FPS target based on the mouse wheel float mouseWheel = GetMouseWheelMove(); if (mouseWheel != 0) { currentFps += (int)mouseWheel; + if (currentFps < 0) currentFps = 0; SetTargetFPS(currentFps); } // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time) // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS - // Multiply by 6.0 (an arbitrary value) in order to make the speed visually closer to the other circle (at 60 fps), for comparison - deltaCircle.x += GetFrameTime() * 6.0f * speed; + // Multiply by 6.0 (an arbitrary value) in order to make the speed + // visually closer to the other circle (at 60 fps), for comparison + deltaCircle.x += GetFrameTime()*6.0f*speed; // This circle can move faster or slower visually depending on the FPS - frameCircle.x += .1f * speed; + frameCircle.x += 0.1f*speed; // If either circle is off the screen, reset it back to the start - if (deltaCircle.x > screenWidth) + if (deltaCircle.x > screenWidth) deltaCircle.x = 0; + if (frameCircle.x > screenWidth) frameCircle.x = 0; + + // Reset both circles positions + if (IsKeyPressed(KEY_R)) { deltaCircle.x = 0; - } - - if (frameCircle.x > screenWidth) - { frameCircle.x = 0; } + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- BeginDrawing(); + ClearBackground(RAYWHITE); - ClearBackground(RAYWHITE); + // Draw both circles to the screen + DrawCircleV(deltaCircle, circleRadius, RED); + DrawCircleV(frameCircle, circleRadius, BLUE); - // Draw both circles to the screen - DrawCircleV(deltaCircle, circleRadius, RED); - DrawCircleV(frameCircle, circleRadius, BLUE); + // Draw the help text + // Determine what help text to show depending on the current FPS target + const char *fpsText = 0; + if (currentFps <= 0) fpsText = TextFormat("FPS: unlimited (%i)", GetFPS()); + else fpsText = TextFormat("FPS: %i (target: %i)", GetFPS(), currentFps); + DrawText(fpsText, 10, 10, 20, DARKGRAY); + DrawText(TextFormat("Frame time: %02.02f ms", GetFrameTime()), 10, 30, 20, DARKGRAY); + DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, DARKGRAY); - // Determine what help text to show depending on the current FPS target - const char* fpsText; - - if (currentFps <= 0) - { - if (currentFps < 0) - { - // Clamp values below 0 - currentFps = 0; - } - - // Special text for when the FPS target is set to 0 or less, which makes it unlimited - fpsText = TextFormat("fps: unlimited (%i)", GetFPS()); - } - else - { - fpsText = TextFormat("fps: %i (target: %i)", GetFPS(), currentFps); - } - - // Draw the help text - DrawText(fpsText, 10, 10, 20, DARKGRAY); - DrawText(TextFormat("frame time: %02.02f ms", GetFrameTime()), 10, 30, 20, DARKGRAY); - DrawText("use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, DARKGRAY); - - // Draw the text above the circles - DrawText("x += GetFrameTime() * speed", 10, 90, 20, RED); - DrawText("x += speed", 10, 240, 20, BLUE); + // Draw the text above the circles + DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, RED); + DrawText("FUNC: x += speed", 10, 240, 20, BLUE); EndDrawing(); //---------------------------------------------------------------------------------- From 6ed61e107b5e21a09d13ed261e3110c7b2c035b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20=E2=9D=A4=EF=B8=8F?= Date: Tue, 30 Sep 2025 15:49:58 -0400 Subject: [PATCH 24/31] flipped binary check to fix bug (#5220) --- examples/core/core_input_mouse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_input_mouse.c b/examples/core/core_input_mouse.c index bbe519f82..f429018b0 100644 --- a/examples/core/core_input_mouse.c +++ b/examples/core/core_input_mouse.c @@ -72,7 +72,7 @@ int main(void) DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY); DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, DARKGRAY); - if (!IsCursorHidden()) DrawText("CURSOR HIDDEN", 20, 60, 20, RED); + if (IsCursorHidden()) DrawText("CURSOR HIDDEN", 20, 60, 20, RED); else DrawText("CURSOR VISIBLE", 20, 60, 20, LIME); EndDrawing(); From e5f809f534d335e030c5ae71756e1ca6e5613862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20=E2=9D=A4=EF=B8=8F?= Date: Wed, 1 Oct 2025 05:42:35 -0400 Subject: [PATCH 25/31] switch to enum (#5223) --- examples/core/core_3d_camera_free.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index dde6a6c7d..448eb583e 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -49,7 +49,7 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_FREE); - if (IsKeyPressed('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + if (IsKeyPressed(KEY_Z)) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; //---------------------------------------------------------------------------------- // Draw From f680776941823a56142f61ec93bb062cc792fbde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Almeida?= <60551627+luis605@users.noreply.github.com> Date: Wed, 1 Oct 2025 10:43:38 +0100 Subject: [PATCH 26/31] feat:Added DrawLineDashed() and a new example explaining how to use this new function. (#5222) --- examples/Makefile | 5 +- examples/Makefile.Web | 5 +- examples/README.md | 1 + examples/examples_list.txt | 1 + examples/shapes/shapes_dashed_line.c | 103 +++++++++++++++++++++++++ examples/shapes/shapes_dashed_line.png | Bin 0 -> 16908 bytes src/raylib.h | 1 + src/rshapes.c | 49 ++++++++++++ 8 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 examples/shapes/shapes_dashed_line.c create mode 100644 examples/shapes/shapes_dashed_line.png diff --git a/examples/Makefile b/examples/Makefile index de02ae085..9a3f640b7 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -355,7 +355,7 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R ifeq ($(BUILD_WEB_WEBGL2),TRUE) LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 endif - + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) @@ -559,7 +559,8 @@ SHAPES = \ shapes/shapes_ring_drawing \ shapes/shapes_rounded_rectangle_drawing \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights + shapes/shapes_top_down_lights \ + shapes/shapes_dashed_line TEXTURES = \ textures/textures_background_scrolling \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 8c9707274..20d737c38 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -355,7 +355,7 @@ ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_R ifeq ($(BUILD_WEB_WEBGL2),TRUE) LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 endif - + # Add resources building if required ifeq ($(BUILD_WEB_RESOURCES),TRUE) LDFLAGS += --preload-file $(BUILD_WEB_RESOURCES_PATH) @@ -559,7 +559,8 @@ SHAPES = \ shapes/shapes_ring_drawing \ shapes/shapes_rounded_rectangle_drawing \ shapes/shapes_splines_drawing \ - shapes/shapes_top_down_lights + shapes/shapes_top_down_lights \ + shapes/shapes_dashed_line TEXTURES = \ textures/textures_background_scrolling \ diff --git a/examples/README.md b/examples/README.md index 92ea9aa07..a5ee53afd 100644 --- a/examples/README.md +++ b/examples/README.md @@ -91,6 +91,7 @@ Examples using raylib shapes drawing functionality, provided by raylib [shapes]( | [shapes_splines_drawing](shapes/shapes_splines_drawing.c) | shapes_splines_drawing | ⭐⭐⭐☆ | 5.0 | 5.0 | [Ramon Santamaria](https://github.com/raysan5) | | [shapes_digital_clock](shapes/shapes_digital_clock.c) | shapes_digital_clock | ⭐⭐⭐⭐️ | 5.5 | 5.6 | [Hamza RAHAL](https://github.com/hmz-rhl) | | [shapes_double_pendulum](shapes/shapes_double_pendulum.c) | shapes_double_pendulum | ⭐⭐☆☆ | 5.5 | 5.5 | [JoeCheong](https://github.com/Joecheong2006) | +| [shapes_dashed_line](shapes/shapes_dashed_line.c) | shapes_dashed_line | ⭐☆☆☆ | 5.5 | 5.5 | [Luís Almeida](https://github.com/luis605) | ### category: textures [26] diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 6af9bb6eb..10bf09ceb 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -66,6 +66,7 @@ shapes;shapes_rectangle_advanced;★★★★;5.5;5.5;2024;2025;"Everton Jr.";@e shapes;shapes_splines_drawing;★★★☆;5.0;5.0;2023;2025;"Ramon Santamaria";@raysan5 shapes;shapes_digital_clock;★★★★;5.5;5.6;2025;2025;"Hamza RAHAL";@hmz-rhl shapes;shapes_double_pendulum;★★☆☆;5.5;5.5;2025;2025;"JoeCheong";@Joecheong2006 +shapes;shapes_dashed_line;★☆☆☆;5.5;5.5;2025;2025;"Luís Almeida";@luis605 textures;textures_logo_raylib;★☆☆☆;1.0;1.0;2014;2025;"Ramon Santamaria";@raysan5 textures;textures_srcrec_dstrec;★★★☆;1.3;1.3;2015;2025;"Ramon Santamaria";@raysan5 textures;textures_image_drawing;★★☆☆;1.4;1.4;2016;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/shapes/shapes_dashed_line.c b/examples/shapes/shapes_dashed_line.c new file mode 100644 index 000000000..fd017562f --- /dev/null +++ b/examples/shapes/shapes_dashed_line.c @@ -0,0 +1,103 @@ +/******************************************************************************************* +* +* raylib [shapes] example - dashed line drawing +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 2.5, last time updated with raylib 2.5 +* +* Example contributed by Luís Almeida (@luis605) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025 Luís Almeida (@luis605) +* +********************************************************************************************/ + +#include "raylib.h" +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for GUI controls + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - interactive dashed line"); + + // Line Properties + Vector2 lineStartPosition = { 20.0f, 50.0f }; + Vector2 lineEndPosition = { 780.0f, 400.0f }; + float dashLength = 25.0f; + float blankLength = 15.0f; + + // Color selection + Color lineColors[] = { RED, ORANGE, GOLD, GREEN, BLUE, VIOLET, PINK, BLACK }; + int colorIndex = 0; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + lineEndPosition = GetMousePosition(); // Line endpoint follows the mouse + + // --- Keyboard Controls --- + + // Change Dash Length (UP/DOWN arrows) + if (IsKeyDown(KEY_UP)) dashLength += 1.0f; + if (IsKeyDown(KEY_DOWN) && dashLength > 1.0f) dashLength -= 1.0f; + + // Change Space Length (LEFT/RIGHT arrows) + if (IsKeyDown(KEY_RIGHT)) blankLength += 1.0f; + if (IsKeyDown(KEY_LEFT) && blankLength > 1.0f) blankLength -= 1.0f; + + // Cycle through colors ('C' key) + if (IsKeyPressed(KEY_C)) + { + colorIndex = (colorIndex + 1) % (sizeof(lineColors)/sizeof(Color)); + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw the dashed line with the current properties + DrawLineDashed(lineStartPosition, lineEndPosition, dashLength, blankLength, lineColors[colorIndex]); + + // Draw UI and Instructions + DrawRectangle(5, 5, 265, 95, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(5, 5, 265, 95, BLUE); + + DrawText("CONTROLS:", 15, 15, 10, BLACK); + DrawText("UP/DOWN: Change Dash Length", 15, 35, 10, BLACK); + DrawText("LEFT/RIGHT: Change Space Length", 15, 55, 10, BLACK); + DrawText("C: Cycle Color", 15, 75, 10, BLACK); + + DrawText(TextFormat("Dash: %.0f | Space: %.0f", dashLength, blankLength), 15, 115, 10, DARKGRAY); + + DrawFPS(screenWidth - 80, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/shapes/shapes_dashed_line.png b/examples/shapes/shapes_dashed_line.png new file mode 100644 index 0000000000000000000000000000000000000000..5c3ce3ef3fc5ae84996f01b90b56b235fcd42308 GIT binary patch literal 16908 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 literal 0 HcmV?d00001 diff --git a/src/raylib.h b/src/raylib.h index 642f9318c..b7ccaae22 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1258,6 +1258,7 @@ RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source re RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care] RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel using geometry (Vector version) [Can be slow, use with care] RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line +RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int whiteSpaceSize, Color color); // Draw a dashed line RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (using gl lines) RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads) RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color); // Draw lines sequence (using gl lines) diff --git a/src/rshapes.c b/src/rshapes.c index 14e3f856a..a62b1c5f8 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -182,6 +182,55 @@ void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color colo rlEnd(); } +void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int whiteSpaceSize, Color color) +{ + // Calculate the vector and length of the line + float dx = endPos.x - startPos.x; + float dy = endPos.y - startPos.y; + float lineLength = sqrtf(dx*dx + dy*dy); + + // If the line is too short for dashing or dash size is invalid, draw a solid thick line + if (lineLength < (dashSize + whiteSpaceSize) || dashSize <= 0) + { + DrawLineV(startPos, endPos, color); + return; + } + + // Calculate the normalized direction vector of the line + float invLineLength = 1 / lineLength; + float dirX = dx * invLineLength; + float dirY = dy * invLineLength; + + Vector2 currentPos = startPos; + float distanceTraveled = 0; + + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + + while (distanceTraveled < lineLength) + { + // Calculate the end of the current dash + float dashEndDist = distanceTraveled + dashSize; + if (dashEndDist > lineLength) + { + dashEndDist = lineLength; + } + + Vector2 dashEndPos = { startPos.x + dashEndDist * dirX, startPos.y + dashEndDist * dirY }; + + // Draw the dash segment + rlVertex2f(currentPos.x, currentPos.y); + rlVertex2f(dashEndPos.x, dashEndPos.y); + + // Update the distance traveled and move the current position for the next dash + distanceTraveled = dashEndDist + whiteSpaceSize; + currentPos.x = startPos.x + distanceTraveled * dirX; + currentPos.y = startPos.y + distanceTraveled * dirY; + } + + rlEnd(); +} + // Draw a line (using gl lines) void DrawLineV(Vector2 startPos, Vector2 endPos, Color color) { From 6f1077737e48d447380cf5573adf1394dfe3aac6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 09:43:50 +0000 Subject: [PATCH 27/31] Update raylib_api.* by CI --- tools/parser/output/raylib_api.json | 27 + tools/parser/output/raylib_api.lua | 12 + tools/parser/output/raylib_api.txt | 759 ++++++++++++++-------------- tools/parser/output/raylib_api.xml | 9 +- 4 files changed, 431 insertions(+), 376 deletions(-) diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 5c641bac3..7b2ffdc9d 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -5508,6 +5508,33 @@ } ] }, + { + "name": "DrawLineDashed", + "description": "Draw a dashed line", + "returnType": "void", + "params": [ + { + "type": "Vector2", + "name": "startPos" + }, + { + "type": "Vector2", + "name": "endPos" + }, + { + "type": "int", + "name": "dashSize" + }, + { + "type": "int", + "name": "whiteSpaceSize" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawLineV", "description": "Draw a line (using gl lines)", diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index ce3671159..62dc9b25d 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -4758,6 +4758,18 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawLineDashed", + description = "Draw a dashed line", + returnType = "void", + params = { + {type = "Vector2", name = "startPos"}, + {type = "Vector2", name = "endPos"}, + {type = "int", name = "dashSize"}, + {type = "int", name = "whiteSpaceSize"}, + {type = "Color", name = "color"} + } + }, { name = "DrawLineV", description = "Draw a line (using gl lines)", diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index 94568b690..ea00fc74e 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -993,7 +993,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 595 +Functions found: 596 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2190,14 +2190,23 @@ Function 221: DrawLine() (5 input parameters) Param[3]: endPosX (type: int) Param[4]: endPosY (type: int) Param[5]: color (type: Color) -Function 222: DrawLineV() (3 input parameters) +Function 222: DrawLineDashed() (5 input parameters) + Name: DrawLineDashed + Return type: void + Description: Draw a dashed line + Param[1]: startPos (type: Vector2) + Param[2]: endPos (type: Vector2) + Param[3]: dashSize (type: int) + Param[4]: whiteSpaceSize (type: int) + Param[5]: color (type: Color) +Function 223: DrawLineV() (3 input parameters) Name: DrawLineV Return type: void Description: Draw a line (using gl lines) Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: color (type: Color) -Function 223: DrawLineEx() (4 input parameters) +Function 224: DrawLineEx() (4 input parameters) Name: DrawLineEx Return type: void Description: Draw a line (using triangles/quads) @@ -2205,14 +2214,14 @@ Function 223: DrawLineEx() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 224: DrawLineStrip() (3 input parameters) +Function 225: DrawLineStrip() (3 input parameters) Name: DrawLineStrip Return type: void Description: Draw lines sequence (using gl lines) Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 225: DrawLineBezier() (4 input parameters) +Function 226: DrawLineBezier() (4 input parameters) Name: DrawLineBezier Return type: void Description: Draw line segment cubic-bezier in-out interpolation @@ -2220,7 +2229,7 @@ Function 225: DrawLineBezier() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 226: DrawCircle() (4 input parameters) +Function 227: DrawCircle() (4 input parameters) Name: DrawCircle Return type: void Description: Draw a color-filled circle @@ -2228,7 +2237,7 @@ Function 226: DrawCircle() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 227: DrawCircleSector() (6 input parameters) +Function 228: DrawCircleSector() (6 input parameters) Name: DrawCircleSector Return type: void Description: Draw a piece of a circle @@ -2238,7 +2247,7 @@ Function 227: DrawCircleSector() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 228: DrawCircleSectorLines() (6 input parameters) +Function 229: DrawCircleSectorLines() (6 input parameters) Name: DrawCircleSectorLines Return type: void Description: Draw circle sector outline @@ -2248,7 +2257,7 @@ Function 228: DrawCircleSectorLines() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 229: DrawCircleGradient() (5 input parameters) +Function 230: DrawCircleGradient() (5 input parameters) Name: DrawCircleGradient Return type: void Description: Draw a gradient-filled circle @@ -2257,14 +2266,14 @@ Function 229: DrawCircleGradient() (5 input parameters) Param[3]: radius (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 230: DrawCircleV() (3 input parameters) +Function 231: DrawCircleV() (3 input parameters) Name: DrawCircleV Return type: void Description: Draw a color-filled circle (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 231: DrawCircleLines() (4 input parameters) +Function 232: DrawCircleLines() (4 input parameters) Name: DrawCircleLines Return type: void Description: Draw circle outline @@ -2272,14 +2281,14 @@ Function 231: DrawCircleLines() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 232: DrawCircleLinesV() (3 input parameters) +Function 233: DrawCircleLinesV() (3 input parameters) Name: DrawCircleLinesV Return type: void Description: Draw circle outline (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 233: DrawEllipse() (5 input parameters) +Function 234: DrawEllipse() (5 input parameters) Name: DrawEllipse Return type: void Description: Draw ellipse @@ -2288,7 +2297,7 @@ Function 233: DrawEllipse() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 234: DrawEllipseV() (4 input parameters) +Function 235: DrawEllipseV() (4 input parameters) Name: DrawEllipseV Return type: void Description: Draw ellipse (Vector version) @@ -2296,7 +2305,7 @@ Function 234: DrawEllipseV() (4 input parameters) Param[2]: radiusH (type: float) Param[3]: radiusV (type: float) Param[4]: color (type: Color) -Function 235: DrawEllipseLines() (5 input parameters) +Function 236: DrawEllipseLines() (5 input parameters) Name: DrawEllipseLines Return type: void Description: Draw ellipse outline @@ -2305,7 +2314,7 @@ Function 235: DrawEllipseLines() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 236: DrawEllipseLinesV() (4 input parameters) +Function 237: DrawEllipseLinesV() (4 input parameters) Name: DrawEllipseLinesV Return type: void Description: Draw ellipse outline (Vector version) @@ -2313,7 +2322,7 @@ Function 236: DrawEllipseLinesV() (4 input parameters) Param[2]: radiusH (type: float) Param[3]: radiusV (type: float) Param[4]: color (type: Color) -Function 237: DrawRing() (7 input parameters) +Function 238: DrawRing() (7 input parameters) Name: DrawRing Return type: void Description: Draw ring @@ -2324,7 +2333,7 @@ Function 237: DrawRing() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 238: DrawRingLines() (7 input parameters) +Function 239: DrawRingLines() (7 input parameters) Name: DrawRingLines Return type: void Description: Draw ring outline @@ -2335,7 +2344,7 @@ Function 238: DrawRingLines() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 239: DrawRectangle() (5 input parameters) +Function 240: DrawRectangle() (5 input parameters) Name: DrawRectangle Return type: void Description: Draw a color-filled rectangle @@ -2344,20 +2353,20 @@ Function 239: DrawRectangle() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 240: DrawRectangleV() (3 input parameters) +Function 241: DrawRectangleV() (3 input parameters) Name: DrawRectangleV Return type: void Description: Draw a color-filled rectangle (Vector version) Param[1]: position (type: Vector2) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 241: DrawRectangleRec() (2 input parameters) +Function 242: DrawRectangleRec() (2 input parameters) Name: DrawRectangleRec Return type: void Description: Draw a color-filled rectangle Param[1]: rec (type: Rectangle) Param[2]: color (type: Color) -Function 242: DrawRectanglePro() (4 input parameters) +Function 243: DrawRectanglePro() (4 input parameters) Name: DrawRectanglePro Return type: void Description: Draw a color-filled rectangle with pro parameters @@ -2365,7 +2374,7 @@ Function 242: DrawRectanglePro() (4 input parameters) Param[2]: origin (type: Vector2) Param[3]: rotation (type: float) Param[4]: color (type: Color) -Function 243: DrawRectangleGradientV() (6 input parameters) +Function 244: DrawRectangleGradientV() (6 input parameters) Name: DrawRectangleGradientV Return type: void Description: Draw a vertical-gradient-filled rectangle @@ -2375,7 +2384,7 @@ Function 243: DrawRectangleGradientV() (6 input parameters) Param[4]: height (type: int) Param[5]: top (type: Color) Param[6]: bottom (type: Color) -Function 244: DrawRectangleGradientH() (6 input parameters) +Function 245: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void Description: Draw a horizontal-gradient-filled rectangle @@ -2385,7 +2394,7 @@ Function 244: DrawRectangleGradientH() (6 input parameters) Param[4]: height (type: int) Param[5]: left (type: Color) Param[6]: right (type: Color) -Function 245: DrawRectangleGradientEx() (5 input parameters) +Function 246: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors @@ -2394,7 +2403,7 @@ Function 245: DrawRectangleGradientEx() (5 input parameters) Param[3]: bottomLeft (type: Color) Param[4]: bottomRight (type: Color) Param[5]: topRight (type: Color) -Function 246: DrawRectangleLines() (5 input parameters) +Function 247: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void Description: Draw rectangle outline @@ -2403,14 +2412,14 @@ Function 246: DrawRectangleLines() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 247: DrawRectangleLinesEx() (3 input parameters) +Function 248: DrawRectangleLinesEx() (3 input parameters) Name: DrawRectangleLinesEx Return type: void Description: Draw rectangle outline with extended parameters Param[1]: rec (type: Rectangle) Param[2]: lineThick (type: float) Param[3]: color (type: Color) -Function 248: DrawRectangleRounded() (4 input parameters) +Function 249: DrawRectangleRounded() (4 input parameters) Name: DrawRectangleRounded Return type: void Description: Draw rectangle with rounded edges @@ -2418,7 +2427,7 @@ Function 248: DrawRectangleRounded() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 249: DrawRectangleRoundedLines() (4 input parameters) +Function 250: DrawRectangleRoundedLines() (4 input parameters) Name: DrawRectangleRoundedLines Return type: void Description: Draw rectangle lines with rounded edges @@ -2426,7 +2435,7 @@ Function 249: DrawRectangleRoundedLines() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 250: DrawRectangleRoundedLinesEx() (5 input parameters) +Function 251: DrawRectangleRoundedLinesEx() (5 input parameters) Name: DrawRectangleRoundedLinesEx Return type: void Description: Draw rectangle with rounded edges outline @@ -2435,7 +2444,7 @@ Function 250: DrawRectangleRoundedLinesEx() (5 input parameters) Param[3]: segments (type: int) Param[4]: lineThick (type: float) Param[5]: color (type: Color) -Function 251: DrawTriangle() (4 input parameters) +Function 252: DrawTriangle() (4 input parameters) Name: DrawTriangle Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -2443,7 +2452,7 @@ Function 251: DrawTriangle() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 252: DrawTriangleLines() (4 input parameters) +Function 253: DrawTriangleLines() (4 input parameters) Name: DrawTriangleLines Return type: void Description: Draw triangle outline (vertex in counter-clockwise order!) @@ -2451,21 +2460,21 @@ Function 252: DrawTriangleLines() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 253: DrawTriangleFan() (3 input parameters) +Function 254: DrawTriangleFan() (3 input parameters) Name: DrawTriangleFan Return type: void Description: Draw a triangle fan defined by points (first vertex is the center) Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 254: DrawTriangleStrip() (3 input parameters) +Function 255: DrawTriangleStrip() (3 input parameters) Name: DrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 255: DrawPoly() (5 input parameters) +Function 256: DrawPoly() (5 input parameters) Name: DrawPoly Return type: void Description: Draw a regular polygon (Vector version) @@ -2474,7 +2483,7 @@ Function 255: DrawPoly() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 256: DrawPolyLines() (5 input parameters) +Function 257: DrawPolyLines() (5 input parameters) Name: DrawPolyLines Return type: void Description: Draw a polygon outline of n sides @@ -2483,7 +2492,7 @@ Function 256: DrawPolyLines() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 257: DrawPolyLinesEx() (6 input parameters) +Function 258: DrawPolyLinesEx() (6 input parameters) Name: DrawPolyLinesEx Return type: void Description: Draw a polygon outline of n sides with extended parameters @@ -2493,7 +2502,7 @@ Function 257: DrawPolyLinesEx() (6 input parameters) Param[4]: rotation (type: float) Param[5]: lineThick (type: float) Param[6]: color (type: Color) -Function 258: DrawSplineLinear() (4 input parameters) +Function 259: DrawSplineLinear() (4 input parameters) Name: DrawSplineLinear Return type: void Description: Draw spline: Linear, minimum 2 points @@ -2501,7 +2510,7 @@ Function 258: DrawSplineLinear() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 259: DrawSplineBasis() (4 input parameters) +Function 260: DrawSplineBasis() (4 input parameters) Name: DrawSplineBasis Return type: void Description: Draw spline: B-Spline, minimum 4 points @@ -2509,7 +2518,7 @@ Function 259: DrawSplineBasis() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 260: DrawSplineCatmullRom() (4 input parameters) +Function 261: DrawSplineCatmullRom() (4 input parameters) Name: DrawSplineCatmullRom Return type: void Description: Draw spline: Catmull-Rom, minimum 4 points @@ -2517,7 +2526,7 @@ Function 260: DrawSplineCatmullRom() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 261: DrawSplineBezierQuadratic() (4 input parameters) +Function 262: DrawSplineBezierQuadratic() (4 input parameters) Name: DrawSplineBezierQuadratic Return type: void Description: Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] @@ -2525,7 +2534,7 @@ Function 261: DrawSplineBezierQuadratic() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 262: DrawSplineBezierCubic() (4 input parameters) +Function 263: DrawSplineBezierCubic() (4 input parameters) Name: DrawSplineBezierCubic Return type: void Description: Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] @@ -2533,7 +2542,7 @@ Function 262: DrawSplineBezierCubic() (4 input parameters) Param[2]: pointCount (type: int) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 263: DrawSplineSegmentLinear() (4 input parameters) +Function 264: DrawSplineSegmentLinear() (4 input parameters) Name: DrawSplineSegmentLinear Return type: void Description: Draw spline segment: Linear, 2 points @@ -2541,7 +2550,7 @@ Function 263: DrawSplineSegmentLinear() (4 input parameters) Param[2]: p2 (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 264: DrawSplineSegmentBasis() (6 input parameters) +Function 265: DrawSplineSegmentBasis() (6 input parameters) Name: DrawSplineSegmentBasis Return type: void Description: Draw spline segment: B-Spline, 4 points @@ -2551,7 +2560,7 @@ Function 264: DrawSplineSegmentBasis() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 265: DrawSplineSegmentCatmullRom() (6 input parameters) +Function 266: DrawSplineSegmentCatmullRom() (6 input parameters) Name: DrawSplineSegmentCatmullRom Return type: void Description: Draw spline segment: Catmull-Rom, 4 points @@ -2561,7 +2570,7 @@ Function 265: DrawSplineSegmentCatmullRom() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 266: DrawSplineSegmentBezierQuadratic() (5 input parameters) +Function 267: DrawSplineSegmentBezierQuadratic() (5 input parameters) Name: DrawSplineSegmentBezierQuadratic Return type: void Description: Draw spline segment: Quadratic Bezier, 2 points, 1 control point @@ -2570,7 +2579,7 @@ Function 266: DrawSplineSegmentBezierQuadratic() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: thick (type: float) Param[5]: color (type: Color) -Function 267: DrawSplineSegmentBezierCubic() (6 input parameters) +Function 268: DrawSplineSegmentBezierCubic() (6 input parameters) Name: DrawSplineSegmentBezierCubic Return type: void Description: Draw spline segment: Cubic Bezier, 2 points, 2 control points @@ -2580,14 +2589,14 @@ Function 267: DrawSplineSegmentBezierCubic() (6 input parameters) Param[4]: p4 (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 268: GetSplinePointLinear() (3 input parameters) +Function 269: GetSplinePointLinear() (3 input parameters) Name: GetSplinePointLinear Return type: Vector2 Description: Get (evaluate) spline point: Linear Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: t (type: float) -Function 269: GetSplinePointBasis() (5 input parameters) +Function 270: GetSplinePointBasis() (5 input parameters) Name: GetSplinePointBasis Return type: Vector2 Description: Get (evaluate) spline point: B-Spline @@ -2596,7 +2605,7 @@ Function 269: GetSplinePointBasis() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 270: GetSplinePointCatmullRom() (5 input parameters) +Function 271: GetSplinePointCatmullRom() (5 input parameters) Name: GetSplinePointCatmullRom Return type: Vector2 Description: Get (evaluate) spline point: Catmull-Rom @@ -2605,7 +2614,7 @@ Function 270: GetSplinePointCatmullRom() (5 input parameters) Param[3]: p3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 271: GetSplinePointBezierQuad() (4 input parameters) +Function 272: GetSplinePointBezierQuad() (4 input parameters) Name: GetSplinePointBezierQuad Return type: Vector2 Description: Get (evaluate) spline point: Quadratic Bezier @@ -2613,7 +2622,7 @@ Function 271: GetSplinePointBezierQuad() (4 input parameters) Param[2]: c2 (type: Vector2) Param[3]: p3 (type: Vector2) Param[4]: t (type: float) -Function 272: GetSplinePointBezierCubic() (5 input parameters) +Function 273: GetSplinePointBezierCubic() (5 input parameters) Name: GetSplinePointBezierCubic Return type: Vector2 Description: Get (evaluate) spline point: Cubic Bezier @@ -2622,13 +2631,13 @@ Function 272: GetSplinePointBezierCubic() (5 input parameters) Param[3]: c3 (type: Vector2) Param[4]: p4 (type: Vector2) Param[5]: t (type: float) -Function 273: CheckCollisionRecs() (2 input parameters) +Function 274: CheckCollisionRecs() (2 input parameters) Name: CheckCollisionRecs Return type: bool Description: Check collision between two rectangles Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 274: CheckCollisionCircles() (4 input parameters) +Function 275: CheckCollisionCircles() (4 input parameters) Name: CheckCollisionCircles Return type: bool Description: Check collision between two circles @@ -2636,14 +2645,14 @@ Function 274: CheckCollisionCircles() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector2) Param[4]: radius2 (type: float) -Function 275: CheckCollisionCircleRec() (3 input parameters) +Function 276: CheckCollisionCircleRec() (3 input parameters) Name: CheckCollisionCircleRec Return type: bool Description: Check collision between circle and rectangle Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: rec (type: Rectangle) -Function 276: CheckCollisionCircleLine() (4 input parameters) +Function 277: CheckCollisionCircleLine() (4 input parameters) Name: CheckCollisionCircleLine Return type: bool Description: Check if circle collides with a line created betweeen two points [p1] and [p2] @@ -2651,20 +2660,20 @@ Function 276: CheckCollisionCircleLine() (4 input parameters) Param[2]: radius (type: float) Param[3]: p1 (type: Vector2) Param[4]: p2 (type: Vector2) -Function 277: CheckCollisionPointRec() (2 input parameters) +Function 278: CheckCollisionPointRec() (2 input parameters) Name: CheckCollisionPointRec Return type: bool Description: Check if point is inside rectangle Param[1]: point (type: Vector2) Param[2]: rec (type: Rectangle) -Function 278: CheckCollisionPointCircle() (3 input parameters) +Function 279: CheckCollisionPointCircle() (3 input parameters) Name: CheckCollisionPointCircle Return type: bool Description: Check if point is inside circle Param[1]: point (type: Vector2) Param[2]: center (type: Vector2) Param[3]: radius (type: float) -Function 279: CheckCollisionPointTriangle() (4 input parameters) +Function 280: CheckCollisionPointTriangle() (4 input parameters) Name: CheckCollisionPointTriangle Return type: bool Description: Check if point is inside a triangle @@ -2672,7 +2681,7 @@ Function 279: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 280: CheckCollisionPointLine() (4 input parameters) +Function 281: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2680,14 +2689,14 @@ Function 280: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 281: CheckCollisionPointPoly() (3 input parameters) +Function 282: CheckCollisionPointPoly() (3 input parameters) Name: CheckCollisionPointPoly Return type: bool Description: Check if point is within a polygon described by array of vertices Param[1]: point (type: Vector2) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) -Function 282: CheckCollisionLines() (5 input parameters) +Function 283: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2696,18 +2705,18 @@ Function 282: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 283: GetCollisionRec() (2 input parameters) +Function 284: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 284: LoadImage() (1 input parameters) +Function 285: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 285: LoadImageRaw() (5 input parameters) +Function 286: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2716,13 +2725,13 @@ Function 285: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 286: LoadImageAnim() (2 input parameters) +Function 287: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 287: LoadImageAnimFromMemory() (4 input parameters) +Function 288: LoadImageAnimFromMemory() (4 input parameters) Name: LoadImageAnimFromMemory Return type: Image Description: Load image sequence from memory buffer @@ -2730,60 +2739,60 @@ Function 287: LoadImageAnimFromMemory() (4 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: frames (type: int *) -Function 288: LoadImageFromMemory() (3 input parameters) +Function 289: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 289: LoadImageFromTexture() (1 input parameters) +Function 290: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 290: LoadImageFromScreen() (0 input parameters) +Function 291: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 291: IsImageValid() (1 input parameters) +Function 292: IsImageValid() (1 input parameters) Name: IsImageValid Return type: bool Description: Check if an image is valid (data and parameters) Param[1]: image (type: Image) -Function 292: UnloadImage() (1 input parameters) +Function 293: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 293: ExportImage() (2 input parameters) +Function 294: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 294: ExportImageToMemory() (3 input parameters) +Function 295: ExportImageToMemory() (3 input parameters) Name: ExportImageToMemory Return type: unsigned char * Description: Export image to memory buffer Param[1]: image (type: Image) Param[2]: fileType (type: const char *) Param[3]: fileSize (type: int *) -Function 295: ExportImageAsCode() (2 input parameters) +Function 296: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 296: GenImageColor() (3 input parameters) +Function 297: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 297: GenImageGradientLinear() (5 input parameters) +Function 298: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient @@ -2792,7 +2801,7 @@ Function 297: GenImageGradientLinear() (5 input parameters) Param[3]: direction (type: int) Param[4]: start (type: Color) Param[5]: end (type: Color) -Function 298: GenImageGradientRadial() (5 input parameters) +Function 299: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2801,7 +2810,7 @@ Function 298: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 299: GenImageGradientSquare() (5 input parameters) +Function 300: GenImageGradientSquare() (5 input parameters) Name: GenImageGradientSquare Return type: Image Description: Generate image: square gradient @@ -2810,7 +2819,7 @@ Function 299: GenImageGradientSquare() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 300: GenImageChecked() (6 input parameters) +Function 301: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2820,14 +2829,14 @@ Function 300: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 301: GenImageWhiteNoise() (3 input parameters) +Function 302: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 302: GenImagePerlinNoise() (5 input parameters) +Function 303: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2836,45 +2845,45 @@ Function 302: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 303: GenImageCellular() (3 input parameters) +Function 304: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 304: GenImageText() (3 input parameters) +Function 305: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 305: ImageCopy() (1 input parameters) +Function 306: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 306: ImageFromImage() (2 input parameters) +Function 307: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 307: ImageFromChannel() (2 input parameters) +Function 308: ImageFromChannel() (2 input parameters) Name: ImageFromChannel Return type: Image Description: Create an image from a selected channel of another image (GRAYSCALE) Param[1]: image (type: Image) Param[2]: selectedChannel (type: int) -Function 308: ImageText() (3 input parameters) +Function 309: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 309: ImageTextEx() (5 input parameters) +Function 310: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2883,76 +2892,76 @@ Function 309: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 310: ImageFormat() (2 input parameters) +Function 311: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 311: ImageToPOT() (2 input parameters) +Function 312: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 312: ImageCrop() (2 input parameters) +Function 313: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 313: ImageAlphaCrop() (2 input parameters) +Function 314: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 314: ImageAlphaClear() (3 input parameters) +Function 315: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 315: ImageAlphaMask() (2 input parameters) +Function 316: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 316: ImageAlphaPremultiply() (1 input parameters) +Function 317: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 317: ImageBlurGaussian() (2 input parameters) +Function 318: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 318: ImageKernelConvolution() (3 input parameters) +Function 319: ImageKernelConvolution() (3 input parameters) Name: ImageKernelConvolution Return type: void Description: Apply custom square convolution kernel to image Param[1]: image (type: Image *) Param[2]: kernel (type: const float *) Param[3]: kernelSize (type: int) -Function 319: ImageResize() (3 input parameters) +Function 320: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 320: ImageResizeNN() (3 input parameters) +Function 321: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 321: ImageResizeCanvas() (6 input parameters) +Function 322: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2962,12 +2971,12 @@ Function 321: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 322: ImageMipmaps() (1 input parameters) +Function 323: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 323: ImageDither() (5 input parameters) +Function 324: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2976,109 +2985,109 @@ Function 323: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 324: ImageFlipVertical() (1 input parameters) +Function 325: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 325: ImageFlipHorizontal() (1 input parameters) +Function 326: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 326: ImageRotate() (2 input parameters) +Function 327: ImageRotate() (2 input parameters) Name: ImageRotate Return type: void Description: Rotate image by input angle in degrees (-359 to 359) Param[1]: image (type: Image *) Param[2]: degrees (type: int) -Function 327: ImageRotateCW() (1 input parameters) +Function 328: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 328: ImageRotateCCW() (1 input parameters) +Function 329: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 329: ImageColorTint() (2 input parameters) +Function 330: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 330: ImageColorInvert() (1 input parameters) +Function 331: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 331: ImageColorGrayscale() (1 input parameters) +Function 332: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 332: ImageColorContrast() (2 input parameters) +Function 333: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 333: ImageColorBrightness() (2 input parameters) +Function 334: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 334: ImageColorReplace() (3 input parameters) +Function 335: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 335: LoadImageColors() (1 input parameters) +Function 336: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 336: LoadImagePalette() (3 input parameters) +Function 337: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 337: UnloadImageColors() (1 input parameters) +Function 338: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 338: UnloadImagePalette() (1 input parameters) +Function 339: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 339: GetImageAlphaBorder() (2 input parameters) +Function 340: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 340: GetImageColor() (3 input parameters) +Function 341: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 341: ImageClearBackground() (2 input parameters) +Function 342: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 342: ImageDrawPixel() (4 input parameters) +Function 343: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -3086,14 +3095,14 @@ Function 342: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 343: ImageDrawPixelV() (3 input parameters) +Function 344: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 344: ImageDrawLine() (6 input parameters) +Function 345: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -3103,7 +3112,7 @@ Function 344: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 345: ImageDrawLineV() (4 input parameters) +Function 346: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -3111,7 +3120,7 @@ Function 345: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 346: ImageDrawLineEx() (5 input parameters) +Function 347: ImageDrawLineEx() (5 input parameters) Name: ImageDrawLineEx Return type: void Description: Draw a line defining thickness within an image @@ -3120,7 +3129,7 @@ Function 346: ImageDrawLineEx() (5 input parameters) Param[3]: end (type: Vector2) Param[4]: thick (type: int) Param[5]: color (type: Color) -Function 347: ImageDrawCircle() (5 input parameters) +Function 348: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -3129,7 +3138,7 @@ Function 347: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 348: ImageDrawCircleV() (4 input parameters) +Function 349: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -3137,7 +3146,7 @@ Function 348: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 349: ImageDrawCircleLines() (5 input parameters) +Function 350: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -3146,7 +3155,7 @@ Function 349: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 350: ImageDrawCircleLinesV() (4 input parameters) +Function 351: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -3154,7 +3163,7 @@ Function 350: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 351: ImageDrawRectangle() (6 input parameters) +Function 352: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -3164,7 +3173,7 @@ Function 351: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 352: ImageDrawRectangleV() (4 input parameters) +Function 353: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -3172,14 +3181,14 @@ Function 352: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 353: ImageDrawRectangleRec() (3 input parameters) +Function 354: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 354: ImageDrawRectangleLines() (4 input parameters) +Function 355: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -3187,7 +3196,7 @@ Function 354: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 355: ImageDrawTriangle() (5 input parameters) +Function 356: ImageDrawTriangle() (5 input parameters) Name: ImageDrawTriangle Return type: void Description: Draw triangle within an image @@ -3196,7 +3205,7 @@ Function 355: ImageDrawTriangle() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 356: ImageDrawTriangleEx() (7 input parameters) +Function 357: ImageDrawTriangleEx() (7 input parameters) Name: ImageDrawTriangleEx Return type: void Description: Draw triangle with interpolated colors within an image @@ -3207,7 +3216,7 @@ Function 356: ImageDrawTriangleEx() (7 input parameters) Param[5]: c1 (type: Color) Param[6]: c2 (type: Color) Param[7]: c3 (type: Color) -Function 357: ImageDrawTriangleLines() (5 input parameters) +Function 358: ImageDrawTriangleLines() (5 input parameters) Name: ImageDrawTriangleLines Return type: void Description: Draw triangle outline within an image @@ -3216,7 +3225,7 @@ Function 357: ImageDrawTriangleLines() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 358: ImageDrawTriangleFan() (4 input parameters) +Function 359: ImageDrawTriangleFan() (4 input parameters) Name: ImageDrawTriangleFan Return type: void Description: Draw a triangle fan defined by points within an image (first vertex is the center) @@ -3224,7 +3233,7 @@ Function 358: ImageDrawTriangleFan() (4 input parameters) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 359: ImageDrawTriangleStrip() (4 input parameters) +Function 360: ImageDrawTriangleStrip() (4 input parameters) Name: ImageDrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points within an image @@ -3232,7 +3241,7 @@ Function 359: ImageDrawTriangleStrip() (4 input parameters) Param[2]: points (type: const Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 360: ImageDraw() (5 input parameters) +Function 361: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -3241,7 +3250,7 @@ Function 360: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 361: ImageDrawText() (6 input parameters) +Function 362: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -3251,7 +3260,7 @@ Function 361: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 362: ImageDrawTextEx() (7 input parameters) +Function 363: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -3262,79 +3271,79 @@ Function 362: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 363: LoadTexture() (1 input parameters) +Function 364: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 364: LoadTextureFromImage() (1 input parameters) +Function 365: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 365: LoadTextureCubemap() (2 input parameters) +Function 366: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 366: LoadRenderTexture() (2 input parameters) +Function 367: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 367: IsTextureValid() (1 input parameters) +Function 368: IsTextureValid() (1 input parameters) Name: IsTextureValid Return type: bool Description: Check if a texture is valid (loaded in GPU) Param[1]: texture (type: Texture2D) -Function 368: UnloadTexture() (1 input parameters) +Function 369: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 369: IsRenderTextureValid() (1 input parameters) +Function 370: IsRenderTextureValid() (1 input parameters) Name: IsRenderTextureValid Return type: bool Description: Check if a render texture is valid (loaded in GPU) Param[1]: target (type: RenderTexture2D) -Function 370: UnloadRenderTexture() (1 input parameters) +Function 371: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 371: UpdateTexture() (2 input parameters) +Function 372: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data (pixels should be able to fill texture) Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 372: UpdateTextureRec() (3 input parameters) +Function 373: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data (pixels and rec should fit in texture) Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 373: GenTextureMipmaps() (1 input parameters) +Function 374: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 374: SetTextureFilter() (2 input parameters) +Function 375: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 375: SetTextureWrap() (2 input parameters) +Function 376: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 376: DrawTexture() (4 input parameters) +Function 377: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -3342,14 +3351,14 @@ Function 376: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 377: DrawTextureV() (3 input parameters) +Function 378: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 378: DrawTextureEx() (5 input parameters) +Function 379: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -3358,7 +3367,7 @@ Function 378: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 379: DrawTextureRec() (4 input parameters) +Function 380: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -3366,7 +3375,7 @@ Function 379: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 380: DrawTexturePro() (6 input parameters) +Function 381: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -3376,7 +3385,7 @@ Function 380: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 381: DrawTextureNPatch() (6 input parameters) +Function 382: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -3386,119 +3395,119 @@ Function 381: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 382: ColorIsEqual() (2 input parameters) +Function 383: ColorIsEqual() (2 input parameters) Name: ColorIsEqual Return type: bool Description: Check if two colors are equal Param[1]: col1 (type: Color) Param[2]: col2 (type: Color) -Function 383: Fade() (2 input parameters) +Function 384: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 384: ColorToInt() (1 input parameters) +Function 385: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color (0xRRGGBBAA) Param[1]: color (type: Color) -Function 385: ColorNormalize() (1 input parameters) +Function 386: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 386: ColorFromNormalized() (1 input parameters) +Function 387: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 387: ColorToHSV() (1 input parameters) +Function 388: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 388: ColorFromHSV() (3 input parameters) +Function 389: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 389: ColorTint() (2 input parameters) +Function 390: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 390: ColorBrightness() (2 input parameters) +Function 391: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 391: ColorContrast() (2 input parameters) +Function 392: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 392: ColorAlpha() (2 input parameters) +Function 393: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 393: ColorAlphaBlend() (3 input parameters) +Function 394: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 394: ColorLerp() (3 input parameters) +Function 395: ColorLerp() (3 input parameters) Name: ColorLerp Return type: Color Description: Get color lerp interpolation between two colors, factor [0.0f..1.0f] Param[1]: color1 (type: Color) Param[2]: color2 (type: Color) Param[3]: factor (type: float) -Function 395: GetColor() (1 input parameters) +Function 396: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 396: GetPixelColor() (2 input parameters) +Function 397: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 397: SetPixelColor() (3 input parameters) +Function 398: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 398: GetPixelDataSize() (3 input parameters) +Function 399: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 399: GetFontDefault() (0 input parameters) +Function 400: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 400: LoadFont() (1 input parameters) +Function 401: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 401: LoadFontEx() (4 input parameters) +Function 402: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height @@ -3506,14 +3515,14 @@ Function 401: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: codepoints (type: const int *) Param[4]: codepointCount (type: int) -Function 402: LoadFontFromImage() (3 input parameters) +Function 403: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 403: LoadFontFromMemory() (6 input parameters) +Function 404: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3523,12 +3532,12 @@ Function 403: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: codepoints (type: const int *) Param[6]: codepointCount (type: int) -Function 404: IsFontValid() (1 input parameters) +Function 405: IsFontValid() (1 input parameters) Name: IsFontValid Return type: bool Description: Check if a font is valid (font data loaded, WARNING: GPU texture not checked) Param[1]: font (type: Font) -Function 405: LoadFontData() (7 input parameters) +Function 406: LoadFontData() (7 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3539,7 +3548,7 @@ Function 405: LoadFontData() (7 input parameters) Param[5]: codepointCount (type: int) Param[6]: type (type: int) Param[7]: glyphCount (type: int *) -Function 406: GenImageFontAtlas() (6 input parameters) +Function 407: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3549,30 +3558,30 @@ Function 406: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 407: UnloadFontData() (2 input parameters) +Function 408: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: glyphs (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 408: UnloadFont() (1 input parameters) +Function 409: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 409: ExportFontAsCode() (2 input parameters) +Function 410: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 410: DrawFPS() (2 input parameters) +Function 411: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 411: DrawText() (5 input parameters) +Function 412: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3581,7 +3590,7 @@ Function 411: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 412: DrawTextEx() (6 input parameters) +Function 413: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3591,7 +3600,7 @@ Function 412: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 413: DrawTextPro() (8 input parameters) +Function 414: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3603,7 +3612,7 @@ Function 413: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 414: DrawTextCodepoint() (5 input parameters) +Function 415: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3612,7 +3621,7 @@ Function 414: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 415: DrawTextCodepoints() (7 input parameters) +Function 416: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3623,18 +3632,18 @@ Function 415: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 416: SetTextLineSpacing() (1 input parameters) +Function 417: SetTextLineSpacing() (1 input parameters) Name: SetTextLineSpacing Return type: void Description: Set vertical line spacing when drawing with line-breaks Param[1]: spacing (type: int) -Function 417: MeasureText() (2 input parameters) +Function 418: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 418: MeasureTextEx() (4 input parameters) +Function 419: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3642,137 +3651,137 @@ Function 418: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 419: GetGlyphIndex() (2 input parameters) +Function 420: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 420: GetGlyphInfo() (2 input parameters) +Function 421: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 421: GetGlyphAtlasRec() (2 input parameters) +Function 422: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 422: LoadUTF8() (2 input parameters) +Function 423: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 423: UnloadUTF8() (1 input parameters) +Function 424: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 424: LoadCodepoints() (2 input parameters) +Function 425: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 425: UnloadCodepoints() (1 input parameters) +Function 426: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 426: GetCodepointCount() (1 input parameters) +Function 427: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 427: GetCodepoint() (2 input parameters) +Function 428: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 428: GetCodepointNext() (2 input parameters) +Function 429: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 429: GetCodepointPrevious() (2 input parameters) +Function 430: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 430: CodepointToUTF8() (2 input parameters) +Function 431: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 431: LoadTextLines() (2 input parameters) +Function 432: LoadTextLines() (2 input parameters) Name: LoadTextLines Return type: char ** Description: Load text as separate lines ('\n') Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 432: UnloadTextLines() (2 input parameters) +Function 433: UnloadTextLines() (2 input parameters) Name: UnloadTextLines Return type: void Description: Unload text lines Param[1]: text (type: char **) Param[2]: lineCount (type: int) -Function 433: TextCopy() (2 input parameters) +Function 434: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 434: TextIsEqual() (2 input parameters) +Function 435: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 435: TextLength() (1 input parameters) +Function 436: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 436: TextFormat() (2 input parameters) +Function 437: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 437: TextSubtext() (3 input parameters) +Function 438: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 438: TextRemoveSpaces() (1 input parameters) +Function 439: TextRemoveSpaces() (1 input parameters) Name: TextRemoveSpaces Return type: const char * Description: Remove text spaces, concat words Param[1]: text (type: const char *) -Function 439: GetTextBetween() (3 input parameters) +Function 440: GetTextBetween() (3 input parameters) Name: GetTextBetween Return type: char * Description: Get text between two strings Param[1]: text (type: const char *) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) -Function 440: TextReplace() (3 input parameters) +Function 441: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: search (type: const char *) Param[3]: replacement (type: const char *) -Function 441: TextReplaceBetween() (4 input parameters) +Function 442: TextReplaceBetween() (4 input parameters) Name: TextReplaceBetween Return type: char * Description: Replace text between two specific strings (WARNING: memory must be freed!) @@ -3780,89 +3789,89 @@ Function 441: TextReplaceBetween() (4 input parameters) Param[2]: begin (type: const char *) Param[3]: end (type: const char *) Param[4]: replacement (type: const char *) -Function 442: TextInsert() (3 input parameters) +Function 443: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 443: TextJoin() (3 input parameters) +Function 444: TextJoin() (3 input parameters) Name: TextJoin Return type: char * Description: Join text strings with delimiter Param[1]: textList (type: char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 444: TextSplit() (3 input parameters) +Function 445: TextSplit() (3 input parameters) Name: TextSplit Return type: char ** Description: Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 445: TextAppend() (3 input parameters) +Function 446: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 446: TextFindIndex() (2 input parameters) +Function 447: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string, -1 if not found Param[1]: text (type: const char *) Param[2]: search (type: const char *) -Function 447: TextToUpper() (1 input parameters) +Function 448: TextToUpper() (1 input parameters) Name: TextToUpper Return type: char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 448: TextToLower() (1 input parameters) +Function 449: TextToLower() (1 input parameters) Name: TextToLower Return type: char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 449: TextToPascal() (1 input parameters) +Function 450: TextToPascal() (1 input parameters) Name: TextToPascal Return type: char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 450: TextToSnake() (1 input parameters) +Function 451: TextToSnake() (1 input parameters) Name: TextToSnake Return type: char * Description: Get Snake case notation version of provided string Param[1]: text (type: const char *) -Function 451: TextToCamel() (1 input parameters) +Function 452: TextToCamel() (1 input parameters) Name: TextToCamel Return type: char * Description: Get Camel case notation version of provided string Param[1]: text (type: const char *) -Function 452: TextToInteger() (1 input parameters) +Function 453: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text Param[1]: text (type: const char *) -Function 453: TextToFloat() (1 input parameters) +Function 454: TextToFloat() (1 input parameters) Name: TextToFloat Return type: float Description: Get float value from text Param[1]: text (type: const char *) -Function 454: DrawLine3D() (3 input parameters) +Function 455: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 455: DrawPoint3D() (2 input parameters) +Function 456: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 456: DrawCircle3D() (5 input parameters) +Function 457: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3871,7 +3880,7 @@ Function 456: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 457: DrawTriangle3D() (4 input parameters) +Function 458: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3879,14 +3888,14 @@ Function 457: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 458: DrawTriangleStrip3D() (3 input parameters) +Function 459: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 459: DrawCube() (5 input parameters) +Function 460: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3895,14 +3904,14 @@ Function 459: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 460: DrawCubeV() (3 input parameters) +Function 461: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 461: DrawCubeWires() (5 input parameters) +Function 462: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3911,21 +3920,21 @@ Function 461: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 462: DrawCubeWiresV() (3 input parameters) +Function 463: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 463: DrawSphere() (3 input parameters) +Function 464: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 464: DrawSphereEx() (5 input parameters) +Function 465: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3934,7 +3943,7 @@ Function 464: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 465: DrawSphereWires() (5 input parameters) +Function 466: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3943,7 +3952,7 @@ Function 465: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 466: DrawCylinder() (6 input parameters) +Function 467: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3953,7 +3962,7 @@ Function 466: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 467: DrawCylinderEx() (6 input parameters) +Function 468: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3963,7 +3972,7 @@ Function 467: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 468: DrawCylinderWires() (6 input parameters) +Function 469: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3973,7 +3982,7 @@ Function 468: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 469: DrawCylinderWiresEx() (6 input parameters) +Function 470: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3983,7 +3992,7 @@ Function 469: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 470: DrawCapsule() (6 input parameters) +Function 471: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3993,7 +4002,7 @@ Function 470: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 471: DrawCapsuleWires() (6 input parameters) +Function 472: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -4003,51 +4012,51 @@ Function 471: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 472: DrawPlane() (3 input parameters) +Function 473: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 473: DrawRay() (2 input parameters) +Function 474: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 474: DrawGrid() (2 input parameters) +Function 475: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 475: LoadModel() (1 input parameters) +Function 476: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 476: LoadModelFromMesh() (1 input parameters) +Function 477: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 477: IsModelValid() (1 input parameters) +Function 478: IsModelValid() (1 input parameters) Name: IsModelValid Return type: bool Description: Check if a model is valid (loaded in GPU, VAO/VBOs) Param[1]: model (type: Model) -Function 478: UnloadModel() (1 input parameters) +Function 479: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 479: GetModelBoundingBox() (1 input parameters) +Function 480: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 480: DrawModel() (4 input parameters) +Function 481: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -4055,7 +4064,7 @@ Function 480: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 481: DrawModelEx() (6 input parameters) +Function 482: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -4065,7 +4074,7 @@ Function 481: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 482: DrawModelWires() (4 input parameters) +Function 483: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -4073,7 +4082,7 @@ Function 482: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 483: DrawModelWiresEx() (6 input parameters) +Function 484: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -4083,7 +4092,7 @@ Function 483: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 484: DrawModelPoints() (4 input parameters) +Function 485: DrawModelPoints() (4 input parameters) Name: DrawModelPoints Return type: void Description: Draw a model as points @@ -4091,7 +4100,7 @@ Function 484: DrawModelPoints() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 485: DrawModelPointsEx() (6 input parameters) +Function 486: DrawModelPointsEx() (6 input parameters) Name: DrawModelPointsEx Return type: void Description: Draw a model as points with extended parameters @@ -4101,13 +4110,13 @@ Function 485: DrawModelPointsEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 486: DrawBoundingBox() (2 input parameters) +Function 487: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 487: DrawBillboard() (5 input parameters) +Function 488: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -4116,7 +4125,7 @@ Function 487: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 488: DrawBillboardRec() (6 input parameters) +Function 489: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -4126,7 +4135,7 @@ Function 488: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 489: DrawBillboardPro() (9 input parameters) +Function 490: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -4139,13 +4148,13 @@ Function 489: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 490: UploadMesh() (2 input parameters) +Function 491: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 491: UpdateMeshBuffer() (5 input parameters) +Function 492: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4154,19 +4163,19 @@ Function 491: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 492: UnloadMesh() (1 input parameters) +Function 493: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 493: DrawMesh() (3 input parameters) +Function 494: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 494: DrawMeshInstanced() (4 input parameters) +Function 495: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4174,35 +4183,35 @@ Function 494: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 495: GetMeshBoundingBox() (1 input parameters) +Function 496: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 496: GenMeshTangents() (1 input parameters) +Function 497: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 497: ExportMesh() (2 input parameters) +Function 498: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 498: ExportMeshAsCode() (2 input parameters) +Function 499: ExportMeshAsCode() (2 input parameters) Name: ExportMeshAsCode Return type: bool Description: Export mesh as code file (.h) defining multiple arrays of vertex attributes Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 499: GenMeshPoly() (2 input parameters) +Function 500: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 500: GenMeshPlane() (4 input parameters) +Function 501: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4210,42 +4219,42 @@ Function 500: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 501: GenMeshCube() (3 input parameters) +Function 502: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 502: GenMeshSphere() (3 input parameters) +Function 503: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 503: GenMeshHemiSphere() (3 input parameters) +Function 504: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 504: GenMeshCylinder() (3 input parameters) +Function 505: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 505: GenMeshCone() (3 input parameters) +Function 506: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 506: GenMeshTorus() (4 input parameters) +Function 507: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4253,7 +4262,7 @@ Function 506: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 507: GenMeshKnot() (4 input parameters) +Function 508: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4261,91 +4270,91 @@ Function 507: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 508: GenMeshHeightmap() (2 input parameters) +Function 509: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 509: GenMeshCubicmap() (2 input parameters) +Function 510: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 510: LoadMaterials() (2 input parameters) +Function 511: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 511: LoadMaterialDefault() (0 input parameters) +Function 512: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 512: IsMaterialValid() (1 input parameters) +Function 513: IsMaterialValid() (1 input parameters) Name: IsMaterialValid Return type: bool Description: Check if a material is valid (shader assigned, map textures loaded in GPU) Param[1]: material (type: Material) -Function 513: UnloadMaterial() (1 input parameters) +Function 514: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 514: SetMaterialTexture() (3 input parameters) +Function 515: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 515: SetModelMeshMaterial() (3 input parameters) +Function 516: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 516: LoadModelAnimations() (2 input parameters) +Function 517: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: int *) -Function 517: UpdateModelAnimation() (3 input parameters) +Function 518: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose (CPU) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 518: UpdateModelAnimationBones() (3 input parameters) +Function 519: UpdateModelAnimationBones() (3 input parameters) Name: UpdateModelAnimationBones Return type: void Description: Update model animation mesh bone matrices (GPU skinning) Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 519: UnloadModelAnimation() (1 input parameters) +Function 520: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 520: UnloadModelAnimations() (2 input parameters) +Function 521: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: animCount (type: int) -Function 521: IsModelAnimationValid() (2 input parameters) +Function 522: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 522: CheckCollisionSpheres() (4 input parameters) +Function 523: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4353,40 +4362,40 @@ Function 522: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 523: CheckCollisionBoxes() (2 input parameters) +Function 524: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 524: CheckCollisionBoxSphere() (3 input parameters) +Function 525: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 525: GetRayCollisionSphere() (3 input parameters) +Function 526: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 526: GetRayCollisionBox() (2 input parameters) +Function 527: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 527: GetRayCollisionMesh() (3 input parameters) +Function 528: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 528: GetRayCollisionTriangle() (4 input parameters) +Function 529: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4394,7 +4403,7 @@ Function 528: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 529: GetRayCollisionQuad() (5 input parameters) +Function 530: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4403,158 +4412,158 @@ Function 529: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 530: InitAudioDevice() (0 input parameters) +Function 531: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 531: CloseAudioDevice() (0 input parameters) +Function 532: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 532: IsAudioDeviceReady() (0 input parameters) +Function 533: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 533: SetMasterVolume() (1 input parameters) +Function 534: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 534: GetMasterVolume() (0 input parameters) +Function 535: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 535: LoadWave() (1 input parameters) +Function 536: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 536: LoadWaveFromMemory() (3 input parameters) +Function 537: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 537: IsWaveValid() (1 input parameters) +Function 538: IsWaveValid() (1 input parameters) Name: IsWaveValid Return type: bool Description: Checks if wave data is valid (data loaded and parameters) Param[1]: wave (type: Wave) -Function 538: LoadSound() (1 input parameters) +Function 539: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 539: LoadSoundFromWave() (1 input parameters) +Function 540: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 540: LoadSoundAlias() (1 input parameters) +Function 541: LoadSoundAlias() (1 input parameters) Name: LoadSoundAlias Return type: Sound Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data Param[1]: source (type: Sound) -Function 541: IsSoundValid() (1 input parameters) +Function 542: IsSoundValid() (1 input parameters) Name: IsSoundValid Return type: bool Description: Checks if a sound is valid (data loaded and buffers initialized) Param[1]: sound (type: Sound) -Function 542: UpdateSound() (3 input parameters) +Function 543: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data (data and frame count should fit in sound) Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 543: UnloadWave() (1 input parameters) +Function 544: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 544: UnloadSound() (1 input parameters) +Function 545: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 545: UnloadSoundAlias() (1 input parameters) +Function 546: UnloadSoundAlias() (1 input parameters) Name: UnloadSoundAlias Return type: void Description: Unload a sound alias (does not deallocate sample data) Param[1]: alias (type: Sound) -Function 546: ExportWave() (2 input parameters) +Function 547: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 547: ExportWaveAsCode() (2 input parameters) +Function 548: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 548: PlaySound() (1 input parameters) +Function 549: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 549: StopSound() (1 input parameters) +Function 550: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 550: PauseSound() (1 input parameters) +Function 551: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 551: ResumeSound() (1 input parameters) +Function 552: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 552: IsSoundPlaying() (1 input parameters) +Function 553: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 553: SetSoundVolume() (2 input parameters) +Function 554: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 554: SetSoundPitch() (2 input parameters) +Function 555: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 555: SetSoundPan() (2 input parameters) +Function 556: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 556: WaveCopy() (1 input parameters) +Function 557: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 557: WaveCrop() (3 input parameters) +Function 558: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined frames range Param[1]: wave (type: Wave *) Param[2]: initFrame (type: int) Param[3]: finalFrame (type: int) -Function 558: WaveFormat() (4 input parameters) +Function 559: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4562,203 +4571,203 @@ Function 558: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 559: LoadWaveSamples() (1 input parameters) +Function 560: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 560: UnloadWaveSamples() (1 input parameters) +Function 561: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 561: LoadMusicStream() (1 input parameters) +Function 562: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 562: LoadMusicStreamFromMemory() (3 input parameters) +Function 563: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 563: IsMusicValid() (1 input parameters) +Function 564: IsMusicValid() (1 input parameters) Name: IsMusicValid Return type: bool Description: Checks if a music stream is valid (context and buffers initialized) Param[1]: music (type: Music) -Function 564: UnloadMusicStream() (1 input parameters) +Function 565: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 565: PlayMusicStream() (1 input parameters) +Function 566: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 566: IsMusicStreamPlaying() (1 input parameters) +Function 567: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 567: UpdateMusicStream() (1 input parameters) +Function 568: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 568: StopMusicStream() (1 input parameters) +Function 569: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 569: PauseMusicStream() (1 input parameters) +Function 570: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 570: ResumeMusicStream() (1 input parameters) +Function 571: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 571: SeekMusicStream() (2 input parameters) +Function 572: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 572: SetMusicVolume() (2 input parameters) +Function 573: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 573: SetMusicPitch() (2 input parameters) +Function 574: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 574: SetMusicPan() (2 input parameters) +Function 575: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 575: GetMusicTimeLength() (1 input parameters) +Function 576: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 576: GetMusicTimePlayed() (1 input parameters) +Function 577: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 577: LoadAudioStream() (3 input parameters) +Function 578: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 578: IsAudioStreamValid() (1 input parameters) +Function 579: IsAudioStreamValid() (1 input parameters) Name: IsAudioStreamValid Return type: bool Description: Checks if an audio stream is valid (buffers initialized) Param[1]: stream (type: AudioStream) -Function 579: UnloadAudioStream() (1 input parameters) +Function 580: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 580: UpdateAudioStream() (3 input parameters) +Function 581: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 581: IsAudioStreamProcessed() (1 input parameters) +Function 582: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 582: PlayAudioStream() (1 input parameters) +Function 583: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 583: PauseAudioStream() (1 input parameters) +Function 584: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 584: ResumeAudioStream() (1 input parameters) +Function 585: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 585: IsAudioStreamPlaying() (1 input parameters) +Function 586: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 586: StopAudioStream() (1 input parameters) +Function 587: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 587: SetAudioStreamVolume() (2 input parameters) +Function 588: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 588: SetAudioStreamPitch() (2 input parameters) +Function 589: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 589: SetAudioStreamPan() (2 input parameters) +Function 590: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 590: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 591: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 591: SetAudioStreamCallback() (2 input parameters) +Function 592: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 592: AttachAudioStreamProcessor() (2 input parameters) +Function 593: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo) Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 593: DetachAudioStreamProcessor() (2 input parameters) +Function 594: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 594: AttachAudioMixedProcessor() (1 input parameters) +Function 595: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo) Param[1]: processor (type: AudioCallback) -Function 595: DetachAudioMixedProcessor() (1 input parameters) +Function 596: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index 6ea73d8dc..29546a2a8 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -679,7 +679,7 @@ - + @@ -1359,6 +1359,13 @@ + + + + + + + From 14a0a4d0c414b3c45b5d339dd1117682e537073c Mon Sep 17 00:00:00 2001 From: Teeto44 <160558926+Teeto44@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:27:28 -0400 Subject: [PATCH 28/31] FIXED: rexm's makefile and default paths (#5224) * FIXED: rexm's makefile and default paths Fixed a windows only backslash in rexm's makefile causing build errors on macOS and linux Added unix paths to rexm for better compatibility * Update rexm.c Readded the fall-through in OP_CREATE Added some safety checks to UpdateRequiredFiles() so it plays nicely outside of the raylib.com context (would segfault previously) --- tools/rexm/Makefile | 5 +- tools/rexm/rexm.c | 111 ++++++++++++++++++++++++++------------------ 2 files changed, 69 insertions(+), 47 deletions(-) diff --git a/tools/rexm/Makefile b/tools/rexm/Makefile index f5e7d1fe4..d7d71f64f 100644 --- a/tools/rexm/Makefile +++ b/tools/rexm/Makefile @@ -35,7 +35,7 @@ PROJECT_BUILD_PATH ?= . PROJECT_SOURCE_FILES ?= rexm.c # raylib library variables -RAYLIB_SRC_PATH ?= ..\..\src +RAYLIB_SRC_PATH ?= ../../src RAYLIB_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH) RAYLIB_LIB_PATH ?= $(RAYLIB_SRC_PATH) @@ -236,7 +236,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # --source-map-base # allow debugging in browser with source map # --shell-file shell.html # define a custom shell .html and output extension LDFLAGS += -sUSE_GLFW=3 -sTOTAL_MEMORY=$(BUILD_WEB_HEAP_SIZE) -sSTACK_SIZE=$(BUILD_WEB_STACK_SIZE) -sFORCE_FILESYSTEM=1 -sMINIFY_HTML=0 - + # Build using asyncify ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) LDFLAGS += -sASYNCIFY -sASYNCIFY_STACK_SIZE=$(BUILD_WEB_ASYNCIFY_STACK_SIZE) @@ -363,4 +363,3 @@ ifeq ($(PLATFORM),PLATFORM_WEB) del *.o *.html *.js endif @echo Cleaning done - diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index f6c297769..3306e613a 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -205,12 +205,22 @@ int main(int argc, char *argv[]) exCollectionFilePath = getenv("REXM_EXAMPLES_COLLECTION_FILE_PATH"); exVSProjectSolutionFile = getenv("REXM_EXAMPLES_VS2022_SLN_FILE"); +#if defined(_WIN32) if (!exBasePath) exBasePath = "C:/GitHub/raylib/examples"; if (!exWebPath) exWebPath = "C:/GitHub/raylib.com/examples"; if (!exTemplateFilePath) exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c"; if (!exTemplateScreenshot) exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png"; if (!exCollectionFilePath) exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt"; if (!exVSProjectSolutionFile) exVSProjectSolutionFile = "C:/GitHub/raylib/projects/VS2022/raylib.sln"; +#else + // Cross-platform relative fallbacks (run from tools/rexm directory) + if (!exBasePath) exBasePath = "../../examples"; + if (!exWebPath) exWebPath = "../../raylib.com/examples"; + if (!exTemplateFilePath) exTemplateFilePath = "../../examples/examples_template.c"; + if (!exTemplateScreenshot) exTemplateScreenshot = "../../examples/examples_template.png"; + if (!exCollectionFilePath) exCollectionFilePath = "../../examples/examples_list.txt"; + if (!exVSProjectSolutionFile) exVSProjectSolutionFile = "../../projects/VS2022/raylib.sln"; +#endif char inFileName[1024] = { 0 }; // Example input filename (to be added) @@ -1660,54 +1670,67 @@ static int UpdateRequiredFiles(void) // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core' , 'basic_window'), //------------------------------------------------------------------------------------------------ char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath)); - char *jsTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated examples.js copy, 2MB - - int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); - int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); - - int jsIndex = 0; - memcpy(jsTextUpdated, jsText, jsListStartIndex); - jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); - - char starsText[16] = { 0 }; - - // NOTE: We avoid "others" category - for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + if (!jsText) { - int exCollectionCount = 0; - rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); - for (int x = 0; x < exCollectionCount; x++) - { - for (int s = 0; s < 4; s++) - { - if (s < exCollection[x].stars) strcpy(starsText + 3*s, "⭐️"); // WARNING: Different than '★', more visual - else strcpy(starsText + 3*s, "☆"); - } - - if ((i == 6) && (x == (exCollectionCount - 1))) - { - // NOTE: Last line to add, special case to consider - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s')];\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); - } - else - { - jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, - TextFormat(" exampleEntry('%s', '%s', '%s'),\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); - } - } - - UnloadExamplesData(exCollection); + LOG("INFO: examples.js not found, skipping web examples list update\n"); } + else + { + int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START"); + int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END"); + if ((jsListStartIndex < 0) || (jsListEndIndex < 0)) + { + LOG("WARNING: examples.js markers not found, skipping update\n"); + UnloadFileText(jsText); + } + else + { + char *jsTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated examples.js copy, 2MB + int jsIndex = 0; + memcpy(jsTextUpdated, jsText, jsListStartIndex); + jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n"); + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, " var exampleData = [\n"); - // Add the remaining part of the original file - memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); + char starsText[16] = { 0 }; - // Save updated file - SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); - UnloadFileText(jsText); - RL_FREE(jsTextUpdated); + // NOTE: We avoid "others" category + for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++) + { + int exCollectionCount = 0; + rlExampleInfo *exCollection = LoadExamplesData(exCollectionFilePath, exCategories[i], false, &exCollectionCount); + for (int x = 0; x < exCollectionCount; x++) + { + for (int s = 0; s < 4; s++) + { + if (s < exCollection[x].stars) strcpy(starsText + 3*s, "⭐️"); // WARNING: Different than '★', more visual + else strcpy(starsText + 3*s, "☆"); + } + + if ((i == 6) && (x == (exCollectionCount - 1))) + { + // NOTE: Last line to add, special case to consider + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s')];\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); + } + else + { + jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, + TextFormat(" exampleEntry('%s', '%s', '%s'),\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1)); + } + } + + UnloadExamplesData(exCollection); + } + + // Add the remaining part of the original file + memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex); + + // Save updated file + SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated); + UnloadFileText(jsText); + RL_FREE(jsTextUpdated); + } + } //------------------------------------------------------------------------------------------------ return result; From 452cac33b86f8ecd87788ba0eb0633d779c155d1 Mon Sep 17 00:00:00 2001 From: Maicon Santana Date: Wed, 1 Oct 2025 17:31:03 +0100 Subject: [PATCH 29/31] [examples] Add `core_monitor_change` (#5215) * Add core monitor change example * Add monitor drawing and more information * Update monitor information every frame * Show info and window position inside the rectangle --- examples/core/core_monitor_change.c | 170 ++++++++++++++++++++++++++ examples/core/core_monitor_change.png | Bin 0 -> 17430 bytes 2 files changed, 170 insertions(+) create mode 100644 examples/core/core_monitor_change.c create mode 100644 examples/core/core_monitor_change.png diff --git a/examples/core/core_monitor_change.c b/examples/core/core_monitor_change.c new file mode 100644 index 000000000..8983556af --- /dev/null +++ b/examples/core/core_monitor_change.c @@ -0,0 +1,170 @@ +/******************************************************************************************* +* +* raylib [core] example - monitor change +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.6 +* +* Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Maicon Santana (@maiconpintoabreu) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MAX_MONITORS 10 + +// Monitor Details +typedef struct Monitor { + Vector2 position; + char *name; + int width; + int height; + int physicalWidth; + int physicalHeight; + int refreshRate; +} Monitor; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + Monitor monitors[MAX_MONITORS] = { 0 }; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - monitor change"); + + int currentMonitorIndex = GetCurrentMonitor(); + int monitorCount = 0; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + + // Variables to find the max x and Y to calculate the scale + int maxWidth = 1; + int maxHeight = 1; + + // Monitor offset is to fix when monitor position x is negative + int monitorOffsetX = 0; + + // Rebuild monitors array every frame + monitorCount = GetMonitorCount(); + for (int i = 0; i < monitorCount; i++) + { + monitors[i] = (Monitor){ + GetMonitorPosition(i), + GetMonitorName(i), + GetMonitorWidth(i), + GetMonitorHeight(i), + GetMonitorPhysicalWidth(i), + GetMonitorPhysicalHeight(i), + GetMonitorRefreshRate(i) + }; + if (monitors[i].position.x < monitorOffsetX) monitorOffsetX = monitors[i].position.x*-1; + + const int width = monitors[i].position.x + monitors[i].width; + const int height = monitors[i].position.y + monitors[i].height; + + if (maxWidth < width) maxWidth = width; + if (maxHeight < height) maxHeight = height; + } + + if (IsKeyPressed(KEY_ENTER) && monitorCount > 1) + { + currentMonitorIndex += 1; + + // Set index to 0 if the last one + if(currentMonitorIndex == monitorCount) currentMonitorIndex = 0; + + SetWindowMonitor(currentMonitorIndex); // Move window to currentMonitorIndex + } + else + { + // Get currentMonitorIndex if manually moved + currentMonitorIndex = GetCurrentMonitor(); + } + const Monitor currentMonitor = monitors[currentMonitorIndex]; + + float monitorScale = 0.6; + + if(maxHeight > maxWidth + monitorOffsetX) monitorScale *= ((float)screenHeight/(float)maxHeight); + else monitorScale *= ((float)screenWidth/(float)(maxWidth + monitorOffsetX)); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Press [Enter] to move window to next monitor available", 20, 20, 20, DARKGRAY); + + DrawRectangleLines(20, 60, screenWidth - 40, screenHeight - 100, DARKGRAY); + + // Draw Monitor Rectangles with information inside + for (int i = 0; i < monitorCount; i++) + { + // Calculate retangle position and size using monitorScale + const Rectangle rec = (Rectangle){ + (monitors[i].position.x + monitorOffsetX) * monitorScale + 140, + monitors[i].position.y * monitorScale + 80, + monitors[i].width * monitorScale, + monitors[i].height * monitorScale + }; + + // Draw monitor name and information inside the rectangle + DrawText(TextFormat("[%i] %s", i, monitors[i].name), rec.x + 10, rec.y + (int)(100*monitorScale), (int)(120*monitorScale), BLUE); + DrawText( + TextFormat("Resolution: [%ipx x %ipx]\nRefreshRate: [%ihz]\nPhysical Size: [%imm x %imm]\nPosition: %3.0f x %3.0f", + monitors[i].width, + monitors[i].height, + monitors[i].refreshRate, + monitors[i].physicalWidth, + monitors[i].physicalHeight, + monitors[i].position.x, + monitors[i].position.y + ), rec.x + 10, rec.y + (int)(200*monitorScale), (int)(120*monitorScale), DARKGRAY); + + // Highlight current monitor + if (i == currentMonitorIndex) + { + DrawRectangleLinesEx(rec, 5, RED); + Vector2 windowPosition = (Vector2){ (GetWindowPosition().x + monitorOffsetX)*monitorScale + 140, GetWindowPosition().y*monitorScale + 80 }; + + // Draw window position based on monitors + DrawRectangleV(windowPosition, (Vector2){screenWidth * monitorScale, screenHeight * monitorScale}, Fade(GREEN, 0.5)); + } + else + { + DrawRectangleLinesEx(rec, 5, GRAY); + } + + } + + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_monitor_change.png b/examples/core/core_monitor_change.png new file mode 100644 index 0000000000000000000000000000000000000000..81f9eed875bf8edf2c11e911cad5265418e054ed GIT binary patch literal 17430 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 Date: Wed, 1 Oct 2025 13:31:53 -0300 Subject: [PATCH 30/31] [examples] Add `models_geometry_textures_cube` (#5221) * ADDED: example: models_geometry_textures_cube * FIXED: example: models_geometry_textures_cube - removed leftover TODO and aligned title with header --- examples/Makefile | 1 + examples/Makefile.Web | 5 + examples/README.md | 5 +- examples/examples_list.txt | 1 + .../models/models_geometry_textures_cube.c | 88 ++++++++++++++++++ .../models/models_geometry_textures_cube.png | Bin 0 -> 49462 bytes 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 examples/models/models_geometry_textures_cube.c create mode 100644 examples/models/models_geometry_textures_cube.png diff --git a/examples/Makefile b/examples/Makefile index 9a3f640b7..f594e2867 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -616,6 +616,7 @@ MODELS = \ models/models_cubicmap_rendering \ models/models_first_person_maze \ models/models_geometric_shapes \ + models/models_geometry_textures_cube \ models/models_heightmap_rendering \ models/models_loading \ models/models_loading_gltf \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 20d737c38..e5dad0430 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -616,6 +616,7 @@ MODELS = \ models/models_cubicmap_rendering \ models/models_first_person_maze \ models/models_geometric_shapes \ + models/models_geometry_textures_cube \ models/models_heightmap_rendering \ models/models_loading \ models/models_loading_gltf \ @@ -1085,6 +1086,10 @@ models/models_first_person_maze: models/models_first_person_maze.c models/models_geometric_shapes: models/models_geometric_shapes.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +models/models_geometry_textures_cube: models/models_geometry_textures_cube.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png + models/models_heightmap_rendering: models/models_heightmap_rendering.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/heightmap.png@resources/heightmap.png diff --git a/examples/README.md b/examples/README.md index a5ee53afd..d52f96280 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to - `zig build [module]` to compile all examples for a module (e.g. `zig build core`) - `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`) -## EXAMPLES COLLECTION [TOTAL: 165] +## EXAMPLES COLLECTION [TOTAL: 166] ### category: core [38] @@ -147,7 +147,7 @@ Examples using raylib text functionality, including sprite fonts loading/generat | [text_codepoints_loading](text/text_codepoints_loading.c) | text_codepoints_loading | ⭐⭐⭐☆ | 4.2 | 4.2 | [Ramon Santamaria](https://github.com/raysan5) | | [text_inline_styling](text/text_inline_styling.c) | text_inline_styling | ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [Wagner Barongello](https://github.com/SultansOfCode) | -### category: models [24] +### category: models [25] Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module. @@ -177,6 +177,7 @@ Examples using raylib models functionality, including models loading/generation | [models_bone_socket](models/models_bone_socket.c) | models_bone_socket | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [iP](https://github.com/ipzaur) | | [models_tesseract_view](models/models_tesseract_view.c) | models_tesseract_view | ⭐⭐☆☆ | 5.6-dev | 5.6-dev | [Timothy van der Valk](https://github.com/arceryz) | | [models_basic_voxel](models/models_basic_voxel.c) | models_basic_voxel | ⭐⭐☆☆ | 5.5 | 5.5 | [Tim Little](https://github.com/timlittle) | +| [models_geometry_textures_cube](models/models_geometry_textures_cube.c) | models_geometry_textures_cube | ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) | ### category: shaders [29] diff --git a/examples/examples_list.txt b/examples/examples_list.txt index 10bf09ceb..52104fbd9 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -131,6 +131,7 @@ models;models_animation_gpu_skinning;★★★☆;4.5;4.5;2024;2025;"Daniel Hold models;models_bone_socket;★★★★;4.5;4.5;2024;2025;"iP";@ipzaur models;models_tesseract_view;★★☆☆;5.6-dev;5.6-dev;2024;2025;"Timothy van der Valk";@arceryz models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle +models;models_geometry_textures_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5 shaders;shaders_shapes_textures;★★☆☆;1.7;3.7;2015;2025;"Ramon Santamaria";@raysan5 diff --git a/examples/models/models_geometry_textures_cube.c b/examples/models/models_geometry_textures_cube.c new file mode 100644 index 000000000..4608ad951 --- /dev/null +++ b/examples/models/models_geometry_textures_cube.c @@ -0,0 +1,88 @@ +/******************************************************************************************* +* +* raylib [models] example - geometry textures cube +* +* Example complexity rating: [★☆☆☆] 1/4 +* +* Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev +* +* Example contributed by Jopestpe (@jopestpe) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Jopestpe (@jopestpe) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - geometry textures cube"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 0.0f, 0.0f, 4.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + // Load image to create texture for the cube + Model model = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f)); + Image img = LoadImage("resources/cubicmap_atlas.png"); + Image crop = ImageFromImage(img, (Rectangle){0, img.height/2, img.width/2, img.height/2}); + Texture2D texture = LoadTextureFromImage(crop); + UnloadImage(img); + UnloadImage(crop); + + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; + + float rotation = 0.0f; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + rotation += 1.0f; + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawModelEx(model, (Vector3){0,0,0}, (Vector3){0.5f,1,0}, rotation, (Vector3){1,1,1}, WHITE); + + EndMode3D(); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/models/models_geometry_textures_cube.png b/examples/models/models_geometry_textures_cube.png new file mode 100644 index 0000000000000000000000000000000000000000..4c199bdc4f363f3f714c4bd00a0e2f80072a5f2d GIT binary patch literal 49462 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%0A!s3WfJA7 zP95LQD-g^T-A-j;j=xKQxb@H)N)h$5F*-#2el`zEpd}4nNps6>K*{j?^eWYE9|ePw z#5nikepqY(t|o`9vH30kJk!94o)Tfzz7E@?hmS2@*ZKIKlN=Y{ik*MNq^p!`)WKmNC{IWFYOBcly^?rWc|a;uY6!SdUEMVB4fRq zMAl-~+ZkTb_NP4-)(%-14XK&4SK?4&{440wR|e6DUFSd>r}w@^LmvLQpJx|J);+pj zk85Q#89+HIV>(!aDJ_oYZ9*(>-iI>Dml5|9E1lByE%L%DCO)G-z!pZqy6tA3C5gk8 zC1r<=4KUP_WCJj((-rjSy1My?I21`GT2d-`oBdl7KwEEq+lj;HFOA|U2c>~Igfhs& zVp+1VC0=_6A>Ahe*Yj9fzM@~$=7*h^=~m0)nPgyu?`_gCOR_Wa+`NlIM8fV#OaIEp z(=YW;_&d67VaVT}H05?E%Ka4Ze9M?jUy$&=&jvH(_I$*k~0c>l&a&reESk0z_1`TG6CA~B* z$62uP?NiG#leAh($jplX|0NGfSm0p%$+o2=16HbEp?Nq44l*n$%%!@@y8EeoRV_iN zX(RMU=yMAHCn+v$tubQcUdlJ)o(Ed#ioW9rkrq|%bu-^reXXkI`A2o7CFt9}v~J|q z1wRr*=XGWdj&)6n=>M{M{K+H!$@c*F$J6EF7vn7?N>*PWagyr**WH=(3D-DpX6YcEy%Rq;GT^kZIW z4OE9c<%3mf5~Ar7*Z8T9)ro>&X_weoI_3w+Y7_3%E_iO;U&Pmj6Aw`af`yfH-RZ)= zOHZZ2UN&Au(+vA6BC*u!Y5G*0GA}7Lh1^f&Vj<2#`>3^Derym_2gYJjBm|^*fPWY( zT7Dc7$dK6nDP@#j%q*qjmGsenoiV+eXW|0C#H)kGHR)yclbY4D#-K{#j!-frZUa94 zu}E!>8W@x>rBuy^$*j$OY|OC>lge6D86()96D;aGCw^FM61|^)h;ouYL%N5>@HI44 z-UXE6!&3D(Am2!^92P6Cm;8Qk;l$a8c?HsSixDHz^db={%t&hPwqsEMGa*pnB+4dK zC8wHJNj) z*XmgB?`+EGtq_&s#CNO+x6F{k*&U@~ves{_z589!s8za5-u=l*dJPG8W?I}-Y?^a# z#?WQ=oy}^8@H`6rciszrrubmc@e!f63Y*Ji!V^}`2pGYox>NL?#A|Qs)iqtSMd+K< zdPvPienI)eUxZkFl3!qf)Kuge0>}e)Hv%|dB@_Kq3&%Fa0h3{;Xgs1j}RzP%A91)}g0l&B(uu)8M*@$6{MjMAw^tFHrXyp^Ll zgr(g+JEy18RMUmlS|i=eSXl|>@e4HBRy5wHO2TtX@;~KovcHzGnYvGQNQn2{b)?ye z+8h#y6Ux8yv$w76f>`|ZgvdMFZ?CR7M@azn^K?BjxxN@c0rc)Jw%Ft@`trv6zm`+J zkEFXtGmOvPx&K$*q838Cg?0`E#u-dE%I;l|_DYa61kV39 zEbSLLUS|K{Mw<9;U->-;m)~TnxT75LjmztcHF=pp`d-h(PY=LOqhW{t_C{neX_<>u zE}iRLPANC`{42TH_HV=GuYS8jprtQ8VrX_?~)-Jn4&EK^Adi%8NE5}K| z)hT^uh^T>>sC-+A>{2$2;?>#M*KWC zEmJwjQ^n|Zh|k%rJQP6w{5Tgas)~Ipgvahg3^<||ZpM9VX)TNQ{&7P&Bn1OcwKGbj zW+6SvW`)m{*i0Hm*>O^dMLy?A3SHMsghy@Dza1sp4P6$oJY*%BAzc*XCwnES6y@uu z#jmW#hVv;xjm*B&*R&35HYV9i$V$5wV4d?Issq9w}ZbdJs*NWm&FB#(ul29sR0iPyLC_0Zn#+_eGTuT>ei?`<7pT2^uDq0V~zIAlG2z zc#mv<;cCa_xEQhQY^>>z=DzPOLn=&b(WFFybDv5E71wPBcW-0*IZH?puq-TBFH}Ds zlz`ByU-taaL%9>g&d5%yU!fsnXFMl`)}iK?U^!68_%li(Y6j{}-q1%Te?4?E41eS) zuXQ0eL<^`g3d{rQ$&N_@lac1rIY@;jHeyDrfg2|~u!cLHKlVI|cX(Bt`>a;L<4D35Q& zBW~sDyyOX}cOfZ|4KA6w5lPKSHri$e|+iW@Ji|`LiCk0@Y)@BYj3?pMTgBt8|9bEMIW7s3Am3?mYHH#MUt#4 zAc0gx(Lr}9&szd0|n*Zrud`yx&`7R-0WZ%s~oi4K2hYG@|$&a*N36UBk4dIzZ ztAzRHy2(!KzHs?3`Vv4CNQ(Tn?0M2>isSpCBwG(<)WHYN)W|mGfZR%44SFK)B3{b| z^O{)1&!Kx3=OaSbz8fIO4KDbW#p#%hdqNH?3mF>q1!*898U0~V`v2$y7aO%LzNjU4~=HF422kr2>FMXd8x~6-F1W=@M46}s0 zUH1D?L)As34LyS)E(*CClf_`5EA$XO*;o$cpMww6-C z#}$l7r|Pv{?NG{x`(F3(2G6rMks2Qv4#Eb5`dpqL2~I1~$y9J4R>}>H?V$}&CDOLm zUj3n()a~CEy!hFB1sk(;QXheH8-{J^INMDD!6>T}6wj3bjNk0uB+Rj$n!B{rlI-X@ ze;RJ(X)>%)p=VovJ_g9UHR^w~kt`AyB1`a`93;zxKxPzp9W}3xvnSy=|AlWqTaauI z1v7p>^MR|=CjB_q{mgV%l4W0|H<@rGQj(<=5j8*hlXTFCsnI*dRx7n@X_A_ojvhVb zh~SBjL9%F8TNeMiZ2OkHHjr;3h~IZURQbxy&vwz_KFcAr0I3jqJ?yW~#)|90e^^-r zsF21T18%V1Av};nRDSyTRqTScWy17lBnx?DYygGl;Gl?NltQ=*b>4V0Dmm$7}g>U z?b-K_V>LqRNDoY+410*JX5I`Uff=<(o~)#8P9mad!spY6UXED>*MbW&HGW05uH?ya%<=YDs(g=2;w)5AKTi^6b^=7s>}H|N46WBJ)pNA1ekQVxk1E9DenHChelp&v1##$y$+t6&T^P}q zFNX+T#l({oyeKQA?Zqs39KT=h^cfC4=R6XsER_a;EaS>fojA`d5ErwtaS_%00}?~H zWc%d>6M%a^u*QfV7Iz1mV-}3pdu|q(>%v6}Zn4`QjM3$s?pZ~b=EVNsT-4k5b&^hW zIx5>gA4EPHFTRVPXuSxD)K*;-X@7}RLtr{S{$15m5|cetu-?Sn^<0!CLXrXP7^goQ zUSl?4P>EpnD%2RF9G^aVZ8{koDiCIRYT)+j^9|nkqumTRAL3}S0LCHn>OYHJ_gV?r%>Nk1>WyKz8w0B9EKmuS?pZS*{nq+ z!tB-35v+O`Y?gbm{Kr{Rwc!&`gm6^A7Hqy+BJ}T8>+eYy@9P+JE{YEtYqK!)hx< zv+l8S2aDT7LUQL=wynQIe@6RKlL;kTx%<@m@^CKJ;g+ShNM%`p}gB_W> zwRUC~{_b9uk$TD1?}MzK)Jz`~ChI-*nKH8Pify(=&r^mPEmk9x51>k&CWM87w5lH%>96x3@ukLd1bs;}iRnsBhPk$mgC z`O{%Sv>I@S9pUJAs^ilJYmGe@_6AQwb}l z9ac`yW49eZPJN9z)KI}g>rsJ_R*~TeHDERyaY0rWn+-G|CQF@C4_{B)TwSWkuJS@*K{C+-P-ubHXIB?9h!-z8>-$G+mh zMjau@V5dv`-w>(g6B4CE`oJe_GW@0t&olo%rIvWiMF#sO9IhJ6PrM%)WRu> zc0#3+i4)vKlkpOjR_Dm0-QcxS{m2|WZDnB+)|qutk0TZ!+#@Z-vV?s}5S z%BSIGB3G3h_Ep(Om5K#7d*q|h;I~@xIF6tK^I_BFi2l{`WWQI5vO?mv9*=iJq!Ukq zKD(r{-I0)u{K%;K8tv0GO(8}1{axm#q}ijeyMx9x2$_?M@kNB|ewnZM2W=MQ-d<%a zE-Ie-^Swrk#I~Q;B%d+%=996Mv(y_PN`2+PUR4`%iV5OohgypNB(7AO;jJJBcYkpg z3O%v05=Brg1j(nInM%(!WOMfCe>A(YKi2JhW&q^{Ihmb#jss7Fxx|BX$=TD|Yc2_Rv z{d>N*G^n%)8Sh)sR9?Jo3OuptO{|+N|M~X2`Dc&l=YJ}r0Be$R0z?8nk4hd;!8v(> zs!yw_(clfDaq@S2zI#MsHW!nadi)ZRTTt61o{ZJPKGTI7vv&+>eA9M61gtjSyqYt# zDj|d`tX-;?UG-hk_sg3P+09)aT%nZ?vX&%7N5lQvt9H;XoK8N*h8+_X*Ln2HcPowC#vYSG_e1A)V%`{n)1UTn6+d@5*HDk*P2uq|3? zdt>QDn^?~{NQ7H@e$!|h#E#Ui!!TJm*iYFtM!1|GB#(ch$&FG-sR+`4=lT6GQcN5e zA=g*2cIvshdJov(T;wsmW-+PB`1f$0M3Roh`&F}`UA2?yx=kkwzC+SnZ?;Q;%YxM& zCh+wp70Lody#Y`Ns~@FoqJZOn7ugKl;yF}>$B8n5?{mn#BzU)S(^8^*B!4mn)$(_H z01TBq?CIC@lGiK85GGXy7kLzwSpX&E4W$E-43}R+%Mr5j2wu#^;WTf;jRFZ_>ack5 z7%wm`^cWP4S8uAD72n!fq@DM^18wt#KLC?*sQIlV1@C1)KnF4o!`Tz{sO27O)89 zx0O@5-VP3F>pOWg#no{HU6h4fuO}Q%K5M2l(YaV4I^9Y7xW3k_3z#D={j20h$#x;R zLK#UC+vAi~)9I6I9{t&2Y0gA0rvC}zTN_8G%Fcz?nSR0CrMfs|Cq=5zK*4@2fH3Ku zh4qea*69TNG9MoOiN6n++jIj*4$7XqMV>4sn%ME*Nl(HOl(*=BX2)XdrvxCut}p^@ zC1Hn}0n67G)7rV4)qHEX3`oLHWRnZvi+iw=({@JDE8uoRQdo?spx?gl#laQYiSS_& z3lK7RmC4!vwvkP=7;JZY zOpS(``|}GWxb?xZk$C6v^B2=8?P*JhBxGhMbN^ggSOjsnGC%aSZL7^#G}-?YzeW^rI^nI|bK^kUsHgS|@<$GAa=ewpPVUA{k?S@{@Q1x*E^18mb?|vKb;Vw>*7i%=%Kb`Ik`>W0E zpzH%q7Bezss5o48X1*|}ehIcD)115f!dtO%?rlvaWy28}p(X*!sUYbm^eG+52mAJ% z{N)9=ORg-x2@QS|M`5cPl2 zOw2g&s=ge2Z0u^76JNg~kwCpNR@B8h_ZrN9}v!PV$tIcW2_{mMGtmL|Bk% zJ3OTdpXh1{vdK<8gVIxrhw4*GHT?pvZjz7IfVri5hU=keFA{&7jo|I76;?AP3i5_* zvY=+R-iS~OFNX_W5xXc#bX{;1Ia1VITQUf&2aqGz+I?|S#?zz<=c4sFuw*WynW z4`-;P$^lL2nuqU56Jv1!i0FC%3!_{LLF13 z3trY$PT*jo6;D9dps)k4$5aGAS_*sb<+Fe0S2Jw@Yd71h8Qw}c;n;Qv;Yf4Md$N9M z4@^w9rKWfQoF@rMTq*DNIjHgUVt!_q%^_5zhyoPO=2cxf$z&maH_iF@)%;4dS-huI z=NwHAWchXs`(EMHQ{V1$(N%}LW-*``*dG}cL9-Y@{ zJd%)1l{@47wBPWR5>=DQ9np3TD)c-NU$ehp?^yS$-&|6jgT*WJ_v~ZmWAd}Ag#ozEZPdn7YqTzmp_WEV1h#+#PPw6f!D#%1H zld$(zbT4s)dD)3qVO`U6!TQif=neXenJ;c`^b2j#y?HYZZ=Zt3;EX+_+*0D&Z|>}d zTD*~Vk+&9a{Q~-U(k9nYHBC-Cds<9W#b`ZYNXg@O_?~&b(;c+<5FNFXEoz{^6oHPe z#MS2(~{Z% zp%-bQJ`Cd{Ls(s7H+AE_k(-qMxaBInMp4#6b|&j-;QMxSYEOuO@J1)S@1_<_N%ehi z-&6W$SD|k*`-~aV4D0R*9I-UjodoGm22Mxc+3gC1RXhCpjydRE_O%sP|IjqRq!U*a z;K{6$QuFE1A)l0#Dh*z?=xJ^XV6n{LwRy8Phb1f3K=ExbNz(b=Q>T{h2W(zDW18arMOOr?!$-MofM%EPn8BQKK(e1%!7*b) z*C|H4% zlj}`BsxgxPL(^FXMA>z1TS6In2m$FCO6l&B?oR0tM7p~N7+N|cBoqatk?!u2?(XjX zF7M}i|MHJD`&xU&c^(@SodaGKkiMQCu8^KXA9?60`}Tb-nM^7hdkf0Z(|Inm=U_AU z1EZlY>_O+KLORfd&LBDGkYaijAUvub(l(?p}2ikUzYXyBtPGQAG$>#2_ip_NitFz3m*H^NE#W8DU~T`D&ws8n@e` zx!}24$BX}UsW!u~Fedp-Fv{sTY!PFVF0JVEpS4h-)n%cx_#tbdBep5Foi?HOCToZZ z5RW=V#s@&^xSQ=x2EY$i{$)Ep--Cb9fj}#2*0}2qaobNP6DwvQ0&!E`RMrLKP|Ju$ zf9#=6MyZy>uwh1lb=J>wSH1EuN+ov&S{#kpy8YlNiOi}OC3x5DfKi_I0|@*d_FAsdl?~m2viuq zGV7+2B19E&XR57J46gSg)z>`xufIcNN|B-!XVO5C5mL)F{uyF6j8bUml;p`zxAQqz z$M~r4*61hRT%~+s7i>84dB%w%F|lEW?HIE=o6-9$^&ihrWLNR5a-cXbY`4jat*++i z&TeOzFhJO=YaG+ggCL&z#@CV;pIx~Li`DDiX(+jB-O^`^QAGx47tT-~FDNtH3R}ng{!J7O)*44NrKz=|sS+w0>)567~GE1wM%)@#5Lh-ut>mZLp|T)Jj&jT)EG zqDXt;m(RCBI)T^yfc?Xg@*@>vTcd&dvV_6NgcM_RG9l)|D0~09LCvmnkr-ip7Efvc z`VbvXP*Q17w>WUOf5$#E>npkGnR{mbGy=ffl`kGxS0$gj(`Oqj7Tpa^8-;Av)Q zFA=nUV9R1P`ESco86_Gall9T^_XIHb00*LifC}NQaK$QiO#%vRpofw=b-JyM+L1V0 zSk4<9Dv6#=kgNM}nqp$pv2l(#mp;MK26I90U8{GMp}*=hKSJM6#xgWEOcd*3SY5yF z+WR1KH{?+Qf|mA1~7 zWuME-#T-phT#&eEDpIg~I8VA9 zhj_9ur|tZ>8TH{R#V=1wS3|(i!xMPfz8i|zACDkgjDhyZzP+U|m>3LuRku2vrP6T| zhp$B7A$18m;QMuWuw*fTF?mJkrOqgZYQ=qnd{3+^{xOPn{o|Hqxti8p${%!SCmMRb zkdCmI9Yt~Y_kQgjuA~nD^v5_ESPZ0X;{BWeC_SmEqQHQ}^!0JWdmJEdDls3(IFKHE=HIM=IT9bhz#0DF6d2{y3>!aD7H6m)>o20p27{4QzCD7NJ___VrL@P z>lH`likirBkyxMNKJ2HzhHbSUzX3x`Krb8|6 z*JDFBZk?D|MLb2`BDUNid5BB_TMEgbHsnjj@L-Y@;Llp}$@i;4e~PRbT_}DG45FEY zWCg1241`!c?$sTd3?vT~mirw1n)o+hb%q1>wyja~JvE7MTBxL47icQw{fU%_0x6E}Oi7V!}! zOfH#l&ERjH9yj+ze3KbKTWO{S)beX0A##>4-+1bf(8Ya>vsIy6`z;hMqW9`)=wbMl zM6O}jE%kw3_S=c}Fs8GJ&10gJ3(*qxvdFSJ*OHCpQI5b`XEiYC26}PIe*mGC{JnUI zZ^@vIt{zQogO4ds?dU8s3?hRHQP{a=&)8m!srA4;@-DxSBH4r@%XB1oM#um1_3Bj7 zG_3tiDXjh8uzV47&Ar4dJBR<(39*!*2m>)2iaEx7jP7K9#=&XNQI(C5H0%|N;Q_;H z6HUo-Ba?p1N4*_U9W7~e2G$(xmx53Q*pI-$jZauxg`BG&lPUx;B6Ea16baHd8EeDj z%o>xA2pKFhkiKj_8o!BrQD&N^{p)TI@5FBb`B=J;d-TQkemTo~3V9Qqtw$NOU@+VD z^PZrG$oOA(*N*$KgyxmsB&a(`x(eDLz+&y*8N1H#P?&rqVNeA6%MC=}v-gwhC6tv8 zRC}`0WwR9JKoF()mHQ*K^FE-HoN{bt9q%L+J*W{g0>{TRHNmwxl>L|d=egm=2a=y`8q$50f9Rt8v+UkXxQ&TI$XCx=MZG$-IId&wE5-71)I3VBj^ zV#^*AaT*`{`Gom4?@xF{#Lz;IMOQ{yf24fG=*eZ>?fOF=v$^^{nR3Ls;}2C||1e$A zc6Nm~q6?FtXN~m`ALXF+rgI?6(9vVr=8hLu8&A3*F$4DzYQwR#OlElWjcLh9;IXlv zedj~GGjo1K(Sob*nO`k{wU$Eu!0IJeV*ct% zS>HT+c{i};B?0rxUK2Pjan~dHeaLc{8qE*Y(q7m^Sub1h&d9Fk?^*Ht3_4?GbR0rT z@{;c33-WRVYSr>*G`!*2>tHiT`-$Dt5yy!_S1tm6xOa=M*3We+fhC^7O@y*89@T(+ zSvlt$@6?)7|5xALQJ|-GjU2a)C`;H_Uej+y|Fqq=y~hUm>-rDexs>kw2jtok{`XBh zk?x~O8V}=rJNY)6Dg)mTg3j0Ulod}hj>z9H3PQ`F1b7o>hJ5uJ^B+P+qa5Ca4tgwu znZJYUE)!Heif_gyuA0w3cu4HqoY76!H%k4>;-=fd8cG?jL+9s3be$9%e{hQ+1K zQXscgC{2C*$ciiLLxd5frSax`EWnT~Q|9SdIU2>*lbMG)KEEdzJfX#R+;gwp>g~$Z zNI^P-LW0fi-qXf$>%&`f{8rfMDD6~z3=rD^X?p@W5sAH^K%0;bv08HF;d}%eV79SC z)=WE5Rp7NmvoR0g=D~|#Uzz!gwT^o%_G~jEBORO~kvFLqoEPFz7bh=ykcax`DJ5?B zpL<4N=h;!v917*e`ySJW;Ukn01SMr0mhEoFg)FQd_Mpi>c-L}=aaSi-aR?@a~8tM(F9esu^K-Ikna;ZEUg3Q2@3 z-#BN*)j6TmU3ZiggfI4~R!4adK$b1uIN4&nGsO1+%!VXi$flK_P1UN#zgMN?=#uu` zzlR-=R2-{dBvb~596dVl;5&<)i7&<0Ii>9K1L-g(1uz2l$jkks?hc|M8v3Qk?)03& z1&_&bU>0l?)WlI^cNR`OA#f%|5w=qSinGpa0#S^GyYOm)X4f_r!zx>&r;89(*afY* z&)!grt!LDZ-J20rceCY`o&-3FDIvquF=hjWF0tDZsbp_|SQaKpja0d_HX4Ee%m+UL zTWg%2HRok;^y{w>u?W_R`?#;|;^SXCMyas0Mk}aRZ5!jY_>CGjm92et5AIwzk5Q_T zC5gkVIJf-DJfWc=X2?J+;d=BSovRt_3NKF@il%mHjo6~NUuo1;9(X9?W8yiwvk`HQxkmp z+fZ(I>A*&u!%cM7sS1P0A^8bE-L$_ZT&)4j{(WyHo{r=REtGpW60($MD@Tjh@u4&M zbxGAh=qB5QrbJWyJ=v)41>iD85CTIk(i%jk_8iB#tE?!?#gEgcrz2`-NtA{*!L=Bv0X{S^=vVH8cSplct4=XQ-oJPr|vq~kP|I7pS{@lMRIwh zZ}SyUcp%;D_@6Uz9JP>Te)-R0%kz)S#ba`41e7TsP+3tkI}(P@!?*m5g;5K7dG8M@ zZB>PJ7dxVl>aEq?vQpIGt?d{3F6kM%lhi&}<28yd-lGIvPrImTVWdG_@O3Lus23jj z@xu8znMJ|V011WMJ^9FKuiLQE8Nuf*FQVAsxdD|D$VT$Fa586fqW+0kx&u;%7K$ba z>9u|ffr&PRwAJ2~^~b&=xkA8})s>DXOBQ2RcLCkCZ(YHoXDs{}klXfM{E=KAIjy%%7-Wg{dkKHxS z!rn$WiV^WU`(F0cpL+iuX9lzRLU0U;y;mD;Jx{AIWr?Z$ZzXbXR8yFXDNy1}NCOzH zi5@L)z@NoShHSi{i*%MaurgWlR>99dh!n@!WySS+@#|sNDQRc_`FxGuOZpKu3)~ty zrwJX)KE34S&Bz{8Lm6z05ptL~nat}ifApK^7ow+utuTSG$C7p2tKTZkX9C-KPS+8m z{X9k4gt$DrjUTbo$+}|}F?TfE+A^@4-TVOk_!hd&EZL4;IYQ8ef`@!^exZKmr%$!W z$~Uu`nRQsC7(%%SO5zJm2vh1Xi2g>mdavV!IzQQjXJLYyN7vtmtdm2|@Pk^2GC?k3UWFl;Z0vnA~CSoKU@TYww~h~EJM zBlk$wFhHYd1*9#Cbl&WEkR|Nk>SAMT_Q8nYTZ&s!oY-PV9I;rCB&dg`NL!lZ^}QQT zd#KA1moYCv>@=EnigL@o@nCaU!G_{a`r@o1##dp&?3ip{zYBQdrL~8|*?0F_szs)c zIl6w3^#q&nQ7WIrkxMfyL;;uMr3yy*bY@0RnuQykiwMEfO)eAir({dvcHHDwv<%%i ztIq~MM>h;#T-%OP#fMnXY<8&^9ux~t*f%r++ji_0X20tN?3S3Akk@(}P9nMSWG!n= zv6JC_j&eo&;`uUpT#N$M;~iF|8Gkc3D^4cr za3tF5KcmRXc^w!^YEKT4s7)|$I{PI+v0Om(v;EVE+myb~7;8pF#;M5yx zdM|__fh=d7CW;Xu5yRVvRo{Jk)uW!>S`iloAkn407lr6>n322uYdO^=0=j9xqZ~?y zo^lWd2??hvDCMOm<)o|DCmW2s8F!hbDe7o>dZZe-R>Du0j0G9p!r##rop%AWtb8Jq zUxvret%l%xud%2jO2e?cTkremqV_|c(jc(sq9Hfn^hP5(w#gy`iRfy(dc0+1M_&~o zb(J~#87wpFJ)&D4nai{hlBY_ZzEekJV#?JKnYz8AeE-I)lMwT?OTnSXSaz8k<`t(% z_4Uxv-FzpAxh@^Lo+hxKv-aWtv;b!iGyJ>^_X%vUcB52`hBBoKLOjwo_zw2k~|Z|)wX;`JgCQH$4=wH_~a_Kk`$bJebPxHzRfDU2OlD% zAsjF*=tbcrpF>j0EXcPV*#QF?Zi~V)Y{ii2vNqu>MReaBE)&6aYL<-CBS@yOS65H= zITAKg(#ZRkVjEPtng#pZ;&7We3uu0xkeDf=o3}HBF$laG8D?&IvX$t(mIb!gG(EEm zMT={E+rNEz&i}5kcp1Hm^vsN3=eEC=AWC+&*GXi2yfG#~)Ms5F^F&y3?6g`rci&U) zWczwa-_nb|_Cb68t7}Wf)-zERKzakJpjBl2Ag&HNnF2as!)l8B=^NF{jz;nw96LAO zmnb19*;^Ab`4D2vj#Sa5sAZz}9EkfDP974U^2a9}8*_A1Oi7Mndd^b)i5oFK=Jss9 zpjTx}FGLFUbH_FyI-MaULyuUEAu)~VMFCKc+ZG0+15OfUVDcCo?{=)Vz@u$aHWN~R zQ5a)A5_x?85!uC$e7d_UmuzcBPcDwy8oTE9WLMXu-1jkT{2wqB%yzo@c-DnhHRh*=DPqO?hItmn@U>2fPf^5X`T0#Z83eAw8!4;= zya&26f8VTSl>zpl?iPfo9fVsl*pEnZ?y^B|U=b#H92R@I|JpA}x@ZJA1l7_2(Ee&m zUz-upIVM2c<_w(yy)6gS7HQDd^0*#Bw>YyhpluLyG&v^m9c;4g%uQ+jiNJPv9S2Ij ztPBy$`zjWeqr+Vm;1r0u%oslK-mK-`;V$!ILM?`g3P~CjVu#0Vf6Bp(eWee8-RTyi zljt_2&WKGFpmyWle)1ed+UalF;f8~cxEUTheb=?K2F75$v>WiUFv)419hpt5H@Te; zv4%f!xQ#cm1asl9TPr#*&>-#3r-G3#w{E@@OcF6$04*rm#QDSrBGOYj)_(*Q{B5(r zV3Zcre>e-1Z;XeQe45}QoVE0--Fkn?elRR3X%&{_`l`SFLo$WtKWnDZp}WrOSOVIW-qL9R#^#Vj7$z!PM_>oUD{!-Rw76h*w|rt{ex2C7;8 z8D_$-5=STsapDCyUq?7OUBmrytZo7-lb_FB!O-4o9XC4#E&pvrv^Wn_TAS1btoQF@ zqdg_cvIqCvy=P%EPS+k{m*!%~KpC4{j74=%28xqp{Tqgp#o2z|?pjvx64TRuYxwew z8{ikN75g~Jn5Fhq^<$s8_`)xBi+we)Uz@qrfv-5Lf${Q@weaH-Ha$F|a;TBw6vX(; zrl_o42iwW#%l5kFwAb(T+nY4f-A5e}gOt)oU?%`?Ee14{?AAZz=l}&VD^4IL2{*F6 zl+Km6Q*fg3-`KVh4sdUCc>STKa&_Xy`rl;tcIQcDqwr^HQKj&3q17;{5u!))O;T=S$GR(3%O zvsj&OviIe^sC<)y$=K%i4vmo$eO2XmC|Q{xJ*g2vPqn#* zV$*Lq*klpwy@aw1zCbNjXUW)C^Wex6HazH!KH~n3dL7s?!H*x-3y9m;E6*BaVS`~H zlL8OD7lH1J;z>)f3t2HIehzl=*GMgkR^OA>A3IoCw(cEx)2Wt=P(#&z`&Jm!4{u_ z)v#_Pq?d4Y!i9X;dx1$pa5|;5@hygUDzI#goY|{BIqP8&!xMBq>^u+p_DPhziBGno zn@rZ2FM}Ntk0qQ2mfiHG5KZXtvssR&w$_Rz4yip+>qg@3E3SDp92izUTHG1FszfW{ zL>}j(`u2EHP4(?wzI{9UG0J%W(DZ0tQQ z)orL9@u$rnY*coPwl%S@37oYt=&DA4{F(rHzEIm7U|BO(!uf(?uB2GNEQS0zMbyH6 zEm%^A24QXTYkyXJ2X9snDO$5x$dNz_Nv&$~->$s<=gV0JlW#F0Ng|S#r^+Rj{Zfw} zm~BBHi?E5aQ`F(DOXl#Q*le%KLGA)g z8$x`V5O$#P+&0K|8}6uHgMEMq?lKYwfM!pcJZ$ZKwg<~hAD49A4~BV)bhlVc_Toxu zF;}7kisY$}aR@y_YJ;A~J;_X8D;Lrh$S+m@P3S(S16Q%4atpH|@)jr%ZJv$$WmMnF zV8_b9U7)!ynGOT`bMT9IYm!K_a#yAuY*($VZqcBQJEQD6+$N#!c2ALI0wL%L@+cL1 zDy;V{esB7K3Kx3!+ibVW&jgC90IB-Ls7#+#WtFArRPLWvfaII)U7r%OB|1ij!d zt3k%R8uJ3-9{30|@1bi`L(a|#4~hK!FAGPPv5SaJbQ&mxIT)XD+si6KLv>8VAn|qd z-O={va9G*eRFlaz>)CtCPg;K4vt_q^1&58twO*M941BfVRi`uZPHYSm0N;4VMHjfZywyiz&ZNtHCGtX0m zlCdI-X=Y0PT_qiLX+cD*Gpg8a+5 ze?8Me8!3*LL|KN3i~<@(Xh;Kr7)-f&0MJx0u{6;t^v@4nt^{W;ZKZ~2Jf#gFG4sEjAc5@4 zQ_6(IX@lmuD3vXL!OQgxoHPk|B2WXSRwdX1sKkPG$x=kj#~E)b<~%ST=zV*^@!?G) zIUn>Atm}UlOtuvW-}LtVpe~5RvNSoApQEd9$;fE*Y&UaHSzp96A=-1Qx@)!V3hHRr zsk_{DSXS-{0mV)Squc%1`Q|_pJ#|eff#Utm!GnlYGI=}Nt*YfHgQUlMZMf zv1Sk3yL(Z@Wu*COK;oU{Pf|5_jR`|fu(*p`s}pW1b!<0d0Z7)N_HT}IH&9U28XRJ? zxYD+7JXx&ef*DKYMfB>+3%^1!b0G7VemytWj!HVqG0iwWwvAkZ4m)!w{gpryg7B5- zMsML4CLM)3SbipwjcY5p(o#yJ3nGj}-w11@_{e+lAHKC8_zHJI{}tEj$ZB?F26%Wm z=ol$EE1$L|uGMrL@v>abQJxNah`v4Kxd{*R*s7f_2>l^d&cnxAs2Rj9eLf|i4(u3x zYH7JQYG2IO4RIg^;ZT3(cvpul2D>H0hjK=wJv%DI!c&ezz^iXgIGghR%=a=&)Q*tP zHS1WSdPN>c%Nlw4B(ngQY6f}_%82)vAYy}Grv0Z>=5-?SxM9rW(8p^gOe$U(q$Co) zA3m?W%-swV{*i31g(5x9`xtS!!;tyYp z#LT6jWbmw)P1YwQd&ZXe4Djg1O|2~)e@Y3@Wrc{{wF6vz=1y_|A(+@J)m9DDm0QTtVk3}{Bb(yhiQ?p zrLA=I(rA$J})l9w?+n&`zFi5;Mdhk*o&oK+;EG!uir2P#5Z9a|eYzB|xn?HnI| zWao8~ARuV(-iJsS>t{Rr*?hX0VhfQuqe$zDb>Q?s-Fr(6mf}P_Mv*7%N#1c3LW|DK zHYC(qxGqUzzFLU57p~#vT&+b>m9OP+)yhV(M{{<<63s7P(;j^O$E)Fx+!m`?ejGP| zZ%D$erKZD{E;9!5K!F%9ME(39W;Y&N)wV)XuY*l=c(n}~LcR|%gtoeCi_e%Uf`XS{%H0|$y+A=zjxs=s7KIezu z3k=cS7o%Q+Hen1j_R?DjtFQF4p~$NeU<#4C5X~}ZR<@rIOj+m#{Q~X%GKjdOid(2I zP>TPF`>~+rX%q%Je|aQYt8}09R)VOoLl7Z!ca&;PxsM|jRAgZ?Dl^Q(X&!Ebg$I4d z-|tNKQ1z7(O&|XEbUr4vYtN+^{i3pj* zP`=V=A$6iZoQN`jl~Jz5#nx*1rK2w>Zr<-i!DHIM#f~Q&6O+zKVfNI9xZ5&$;Dd@#R4^k!>-m`6G`RbzLuhq+x+mX@q;`S7 zL;@jjB=RWWrp%khpk;eq=Sr|J;r+s_eQll2uR#W*S=)rog^kir4=Q*6dv=di>D%ah zq(9j|?~#Nt0p86O5_YPA2Vwg2n2u!?CIegbn(RH4s7`*1aER(;==c!6D#Q6rQG!~0 z?zbLs6DR-eh5s1Cs%1?HW(x^?GMe|(OmpcwnDWB2!do&Ku}*p@4N_{6>TUjGna%zo z4;3f6?0^;;+QX+I%aEWR|Mvlj@i{_kzc(RD zp_FsU%aJGF^^v05|uWYFK&USRSDV9Iry&pma<%+R9?b26sJ%^Z3^ksuAHs zXJwQaQ^<<-yC(`**Oc);GVOZJ#nE*jUkLaSHDtv$Uyqe}ql@i~yQ&4~q>^J}w*9#& z0z>d~NNWj)Kb(5a1hMzHJ%K50@MVXb0?t$04-Nz;eCmN}5pvvfleqielXn~c9sT%{5BeeGsIA}qCUA}_A>Lh#nmj>0G90#7=^c8@hasZz|Sw5>kl=CCa*vax; zHJ2)~i3i09(|5ocUBe*JwKxR1DK(@&+fi~_jACwEMYj6;ivUI7kS#Q{^WY0TK~ z%@hmZeJ;))zsvmynnF~XaB5BYdDu~?pPDZ+rVlcab{VHKv%I<|#1}Zamv@JVM;=9Y z7xnD={!IP{a{vQ0-=i)V6~FBl9&s-F z(&$cY%yuIMVko4g7iuP5d`MC;DAm^NQlfh$2#)`P7=)CB93NmSLu9kgii|s-Mu|EK z{>Jc&l`p{alC^lUsoqGY#ZR>GL~rulD@!LLj6WY|({1GIkMFPe?)T*WAeimzB>Ocu z$!T9_d513ZvEK_U`_I%mnl%y-KvknQ#5#^=aZcABz9dqaTJ}WJO(?;Sv_J_R!KN`m zq0TC9d#Plvwz@iE;Nl$_Kgscmxk&9cXt2UQSm{la`mr6jS*`j69Tn=ky;PG(oUQ%d zuNA_jDK_Ra%yue5Xb$kM&T?uQKa_JbZwXkLzQdy;kidl+(nhLBW}stV6F7=@vl*VT z*3|Cmn|D?8-=63(;S%)y<(1R7?E7d{77%qmLi#3lcfd$%lfk}sO7*#pm*vyMBnQJ$ zn0-p*q}k@{@Z7&bLT>gjE(Y&Ji)&@TqBITh1RFq-4MzyOmx$dnr?hJR zj7RR{Vc`>6IPFlYHSQV_A1U%@W1Xg^DX}(vx{ubasHeNv_VN1D9;#no!{R1!zDnyap{Awx?^*Q@#(mhe!PwR_ZuV8NQSWOq>dxOu+6$6i%|8a@)pzN zGHUif{(~g6bSPSHUcuJ<`puf!v@|@HHy>KT1*V4cQko- zvUPMR(dkhF_HD{-@^Z{>XO6M7xea8WX{yKdlF?T(&{Fg-k%f{SCzYA9>bp-UfA5F? zqI)z*Zxu-F5AREuvu=+jfG(G!PA@eIKWr^$Tx8J&^elXNSg9Hz^iSA?4+{E;-|PYS zs3Ba)5yRLovz;kw_beEkt`YmUw=ZPP*)W-|sG(oWi=HAxCW5d6JXR+EMXZvYP`u~h ztjS42utd=MkA9-s&D01vrZm~Lg5WY8!SJcBrK;|xf+p`q-z`Pf%(RtnWc0g%M0ggx zjOH0bhKF$P>zNd=ZV4$)NJyGzsnQ%oxwN<1(W&W;PR8UmGrU@}_*)Tr+$7%H5J$Vz z)Yvp8rzCv4sT8`wl$xssic@r~H|kwQw#(_S`*FXNQ~G9|?aUHvMV9Pe&0f z?Ic?0bYym;$v267B!d6nQ4VMo22qOEGEu$=jFY4!Brj=9cNJqPBKbpb?RAp&qu?xy zQ-H1$?}F2@D)^i1KEhCJx0u38=oS-2x7cblH!o2M#JF)9WFMd zuLw-%k}?bK+jo#ze{Rp+Poi1_Q$9z2Ews>t{Tn0!;KB(SiROG+`=a!#!^8*&PlGUD zNyyo&m|iZlfiogXi8It2okO0>LO6q!9&?~|8$RZXp%Y)MAzV4VD&_2RZEAkE-R+@0 z(x=sX@@TP*6#hRY_>qY{-z6crXDtLadiEJOKlEvFa5asa^E4og{Lp(s=&^_URFV0u zXJv;*joS1{ogTav=2&^PF8}em?PZH;eKgfPH8itm-TdMmKixjO{-MQYd+7@>uDO#- zX&-!Z`b=wdr8eW$Q9Mc|St-CL<@KDH4FrD4GOR?>20L$B_{YQG+6cW0l9b7;bRdvpmmk{TllZ-CbJ6`{REhFLVIxcFA^^oG2#aaL;BX!X9CEu z&UFU6#qa$BCrLqlT(_|JZ$hh!d9E2C1EE}tK8l#0(*1^y;W!Q0M<-1kWMi+>x!&^; z^7y^ygHFrBeeMHK4zvkjQk!(O3H5ljiu?xvFw3)F5h! zrMH{|J)Ao5T7+ErPOrZaVcv0_aRN|gCejdiXhNB9Ni(~Zhf z?=cYOw-STS?#U9Wr4A+JZ1atBn)!bvg<>MWWBULeyTO`cV?PyI567Nqv15M_Jqob#DSM+w>!)f3`1)gnb2<|2>HUBtGu6! zM?8O)iE1xRF4%wdci}&aJ(Tmj5fWgTtPyuzZ`)HmCq@JZyg_M_%RJ99WT52Px!tRXH$Z?ket zN3qE0OFry?*JRQw4eZG7@$NunGoLAaqXmjkdy%{uKX zIwQn)1|2ivM@Dqm%FZ+=H;Elu^nzc7+24|nJ}KXZ4Thh>5l7oQ3i6i|?&E}&C0|qs z0Ba7U)-!ZuAOTE60$!|M0g;XIL6lYAu#6Wf8g`0Vf0)8sq!nRf3Bzl-kjo-1xh__T zPyWdX{(CRde>Qx-@v4?i(w^L6bFuTY`W@dWcHfmY4)hj50)kuQQ0J9QdxQ0|XdgW# zi$ml=LCn1C@H*78rB-Ft#7Tns#hz(bK{^rB#%Otg$o*LA&QnTZdi6zu^M=NSBpWUn zj%{VVk3BAQJHy#+bLNUh!Zb$a2+k{=FF#*?bD~UDRx}E6Tyd85DpJjGlX>CE$nUCz zAzlBN!^k7@HMAeKN}k*ES*@Ua>eV*<%Q z9T5rny+U%y-tTSCU$iN+7o5L!nWzelEVHM(!M`^=%#GynMw;rKuubEyz9c;k(H8rC zu^StGr`?IVKTpHTENty}^Xp7f>q%5>;~W?2H6lcxL3%u1>DpiU)BXTm`LPKGr|o_2 zX^IUQ(BLXk44)9FYI6(6Lr>fb4Rv33br+xB(-@Px5xXMAG@y0%tH0L%WVem{NOsNJ_$u+ewiWG(oAT7W$+w)b99op8Lp z{UkQv77@74WqIg179lUWJvbPpmo|^2?YBL;&Dmr^#Ys9ooYtMi5uj#7!@&&W`97^X z9tndp5(_}xJ~L~;Q+u#qCtqrd_%RZbJRi2ln$vxLe;O)A9hM4%vwUlIAN*bl@H&P; zG-2(1`yQ+3mT!%u7U%5;I=e0-hcVP&r*Dp_sFMgkH+i=U89hqiQ>g28hfW*bi9Sz; z_Z`=*oHk6R%x@+#72=0(ikG!kif?d3=3QeNE_BH`2^M2{&nPC3Q2Ms1_Ofx5UI7KG zDI(|#TaoJ!G}G_7CZ7Rq)zkVOpBIb0y>y%%%HV!Iq3FG4NsU-}TNdR??kf)O=8So`Vzu zXoK?@n^eE4c6z|RhcEB?h2dA)-4brMQ@X9UQN^TF3zfnTmqW3?e6&Dcvj%VnI3V%r zWkfUfpOUqQ#lCa_ z=)rdJ6Cb6otB{u-s6go=7qP9=f7g4`OIBVC{r$M4C;KsuK_K>46yx7{1M`A`kRj!C z)dSad86BAJ_O@|it)q_c@Giwptw6rYt0Fboh29F0Pe$%%onan5Rh^H0!5=dmT<5<< zdDU27x?e!n)S8uwV@lxtt#hwc#Tq-(OIk!tq}4fa#zih{=h2afi9f&_abvtkI9B^3 zEyBY0Hp`m~3`S@qGF1G2z~I?i6mT%#?|vLd`$Pqi#ET0N^p3(Ba9pa&VF}|eqit4I zmbvd&iA%W2zhY&YJW3nAeeq?ua5-9r81%{D;55J80h4_mORIA^Ms1xBvfHXoSnv{) z*u?S3Y!MFf`G#XJR>Av_iS}YHTnd3FcC@5v?&o_yn_&58+PF=wGkv0#D!}r4WaH{P zeJgitqkj5gyGtk0qf@PA_ilgt#kE=A8d4*k4jFJ6x9-peym+%Xon0B$3a9?kUgpKs zP=i+?&WHDv>!VxSkH^^e{bHI$89Zx!7dk?-*Wx!i;g>@?lO zmT8UpX-@)eA2+|7r%A5W0iI1U>MiL{T{9-CEJd*1?rjd~4cN5}D8(puw0NqSRcX`T zy)Z621Wty79*2S=0KE^i`| zhBl_*j<9L4uxP|`bJ?SDS#Zjix`N&N zUHtmldLFqY3DGzzyXjp_N^FQ5{M|Oqn3;+#2XRn+dyDMyTQ8|&&6=EA`1f`FA_X-? zFg@G$bvvXMBDG-zp?e7=GUI1tFy#Un5HUCu{22Z4tGXbR*>0n+hB$GXtUX~}^$>Po zhMT5r$`Ib04)bpCJM;hfBjX-z$Vp~`nGfRjX6}1x$!((-eBm28v}j>ah30eR)bgVk zbqKLl#j{T}__hMp>xPQM2Td9+_S8+eKoHxo(HxRPslbLf6~bg9vMNL)&Rq^CVxpz< zVtT%R7SdfV5hQ3psr(z>SiLCsKBhMW7fL++Yz&+w@{wa22_L09N^-dP54EWf!#n*= z)5*HG0SmDeSJsd<6Y(OcKH{U^rMu{w5LxXejY+f47*)UDd4DdheVsRIy!#c$O5LoI z4Zf}a^=6KIh>sECE*+V!7EhAPuDduA1x^tB?tayr@&y72|NRRDHRO+1KskrO7sy!} z^(Z1ZGT0SlqTdbrKwSIoMZ5uFs~P6ZgCVpD=*nawyu7zcqizYT^Jhq z+cphcCl=JZPO?0o4eoRNV65pRfzR*W6`6CnR}-QZRiyql`RYxnYvT)D*`*JYi@zON zuw!+WYCc$L{3_a;Yd4fW3SCUgSI>}N$NaEd6<)^>Mbq)_SbWBR2z&KX#GD70BnYS+ z9AIG408$8HLQX@;cd-f>XbEvTO^wmvQa_%RaNkDh1eb(N3HfE{nHV zO0`G*;!#0P2&8iO=bTrB^{=ywDe4*2nI!ZpreiwD8zAL4CZL2Q8XWuLm{ z=%qXHjyIqe9UiNcwCW%s}B5UP7d9aU!lmiwAFoHC>tlk zcDXd2eesdWnhFhtSmhZP;IfeWsd;Po^$p%6&7xhDwi`S(GjyLug7HgZQFMzIC@ri@ zI>I?ZjM}@QH2+XW#==0@bwlI#@@z+vB{0Y5P*Mi2b{gR2vtp9u8hZOnz zE@X;D6g{`MHC@X?gx3zTUn`A^$mNq){7CB?vYCKkOkVg4L~~99+u_%Ud4U0BXiNkY z)gKo0@^o)WnTv?UD@pg(3${NhT5@4C9lJQycZMzPs}+yQ)(C@3ZWr25?tlB56wpp) ziF!Bhg~Gx#Y#WEGWzPMNr|}NR6?YCpdB?oJ-i0dc{zNTG#vp)xsEa2wC3(w-$|Z+* zjr^SC5amRs^|W|UCJT|W?I&{ggA3%cs5py_RATGEcLW^iS#r$zkH}0-)(v?2amv@( zCD+WnrQM|PdgS4~(WmejxS^r@gcoYQWv=keV`Xp%d?``pKvg(2Gl>ltS$!{x4s`HS z&dqNNYHxv$73PU|d}p~sw4A0d(QsWfWz;QE$e359(O2(HJ$^$`BkdwmRh#~Sc7 zEZ3RyLDnheU!(L2ZwI?71oK}hiX%$-l(+Teog(R?N{OuE>0biq;3sCw%rO#9@4sr( z`O`|A%Fk%@Bdv9>$PMV{8}jrfOE>td=;OQ#Xj`#*WZ$7lnw$wvLpvi$Ru2=6XL9=< zeoQT$%uiP}{!Ia3HUp_QFx-O77e_1`9Ome_3@mlH!ncYeS){`adz%geNU>pgU%Nm3 ziq7o(s%g-_W|9T#yOO^pzKkDOmEywB3>(Br^Q)E&G5q}1y9!%CysvX7 za+;Ou-Uf#6A;X+|*73U8dCfyn>~hnGahW6OamHE-$;a1a z6hM-u#uW{S>MZK#dvA=7cXEDtgZ)+UMVL`td=j#B6SDNdA!{9JOr6p##d_5bFJG9& zV-?{6^|lYMNPCX5p}r&oKhE8Zv-ikH2HE+Sj#qJA^twmZxu~_`8HSVQvts7!Tl#SQ z1Jby(doUDn^@bxn>e~V#dR$7D2%pcsWo)S4lrZ_ylm_7s?x~Q|{${)Jb^kDRU3lZM zD-3QB0L)Tx*md9@YgvT=1~;5gd&9}o7#nEa2@gzg#tWCV8PoB!nEPS9#dzGEG^lZk zDI?>a416N(*RH+I^F`>ZdphL4xZwn}nMD>Ku_Wtdb}MLAx0cWQwcddlLc=^k%0-g; zSL`vV_i;rr)@O1ix6%*)>tkwU0>H&JpqNtNv+ID1YMLu%L~Wh--C*mZHGFou%y;5- z?A!8KVLts3J{(@@E#f+j;(nz)CTB|Jra3KbrS!G_8{s>N!t2A}Q+0pkn9Teo=A^3X zJbYSfwU#|G)Hr*3-N&!kFw0VXuu`MO_I_FAjCkkN7bU5d7C;Xlme^6XP981D%S_m04OIzvcr)A#b6Vk+!Sk7bT-sU2g;HuKkz7#ah?0eSAx9RRJM?NJ}tZp zC7yZtQ*OC#azJY|!Xi1hrCF zGC!Tu!0c(-)u-IYqRJR71FyYjU>b(GEf3?A%df?jEeL@pth^kLk=c9EECMmr82Brm z50$vcd@I2ie`bCeN)L(mdZDCI(wm%_W=dN6S!-DNwDuz5GulrxoxJsQw}qC@{hQ7Q zzSBx6KCX9vi)>z-21`SKNSiog^`%!VtWnLoKjel;*Kc4IjqfivPm$Z{l~dZxC;NOU zmeZ-QvXr7p*K&E7kM+FCQ#g{T~zY0{9p_E+arEAMk7b=O#54Ul_G)$3x zE&$pdE+stA>n!-DPf>hQ82b3%sa$@}BJl|8uVR_p)?&|kA>5DiS6L_3&f8($HA(H| zJPU+#b6#Qv!t-W4VhNXQ>2?AKpM%;<{am+t+Usx)YeXJCebOqPQ$-s{|9|a$=RaIs z+qQ@+x?zOm(njwkNTQc8dapyYC=nsr=sktZi0EY!QKCf|Mi&f`7@`FsYP1X^q74$F z$GgY<-1q%F|H1p=`owSB_Fn7UXFbkityR2lM^L=3mr?w=@^l$_5UQ+a!W@LX)TiJF zVU+?x+AvzTz;@k6^)ogG)uiT9`(?T5ac@JVjl?~-{H!eg$)_$^^ zr89U{3A;DvV<*g3?NI$aP9%o!E9`cqAbig{`)Hs!k6pdB8aapZsPynBnxyeN-g|hQ zIv(s!DlQ(rzXiA$4u17_3_!laOfJ$Xl&^afe~P)9SFFqcGc!ky($>y;?a{$s|4QZ8 zuZL^1tvAZYK+}KXMKu=rTl;tP`qnnX!x!FApLnf#?U+aS9=JxBG#^wR;!7QF;HO>@ z>Al0NEtr3+WS>Mdsb~{*)1f^ScQ?;|38}KO_OYK0gH+f!sS}dR>4TgL9~Af-TcZ;2 z6}DS33-f(z|dSEe#iGRdp0vr%@PYSV6_Mjd2!Mma1{^?lOfp!IE9lk15j5?$vqnLl@v_(>YS+< zZFI;zz8#{LT>pyY`0WN)o!g>A9D$th1Z$6ME^wwB1{Vd2k88M9aWF}7^)dOsuz*ADgDMPq*6o=>toLiS zrvsxv=%GF%Dobr$ak_U+ms4>|*_PuXp08 z*_kY6QtT4(`=8se82GU<^$W{u1Mx(V>aOnc8xfEW`|}twQfACi0F(?mKjM~Zfa3iT z#nHtw83S%Br$m$4<%ey8l*NnD8zuags8f~AX2_vTy0zifCG|(wDD&mJK3CdcZoVRf zgv(UdlvI(|+1k!u$!p=B6};IqFUhk=-3C|??sLJ2c{mi+cji6$exPH zO)$Gd%eAkolVOMUw5bg3O;idx2|d`YHnq0>9#9!&JV$EVGb^2YCI<^J&dhj5+;$T@ z2#@)R86DsJe62nADBA6K%Y<_W=m6nPo~~>I`3hf}dU;V%#cmH%7!AjDQU=3lU3Htq zy3&T}`_O6V#tR3uHn(dr+sFQQexYUwdEfG@+mf`63fFJM=S8=JGcDZfmKsmU9MUaU zC}%MQg7va~v;6~fUW$^_hw(QXqFrsjPwoTNYzAF58oZ3c#`k1#yA)ki0peThQ8>)$<) z5c|vAB*5}7Xj+)DwaHRoTtIh0rt3>LdME6dPK{%=gdnG2Ry8YIh2!zptg+nf8MT&< zy1{!xudXB?4r3NL^krpwa7E<>j!DDBn#Etg#w@xE1tS(t^=HH8`NXg}?M)Zctd9DV za@Jrh8EvH4>}Lw-2xVNdlqL|F_7# zV&jHhZ`r3w8(wqHk#^t%RTH_w3p#zS=ihyi(ao0~#i1m(k_T zNwa2P9x%!j9KBJ1j307mFd$EH+!8S za&pEp-ftprgo;(_L}#69q*%TMd1S79`GY68o>tzk9xt6{yAjrF4l9LgXYet;n(f{K zNzSmZ6#H6Qd)YXqUu7+!4jyIBdthg?Y0Vq_He)=keg)y{V9P9tU5=(s1>N&}J*vac z%rE5Fr10Q&VeFqg6i#X3)X*llAXfJkM^W3zH|Pg{$)1{XJig28q z6>3YxNTbU=Hps+@2(>X(bm9xpk|{TnYOFUXu(8G4p9PLQG(Sj7+iYLs^ny^Y_*2C7Ei86174IPBea@|+r*UD3R_bDxk(@(F~yORoW+aLR;CpOH7) zv$SgGxz65*#n+1&ta80iz}WOyRI57ol5UV!-taWkiQNR+k>6vwL+W zNB_KYFex=eJ7^SC+o9F25#kxaGP;z)gRl+y{j?}1(8MN~DCE-Fgub?EY5q{Cedw<7 z&?C91*!ARgjD_Gw{;u6G=||deM`4>@rwYtJwu6CuYH-@-lp!6# ze@P{)ma{5xajAk5u1LM3Jo%!)<+}T0Pyu3F7~O>FKxwRttLGf2&Vnd z#ScJhkFI1|Jbv#XWSR?dVI30iFby5LY*g&~2SKTMc*Cf8`Egfw^3;ux&1UDUqqq#* z46K@dGAAH>s#sLtF0*9Aj}wOIe};bwLwQq^`G^QPj< zK`}L$cams;w7Dbi?wRsGNK$IB&@XhImE-pbyeUR?FDq7e+;+^e9E{})R&&81=c;E_ z;wRM*kaav*%)(X5wO&65P-BTD9jFWmZo&1t? zC;VpxeCI+x<1*{*Irl#xCgYYYZwxHu%rfn;mG6Cg$B`Gn`54H~bdleJQ)DtXjB zDd{csEcK*5aDKH_d*5}rzb#)cejDw)gZgv}NZ$Ao0l=Zy!EA^Uz}3~?^L8zj3*|o# za6>FM2)B8dyNXmE?r0XAtT~n4tbP?G|Mr#`VS4(@o6`qcowliOM3c40sX*LMbASjDkimCARmA$z6?NEj(Iwv-W3=mxUuv$+3rjp8&uUg;IZgeLaU4YgqY;VfIB2UG?_ zIaQbqVoz$2%2x0@BggXoK0K&;W`?Ppq5UghDt7$-cn47Ha#W&yo0uZ6)s;_DkiIz( z1=X1`DbK$e4wQfe_dam}iHo!Do$pUy-#{eY(uTe0nC@DfP(*I9Rje}4693w3gY9OdEv?4n~T?5MUd9lof z%c{`rb9cGC;O&rHxEdn_w++hF1OP{Een7Cuw#uB#6x`eH)RweNO1wG>eYZ_^7ZlJp z?L*Qznf6-zY$sogKj2AI;u5^dyq-yZ!^JQ^tygF#q4UffVkun_JMu<=g%$7 z&)g6m<@HBeYkWUvyJ7)60{RL*6f2k7t&&Wv@%gozkNOm@U-l@3yGf_Wrk zHUyspYt9Pq?8WNt1v#1ltuXUXdI(E|dp99iVqDE%ZGOXvcKuv;uak85DW4sw*!Ckw z2j~!1OY@&;)h59U8#U_ZZ&cl^Y+J6~K29Xz=c(yD%F1F{2W2X2D16%Rr*twi^B~nQ zE-OF@iRHDfxzL>MUK@!)nqg0~QZ?E%v=3HIIR@YP*{0}^*wLr)Mt#r7snT~GDqy}~ zl~%&FC|#C*)d){PsA3Vj3qoX|MX!%=42S*}f`0$5QT+8pmz+PUhn!-6iR$d)_iZ3f z^F1RYCm>txImChVWsJhQ+Lkj*%8tue&D-Z{X49w#wla4P2^#DlmfmBkb#~LHr|IxC4XPE~ zziejr(`&0u>VfhZUFPqqOuj+XZC;HVy&aeBu z^znUXM;&9;u1;3V$2>uNagB0An93_bJoSJ*St{UT=smzarx=U z8oP|6vlcq~w@0XFYAkY|y{Iq9)?`O%Z}%Orvry^D(vjc9Y)2-USu}F~SM2mELy-~E zElF2I8#B#P>*1Bb?xN$?IrY_Vk8}U2TFx2uo_l#>J~s9BlcI0|ncq+*6tH<6u~o|c zWL37SUQVzaz-nGL2Q1xM@d<6S;(2`hAzzJD6f9D6t3SIp#6^-a@}XvZmXs5haXRDu zUd@CzH^$?3vUOw32M;$=#@6?)s^CKuCWq$0yfhHSRmy#Y!SG86Gdfo&trKZK*XP4YQt%uL7WSj zhrEMNXzYAy{bGl-8EesOpEOZXX0SwZt0%oy(`H}+Jb^SILB8+ia9&b81edY$4*Av` zOE(rs8h&;=`ohYqUxz_iEb0Zeyx~xEEoz^y!C)^lkA>(11!w{L)?F8V)$QcMyTwUk z$+#7mmt5D}4}r0h@ElQhqx)rvcw5+PxNqqF+F3>g!=QfJ@O%=It-PT%(cQeLhh>S;3m0Wa2E_ZFv@54Gtl@r&r5EcvpbDicSu2TKHfs zc`g77wP7x!N^J?i*%)O2D}p^Mzc5D$++H|y*-JZck;r^^oJY-P4Oeeyz;}gaF~zsT@(jM+H)(G1?0AEHV}q^^}V>XX!EP zlz8)~CRHCO2fr^Fq$sEXl;jM{3CCmPq2RMHRu5RAJ zA{SGcN~Li8l~SW+6L-phWLLMQKspN9;cg~NWK@NL2*GQFoylX-y~VxA*W&_Qj@y#h?MtidrY=(RC(`xdc`jEwa3s@6Sq^c zjl)7fQYS}*M+LScOnq^^`#@UMF7;s6g$JA=XFjA+f63@I2w%W3{pL4Z4Sr!!--Zh# zIKfNlRtHzlc@b@BAQDMb0z>T7GRps2c{Ft)J~J%+Vj9GU$X$|BIoja#5o8@1?Km}x z&*NDZ)(GC;MYveWa^w^fw|u5*oO^9*oX@!fQ?JzLN3UEbxl3+8VJdOQHvYiCz}WP& zjVE|a1cYJOXPy3R0TO{LWj!&niCWtA>!cpmb{ZWqm zHYJTWNvcS6m~s&5w<;RtP!C#g;=PT2c9C)cX<==rSTKl$1oln*=89m`~_%&8C<{~g9GIe8wo933iuK}8D39#fr^|Ey{`K#Nzfr~RI0^GQOzYx8)McNAVg zfjjBbT1NBw=wjC&o1gL~E+N$^z_bSW)EU;`zwVL*?;B6X&}-NE5k;zz+Z4$An8MET z`Ba!ndezx>7a8fqCcc9lB5~Eoxy`TD+M( zr8ishz`|w!mD1zI3H?RBP3gR(T&i~bhxqoCNC1{e^Z=$g@^C+#mSnSzS>Sidv*@) zt8)EWzoH5jd=8UUt-7&Xh_Xtvj*<>{icjjLzamc8F*D69rlQdIwS{4P*&T7P4f6Pv znSwsCO9b#-K2HJdk82%@OBqo_eXB%Q8M&n^`7d*qJAX_LI^~yai|EnPq7-voH5CkP zEBT|7ZJx>%{eTP*q7NciRlPaET#c+ZpZTO8J3r}GvEE=Sx%FfGf`hj8QYRbMv1$b}eA+`1<0M z@B9^?gX1_n2+Yfw{b*}q@y``j-g5m-yo+P*u`|;hvy`-NsG@vO=X;+w5Oy1k(* zJE(o(2mLD}e5INzE6iUd28suGp^`l@%@I)tUv29(UjZFR4i?(vn=M?DIm;&?ER-u) zO z^x0o{IZyXyBfPF%1_)nzrbV54frv;Fw|e)CrP>}0BX@?Uv&nuvec{1l&X8bTxp@Wh z-Dx#4&xuZ`p!5Vd$hK#9Nea)(3t5VD{~;x$7w%# zEp6Awb2X))m#`4CNP_P~H>3D+(~qgSz?zbG9D<23q7Geqg(#L?%ZdwoY_yHPVWE0p{e z47HYcd8G-R+-sd##;FhRU{dt!yX+s@L_QzHzO;)qw5FQA&e`$TxYk0)SI|Kflt{0ehupN zX#bfb2RMukYH`~-d0^#IJ?PJ|-`SJ(PBfTV%IoJTTJ3ji{g~}((v~r(r^6r$nz7G> z%T&h0Fj`?N2wd8L9Ky>t>>5WYW=+VrieA>v(}h{RdQtSPo;(L#EUe*o!;<*I-#&8l-yuHmROp9+==EHD<0q5=c0X>vh;y<#2qkPW!59 zKaQR~qVvko>5N|w$UT<1kkQ`}8GJ8p#D=|pMgWQd;tbYE!wv2oih%3Axj7S;!q~^o z#?`<$A}JQ7)*zjbtR0`^E|7P8&A!C5(mq>+=EKofiT(ZhGGpbpvD;VB>EF>B)sfF( z(Y7jT_VkABx0($68V6Wvr{Ygl#Wj91hj};v=T}^EhEYZx*8+9}WJ)HEZ@aoIv2K`Z)+l>2MPBW3+C#XPOa6(h>(y!;!S z8(&z*R*Hsz!w9oRma7PU#g0M_s@8G0Pxtn@2G|%XT46w667O|S@^#^XU!OxslJge@`1c zJ-q?vfiGps;;fA}LVD@%&yCyZEk^V4)K&MxMD~kD_caR~iHCUU`)xjwuIopCt=ufU2LbFw_{!w$1(}FQU$FBH_;ZI4<_I80ewO?9h8Da*BI;93%w_$n@ zD@zu7KF``e0BX&L?-@ye>jlf30N|?r&5zNh-rdTk>48>d_P98cjCFxgPsPguG1Ebx z2F7xFBRIc49MYfN5K@t5iTWu*1UBuSQSotqLCr*)G8q6BCF)?H701!n2O4X({invp z{NZYX_ZD%k>R&!&gmTGc!P^JNC48E8MkM(oVs)yH5F3HG5B9NvAmAJX z0ukT5J|#p7PZx#M!AFl|m)J?^@l%0E-3h3V6}B;d9)V)>mACyo(p|ULy;bn5u>IJx zliO4w-e$J*li`{O(fbO(erg3~79S_$0t7QU|ALFmtG>J0X3$~cTTV}J&d7T02AC$S zy&`nzN&3WTc_XySrN%NtbYoSbJ85HST|o6gLg2lHGGy|`+viRKJN!~knF*j6f_L<$ zMZIe^Xr(-KT5z&RlOY}rq|xGfvR0QCU2W#mH~=(^>`@dLNQXb3yL}DnO2>BnD5!Pp zUjVK&j@A>^SthPAH`)h1Ngp?GUSBJEGO$ccUBKcdiCc)p*`wt`Rkp4K)3E`1&nXk{d)s2L?$gVN;jy3F^TZ~P+jej zfPst-!Q{`$Mf`le8i(WN8uhDSBDGZTZtMWc5F|UlBjwx=yID>?4#Dk;IWqK!#|Rjj z4yXJjtB<$PL3JLNjy+8;6^ljNwkwZ072k4pD0gnzH|HyHPA{4-#C5D-eXNJ{>cI;k zfiQy)+veONNObiiC${74Wn znq)mXmo9Lm@GU9&XPozTveosio%~vr(5TMqdo=#{_GSPglw+w;nVJPoOX!qmKZ%$GZ?FfdtRPrmh5z`6LC4O~|`5U$idrg?uU$xut^&w!j*4V!0 z?>L7vMOu#dS7~7U09?T`PR_7H_f+lYENK4(=rIERXw3?+tu!pAjvehNvO0@vfq-~b z8MchN=Fj60#f4MZVUlW+JsN@D>01;S(eT53l*!z3koWZoEx}0TsBBf^*1Qbxlxkth z4^A56kh6?RtTgSSZ}WKzw)HRbhB&?DZrHMv`|WK^0n>rUS&QVWSP!xB`7^O;gf><= z#IHaYUPYIIW(($K;-j=1s*Jxh^@<>jCI>TKq!xPY>{}h`9|~2$?QRRm=e$}RA&$0% zJx3W3Z$}dBkj7L6u%cDsvOR##rK$ru%5SeVbPj&l@z;d!u~DbOLyyXZTz9R~yFN;% z*Ku*FwI1(mx5Xhj62%WSf#tz*T?ay)nqI6=Zz6OX_wJ_J1>YjP$%(o<5$Jbkdd}X+ zLl#)?_j2!~=Gus#$hVFYsde0_$&eX@8AU!69^J4SfCw$ggS3a?K8*e1t1gKn3rm>H zBeB>R(l2vB<^pLdH}%Rf9wdV88xO=-$tRkz9bPR}06)>JCUP z+MeX2hp2MF^m}l1W4p?J*o^8}ejt$$Fe8!K6|fTW19dgLC3zeswL=ho#1dN67R$T4f`)R3V{ z`pLNl#{%0JUL7|-nHJr$+nMY%#`$}0QzxU8N9%iE72?0^5MSFESb~RTouw^|D6ixz z=qv{xKmMZpbr~5b@+A@la5xDboI-h}pV4E#o_t29hEl?ffj4<@1E3i_>}3EcGw^#z zaY>#R%xJNUG|a;8VD2|MFH0oHEhyQ}71##LnV-&C3QivD(`%aNSbpu8+=*y8{&&hp zjm3`r(rkg$t{^CooT)xl>8*9x#Y{?iO3=1OogzsqopphHlun8UK`L|aG&9O+&BxsR9iXa3ZdGz-98bSa#to2$FbkaB1O{XhbyQUFVG(MqC!=}fa^=@M z(+VjBQM!N2^ycBh{H~47&OXF!&T;R=)HijLlcqiGpk^sGJi53z01^P^CPZ;;un>;V zU!(Tua!bTPax74_o(tOKPG6cpK~WLk$swicRjyEv+|Ck9N!aFa4A#};h0?Ka=e~fT zd*uE-=K*v?XOo1A^V`^NE(P{>rHn7NRzn{lnU?SBFN@vYoEV;vplSD%K-6he7apV> z<7YuJc~5_-0u(KhrNF7aJ$#nXh>2R&#**p`DklN4ZwTYYX9}eZ@Gn5Go(f8x8eiT*B=wd4A`|MnvsZF{A&th zFyuOi_{nIU=6Qmp4XBQr$Mm&x@22_sHqs_G^nS)S4^zip2elafz%BEY4;8fpSz1k$ z{(kEikqdE8uULLHGC7t?AVg<43ilF7+>6qIo^7CG$Pj7e==&7LA6IxGu()~ivQ);u z#dyp|*MUusx!U#hLyV7nPSsXY4@%>7!WY(?*H{EX5Cm|o*?B6ROQyh$I0gHWkz}CF z@Tf#YhwubKr-njKNuY`>c&u|Ps%KiULXr$`zY0ti=@_8wlc)TyZGjda+m;i2Jb5%0 z4|`kGc764rtzKCv=V9C(~J7nA(R119N5ct%h%8h@u@>6jas_46@2SP zHVmL@AT@pS=s=!;dP+|!M&vh0zUx2Zk$hBQAj|U;SPBfPjsSK6triD!1gpCI$cz^3 zd6I_Z+mHVCi4@PiJ@f;SzS=`|9*T;%-c}v1_;zZGm`>#*(aLo7avyn{-bc0PLj*o7 z8G|mqcbey1_y%`s81?wMQiTg<4u7;u$p-+o9LNMPr7`P+)92L@EAT*Mx6U5-maC0D zFT;6hoWk^)ZY!nWF78}$fVN^z+;fnOJlVAr(^bk32fK zYKF>82ikhvH74Zp+_V4EI+7)y58N5pet|}FFzFmCBV8dWKd@K8{`t>;-30ttFxy?@Kg*^@z~jPy)IK|LaDe;dSwUp8TKJGIGZixa<6%s{jTN z@t+}_-zfZ?jJ){T*ap?V2X=l{{oi8$&r9L}*ci|fHUp{*8Be>ynlrEkLLZ?N=OFtkJ*v| z@#G($_fNq1Ct#S?gR~X1$jHcE>p^c>o+Ew#KSti|vx{ Date: Wed, 1 Oct 2025 12:33:51 -0400 Subject: [PATCH 31/31] removed unneeded include (#5225) --- examples/shapes/shapes_collision_area.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/shapes/shapes_collision_area.c b/examples/shapes/shapes_collision_area.c index f492cf250..426d56536 100644 --- a/examples/shapes/shapes_collision_area.c +++ b/examples/shapes/shapes_collision_area.c @@ -15,8 +15,6 @@ #include "raylib.h" -#include // Required for: abs() - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------