From faf5f98914ff775f9ee33287411d3888827b4ec6 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 29 Nov 2024 22:56:22 +0100 Subject: [PATCH] started to implement audio mixer, game working but no audio yet --- asset/AssetArchive.h | 198 ++++++ asset/AssetFile.h | 58 -- asset/AssetManagementSystem.h | 42 +- asset/AssetType.h | 1 + audio/Audio.cpp | 92 +-- audio/Audio.h | 1 + audio/AudioMixer.h | 347 ++++++++++ audio/AudioSetting.h | 36 +- environment/Globe.h | 41 +- font/Font.h | 4 +- gpuapi/opengl/OpenglUtils.h | 7 +- image/Image.cpp | 4 +- localization/Language.h | 4 +- math/matrix/MatrixFloat32.h | 12 + memory/ChunkMemory.h | 23 + memory/Queue.h | 12 +- memory/RingMemory.h | 8 - memory/ThreadedQueue.h | 95 ++- module/ModuleManager.h | 4 + object/Animation.h | 4 +- object/Hitbox.h | 4 +- object/Material.h | 4 +- object/Mesh.h | 12 +- platform/linux/FileUtils.cpp | 259 ++++++++ platform/linux/Library.h | 2 +- platform/linux/UtilsLinux.h | 246 +------ platform/linux/threading/Atomic.h | 36 +- platform/linux/threading/Semaphore.h | 1 - platform/linux/threading/Spinlock.h | 2 +- platform/linux/threading/Thread.h | 135 +++- platform/linux/threading/ThreadDefines.h | 34 +- platform/win32/FileUtils.cpp | 801 +++++++++++++++++++++++ platform/win32/Library.h | 4 +- platform/win32/TimeUtils.h | 91 +++ platform/win32/UtilsWin32.h | 573 ---------------- platform/win32/audio/DirectSound.h | 51 +- platform/win32/audio/XAudio2.h | 43 +- platform/win32/threading/Atomic.h | 48 ++ platform/win32/threading/Semaphore.h | 2 +- platform/win32/threading/Thread.h | 14 +- platform/win32/threading/ThreadDefines.h | 3 +- stdlib/HashMap.h | 50 +- stdlib/ThreadedHashMap.h | 134 ++++ stdlib/Types.h | 17 +- ui/UITheme.h | 4 +- utils/MathUtils.h | 2 +- utils/Utils.h | 26 +- 47 files changed, 2381 insertions(+), 1210 deletions(-) create mode 100644 asset/AssetArchive.h delete mode 100644 asset/AssetFile.h create mode 100644 audio/AudioMixer.h create mode 100644 platform/linux/FileUtils.cpp create mode 100644 platform/win32/FileUtils.cpp create mode 100644 platform/win32/TimeUtils.h create mode 100644 stdlib/ThreadedHashMap.h diff --git a/asset/AssetArchive.h b/asset/AssetArchive.h new file mode 100644 index 0000000..2094143 --- /dev/null +++ b/asset/AssetArchive.h @@ -0,0 +1,198 @@ +/** + * Jingga + * + * @copyright Jingga + * @license License 2.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef TOS_ASSET_ARCHIVE_H +#define TOS_ASSET_ARCHIVE_H + +#include "../stdlib/Types.h" +#include "../utils/StringUtils.h" +#include "../utils/EndianUtils.h" +#include "../utils/Utils.h" +#include "../stdlib/simd/SIMD_I32.h" +#include "../memory/RingMemory.h" +#include "../memory/BufferMemory.h" +#include "AssetManagementSystem.h" + +#if _WIN32 + #include + #include "../platform/win32/FileUtils.cpp" +#elif __linux__ + #include "../platform/win32/FileUtils.cpp" +#endif + +struct AssetArchiveElement { + int32 type; + + int32 start; + int32 length; + + int32 dependency_start; // actual index for asset_dependencies + int32 dependency_count; +}; + +struct AssetArchiveHeader { + int32 version; + + uint32 asset_count; + uint32 asset_dependency_count; + + AssetArchiveElement* asset_element; // is not the owner of the data + int32* asset_dependencies; // is not the owner of the data +}; + +struct AssetArchive { + AssetArchiveHeader header; + byte* data; // owner of the data + + FileHandler fd; +}; + +// Calculates how large the header memory has to be to hold all its information +int32 asset_archive_header_size(AssetArchive* archive, byte* data) +{ + data += sizeof(archive->header.version); + + int32 asset_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data)); + data += sizeof(archive->header.asset_count); + + int32 asset_dependency_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data)); + data += sizeof(archive->header.asset_dependency_count); + + return sizeof(archive->header.version) + + sizeof(archive->header.asset_count) + + sizeof(archive->header.asset_dependency_count) + + asset_count * sizeof(AssetArchiveElement) + + asset_dependency_count * sizeof(int32); +} + +void asset_archive_header_load(AssetArchiveHeader* header, byte* data, int32 steps = 8) +{ + header->version = SWAP_ENDIAN_LITTLE(*((int32 *) data)); + data += sizeof(header->version); + + header->asset_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data)); + data += sizeof(header->asset_count); + + header->asset_dependency_count = SWAP_ENDIAN_LITTLE(*((uint32 *) data)); + data += sizeof(header->asset_dependency_count); + + memcpy(header->asset_element, data, header->asset_count * sizeof(AssetArchiveElement)); + data += header->asset_count * sizeof(AssetArchiveElement); + + SWAP_ENDIAN_LITTLE_SIMD( + (int32 *) header->asset_element, + (int32 *) header->asset_element, + header->asset_count * sizeof(AssetArchiveElement) / 4, // everything is 4 bytes -> super easy to swap + steps + ); + + header->asset_dependencies = (int32 *) ((byte *) header->asset_element + header->asset_count * sizeof(AssetArchiveElement)); + + memcpy(header->asset_dependencies, data, header->asset_dependency_count * sizeof(int32)); + SWAP_ENDIAN_LITTLE_SIMD( + (int32 *) header->asset_dependencies, + (int32 *) header->asset_dependencies, + header->asset_count * header->asset_dependency_count, // everything is 4 bytes -> super easy to swap + steps + ); +} + +inline +AssetArchiveElement* asset_archive_element_find(const AssetArchive* archive, int32 id) +{ + return &archive->header.asset_element[id]; +} + +void asset_archive_load(AssetArchive* archive, const char* path, BufferMemory* buf, RingMemory* ring, int32 steps = 8) +{ + // Get file handle + archive->fd = file_read_async_handle(path); + + FileBody file; + file.size = 64; + + // Find header size + file.content = ring_get_memory(ring, file.size); + file_read(archive->fd, &file, 0, file.size); + file.size = asset_archive_header_size(archive, file.content); + + // Reserve memory for the header + archive->data = buffer_get_memory( + buf, + file.size + - sizeof(archive->header.version) + - sizeof(archive->header.asset_count) + - sizeof(archive->header.asset_dependency_count), + 4 + ); + + // Read entire header + file.content = ring_get_memory(ring, file.size); + file_read(archive->fd, &file, 0, file.size); + asset_archive_header_load(&archive->header, file.content, steps); +} + +// @performance This can probably be done much faster by handling the loading of dependencies faster +void asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetManagementSystem* ams_array, RingMemory* ring) +{ + AssetArchiveElement* element = &archive->header.asset_element[id]; + AssetManagementSystem* ams = element->type > 0 + ? &ams_array[element->type] + : &ams_array[0]; + + uint64 hash = hash_djb2((const char *) &id); + + // @performance I think we could optimize the ams_reserver_asset in a way so we don't have to lock it the entire time + pthread_mutex_lock(&ams->mutex); + // @bug this is not how this function works + if (hashmap_get_entry(&ams->hash_map, (const char *) &id, hash)) { + pthread_mutex_unlock(&ams->mutex); + } + + if (element->type == 0) { + // @bug We can't just do this, this won't work. Check if we might want to change the asset management directly to hash indices or at least int values + Asset* asset = ams_reserve_asset(ams, (const char *) &id, ams_calculate_chunks(ams, element->length)); + asset->self = (byte *) (asset + 1); + + FileBody file = {}; + file.content = asset->self; + + // We are directly reading into the correct destination + file_read(archive->fd, &file, element->start, element->length); + } else { + // We are reading into temp memory since we have to perform transformations on the data + FileBodyAsync file = {}; + file_read_async(archive->fd, &file, element->start, element->length, ring); + + // This happens while the file system loads the data + Asset* asset = ams_reserve_asset(ams, (const char *) &id, ams_calculate_chunks(ams, element->length)); + asset->self = (byte *) (asset + 1); + + byte* data = ring_get_memory(ring, element->length, 64); + size_t data_size = 0; + + // @todo create platform wrapper + GetOverlappedResult(archive->fd, &file.ov, NULL, true); + switch (element->type) { + case 1: { + } break; + default: { + } + } + + memcpy(asset->self, data, data_size); + } + pthread_mutex_unlock(&ams->mutex); + + // @performance maybe do in worker threads? + for (int32 i = 0; i < element->dependency_count; ++i) { + asset_archive_asset_load(archive, id, ams, ring); + } +} + +#endif \ No newline at end of file diff --git a/asset/AssetFile.h b/asset/AssetFile.h deleted file mode 100644 index dc20aa1..0000000 --- a/asset/AssetFile.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Jingga - * - * @copyright Jingga - * @license License 2.0 - * @version 1.0.0 - * @link https://jingga.app - */ -#ifndef TOS_ASSET_FILE_H -#define TOS_ASSET_FILE_H - -#include "../stdlib/Types.h" - -enum AssetType { - ASSET_TYPE_RAW_IMG, - ASSET_TYPE_PNG, - - ASSET_TYPE_RAW_SOUND, - ASSET_TYPE_WAV, - - ASSET_TYPE_OBJBIN, - ASSET_TYPE_MATBIN, - ASSET_TYPE_ANIBIN, - - ASSET_TYPE_RAW_TEXTURE, // may include bump map, normal map, ... -}; - -struct AssetFileTableOfContent { - uint32 asset_id; - AssetType asset_type; - uint64 asset_offset; -}; - -struct AssetFileHeader { - uint32 version; - uint32 asset_count; - AssetFileTableOfContent* table_of_content; -}; - -struct AssetFile { - AssetFileHeader header; - byte* data; -}; - -void asset_file_load_header(void* fp, AssetFileHeader* header) {} - -void asset_file_write_header() {} - -// add asset at end (automatically) or in pos -void asset_file_add_asset(void* fp, AssetFile* asset_file, byte* asset, int64 pos = -1) {} - -// remove asset by id or pos -void asset_file_remove_asset(void* fp, AssetFile* asset_file, int64 id, int64 pos = -1) {} - -// load the toc info of an asset by id or pos -AssetFileTableOfContent* asset_file_load_asset_toc(AssetFileHeader* header, int64 id, int64 pos = -1) {} - -#endif \ No newline at end of file diff --git a/asset/AssetManagementSystem.h b/asset/AssetManagementSystem.h index a71ab1d..e336526 100644 --- a/asset/AssetManagementSystem.h +++ b/asset/AssetManagementSystem.h @@ -39,8 +39,11 @@ struct AssetManagementSystem { // Actual asset data ChunkMemory asset_data_memory; + // @performance Do we really need the linked list, the ChunkMemory should allow us to do some smart stuff Asset* first; Asset* last; + + pthread_mutex_t mutex; }; void ams_create(AssetManagementSystem* ams, BufferMemory* buf, int32 chunk_size, int32 count) @@ -56,6 +59,8 @@ void ams_create(AssetManagementSystem* ams, BufferMemory* buf, int32 chunk_size, ams->first = NULL; ams->last = NULL; + + pthread_mutex_init(&ams->mutex, NULL); } // WARNING: buf size see ams_get_buffer_size @@ -84,10 +89,17 @@ void ams_create(AssetManagementSystem* ams, byte* buf, int chunk_size, int count ams->first = NULL; ams->last = NULL; + + pthread_mutex_init(&ams->mutex, NULL); +} + +void ams_free(AssetManagementSystem* ams) +{ + pthread_mutex_destroy(&ams->mutex); } inline -int32 ams_calculate_chunks(AssetManagementSystem* ams, int32 byte_size) +int32 ams_calculate_chunks(const AssetManagementSystem* ams, int32 byte_size) { return (int32) CEIL_DIV(byte_size, ams->asset_data_memory.chunk_size); } @@ -201,8 +213,34 @@ Asset* ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 index) return entry ? (Asset *) entry->value : NULL; } +// @performance We could probably avoid locking by adding a atomic flag to indicate if the value is valid +Asset* threaded_ams_get_asset(AssetManagementSystem* ams, uint64 element) { + pthread_mutex_lock(&ams->mutex); + Asset* asset = ams_get_asset(ams, element); + pthread_mutex_unlock(&ams->mutex); + + return asset; +} + +Asset* threaded_ams_get_asset(AssetManagementSystem* ams, const char* key) { + pthread_mutex_lock(&ams->mutex); + Asset* asset = ams_get_asset(ams, key); + pthread_mutex_unlock(&ams->mutex); + + return asset; +} + +Asset* threaded_ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 index) { + pthread_mutex_lock(&ams->mutex); + Asset* asset = ams_get_asset(ams, key, index); + pthread_mutex_unlock(&ams->mutex); + + return asset; +} + // @todo implement defragment command to optimize memory layout since the memory layout will become fragmented over time +// @performance This function is VERY important, check if we can optimize it Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 elements = 1) { int64 free_asset = chunk_reserve(&ams->asset_memory, elements, true); @@ -224,7 +262,7 @@ Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 el chunk_reserve_index(&ams->asset_data_memory, free_asset, elements, true); asset->self = chunk_get_element(&ams->asset_data_memory, free_asset); - asset->size = elements; // Curcial for freeing + asset->size = elements; // Crucial for freeing asset->ram_size = (ams->asset_memory.chunk_size + ams->asset_data_memory.chunk_size) * elements; DEBUG_MEMORY_RESERVE((uint64) asset->self, elements * ams->asset_data_memory.chunk_size, 180); diff --git a/asset/AssetType.h b/asset/AssetType.h index 4bbbb83..163d0e3 100644 --- a/asset/AssetType.h +++ b/asset/AssetType.h @@ -10,6 +10,7 @@ #define TOS_ASSET_TYPE_H enum AssetType { + ASSET_TYPE_GENERAL, ASSET_TYPE_OBJ, ASSET_TYPE_TEXTURE, ASSET_TYPE_AUDIO, diff --git a/audio/Audio.cpp b/audio/Audio.cpp index ca6dfe7..22f00b3 100644 --- a/audio/Audio.cpp +++ b/audio/Audio.cpp @@ -13,9 +13,9 @@ #include "../memory/RingMemory.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif #include "Audio.h" @@ -32,92 +32,4 @@ void audio_from_file(RingMemory* ring, const char* path, Audio* audio) } } -inline -void audio_fill_buffer( - AudioSetting* setting, uint32 to_fill, const Audio* sound, - int16* buffer1, int32 buffer1_size, - int16* buffer2 = NULL, int32 buffer2_size = 0 -) -{ - uint32 limit = to_fill / setting->sample_size; - buffer1_size /= setting->sample_size; - buffer2_size /= setting->sample_size; - uint32 sample_count = sound->size / sound->sample_size; - f32 volume_scale = setting->volume * setting->volume; - const int16* data = (int16*) sound->data; - uint32 sample_index = setting->sample_index; - - uint32 i; - for (i = 0; i < limit && i < buffer1_size; ++i) { - if (sample_index >= sample_count) { - sample_index = 0; - - // @question why are we doing this? - setting->sample_index = 0; - } - - buffer1[i * 2] = (int16) (data[sample_index * 2] * volume_scale); - buffer1[i * 2 + 1] = (int16) (data[sample_index * 2 + 1] * volume_scale); - - ++sample_index; - } - - for (; i < limit && buffer2_size; ++i) { - if (sample_index >= sample_count) { - sample_index = 0; - - // @question why are we doing this? - setting->sample_index = 0; - } - - buffer2[i * 2] = (int16) (data[sample_index * 2] * volume_scale); - buffer2[i * 2 + 1] = (int16) (data[sample_index * 2 + 1] * volume_scale); - - ++sample_index; - } - - ++setting->sample_output; - setting->sample_buffer_size = to_fill; -} - -// @bug This is slower -/* -inline -void fill_audio_buffer(AudioSetting* audio_handle, uint32 to_fill, Audio* sound, int32 steps = 8) -{ - uint32 limit = to_fill / audio_handle->sample_size; - uint32 sample_count = sound->size / sound->sample_size; - uint32 sample_index = audio_handle->sample_index; - - // Copy all data until end - int32 offset; - memcpy( - audio_handle->buffer, - sound->data + audio_handle->sample_index * audio_handle->sample_size, - (offset = OMS_MIN(sample_count - sample_index, limit)) * audio_handle->sample_size - ); - - // If we are wrapping (play sound on loop) - // @todo we still need a setting if we actually want to do this - if (offset != limit) { - audio_handle->sample_index = 0; - memcpy( - audio_handle->buffer + offset * audio_handle->sample_size, - sound->data, - (limit - offset) * audio_handle->sample_size - ); - } - - simd_mult( - (int16 *) audio_handle->buffer, - (f32) audio_handle->volume / 100.0f, - (int16 *) audio_handle->buffer, limit * 2, - steps - ); - - ++audio_handle->sample_output; - audio_handle->sample_buffer_size = to_fill; -} -*/ - #endif diff --git a/audio/Audio.h b/audio/Audio.h index 8c30f9e..1ac446f 100644 --- a/audio/Audio.h +++ b/audio/Audio.h @@ -11,6 +11,7 @@ #include "../stdlib/Types.h" +// This represents the audio file struct Audio { // bits per sample // usually 48000 or 44100 diff --git a/audio/AudioMixer.h b/audio/AudioMixer.h new file mode 100644 index 0000000..9e8677e --- /dev/null +++ b/audio/AudioMixer.h @@ -0,0 +1,347 @@ +/** + * Jingga + * + * @copyright Jingga + * @license OMS License 2.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef TOS_AUDIO_MIXER_H +#define TOS_AUDIO_MIXER_H + +#include "../stdlib/Types.h" +#include "Audio.h" +#include "AudioSetting.h" +#include "../utils/MathUtils.h" +#include "../memory/ChunkMemory.h" +#include "../math/matrix/MatrixFloat32.h" + +#if DIRECT_SOUND + #include "../platform/win32/audio/DirectSound.h" +#elif XAUDIO2 + #include "../platform/win32/audio/XAudio2.h" +#endif + +enum AudioEffect { + AUDIO_EFFECT_NONE, + AUDIO_EFFECT_ECHO = 1, + AUDIO_EFFECT_REVERB = 2, + AUDIO_EFFECT_UNDERWATER = 4, + AUDIO_EFFECT_CAVE = 8, + AUDIO_EFFECT_LOWPASS = 16, + AUDIO_EFFECT_HIGHPASS = 32, + AUDIO_EFFECT_FLANGER = 64, + AUDIO_EFFECT_TREMOLO = 128, + AUDIO_EFFECT_DISTORTION = 256, + AUDIO_EFFECT_CHORUS = 512, + AUDIO_EFFECT_PITCH_SHIFT = 1024, + AUDIO_EFFECT_GRANULAR_DELAY = 2048, + AUDIO_EFFECT_FM = 4096, + AUDIO_EFFECT_STEREO_PANNING = 8192, + AUDIO_EFFECT_EASE_IN = 16384, + AUDIO_EFFECT_EASE_OUT = 32768, +}; + +struct AudioInstance { + int64 id; + AudioLocationSetting origin; + + uint32 audio_size; + byte* audio_data; +}; + +struct AudioMixer { + ChunkMemory audio_instances; + bool is_active; + + uint64 effect; + + AudioSetting settings; + AudioLocationSetting camera; + + #if DIRECT_SOUND + DirectSoundSetting api_setting; + #elif XAUDIO2 + XAudio2Setting api_setting; + #endif + + int16* buffer_temp; + + // @todo add mutex for locking and create threaded functions + // do we need a condition or semaphore? +}; + +void audio_mixer_add(AudioMixer* mixer, int64 id, Audio* audio, AudioLocationSetting* origin) +{ + int64 index = chunk_reserve(&mixer->audio_instances, 1); + if (index < 0) { + return; + } + + AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, index); + instance->id = id; + instance->audio_size = audio->size; + instance->audio_data = audio->data; + + if (origin) { + memcpy(&instance->origin, origin, sizeof(AudioLocationSetting)); + } +} + +void audio_mixer_add_unique(AudioMixer* mixer, int64 id, Audio* audio, AudioLocationSetting* origin) +{ + for (int32 i = 0; i < mixer->audio_instances.count; ++i) { + // @performance We are not really utilizing chunk memory. + // Maybe a simple array would be better + // Or we need to use more chunk functions / maybe even create a chunk_iterate() function? + AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i); + if (instance->id == id) { + return; + } + } + + audio_mixer_add(mixer, id, audio, origin); +} + +void audio_mixer_remove(AudioMixer* mixer, int64 id) +{ + for (int32 i = 0; i < mixer->audio_instances.count; ++i) { + AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i); + if (instance->id == id) { + instance->id = 0; + chunk_free_element(&mixer->audio_instances, i); + + // No return, since we want to remove all instances + } + } +} + +void apply_echo(int16* buffer, uint16 buffer_size, f32 delay, f32 feedback, int32 sample_rate) { + int32 delay_samples = (int32) (delay * sample_rate); + for (int32 i = delay_samples; i < buffer_size; ++i) { + buffer[i] += (int16) (buffer[i - delay_samples] * feedback); + } +} + +void apply_reverb(int16* buffer, uint16 buffer_size, f32 intensity) { + intensity *= 0.5f; + for (int32 i = 1; i < buffer_size; ++i) { + buffer[i] += (int16) (buffer[i - 1] * intensity); // Simple reverb with decay + } +} + +void apply_cave(int16* buffer, uint16 buffer_size, int32 sample_rate) { + f32 echo_delay = 0.1f; // Echo delay in seconds + f32 feedback = 0.3f; // Echo feedback level + apply_echo(buffer, buffer_size, echo_delay, feedback, sample_rate); + apply_reverb(buffer, buffer_size, 0.4f); // Add mild reverb +} + +void apply_underwater(int16* buffer, uint16 buffer_size) { + for (int32 i = 0; i < buffer_size; ++i) { + buffer[i] = (int16) sinf(buffer[i] * 0.5f); // Dampen + distortion + } +} + +void apply_flanger(int16* buffer, uint16 buffer_size, f32 rate, f32 depth, int32 sample_rate) { + int32 delay_samples = (int32) (depth * sample_rate); + f32 temp = OMS_TWO_PI * rate / sample_rate; + + for (int32 i = 0; i < buffer_size; ++i) { + int32 delay = (int32) (delay_samples * (0.5f + 0.5f * sinf(i * temp))); + if (i >= delay) { + buffer[i] += (int16) (buffer[i - delay] * 0.5f); + } + } +} + +void apply_tremolo(int16* buffer, uint16 buffer_size, f32 rate, f32 depth, int32 sample_rate) { + f32 temp = OMS_TWO_PI * rate / sample_rate; + f32 temp2 = (1.0f - depth) + depth; + + for (int32 i = 0; i < buffer_size; ++i) { + f32 mod = temp2 * (0.5f + 0.5f * sinf(i * temp)); + buffer[i] = (int16) (buffer[i] * mod); + } +} + +void apply_distortion(int16* buffer, uint16 buffer_size, f32 gain) { + for (int32 i = 0; i < buffer_size; ++i) { + buffer[i] = (int16) tanh(buffer[i] * gain); + } +} + +void apply_chorus(int16* buffer, uint16 buffer_size, f32 rate, f32 depth, int32 sample_rate) { + f32 temp = OMS_TWO_PI * rate / sample_rate; + + int32 max_delay = (int32) (depth * sample_rate); + for (int32 i = 0; i < buffer_size; ++i) { + int32 delay = (int32) (max_delay * (0.5f + 0.5f * sinf(i * temp))); + if (i >= delay) { + buffer[i] += (int16) (buffer[i - delay] * 0.5f); + } + } +} + +void apply_pitch_shift(int16* buffer, uint16 buffer_size, f32 pitch_factor) { + for (int32 i = 0; i < buffer_size; ++i) { + buffer[i] = (int16) (buffer[i] * pitch_factor); + } +} + +void apply_granular_delay(int16* buffer, uint16 buffer_size, f32 delay, f32 granularity, int32 sample_rate) { + int32 delay_samples = (int32) (delay * sample_rate); + int32 limit = (int32) (granularity * sample_rate); + + for (int32 i = 0; i < buffer_size; ++i) { + if (i % limit == 0 && i >= delay_samples) { + buffer[i] += (int16) (buffer[i - delay_samples] * 0.6f); + } + } +} + +void apply_frequency_modulation(int16* buffer, uint16 buffer_size, f32 mod_freq, f32 mod_depth, int32 sample_rate) { + f32 temp = OMS_TWO_PI * mod_freq / sample_rate; + for (int32 i = 0; i < buffer_size; ++i) { + buffer[i] = (int16) (buffer[i] * sinf(i * temp) * mod_depth); + } +} + +void apply_stereo_panning(int16* buffer, int32 buffer_size, f32 pan) { + f32 left_gain = 1.0f - pan; + f32 right_gain = pan; + + for (int32 i = 0; i < buffer_size; ++i) { + buffer[i] = (int16) (buffer[i] * left_gain); + buffer[i + 1] = (int16) (buffer[i + 1] * right_gain); + } +} + +void apply_highpass(int16* buffer, uint16 buffer_size, f32 cutoff, int32 sample_rate) { + f32 rc = 1.0f / (OMS_TWO_PI * cutoff); + f32 dt = 1.0f / sample_rate; + f32 alpha = rc / (rc + dt); + f32 previous = buffer[0]; + f32 previous_output = buffer[0]; + + for (int32 i = 1; i < buffer_size; ++i) { + f32 current = buffer[i]; + buffer[i] = (int16) (alpha * (previous_output + current - previous)); + previous = current; + previous_output = buffer[i]; + } +} + + +void apply_lowpass(int16* buffer, uint16 buffer_size, f32 cutoff, int32 sample_rate) { + f32 rc = 1.0f / (OMS_TWO_PI * cutoff); + f32 dt = 1.0f / sample_rate; + f32 alpha = dt / (rc + dt); + f32 previous = buffer[0]; + + for (int32 i = 1; i < buffer_size; ++i) { + buffer[i] = (int16) (previous + alpha * (buffer[i] - previous)); + previous = buffer[i]; + } +} + +void audio_mixer_mix(AudioMixer *mixer) { + uint16 limit = (uint16) (mixer->settings.sample_buffer_size / mixer->settings.sample_size); + + for (int32 i = 0; i < mixer->audio_instances.count; ++i) { + AudioInstance* sound = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i); + if (sound->id == 0) { + continue; + } + + // Compute the vector from the player to the sound's origin + v3_f32 to_sound; + vec3_sub(&to_sound, &sound->origin.audio_location, &mixer->camera.audio_location); + f32 distance = vec3_length(&to_sound); + f32 distance_attenuation = OMS_MAX(0.0f, 1.0f - (distance / 50.0f)); + vec3_normalize(&to_sound); + f32 alignment = vec3_dot(&mixer->camera.audio_lookat, &to_sound); + f32 directional_attenuation = OMS_MAX(0.0f, alignment); + f32 total_attenuation = distance_attenuation * directional_attenuation; + + // Temporary buffer for effects processing + // @performance If there are situations where only one file exists in the mixer that should be played we could directly write to + // the output buffer improving the performance. Some of those mixers are: music, cinematic, ui + // Careful, NOT voice since we will probably manually layer them according to their position? + for (int32 j = 0; j < limit; ++j) { + // @todo if repeat handle here + + mixer->buffer_temp[j] = (int16) (sound->audio_data[j * 2] * mixer->settings.master_volume * total_attenuation); + mixer->buffer_temp[j + 1] = (int16) (sound->audio_data[j * 2 + 2] * mixer->settings.master_volume * total_attenuation); + + // @performance Some adjustments could be made right here the question is if this is faster. + // Probably depends on how likely the adjustment is to happen. + } + + // Apply effects based on sound's effect type + if (mixer->effect) { + if (mixer->effect & AUDIO_EFFECT_ECHO) { + apply_echo(mixer->buffer_temp, limit, 0.2f, 0.4f, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_REVERB) { + apply_reverb(mixer->buffer_temp, limit, 0.3f); + } + + if (mixer->effect & AUDIO_EFFECT_UNDERWATER) { + apply_underwater(mixer->buffer_temp, limit); + } + + if (mixer->effect & AUDIO_EFFECT_CAVE) { + apply_cave(mixer->buffer_temp, limit, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_LOWPASS) { + apply_lowpass(mixer->buffer_temp, limit, 500.0f, mixer->settings.sample_rate); // Cutoff frequency 500 + } + + if (mixer->effect & AUDIO_EFFECT_HIGHPASS) { + apply_highpass(mixer->buffer_temp, limit, 2000.0f, mixer->settings.sample_rate); // Cutoff frequency 2 kHz + } + + if (mixer->effect & AUDIO_EFFECT_FLANGER) { + apply_flanger(mixer->buffer_temp, limit, 0.25f, 0.005f, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_TREMOLO) { + apply_tremolo(mixer->buffer_temp, limit, 5.0f, 0.8f, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_DISTORTION) { + apply_distortion(mixer->buffer_temp, limit, 10.0f); + } + + if (mixer->effect & AUDIO_EFFECT_CHORUS) { + apply_chorus(mixer->buffer_temp, limit, 0.25f, 0.005f, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_PITCH_SHIFT) { + apply_pitch_shift(mixer->buffer_temp, limit, 1.2f); // Slight pitch increase + } + + if (mixer->effect & AUDIO_EFFECT_GRANULAR_DELAY) { + apply_granular_delay(mixer->buffer_temp, limit, 0.1f, 0.2f, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_FM) { + apply_frequency_modulation(mixer->buffer_temp, limit, 2.0f, 0.5f, mixer->settings.sample_rate); + } + + if (mixer->effect & AUDIO_EFFECT_STEREO_PANNING) { + apply_stereo_panning(mixer->buffer_temp, limit, 0.5f); + } + } + + // Add the processed sound to the output buffer + for (int32 j = 0; j < limit; j++) { + mixer->settings.buffer[j] += mixer->buffer_temp[j]; + } + } +} + +#endif \ No newline at end of file diff --git a/audio/AudioSetting.h b/audio/AudioSetting.h index 7e59f3f..ba32693 100644 --- a/audio/AudioSetting.h +++ b/audio/AudioSetting.h @@ -10,11 +10,22 @@ #define TOS_AUDIO_SETTING_H #include "../stdlib/Types.h" +#include "../math/matrix/MatrixFloat32.h" #define SOUND_API_DIRECT_SOUND 0 #define SOUND_API_XAUDIO2 1 struct AudioSetting { + // position in the audio data + // WARNING: not the byte position, but the index based on the sample size + uint32 sample_index; + + // @todo add more settings e.g. repeat etc + + uint32 latency; + + f32 master_volume; + // bits per sample // usually 48000 or 44100 uint32 sample_rate; @@ -24,18 +35,9 @@ struct AudioSetting { // usually 2 * 16 = 4 uint32 sample_size; - // position in the audio data - // WARNING: not the byte position, but the index based on the sample size - uint32 sample_index; - - uint32 latency; - // how often has the audio_play been called (required for xaudio) uint32 sample_output; - // 0.0 - 1.0 - f32 volume; - // max buffer content/size uint32 buffer_size; @@ -44,10 +46,20 @@ struct AudioSetting { uint32 sample_buffer_size; int16* buffer; - bool is_playing = false; byte type = SOUND_API_DIRECT_SOUND; - - // @todo add more settings e.g. repeat etc }; +struct AudioLocationSetting { + v3_f32 audio_location; + v3_f32 audio_lookat; + v3_f32 audio_moveto; + f32 audio_velocity; +}; + +inline +void update_audio_location_settings(AudioLocationSetting* settings) +{ + vec3_normalize(&settings->audio_lookat); +} + #endif diff --git a/environment/Globe.h b/environment/Globe.h index 7ba2f50..c95a107 100644 --- a/environment/Globe.h +++ b/environment/Globe.h @@ -2,30 +2,53 @@ #define TOS_ENVIRONMENT_GLOBE_H #include "../stdlib/Types.h" +#include "../utils/MathUtils.h" #include -// day_length in seconds +/** + * Calculates the time of the day + * + * @param f64 time Can be any reference value, as long as it increments every second + * @param f32 day_length The length of a day in seconds + * + * @return f32 A day time ranging from 0.0 to 1.0 + */ f32 time_of_day(f64 time, f32 day_length) { f64 t = time; - t = t / day_length; - t = t - (int32) t; + t /= day_length; + t -= (int32) t; return (f32) t; } -// time can either be actual time in seconds OR relative time to progra start +/** + * Calculates a daylight intensity based on the time of the day + * + * @param f64 time Can be any reference value, as long as it increments every second + * @param f32 day_length The length of a day in seconds + * + * @return f32 Light intensity + */ f32 daylight_get(f64 time, f32 day_length) { f32 timer = time_of_day(time, day_length); - if (timer < 0.5) { - f32 t = (timer - 0.25) * 100; + if (timer < 0.5f) { + f32 t = (timer - 0.25f) * 100.0f; - return 1 / (1 + powf(2, -t)); + return 1.0f / (1.0f + powf(2.0f, -t)); } - f32 t = (timer - 0.85) * 100; + f32 t = (timer - 0.85f) * 100.0f; - return 1 - 1 / (1 + powf(2, -t)); + return 1.0f - 1.0f / (1.0f + powf(2.0f, -t)); +} + +void sun_position_get(v3_f32* sun_position, f32 time, f32 radius, f32 day_duration) { + f32 angle = (time / day_duration) * 2.0f * OMS_PI; + + sun_position->x = radius * cosf(angle); + sun_position->y = radius * sinf(angle); + sun_position->z = 0.0f; } #endif \ No newline at end of file diff --git a/font/Font.h b/font/Font.h index 3a5b447..40d4d3f 100644 --- a/font/Font.h +++ b/font/Font.h @@ -8,9 +8,9 @@ #include "../stdlib/simd/SIMD_I32.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif struct GlyphMetrics { diff --git a/gpuapi/opengl/OpenglUtils.h b/gpuapi/opengl/OpenglUtils.h index 96dde47..c4571b6 100644 --- a/gpuapi/opengl/OpenglUtils.h +++ b/gpuapi/opengl/OpenglUtils.h @@ -19,10 +19,13 @@ #include "../RenderUtils.h" #include "Opengl.h" -#ifdef _WIN32 +#if _WIN32 #include - #include "../../platform/win32/UtilsWin32.h" + #include "../../platform/win32/FileUtils.cpp" #include "../../platform/win32/Window.h" +#elif __linux__ + #include "../../platform/linux/FileUtils.cpp" + #include "../../platform/linux/Window.h" #endif inline diff --git a/image/Image.cpp b/image/Image.cpp index 219c5a1..3614359 100644 --- a/image/Image.cpp +++ b/image/Image.cpp @@ -13,9 +13,9 @@ #include "../memory/RingMemory.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif #include "Image.h" diff --git a/localization/Language.h b/localization/Language.h index 4e58997..9208e08 100644 --- a/localization/Language.h +++ b/localization/Language.h @@ -5,9 +5,9 @@ #include "../memory/RingMemory.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif struct Language { diff --git a/math/matrix/MatrixFloat32.h b/math/matrix/MatrixFloat32.h index 12a8fe6..cb14c56 100644 --- a/math/matrix/MatrixFloat32.h +++ b/math/matrix/MatrixFloat32.h @@ -95,6 +95,18 @@ f32 vec2_dot(const v2_f32* a, const v2_f32* b) { return a->x * b->x + a->y * b->y; } +inline +f32 vec3_length(f32 x, f32 y, f32 z) +{ + return sqrtf(x * x + y * y + z * z); +} + +inline +f32 vec3_length(v3_f32* vec) +{ + return sqrtf(vec->x * vec->x + vec->y * vec->y + vec->z * vec->z); +} + inline void vec3_normalize(f32* __restrict x, f32* __restrict y, f32* __restrict z) { diff --git a/memory/ChunkMemory.h b/memory/ChunkMemory.h index a8a85d1..7344d78 100644 --- a/memory/ChunkMemory.h +++ b/memory/ChunkMemory.h @@ -23,6 +23,12 @@ #include "../platform/linux/Allocator.h" #endif +#if _WIN32 + #include "../platform/win32/threading/Thread.h" +#elif __linux__ + #include "../platform/linux/threading/Thread.h" +#endif + struct ChunkMemory { byte* memory; @@ -37,6 +43,23 @@ struct ChunkMemory { uint64* free; }; +struct ThreadedChunkMemory { + byte* memory; + + uint64 count; + uint64 size; + uint64 chunk_size; + int64 last_pos; + int32 alignment; + + // length = count + // free describes which locations are used and which are free + uint64* free; + + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + inline void chunk_alloc(ChunkMemory* buf, uint64 count, uint64 chunk_size, int32 alignment = 64) { diff --git a/memory/Queue.h b/memory/Queue.h index 6329f59..531426a 100644 --- a/memory/Queue.h +++ b/memory/Queue.h @@ -15,21 +15,21 @@ typedef RingMemory Queue; inline -void queue_alloc(Queue* queue, uint64 size, uint32 element_size, int32 alignment = 64) +void queue_alloc(Queue* queue, uint64 element_count, uint32 element_size, int32 alignment = 64) { - ring_alloc(queue, size, alignment); + ring_alloc(queue, element_count * element_size, alignment); } inline -void queue_init(Queue* queue, BufferMemory* buf, uint64 size, uint32 element_size, int32 alignment = 64) +void queue_init(Queue* queue, BufferMemory* buf, uint64 element_count, uint32 element_size, int32 alignment = 64) { - ring_init(queue, buf, size, alignment); + ring_init(queue, buf, element_count * element_size, alignment); } inline -void queue_init(Queue* queue, byte* buf, uint64 size, uint32 element_size, int32 alignment = 64) +void queue_init(Queue* queue, byte* buf, uint64 element_count, uint32 element_size, int32 alignment = 64) { - ring_init(queue, buf, size, alignment); + ring_init(queue, buf, element_count * element_size, alignment); } inline diff --git a/memory/RingMemory.h b/memory/RingMemory.h index 95d049d..5a47056 100644 --- a/memory/RingMemory.h +++ b/memory/RingMemory.h @@ -45,14 +45,6 @@ struct RingMemory { uint64 size; int32 alignment; int32 element_alignment; - - // We support both conditional locking and semaphore locking - // These values are not initialized and not used unless you use the queue - pthread_mutex_t mutex; - pthread_cond_t cond; - - sem_t empty; - sem_t full; }; // @bug alignment should also include the end point, not just the start diff --git a/memory/ThreadedQueue.h b/memory/ThreadedQueue.h index 3fedb6c..3f04542 100644 --- a/memory/ThreadedQueue.h +++ b/memory/ThreadedQueue.h @@ -9,6 +9,8 @@ #ifndef TOS_MEMORY_QUEUE_H #define TOS_MEMORY_QUEUE_H +#include "../stdlib/Types.h" +#include "../utils/Utils.h" #include "RingMemory.h" #if _WIN32 @@ -19,12 +21,35 @@ #include "../platform/linux/threading/Semaphore.h" #endif -typedef RingMemory ThreadedQueue; +struct ThreadedQueue { + byte* memory; + byte* end; + + byte* head; + + // This variable is usually only used by single producer/consumer code mostly found in threads. + // One thread inserts elements -> updates head + // The other thread reads elements -> updates tail + // This code itself doesn't change this variable + byte* tail; + + uint64 size; + int32 alignment; + int32 element_alignment; + + // We support both conditional locking and semaphore locking + // These values are not initialized and not used unless you use the queue + pthread_mutex_t mutex; + pthread_cond_t cond; + + sem_t empty; + sem_t full; +}; inline -void threaded_queue_alloc(ThreadedQueue* queue, uint64 size, uint32 element_count, int32 alignment = 64) +void threaded_queue_alloc(ThreadedQueue* queue, uint32 element_count, uint64 element_size, int32 alignment = 64) { - ring_alloc(queue, size, alignment); + ring_alloc((RingMemory *) queue, element_count * element_size, alignment); pthread_mutex_init(&queue->mutex, NULL); pthread_cond_init(&queue->cond, NULL); @@ -34,9 +59,9 @@ void threaded_queue_alloc(ThreadedQueue* queue, uint64 size, uint32 element_coun } inline -void threaded_queue_init(ThreadedQueue* queue, BufferMemory* buf, uint64 size, uint32 element_count, int32 alignment = 64) +void threaded_queue_init(ThreadedQueue* queue, BufferMemory* buf, uint32 element_count, uint64 element_size, int32 alignment = 64) { - ring_init(queue, buf, size, alignment); + ring_init((RingMemory *) queue, buf, element_count * element_size, alignment); pthread_mutex_init(&queue->mutex, NULL); pthread_cond_init(&queue->cond, NULL); @@ -46,9 +71,9 @@ void threaded_queue_init(ThreadedQueue* queue, BufferMemory* buf, uint64 size, u } inline -void threaded_queue_init(ThreadedQueue* queue, byte* buf, uint64 size, uint32 element_count, int32 alignment = 64) +void threaded_queue_init(ThreadedQueue* queue, byte* buf, uint32 element_count, uint64 element_size, int32 alignment = 64) { - ring_init(queue, buf, size, alignment); + ring_init((RingMemory *) queue, buf, element_count * element_size, alignment); pthread_mutex_init(&queue->mutex, NULL); pthread_cond_init(&queue->cond, NULL); @@ -60,24 +85,56 @@ void threaded_queue_init(ThreadedQueue* queue, byte* buf, uint64 size, uint32 el inline void threaded_queue_free(ThreadedQueue* queue) { - ring_free(queue); + ring_free((RingMemory *) queue); sem_destroy(&queue->empty); sem_destroy(&queue->full); pthread_mutex_destroy(&queue->mutex); pthread_cond_destroy(&queue->cond); } +// @todo Create enqueue_unique +inline +void threaded_queue_enqueue_unique(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0) +{ + ASSERT_SIMPLE((uint64_t) data % 4 == 0); + pthread_mutex_lock(&queue->mutex); + + byte* tail = queue->tail; + while (tail != queue->tail) { + ASSERT_SIMPLE((uint64_t) tail % 4 == 0); + + // @performance we could probably make this faster since we don't need to compare the entire range + if (is_equal_aligned(tail, data, size) == 0) { + pthread_mutex_unlock(&queue->mutex); + + return; + } + + ring_move_pointer((RingMemory *) queue, &tail, size, aligned); + } + + while (!ring_commit_safe((RingMemory *) queue, size)) { + pthread_cond_wait(&queue->cond, &queue->mutex); + } + + byte* mem = ring_get_memory((RingMemory *) queue, size, aligned); + memcpy(mem, data, size); + + pthread_cond_signal(&queue->cond); + pthread_mutex_unlock(&queue->mutex); +} + // Conditional Lock inline void threaded_queue_enqueue(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0) { pthread_mutex_lock(&queue->mutex); - while (!ring_commit_safe(queue, size)) { + while (!ring_commit_safe((RingMemory *) queue, size)) { pthread_cond_wait(&queue->cond, &queue->mutex); } - byte* mem = ring_get_memory(queue, size, aligned); + byte* mem = ring_get_memory((RingMemory *) queue, size, aligned); memcpy(mem, data, size); pthread_cond_signal(&queue->cond); @@ -89,11 +146,11 @@ byte* threaded_queue_enqueue_start(ThreadedQueue* queue, uint64 size, byte align { pthread_mutex_lock(&queue->mutex); - while (!ring_commit_safe(queue, size, aligned)) { + while (!ring_commit_safe((RingMemory *) queue, size, aligned)) { pthread_cond_wait(&queue->cond, &queue->mutex); } - return ring_get_memory(queue, size, aligned); + return ring_get_memory((RingMemory *) queue, size, aligned); } inline @@ -104,7 +161,7 @@ void threaded_queue_enqueue_end(ThreadedQueue* queue) } inline -byte* threaded_queue_dequeue(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0) +void threaded_queue_dequeue(ThreadedQueue* queue, byte* data, uint64 size, byte aligned = 0) { pthread_mutex_lock(&queue->mutex); @@ -113,7 +170,7 @@ byte* threaded_queue_dequeue(ThreadedQueue* queue, byte* data, uint64 size, byte } memcpy(data, queue->tail, size); - ring_move_pointer(queue, &queue->tail, size, aligned); + ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned); pthread_cond_signal(&queue->cond); pthread_mutex_unlock(&queue->mutex); @@ -134,7 +191,7 @@ byte* threaded_queue_dequeue_start(ThreadedQueue* queue) inline void threaded_queue_dequeue_end(ThreadedQueue* queue, uint64 size, byte aligned = 0) { - ring_move_pointer(queue, &queue->tail, size, aligned); + ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned); pthread_cond_signal(&queue->cond); pthread_mutex_unlock(&queue->mutex); @@ -147,7 +204,7 @@ void threaded_queue_enqueue_sem(ThreadedQueue* queue, byte* data, uint64 size, b sem_wait(&queue->empty); pthread_mutex_lock(&queue->mutex); - byte* mem = ring_get_memory(queue, size, aligned); + byte* mem = ring_get_memory((RingMemory *) queue, size, aligned); memcpy(mem, data, size); pthread_mutex_unlock(&queue->mutex); @@ -160,7 +217,7 @@ byte* threaded_queue_enqueue_start_sem(ThreadedQueue* queue, uint64 size, byte a sem_wait(&queue->empty); pthread_mutex_lock(&queue->mutex); - return ring_get_memory(queue, size, aligned); + return ring_get_memory((RingMemory *) queue, size, aligned); } inline @@ -177,7 +234,7 @@ byte* threaded_queue_dequeue_sem(ThreadedQueue* queue, byte* data, uint64 size, pthread_mutex_lock(&queue->mutex); memcpy(data, queue->tail, size); - ring_move_pointer(queue, &queue->tail, size, aligned); + ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned); pthread_mutex_unlock(&queue->mutex); sem_post(&queue->empty); @@ -195,7 +252,7 @@ byte* threaded_queue_dequeue_start_sem(ThreadedQueue* queue) inline void threaded_queue_dequeue_end_sem(ThreadedQueue* queue, uint64 size, byte aligned = 0) { - ring_move_pointer(queue, &queue->tail, size, aligned); + ring_move_pointer((RingMemory *) queue, &queue->tail, size, aligned); pthread_mutex_unlock(&queue->mutex); sem_post(&queue->empty); diff --git a/module/ModuleManager.h b/module/ModuleManager.h index 12a7c8d..a86d42d 100644 --- a/module/ModuleManager.h +++ b/module/ModuleManager.h @@ -2,9 +2,13 @@ #define TOS_MODULE_MANAGER_H #include "Module.h" +#include "../memory/RingMemory.h" #if _WIN32 + #include "../platform/win32/FileUtils.cpp" #include "../platform/win32/UtilsWin32.h" +#elif __linux__ + #include "../platform/linux/FileUtils.cpp" #endif struct ModuleManager { diff --git a/object/Animation.h b/object/Animation.h index 776fb73..5fa4db6 100644 --- a/object/Animation.h +++ b/object/Animation.h @@ -13,9 +13,9 @@ #include "../memory/RingMemory.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif struct Skeleton { diff --git a/object/Hitbox.h b/object/Hitbox.h index 00e882c..592adc7 100644 --- a/object/Hitbox.h +++ b/object/Hitbox.h @@ -13,9 +13,9 @@ #include "../memory/RingMemory.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif struct Hitbox { diff --git a/object/Material.h b/object/Material.h index 565d093..f966fc0 100644 --- a/object/Material.h +++ b/object/Material.h @@ -13,9 +13,9 @@ #include "../memory/RingMemory.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif struct Material { diff --git a/object/Mesh.h b/object/Mesh.h index fcafffd..e7a62a5 100644 --- a/object/Mesh.h +++ b/object/Mesh.h @@ -13,9 +13,9 @@ #include "../stdlib/Types.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif #include "../memory/RingMemory.h" @@ -27,7 +27,7 @@ // @todo how to handle different objects and groups? // maybe make a mesh hold other meshes? -// @todo handle vertice arrays where for example no texture coordinates are defined/used +// @todo handle vertices arrays where for example no texture coordinates are defined/used struct Mesh { byte* data; // memory owner that subdevides into the pointers below @@ -406,7 +406,7 @@ void mesh_from_file_txt( mesh->vertex_count = face_count * 3; - // Every face consists of 3 vertecies -> *3 + // Every face consists of 3 vertices -> *3 for (int32 i = 0; i < face_count * 3; ++i) { const f32* vertex_pos = &vertices[faces[i * face_size] * 3]; memcpy( @@ -415,7 +415,7 @@ void mesh_from_file_txt( 3 * sizeof(f32) ); - // Normals come before texture coordinates since we most likely need them more than texture (e.g. pre-copmuting of shadows on cpu etc.) + // Normals come before texture coordinates since we most likely need them more than texture (e.g. pre-computing of shadows on cpu etc.) if (mesh->vertex_type & VERTEX_TYPE_NORMAL) { const f32* normal_pos = &normals[faces[i * face_size + 1 + normal_offset] * 3]; @@ -486,7 +486,7 @@ int32 mesh_from_file( #if !_WIN32 && !__LITTLE_ENDIAN mesh->version = endian_swap(mesh->version); mesh->vertex_type = endian_swap(mesh->vertex_type); - mesh->verted_count = endian_swap(mesh->verted_count); + mesh->vertex_count = endian_swap(mesh->vertex_count); #endif int32 vertex_size = 0; diff --git a/platform/linux/FileUtils.cpp b/platform/linux/FileUtils.cpp new file mode 100644 index 0000000..d8f8594 --- /dev/null +++ b/platform/linux/FileUtils.cpp @@ -0,0 +1,259 @@ +/** + * Jingga + * + * @copyright Jingga + * @license OMS License 2.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef TOS_PLATFORM_LINUX_FILE_UTILS_C +#define TOS_PLATFORM_LINUX_FILE_UTILS_C + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../stdlib/Types.h" +#include "../../utils/Utils.h" +#include "../../utils/TestUtils.h" +#include "../../memory/RingMemory.h" + +#ifndef MAX_PATH + #define MAX_PATH PATH_MAX +#endif + +typedef int32 FileHandler; + +inline +void relative_to_absolute(const char* rel, char* path) +{ + char self_path[MAX_PATH]; + int32 self_path_length = readlink("/proc/self/exe", self_path, MAX_PATH - 1); + if (self_path_length == -1) { + return; + } + + const char* temp = rel; + if (temp[0] == '.' && temp[1] == '/') { + temp += 2; + } + + char* last = self_path + self_path_length; + while (*last != '/' && self_path_length > 0) { + --last; + --self_path_length; + } + + ++self_path_length; + + memcpy(path, self_path, self_path_length); + strcpy(path + self_path_length, temp); +} + +// @todo implement relative path support, similar to UtilsWin32 +inline +uint64 file_size(const char* filename) { + struct stat buffer; + if (stat(filename, &buffer) != 0) { + return 0; + } + + return buffer.st_size; +} + +inline +uint64 file_last_modified(const char* filename) +{ + struct stat buffer; + stat(filename, &buffer); + + return (uint64) buffer.st_mtime; +} + +inline +FileHandler file_append_handle(const char* path) { + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = open(full_path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); + } else { + fp = open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); + } + + return fp; +} + +inline +bool file_exists(const char* path) { + struct stat buffer; + const char* full_path = path; + char abs_path[MAX_PATH]; + + if (*path == '.') { + relative_to_absolute(path, abs_path); + full_path = abs_path; + } + + return stat(full_path, &buffer) == 0; +} + +inline +bool file_copy(const char* src, const char* dst) { + char src_full_path[MAX_PATH]; + char dst_full_path[MAX_PATH]; + + if (*src == '.') { + relative_to_absolute(src, src_full_path); + src = src_full_path; + } + + if (*dst == '.') { + relative_to_absolute(dst, dst_full_path); + dst = dst_full_path; + } + + int32 src_fd = open(src, O_RDONLY); + if (src_fd < 0) { + return false; + } + + int32 dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (dst_fd < 0) { + close(src_fd); + + return false; + } + + char buffer[8192]; + ssize_t bytes_read, bytes_written; + bool success = true; + + while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) { + bytes_written = write(dst_fd, buffer, bytes_read); + if (bytes_written != bytes_read) { + success = false; + break; + } + } + + if (bytes_read < 0) { + success = false; + } + + close(src_fd); + close(dst_fd); + + return success; +} + +inline +void file_read(const char* path, FileBody* file, RingMemory* ring) { + char full_path[MAX_PATH]; + const char* abs_path = path; + + if (*path == '.') { + relative_to_absolute(path, full_path); + abs_path = full_path; + } + + int32 fp = open(abs_path, O_RDONLY); + if (fp < 0) { + file->size = 0; + file->content = NULL; + + return; + } + + struct stat file_stat; + if (fstat(fp, &file_stat) == -1) { + close(fp); + file->size = 0; + file->content = NULL; + + return; + } + + if (file_stat.st_size > MAX_UINT32) { + close(fp); + file->size = 0; + file->content = NULL; + + return; + } + + if (ring != NULL) { + file->content = ring_get_memory(ring, file_stat.st_size); + } + + ssize_t bytes_read = read(fp, file->content, file_stat.st_size); + if (bytes_read != file_stat.st_size) { + close(fp); + file->content = NULL; + file->size = 0; + + return; + } + + file->content[bytes_read] = '\0'; + file->size = bytes_read; + + close(fp); +} + +inline +bool file_write(const char* path, const FileBody* file) { + int32 fd; + char full_path[PATH_MAX]; + + if (*path == '.') { + relative_to_absolute(path, full_path); + path = full_path; + } + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + return false; + } + + ASSERT_SIMPLE(file->size < MAX_UINT32); + + ssize_t written = write(fd, file->content, file->size); + if (written < 0 || (size_t) written != file->size) { + close(fd); + return false; + } + + if (close(fd) < 0) { + return false; + } + + return true; +} + +inline +void close_handle(FileHandler fp) +{ + close(fp); +} + +inline +void self_path(char* path) { + size_t len = readlink("/proc/self/exe", path, PATH_MAX); + if (len > 0) { + path[len] = '\0'; + } else { + path[0] = '\0'; + } +} + + + +#endif \ No newline at end of file diff --git a/platform/linux/Library.h b/platform/linux/Library.h index a77fc68..46e1286 100644 --- a/platform/linux/Library.h +++ b/platform/linux/Library.h @@ -43,7 +43,7 @@ bool library_load(Library* lib) memcpy(dst + dst_len - (sizeof(".so") - 1), "_temp", sizeof("_temp") - 1); memcpy(dst + dst_len - (sizeof(".so") - 1) + (sizeof("_temp") - 1), ".so", sizeof(".so")); - lib->last_load = last_modified(src); + lib->last_load = file_last_modified(src); file_copy(src, dst); #endif diff --git a/platform/linux/UtilsLinux.h b/platform/linux/UtilsLinux.h index 5e16392..1c7fa0c 100644 --- a/platform/linux/UtilsLinux.h +++ b/platform/linux/UtilsLinux.h @@ -11,26 +11,12 @@ #include #include -#include -#include -#include -#include -#include #include -#include -#include #include "../../stdlib/Types.h" -#include "../../utils/Utils.h" -#include "../../utils/TestUtils.h" -#include "../../memory/RingMemory.h" -#ifndef MAX_PATH - #define MAX_PATH PATH_MAX -#endif - -int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) { - int result; +int32 sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) { + int32 result; va_list args; if (buffer == NULL || format == NULL || sizeOfBuffer == 0) { @@ -38,12 +24,10 @@ int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) { } va_start(args, format); - result = vsnprintf(buffer, sizeOfBuffer, format, args); - va_end(args); - if (result >= 0 && (size_t)result >= sizeOfBuffer) { + if (result >= 0 && (size_t) result >= sizeOfBuffer) { buffer[sizeOfBuffer - 1] = '\0'; return 80; } @@ -52,228 +36,4 @@ int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...) { return result; } -inline -void relative_to_absolute(const char* rel, char* path) -{ - char self_path[MAX_PATH]; - int32 self_path_length = readlink("/proc/self/exe", self_path, MAX_PATH - 1); - if (self_path_length == -1) { - return; - } - - const char* temp = rel; - if (temp[0] == '.' && temp[1] == '/') { - temp += 2; - } - - char* last = self_path + self_path_length; - while (*last != '/' && self_path_length > 0) { - --last; - --self_path_length; - } - - ++self_path_length; - - memcpy(path, self_path, self_path_length); - strcpy(path + self_path_length, temp); -} - -// @todo implement relative path support, similar to UtilsWin32 -inline -uint64 file_size(const char* filename) { - struct stat buffer; - if (stat(filename, &buffer) != 0) { - return 0; - } - - return buffer.st_size; -} - -inline -uint64 last_modified(const char* filename) -{ - struct stat buffer; - stat(filename, &buffer); - - return (uint64) buffer.st_mtime; -} - -inline -int32 get_append_handle(const char* path) { - int32 fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = open(full_path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); - } else { - fp = open(path, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); - } - - return fp; -} - -inline -bool file_exists(const char* path) { - struct stat buffer; - const char* full_path = path; - char abs_path[MAX_PATH]; - - if (*path == '.') { - relative_to_absolute(path, abs_path); - full_path = abs_path; - } - - return stat(full_path, &buffer) == 0; -} - -inline -bool file_copy(const char* src, const char* dst) { - char src_full_path[MAX_PATH]; - char dst_full_path[MAX_PATH]; - - if (*src == '.') { - relative_to_absolute(src, src_full_path); - src = src_full_path; - } - - if (*dst == '.') { - relative_to_absolute(dst, dst_full_path); - dst = dst_full_path; - } - - int32 src_fd = open(src, O_RDONLY); - if (src_fd < 0) { - return false; - } - - int32 dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (dst_fd < 0) { - close(src_fd); - - return false; - } - - char buffer[8192]; - ssize_t bytes_read, bytes_written; - bool success = true; - - while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) { - bytes_written = write(dst_fd, buffer, bytes_read); - if (bytes_written != bytes_read) { - success = false; - break; - } - } - - if (bytes_read < 0) { - success = false; - } - - close(src_fd); - close(dst_fd); - - return success; -} - -inline -void file_read(const char* path, FileBody* file, RingMemory* ring) { - char full_path[MAX_PATH]; - const char* abs_path = path; - - if (*path == '.') { - relative_to_absolute(path, full_path); - abs_path = full_path; - } - - int32 fp = open(abs_path, O_RDONLY); - if (fp < 0) { - file->size = 0; - file->content = NULL; - - return; - } - - struct stat file_stat; - if (fstat(fp, &file_stat) == -1) { - close(fp); - file->size = 0; - file->content = NULL; - - return; - } - - if (file_stat.st_size > MAX_INT32) { - close(fp); - file->size = 0; - file->content = NULL; - - return; - } - - if (ring != NULL) { - file->content = ring_get_memory(ring, file_stat.st_size); - } - - ssize_t bytes_read = read(fp, file->content, file_stat.st_size); - if (bytes_read != file_stat.st_size) { - close(fp); - file->content = NULL; - file->size = 0; - - return; - } - - file->content[bytes_read] = '\0'; - file->size = bytes_read; - - close(fp); -} - -inline -bool file_write(const char* path, const FileBody* file) { - int32 fd; - char full_path[PATH_MAX]; - - if (*path == '.') { - relative_to_absolute(path, full_path); - path = full_path; - } - - fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (fd < 0) { - return false; - } - - ASSERT_SIMPLE(file->size < MAX_INT32); - - ssize_t written = write(fd, file->content, file->size); - if (written < 0 || (size_t) written != file->size) { - close(fd); - return false; - } - - if (close(fd) < 0) { - return false; - } - - return true; -} - -inline -void close_handle(int32 fp) -{ - close(fp); -} - -inline -void self_path(char* path) { - size_t len = readlink("/proc/self/exe", path, PATH_MAX); - if (len > 0) { - path[len] = '\0'; - } else { - path[0] = '\0'; - } -} - #endif \ No newline at end of file diff --git a/platform/linux/threading/Atomic.h b/platform/linux/threading/Atomic.h index 3eb79a5..d959284 100644 --- a/platform/linux/threading/Atomic.h +++ b/platform/linux/threading/Atomic.h @@ -9,7 +9,7 @@ #ifndef TOS_PLATFORM_LINUX_THREADING_ATOMIC_H #define TOS_PLATFORM_LINUX_THREADING_ATOMIC_H -#include +#include #include "../../../stdlib/Types.h" inline @@ -18,12 +18,30 @@ void atomic_set(volatile int32* value, int32 new_value) __atomic_store_n(value, new_value, __ATOMIC_SEQ_CST); } +inline +void atomic_get(volatile byte* value, byte data[16]) +{ + __atomic_store((volatile __int128 *) value, (__int128 *) data, __ATOMIC_SEQ_CST); +} + inline int32 atomic_get(volatile int32* value) { return __atomic_load_n((int32 *) value, __ATOMIC_SEQ_CST); } +inline +int64 atomic_get(volatile int64* value) +{ + return __atomic_load_n((int64 *) value, __ATOMIC_SEQ_CST); +} + +inline +void atomic_get(volatile byte* value, byte data[16]) +{ + __atomic_load((volatile __int128 *) value, (__int128 *) data, __ATOMIC_SEQ_CST); +} + inline void atomic_increment(volatile int32* value) { __atomic_fetch_add(value, 1, __ATOMIC_SEQ_CST); @@ -44,4 +62,20 @@ int32 atomic_subtract(volatile int32* value, int32 decrement) { return __atomic_fetch_sub(value, decrement, __ATOMIC_SEQ_CST); } +inline +int32 atomic_compare_exchange_weak(volatile int32* value, int32* expected, int32 desired) { + __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return *expected; +} + +inline +int32 atomic_fetch_add(volatile int32* value, int32 operand) { + return __atomic_fetch_add(value, operand, __ATOMIC_SEQ_CST); +} + +inline +int32 atomic_fetch_sub(volatile int32* value, int32 operand) { + return __atomic_fetch_sub(value, operand, __ATOMIC_SEQ_CST); +} + #endif \ No newline at end of file diff --git a/platform/linux/threading/Semaphore.h b/platform/linux/threading/Semaphore.h index b0bf453..24e5e88 100644 --- a/platform/linux/threading/Semaphore.h +++ b/platform/linux/threading/Semaphore.h @@ -9,7 +9,6 @@ #ifndef TOS_PLATFORM_LINUX_THREADING_SEMAPHORE_H #define TOS_PLATFORM_LINUX_THREADING_SEMAPHORE_H -#include #include #endif \ No newline at end of file diff --git a/platform/linux/threading/Spinlock.h b/platform/linux/threading/Spinlock.h index 09e91fc..c3b6f7f 100644 --- a/platform/linux/threading/Spinlock.h +++ b/platform/linux/threading/Spinlock.h @@ -9,7 +9,7 @@ #ifndef TOS_PLATFORM_LINUX_THREADING_SPINLOCK_H #define TOS_PLATFORM_LINUX_THREADING_SPINLOCK_H -#include +#include #include "../../../stdlib/Types.h" typedef volatile int32 spinlock32; diff --git a/platform/linux/threading/Thread.h b/platform/linux/threading/Thread.h index a1201f2..0dd9d74 100644 --- a/platform/linux/threading/Thread.h +++ b/platform/linux/threading/Thread.h @@ -9,11 +9,144 @@ #ifndef TOS_PLATFORM_LINUX_THREADING_THREAD_H #define TOS_PLATFORM_LINUX_THREADING_THREAD_H +#include +#include #include +#include +#include +#include +#include + #include "../../../stdlib/Types.h" +#include "../Allocator.h" #include "ThreadDefines.h" -uint32 pcthread_get_num_procs() +int32 pthread_create(pthread_t* thread, void *(*start_routine)(void *), void* arg) { + thread->stack = platform_alloc_aligned(1 * MEGABYTE, 64); + if (!thread->stack) { + return -1; + } + + thread->stack = clone( + (int32 (*)(void *)) start_routine, + stack + 1 * MEGABYTE, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD, + arg + ); + + if (thread->stack == -1) { + platform_aligned_free(thread->stack); + + return -1; + } + + return 0; +} + +int32 pthread_join(pthread_t thread, void** retval) { + int32 status; + if (waitpid(thread->id, &status, 0) == -1) { + platform_aligned_free(thread->stack); + + return -1; + } + + if (retval) { + *retval = (void *) status; + } + + platform_aligned_free(thread->stack); + + return 0; +} + +int32 pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t*) { + atomic_set(mutex, 0); + + return 0; +} + +int32 pthread_mutex_lock(pthread_mutex_t* mutex) { + int32 expected = 0; + while (!atomic_compare_exchange_weak(mutex, &expected, 1)) { + syscall(SYS_futex, mutex, FUTEX_WAIT, 1, NULL, NULL, 0); + expected = 0; + } + + return 0; +} + +int32 pthread_mutex_unlock(pthread_mutex_t* mutex) { + atomic_set(mutex, 0); + syscall(SYS_futex, mutex, FUTEX_WAKE, 1, NULL, NULL, 0); + + return 0; +} + +int32 pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t*) { + atomic_set(cond, 0); + + return 0; +} + +int32 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) { + pthread_mutex_unlock(mutex); + syscall(SYS_futex, cond, FUTEX_WAIT, atomic_get(cond), NULL, NULL, 0); + pthread_mutex_lock(mutex); + + return 0; +} + +int32 pthread_cond_signal(pthread_cond_t* cond) { + atomic_fetch_add(cond, 1); + syscall(SYS_futex, cond, FUTEX_WAKE, 1, NULL, NULL, 0); + + return 0; +} + +int32 pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t*) { + atomic_set((int64 *) &rwlock->readers, 0); + //atomic_set(&rwlock->writer, 0); + + return 0; +} + +int32 pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) { + while (atomic_get(&rwlock->writer)) {} + + atomic_fetch_add(&rwlock->readers, 1); + + return 0; +} + +int32 pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) { + while (!atomic_compare_exchange_weak(&rwlock->writer, 0, 1)) {} + + return 0; +} + +int32 pthread_rwlock_unlock(pthread_rwlock_t* rwlock) { + if (atomic_get(&rwlock->writer)) { + atomic_set(&rwlock->writer, 0); + } else { + atomic_fetch_sub(&rwlock->readers, 1); + } + + return 0; +} + +int32 pthread_detach(pthread_t) { + // For detached threads, the OS will clean up automatically. We do nothing here. + // Optionally, mark this thread as detached in your data structure if tracking threads. + return 0; +} + +int32 pthread_rwlock_destroy(pthread_rwlock_t*) +{ + return 0; +} + +uint32 pthread_get_num_procs() { return (uint32) sysconf(_SC_NPROCESSORS_ONLN); } diff --git a/platform/linux/threading/ThreadDefines.h b/platform/linux/threading/ThreadDefines.h index 9a92fa2..3d4f1a7 100644 --- a/platform/linux/threading/ThreadDefines.h +++ b/platform/linux/threading/ThreadDefines.h @@ -9,11 +9,39 @@ #ifndef TOS_PLATFORM_LINUX_THREADING_THREAD_DEFINES_H #define TOS_PLATFORM_LINUX_THREADING_THREAD_DEFINES_H -#include -#include +// #include +// #include + +#include "../../../stdlib/Types.h" typedef void* (*ThreadJobFunc)(void*); - #define THREAD_RETURN void* +typedef volatile int32 pthread_mutex_t; +typedef volatile int32 pthread_cond_t; + +struct pthread_t { + int32 id; + void* stack; +}; + +/* +struct pthread_mutex_t { + volatile int32 lock; +}; + +struct pthread_cond_t { + volatile int32 futex; +}; +*/ + +struct pthread_rwlock_t { + volatile int32 readers; + volatile int32 writer; +}; + +typedef void pthread_mutexattr_t; +typedef void pthread_condattr_t; +typedef void pthread_rwlockattr_t; + #endif \ No newline at end of file diff --git a/platform/win32/FileUtils.cpp b/platform/win32/FileUtils.cpp new file mode 100644 index 0000000..25feead --- /dev/null +++ b/platform/win32/FileUtils.cpp @@ -0,0 +1,801 @@ +/** + * Jingga + * + * @copyright Jingga + * @license OMS License 2.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef TOS_PLATFORM_WIN32_FILE_UTILS_C +#define TOS_PLATFORM_WIN32_FILE_UTILS_C + +#include +#include +#include + +#ifdef _MSC_VER + #include +#endif + +#include "../../stdlib/Types.h" +#include "../../utils/Utils.h" +#include "../../utils/TestUtils.h" +#include "../../memory/RingMemory.h" + +typedef HANDLE FileHandler; + +struct FileBodyAsync { + // doesn't include null termination (same as strlen) + uint64 size; + byte* content; + OVERLAPPED ov; +}; + +// @todo Consider to implement directly mapped files (CreateFileMapping) for certain files (e.g. map data or texture data, ...) + +inline +void relative_to_absolute(const char* rel, char* path) +{ + char self_path[MAX_PATH]; + int32 self_path_length = GetModuleFileNameA(NULL, self_path, MAX_PATH); + if (self_path_length == 0) { + return; + } + + const char* temp = rel; + if (temp[0] == '.' && temp[1] == '/') { + temp += 2; + } + + char* last = self_path + self_path_length; + while (*last != '\\' && self_path_length > 0) { + --last; + --self_path_length; + } + + ++self_path_length; + + memcpy(path, self_path, self_path_length); + strcpy(path + self_path_length, temp); +} + +inline uint64 +file_size(const char* path) +{ + // @performance Profile against fseek strategy + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return 0; + } + + LARGE_INTEGER size; + if (!GetFileSizeEx(fp, &size)) { + CloseHandle(fp); + } + + CloseHandle(fp); + + return size.QuadPart; +} + +inline +bool file_exists(const char* path) +{ + DWORD file_attr; + + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + file_attr = GetFileAttributesA(full_path); + } else { + file_attr = GetFileAttributesA(path); + } + + return file_attr != INVALID_FILE_ATTRIBUTES; +} + +inline void +file_read(const char* path, FileBody* file, RingMemory* ring = NULL) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + file->size = 0; + return; + } + + LARGE_INTEGER size; + if (!GetFileSizeEx(fp, &size)) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + if (ring != NULL) { + file->content = ring_get_memory(ring, size.QuadPart); + } + + DWORD bytes; + ASSERT_SIMPLE(size.QuadPart < MAX_UINT32); + if (!ReadFile(fp, file->content, (uint32) size.QuadPart, &bytes, NULL)) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + CloseHandle(fp); + + file->content[bytes] = '\0'; + file->size = size.QuadPart; +} + +inline +void file_read(const char* path, FileBody* file, uint64 offset, uint64 length = MAX_UINT64, RingMemory* ring = NULL) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + file->size = 0; + + return; + } + + LARGE_INTEGER size; + if (!GetFileSizeEx(fp, &size)) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + // Ensure the offset and length do not exceed the file size + uint64 file_size = size.QuadPart; + if (offset >= file_size) { + file->size = 0; + file->content = NULL; + CloseHandle(fp); + + return; + } + + // Adjust the length to read so that it does not exceed the file size + uint64 read_length = OMS_MIN(length, file_size - offset); + + if (ring != NULL) { + file->content = ring_get_memory(ring, read_length); + } + + // Move the file pointer to the offset position + LARGE_INTEGER li; + li.QuadPart = offset; + if (SetFilePointerEx(fp, li, NULL, FILE_BEGIN) == 0) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + DWORD bytes; + ASSERT_SIMPLE(read_length < MAX_UINT32); + if (!ReadFile(fp, file->content, (uint32) read_length, &bytes, NULL)) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + CloseHandle(fp); + + file->content[bytes] = '\0'; + file->size = bytes; +} + +inline +void file_read(FileHandler fp, FileBody* file, uint64 offset = 0, uint64 length = MAX_UINT64, RingMemory* ring = NULL) +{ + LARGE_INTEGER size; + if (!GetFileSizeEx(fp, &size)) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + // Ensure the offset and length do not exceed the file size + uint64 file_size = size.QuadPart; + if (offset >= file_size) { + file->size = 0; + file->content = NULL; + CloseHandle(fp); + + return; + } + + // Adjust the length to read so that it does not exceed the file size + uint64 read_length = OMS_MIN(length, file_size - offset); + + if (ring != NULL) { + file->content = ring_get_memory(ring, read_length); + } + + // Move the file pointer to the offset position + LARGE_INTEGER li; + li.QuadPart = offset; + if (SetFilePointerEx(fp, li, NULL, FILE_BEGIN) == 0) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + DWORD bytes; + ASSERT_SIMPLE(read_length < MAX_UINT32); + if (!ReadFile(fp, file->content, (uint32) read_length, &bytes, NULL)) { + CloseHandle(fp); + file->content = NULL; + + return; + } + + CloseHandle(fp); + + file->content[bytes] = '\0'; + file->size = bytes; +} + +inline uint64 +file_read_struct(const char* path, void* file, uint32 size) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return 0; + } + + LARGE_INTEGER fsize; + if (!GetFileSizeEx(fp, &fsize)) { + CloseHandle(fp); + + return 0; + } + + DWORD read; + ASSERT_SIMPLE(fsize.QuadPart > size); + if (!ReadFile(fp, file, (uint32) size, &read, NULL)) { + CloseHandle(fp); + + return 0; + } + + CloseHandle(fp); + + return read; +} + +inline bool +file_write(const char* path, const FileBody* file) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return false; + } + + DWORD written; + DWORD length = (DWORD) file->size; + ASSERT_SIMPLE(file->size < MAX_UINT32); + if (!WriteFile(fp, file->content, length, &written, NULL)) { + CloseHandle(fp); + return false; + } + + CloseHandle(fp); + + return true; +} + +inline bool +file_write_struct(const char* path, const void* file, uint32 size) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + DWORD written; + ASSERT_SIMPLE(size < MAX_UINT32); + if (!WriteFile(fp, file, size, &written, NULL)) { + CloseHandle(fp); + return false; + } + + CloseHandle(fp); + + return true; +} + +inline void +file_copy(const char* src, const char* dst) +{ + if (*src == '.') { + char src_full_path[MAX_PATH]; + relative_to_absolute(src, src_full_path); + + if (*dst == '.') { + char dst_full_path[MAX_PATH]; + relative_to_absolute(dst, dst_full_path); + + CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst_full_path, false); + } else { + CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst, false); + } + } else if (*dst == '.') { + char dst_full_path[MAX_PATH]; + relative_to_absolute(dst, dst_full_path); + + CopyFileA((LPCSTR) src, (LPCSTR) dst_full_path, false); + } else { + CopyFileA((LPCSTR) src, (LPCSTR) dst, false); + } +} + +inline +void close_handle(FileHandler fp) +{ + CloseHandle(fp); +} + +inline +HANDLE file_append_handle(const char* path) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + FILE_APPEND_DATA, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + FILE_APPEND_DATA, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return NULL; + } + + return fp; +} + +inline +bool file_read_async( + FileHandler fp, + FileBodyAsync* file, + uint64_t offset = 0, + uint64_t length = MAXUINT64, + RingMemory* ring = NULL +) { + LARGE_INTEGER size; + if (!GetFileSizeEx(fp, &size)) { + CloseHandle(fp); + file->content = NULL; + + return false; + } + + // Ensure the offset and length do not exceed the file size + uint64_t file_size = size.QuadPart; + if (offset >= file_size) { + file->size = 0; + file->content = NULL; + CloseHandle(fp); + + return false; + } + + // Adjust the length to read so that it does not exceed the file size + uint64 read_length = OMS_MIN(length, file_size - offset); + + // Allocate memory for the content + if (ring != NULL) { + file->content = ring_get_memory(ring, read_length); + } + + if (!file->content) { + CloseHandle(fp); + + return false; + } + + file->ov.Offset = (DWORD)(offset & 0xFFFFFFFF); + file->ov.OffsetHigh = (DWORD)(offset >> 32); + + // Auto-reset event + file->ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + DWORD bytes_read = 0; + ASSERT_SIMPLE(read_length < MAXDWORD); + if (!ReadFile(fp, file->content, (DWORD) read_length, &bytes_read, &file->ov)) { + DWORD error = GetLastError(); + if (error != ERROR_IO_PENDING) { + CloseHandle(fp); + free(file->content); + file->content = NULL; + CloseHandle(&file->ov.hEvent); + + return false; + } + } + + file->size = read_length; + return true; +} + +inline +FileHandler file_read_handle(const char* path) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return NULL; + } + + return fp; +} + +inline +FileHandler file_read_async_handle(const char* path) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return NULL; + } + + return fp; +} + +bool file_append(const char* path, const char* file) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + FILE_APPEND_DATA, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + FILE_APPEND_DATA, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return false; + } + + DWORD written; + DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data? + ASSERT_SIMPLE(length < MAX_UINT32); + if (!WriteFile(fp, file, length, &written, NULL)) { + CloseHandle(fp); + return false; + } + + CloseHandle(fp); + return true; +} + +inline bool +file_append(FileHandler fp, const char* file) +{ + if (fp == INVALID_HANDLE_VALUE) { + return false; + } + + DWORD written; + DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data? + ASSERT_SIMPLE(length < MAX_UINT32); + + if (!WriteFile(fp, file, length, &written, NULL)) { + CloseHandle(fp); + return false; + } + + CloseHandle(fp); + return true; +} + +inline bool +file_append(FileHandler fp, const char* file, size_t length) +{ + if (fp == INVALID_HANDLE_VALUE) { + return false; + } + + DWORD written; + if (!WriteFile(fp, file, (uint32) length, &written, NULL)) { + CloseHandle(fp); + return false; + } + + return true; +} + +inline bool +file_append(const char* path, const FileBody* file) +{ + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = CreateFileA((LPCSTR) full_path, + FILE_APPEND_DATA, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } else { + fp = CreateFileA((LPCSTR) path, + FILE_APPEND_DATA, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + } + + if (fp == INVALID_HANDLE_VALUE) { + return false; + } + + DWORD bytes; + DWORD length = (DWORD) file->size; + ASSERT_SIMPLE(file->size < MAX_UINT32); + if (!WriteFile(fp, file->content, length, &bytes, NULL)) { + CloseHandle(fp); + return false; + } + + CloseHandle(fp); + return true; +} + +inline +uint64 file_last_modified(const char* path) +{ + WIN32_FIND_DATA find_data; + + FileHandler fp; + if (*path == '.') { + char full_path[MAX_PATH]; + relative_to_absolute(path, full_path); + + fp = FindFirstFileA(full_path, (LPWIN32_FIND_DATAA) &find_data); + } else { + fp = FindFirstFileA(path, (LPWIN32_FIND_DATAA) &find_data); + } + + FILETIME modified = {}; + if(fp != INVALID_HANDLE_VALUE) { + modified = find_data.ftLastWriteTime; + FindClose(fp); + } + + ULARGE_INTEGER ull; + ull.LowPart = modified.dwLowDateTime; + ull.HighPart = modified.dwHighDateTime; + + return ull.QuadPart; +} + +inline void self_path(char* path) +{ + GetModuleFileNameA(NULL, (LPSTR) path, MAX_PATH); +} + +#endif \ No newline at end of file diff --git a/platform/win32/Library.h b/platform/win32/Library.h index f05eca9..86e3e41 100644 --- a/platform/win32/Library.h +++ b/platform/win32/Library.h @@ -14,7 +14,7 @@ #include #include "../../stdlib/Types.h" -#include "UtilsWin32.h" +#include "FileUtils.cpp" #include "../../utils/StringUtils.h" #include "../Library.h" @@ -41,7 +41,7 @@ bool library_load(Library* lib) memcpy(dst + dst_len - (sizeof(".dll") - 1), "_temp", sizeof(".temp") - 1); memcpy(dst + dst_len - (sizeof(".dll") - 1) + (sizeof(".temp") - 1), ".dll", sizeof(".dll")); - lib->last_load = last_modified(src); + lib->last_load = file_last_modified(src); file_copy(src, dst); #endif diff --git a/platform/win32/TimeUtils.h b/platform/win32/TimeUtils.h new file mode 100644 index 0000000..933e53e --- /dev/null +++ b/platform/win32/TimeUtils.h @@ -0,0 +1,91 @@ +/** + * Jingga + * + * @copyright Jingga + * @license OMS License 2.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef TOS_PLATFORM_WIN32_TIME_UTILS_H +#define TOS_PLATFORM_WIN32_TIME_UTILS_H + +#include +#include +#include +#include "../../log/Debug.cpp" + +void usleep(uint64 microseconds) +{ + if ((microseconds % 1000) == 0) { + Sleep((DWORD) (microseconds / 1000)); + return; + } + + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + LARGE_INTEGER start, end; + QueryPerformanceCounter(&start); + long long target = start.QuadPart + (microseconds * frequency.QuadPart) / 1000000; + + do { + QueryPerformanceCounter(&end); + } while (end.QuadPart < target); +} + +inline +time_t system_time() +{ + SYSTEMTIME systemTime; + FILETIME fileTime; + ULARGE_INTEGER largeInt; + + GetLocalTime(&systemTime); + SystemTimeToFileTime(&systemTime, &fileTime); + + // Convert FILETIME to a 64-bit integer + largeInt.LowPart = fileTime.dwLowDateTime; + largeInt.HighPart = fileTime.dwHighDateTime; + + return ((time_t) (largeInt.QuadPart / 10000000ULL)) - ((time_t) 11644473600ULL); +} + +// doesn't return clock time, only to return time since program start +// -> can be used for profiling +inline +uint64 time_mu() +{ + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + + return (counter.QuadPart * 1000000) / debug_container->performance_count_frequency; +} + +inline +time_t unix_epoch_s() +{ + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + + ULARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + time_t seconds_since_epoch = (li.QuadPart - 116444736000000000ULL) / 10000000ULL; + + return seconds_since_epoch; +} + +static DWORD timespec_to_ms(const timespec* abstime) +{ + if (abstime == NULL) { + return INFINITE; + } + + time_t seconds_since_epoch = unix_epoch_s(); + DWORD t = (DWORD) (((abstime->tv_sec - seconds_since_epoch) * 1000) + (abstime->tv_nsec / 1000000)); + + return t < 0 ? 1 : t; +} + +#endif \ No newline at end of file diff --git a/platform/win32/UtilsWin32.h b/platform/win32/UtilsWin32.h index 47c39db..845897b 100644 --- a/platform/win32/UtilsWin32.h +++ b/platform/win32/UtilsWin32.h @@ -12,580 +12,7 @@ #include #include #include -#include - -#ifdef _MSC_VER - #include -#endif - -#include "../../stdlib/Types.h" -#include "../../utils/Utils.h" -#include "../../utils/TestUtils.h" -#include "../../memory/RingMemory.h" -#include "../../log/Debug.cpp" #define strtok_r strtok_s -void usleep(uint64 microseconds) -{ - if ((microseconds % 1000) == 0) { - Sleep((DWORD) (microseconds / 1000)); - return; - } - - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - - LARGE_INTEGER start, end; - QueryPerformanceCounter(&start); - long long target = start.QuadPart + (microseconds * frequency.QuadPart) / 1000000; - - do { - QueryPerformanceCounter(&end); - } while (end.QuadPart < target); -} - -inline -time_t system_time() -{ - SYSTEMTIME systemTime; - FILETIME fileTime; - ULARGE_INTEGER largeInt; - - GetLocalTime(&systemTime); - SystemTimeToFileTime(&systemTime, &fileTime); - - // Convert FILETIME to a 64-bit integer - largeInt.LowPart = fileTime.dwLowDateTime; - largeInt.HighPart = fileTime.dwHighDateTime; - - return ((time_t) (largeInt.QuadPart / 10000000ULL)) - ((time_t) 11644473600ULL); -} - -// doesn't return clock time, only to return time since program start -// -> can be used for profiling -inline -uint64 time_mu() -{ - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - - return (counter.QuadPart * 1000000) / debug_container->performance_count_frequency; -} - -inline -time_t unix_epoch_s() -{ - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - - ULARGE_INTEGER li; - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - - time_t seconds_since_epoch = (li.QuadPart - 116444736000000000ULL) / 10000000ULL; - - return seconds_since_epoch; -} - -// @todo Consider to implement directly mapped files (CreateFileMapping) for certain files (e.g. map data or texture data, ...) - -inline -void relative_to_absolute(const char* rel, char* path) -{ - char self_path[MAX_PATH]; - int32 self_path_length = GetModuleFileNameA(NULL, self_path, MAX_PATH); - if (self_path_length == 0) { - return; - } - - const char* temp = rel; - if (temp[0] == '.' && temp[1] == '/') { - temp += 2; - } - - char* last = self_path + self_path_length; - while (*last != '\\' && self_path_length > 0) { - --last; - --self_path_length; - } - - ++self_path_length; - - memcpy(path, self_path, self_path_length); - strcpy(path + self_path_length, temp); -} - -// @todo Move file code to FileUtils.h -inline uint64 -file_size(const char* path) -{ - // @performance Profile against fseek strategy - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - return 0; - } - - LARGE_INTEGER size; - if (!GetFileSizeEx(fp, &size)) { - CloseHandle(fp); - } - - CloseHandle(fp); - - return size.QuadPart; -} - -inline -bool file_exists(const char* path) -{ - DWORD file_attr; - - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - file_attr = GetFileAttributesA(full_path); - } else { - file_attr = GetFileAttributesA(path); - } - - return file_attr != INVALID_FILE_ATTRIBUTES; -} - -inline void -file_read(const char* path, FileBody* file, RingMemory* ring = NULL) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - file->size = 0; - return; - } - - LARGE_INTEGER size; - if (!GetFileSizeEx(fp, &size)) { - CloseHandle(fp); - file->content = NULL; - - return; - } - - if (ring != NULL) { - file->content = ring_get_memory(ring, size.QuadPart); - } - - DWORD bytes; - ASSERT_SIMPLE(size.QuadPart < MAX_INT32); - if (!ReadFile(fp, file->content, (uint32) size.QuadPart, &bytes, NULL)) { - CloseHandle(fp); - file->content = NULL; - - return; - } - - CloseHandle(fp); - - file->content[bytes] = '\0'; - file->size = size.QuadPart; -} - -inline uint64 -file_read_struct(const char* path, void* file, uint32 size) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - return 0; - } - - LARGE_INTEGER fsize; - if (!GetFileSizeEx(fp, &fsize)) { - CloseHandle(fp); - - return 0; - } - - DWORD read; - ASSERT_SIMPLE(fsize.QuadPart > size); - if (!ReadFile(fp, file, (uint32) size, &read, NULL)) { - CloseHandle(fp); - - return 0; - } - - CloseHandle(fp); - - return read; -} - -inline bool -file_write(const char* path, const FileBody* file) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - return false; - } - - DWORD written; - DWORD length = (DWORD) file->size; - ASSERT_SIMPLE(file->size < MAX_INT32); - if (!WriteFile(fp, file->content, length, &written, NULL)) { - CloseHandle(fp); - return false; - } - - CloseHandle(fp); - - return true; -} - -inline bool -file_write_struct(const char* path, const void* file, uint32 size) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - DWORD written; - ASSERT_SIMPLE(size < MAX_INT32); - if (!WriteFile(fp, file, size, &written, NULL)) { - CloseHandle(fp); - return false; - } - - CloseHandle(fp); - - return true; -} - -inline void -file_copy(const char* src, const char* dst) -{ - if (*src == '.') { - char src_full_path[MAX_PATH]; - relative_to_absolute(src, src_full_path); - - if (*dst == '.') { - char dst_full_path[MAX_PATH]; - relative_to_absolute(dst, dst_full_path); - - CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst_full_path, false); - } else { - CopyFileA((LPCSTR) src_full_path, (LPCSTR) dst, false); - } - } else if (*dst == '.') { - char dst_full_path[MAX_PATH]; - relative_to_absolute(dst, dst_full_path); - - CopyFileA((LPCSTR) src, (LPCSTR) dst_full_path, false); - } else { - CopyFileA((LPCSTR) src, (LPCSTR) dst, false); - } -} - -inline -void close_handle(HANDLE fp) -{ - CloseHandle(fp); -} - -inline -HANDLE get_append_handle(const char* path) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - FILE_APPEND_DATA, - 0, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - FILE_APPEND_DATA, - 0, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - return NULL; - } - - return fp; -} - -bool file_append(const char* path, const char* file) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - FILE_APPEND_DATA, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - FILE_APPEND_DATA, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - return false; - } - - DWORD written; - DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data? - ASSERT_SIMPLE(length < MAX_INT32); - if (!WriteFile(fp, file, length, &written, NULL)) { - CloseHandle(fp); - return false; - } - - CloseHandle(fp); - return true; -} - -inline bool -file_append(HANDLE fp, const char* file) -{ - if (fp == INVALID_HANDLE_VALUE) { - return false; - } - - DWORD written; - DWORD length = (DWORD) strlen(file); // @question WHY is WriteFile not supporting larger data? - ASSERT_SIMPLE(length < MAX_INT32); - - if (!WriteFile(fp, file, length, &written, NULL)) { - CloseHandle(fp); - return false; - } - - CloseHandle(fp); - return true; -} - -inline bool -file_append(HANDLE fp, const char* file, size_t length) -{ - if (fp == INVALID_HANDLE_VALUE) { - return false; - } - - DWORD written; - if (!WriteFile(fp, file, (uint32) length, &written, NULL)) { - CloseHandle(fp); - return false; - } - - return true; -} - -inline bool -file_append(const char* path, const FileBody* file) -{ - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = CreateFileA((LPCSTR) full_path, - FILE_APPEND_DATA, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } else { - fp = CreateFileA((LPCSTR) path, - FILE_APPEND_DATA, - 0, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - } - - if (fp == INVALID_HANDLE_VALUE) { - return false; - } - - DWORD bytes; - DWORD length = (DWORD) file->size; - ASSERT_SIMPLE(file->size < MAX_INT32); - if (!WriteFile(fp, file->content, length, &bytes, NULL)) { - CloseHandle(fp); - return false; - } - - CloseHandle(fp); - return true; -} - -inline -uint64 last_modified(const char* path) -{ - WIN32_FIND_DATA find_data; - - HANDLE fp; - if (*path == '.') { - char full_path[MAX_PATH]; - relative_to_absolute(path, full_path); - - fp = FindFirstFileA(full_path, (LPWIN32_FIND_DATAA) &find_data); - } else { - fp = FindFirstFileA(path, (LPWIN32_FIND_DATAA) &find_data); - } - - FILETIME modified = {}; - if(fp != INVALID_HANDLE_VALUE) { - modified = find_data.ftLastWriteTime; - FindClose(fp); - } - - ULARGE_INTEGER ull; - ull.LowPart = modified.dwLowDateTime; - ull.HighPart = modified.dwHighDateTime; - - return ull.QuadPart; -} - -inline void self_path(char* path) -{ - GetModuleFileNameA(NULL, (LPSTR) path, MAX_PATH); -} - #endif \ No newline at end of file diff --git a/platform/win32/audio/DirectSound.h b/platform/win32/audio/DirectSound.h index 73fba9c..aec1ae4 100644 --- a/platform/win32/audio/DirectSound.h +++ b/platform/win32/audio/DirectSound.h @@ -106,7 +106,6 @@ void audio_play(AudioSetting* setting, DirectSoundSetting* api_setting) } api_setting->secondary_buffer->Play(0, 0, DSBPLAY_LOOPING); - setting->is_playing = true; } inline @@ -116,7 +115,6 @@ void audio_stop(AudioSetting* setting, DirectSoundSetting* api_setting) { } api_setting->secondary_buffer->Stop(); - setting->is_playing = false; } inline @@ -155,7 +153,8 @@ uint32 audio_buffer_fillable(const AudioSetting* setting, const DirectSoundSetti DWORD target_cursor = (player_cursor + (setting->latency * setting->sample_size)) % setting->buffer_size; if (bytes_to_lock == player_cursor) { - bytes_to_write = setting->is_playing ? 0 : setting->buffer_size; + // @bug What if just started? + bytes_to_write = 0; } else if (bytes_to_lock > target_cursor) { bytes_to_write = setting->buffer_size - bytes_to_lock; bytes_to_write += target_cursor; @@ -173,10 +172,6 @@ void audio_play_buffer(AudioSetting* setting, DirectSoundSetting* api_setting) return; } - if (!setting->is_playing) { - audio_play(setting, api_setting); - } - void *region1; DWORD region1_size; @@ -192,9 +187,6 @@ void audio_play_buffer(AudioSetting* setting, DirectSoundSetting* api_setting) 0 ); - // @performance why are we copying again, we already created our buffer, now we have to copy it again?! - // Ideally we should have already copied it into the correct final one, no? - // We should probably provide a audio_buffer_fill function, that does this -> we could remove one whole memcopy memcpy( (void *) region1, (void *) setting->buffer, @@ -215,43 +207,4 @@ void audio_play_buffer(AudioSetting* setting, DirectSoundSetting* api_setting) setting->sample_buffer_size = 0; } -// Basically the same as audio_play_buffer but by using this we can avoid one copy -// The only reason we have audio_play_buffer is that there might be situations where this is not possible -inline -void audio_fill_play_buffer(AudioSetting* setting, uint32 to_fill, Audio* sound, DirectSoundSetting* api_setting) -{ - setting->sample_buffer_size = to_fill; - - if (setting->sample_buffer_size == 0) { - return; - } - - if (!setting->is_playing) { - audio_play(setting, api_setting); - } - - void *region1; - DWORD region1_size; - - void *region2; - DWORD region2_size; - - DWORD bytes_to_lock = (setting->sample_index * setting->sample_size) % setting->buffer_size; - - api_setting->secondary_buffer->Lock( - bytes_to_lock, setting->sample_buffer_size, - ®ion1, ®ion1_size, - ®ion2, ®ion2_size, - 0 - ); - - audio_fill_buffer(setting, to_fill, sound, (int16 *) region1, (int32) region1_size, (int16 *) region2, (int32) region2_size); - - api_setting->secondary_buffer->Unlock(region1, region1_size, region2, region2_size); - - setting->sample_index += setting->sample_buffer_size / setting->sample_size; - setting->sample_buffer_size = 0; -} - - #endif \ No newline at end of file diff --git a/platform/win32/audio/XAudio2.h b/platform/win32/audio/XAudio2.h index 92cf2f5..8f56f60 100644 --- a/platform/win32/audio/XAudio2.h +++ b/platform/win32/audio/XAudio2.h @@ -117,7 +117,6 @@ void audio_play(AudioSetting* setting, XAudio2Setting* api_setting) { } api_setting->source_voice->Start(0, XAUDIO2_COMMIT_NOW); - setting->is_playing = true; if (setting->sample_index > 1) { setting->sample_index = 0; @@ -131,7 +130,6 @@ void audio_stop(AudioSetting* setting, XAudio2Setting* api_setting) { } api_setting->source_voice->Stop(0, XAUDIO2_COMMIT_NOW); - setting->is_playing = false; } inline @@ -156,10 +154,6 @@ void audio_free(AudioSetting* setting, XAudio2Setting* api_setting) if (api_setting->internal_buffer[1].pAudioData) { free((void *) api_setting->internal_buffer[1].pAudioData); } - - if (setting->buffer) { - free((void *) setting->buffer); - } } /** @@ -190,15 +184,8 @@ void audio_play_buffer(AudioSetting* setting, XAudio2Setting* api_setting) { return; } - if (!setting->is_playing) { - audio_play(setting, api_setting); - } - uint32 idx = setting->sample_output % 2; - // @performance why are we copying again, we already created our buffer, now we have to copy it again?! - // Ideally we should have already copied it into the correct final one, no? - // We should probably provide a audio_buffer_fill function, that does this -> we could remove one whole memcopy memcpy( (void *) api_setting->internal_buffer[idx].pAudioData, setting->buffer, @@ -211,35 +198,7 @@ void audio_play_buffer(AudioSetting* setting, XAudio2Setting* api_setting) { return; } - setting->sample_index += setting->sample_buffer_size / setting->sample_size; - setting->sample_buffer_size = 0; -} - -// Basically the same as audio_play_buffer but by using this we can avoid one copy -// The only reason we have audio_play_buffer is that there might be situations where this is not possible -inline -void audio_fill_play_buffer(AudioSetting* setting, uint32 to_fill, Audio* sound, XAudio2Setting* api_setting) -{ - setting->sample_buffer_size = to_fill; - - if (!api_setting->source_voice || setting->sample_buffer_size == 0) { - return; - } - - if (!setting->is_playing) { - audio_play(setting, api_setting); - } - - uint32 idx = setting->sample_output % 2; - - audio_fill_buffer(setting, to_fill, sound, (int16 *) api_setting->internal_buffer[idx].pAudioData, to_fill); - - if (!SUCCEEDED(api_setting->source_voice->SubmitSourceBuffer(&api_setting->internal_buffer[idx]))) { - LOG("Xaudio2: SubmitSourceBuffer failed\n", true, true); - - return; - } - + ++setting->sample_output; setting->sample_index += setting->sample_buffer_size / setting->sample_size; setting->sample_buffer_size = 0; } diff --git a/platform/win32/threading/Atomic.h b/platform/win32/threading/Atomic.h index 6b2b2b7..6cafda4 100644 --- a/platform/win32/threading/Atomic.h +++ b/platform/win32/threading/Atomic.h @@ -18,12 +18,45 @@ void atomic_set(volatile int32* value, int32 new_value) InterlockedExchange((long *) value, new_value); } +inline +void atomic_set(volatile byte* value, const byte new_value[16]) +{ + int64* value64 = (int64*) value; + const int64* new_value64 = (const int64*) new_value; + + int64 expected_low, expected_high; + + do { + expected_low = value64[0]; + expected_high = value64[1]; + } while ( + !InterlockedCompareExchange128( + (volatile long long*) value, + new_value64[1], + new_value64[0], + &expected_low + ) + ); +} + inline int32 atomic_get(volatile int32* value) { return (int32) InterlockedCompareExchange((long *) value, 0, 0); } +inline +int64 atomic_get(volatile int64* value) +{ + return (int64) InterlockedCompareExchange((long *) value, 0, 0); +} + +inline +void atomic_get(volatile byte* value, byte data[16]) +{ + InterlockedCompareExchange128((volatile long long *) value, 0, 0, (long long *) data); +} + inline void atomic_increment(volatile int32* value) { InterlockedIncrement((long *) value); @@ -44,4 +77,19 @@ int32 atomic_subtract(volatile int32* value, int32 decrement) { return InterlockedExchangeAdd((long *) value, -decrement); } +inline +int32 atomic_compare_exchange_weak(volatile int32* value, int32* expected, int32 desired) { + return (int32) InterlockedCompareExchange((long *) value, desired, *expected); +} + +inline +int32 atomic_fetch_add(volatile int32* value, int32 operand) { + return (int32) InterlockedExchangeAdd((long *) value, operand); +} + +inline +int32 atomic_fetch_sub(volatile int32* value, int32 operand) { + return (int32) InterlockedExchangeSubtract((unsigned long *) value, operand); +} + #endif \ No newline at end of file diff --git a/platform/win32/threading/Semaphore.h b/platform/win32/threading/Semaphore.h index 5544be9..9fb718f 100644 --- a/platform/win32/threading/Semaphore.h +++ b/platform/win32/threading/Semaphore.h @@ -16,7 +16,7 @@ typedef HANDLE sem_t; void sem_init(sem_t* semaphore, int32 value) { - *semaphore = CreateSemaphore(NULL, value, MAX_INT32, NULL); + *semaphore = CreateSemaphore(NULL, value, MAX_UINT32, NULL); } void sem_destroy(sem_t* semaphore) diff --git a/platform/win32/threading/Thread.h b/platform/win32/threading/Thread.h index f1f35ad..b986788 100644 --- a/platform/win32/threading/Thread.h +++ b/platform/win32/threading/Thread.h @@ -10,7 +10,7 @@ #define TOS_PLATFORM_WIN32_THREADING_THREAD_H #include "../../../stdlib/Types.h" -#include "../UtilsWin32.h" +#include "../TimeUtils.h" #include "ThreadDefines.h" #include @@ -105,18 +105,6 @@ int32 pthread_cond_destroy(pthread_cond_t*) return 0; } -static DWORD timespec_to_ms(const timespec* abstime) -{ - if (abstime == NULL) { - return INFINITE; - } - - time_t seconds_since_epoch = unix_epoch_s(); - DWORD t = (DWORD) (((abstime->tv_sec - seconds_since_epoch) * 1000) + (abstime->tv_nsec / 1000000)); - - return t < 0 ? 1 : t; -} - int32 pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime) { if (cond == NULL || mutex == NULL) { diff --git a/platform/win32/threading/ThreadDefines.h b/platform/win32/threading/ThreadDefines.h index ddc0b70..3ebcd45 100644 --- a/platform/win32/threading/ThreadDefines.h +++ b/platform/win32/threading/ThreadDefines.h @@ -12,6 +12,7 @@ #include #include +#define THREAD_RETURN DWORD WINAPI typedef DWORD (WINAPI *ThreadJobFunc)(void*); typedef CRITICAL_SECTION pthread_mutex_t; typedef void pthread_mutexattr_t; @@ -25,6 +26,4 @@ struct pthread_rwlock_t { bool exclusive; }; -#define THREAD_RETURN DWORD WINAPI - #endif \ No newline at end of file diff --git a/stdlib/HashMap.h b/stdlib/HashMap.h index e6c1de3..18e2dac 100644 --- a/stdlib/HashMap.h +++ b/stdlib/HashMap.h @@ -6,8 +6,8 @@ * @version 1.0.0 * @link https://jingga.app */ -#ifndef TOS_STDLIB_HASHMAP_H -#define TOS_STDLIB_HASHMAP_H +#ifndef TOS_STDLIB_HASH_MAP_H +#define TOS_STDLIB_HASH_MAP_H #include "Types.h" #include "../hash/GeneralHash.h" @@ -122,11 +122,6 @@ int64 hashmap_size(const HashMap* hm) } void hashmap_insert(HashMap* hm, const char* key, int32 value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -152,11 +147,6 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) { } void hashmap_insert(HashMap* hm, const char* key, int64 value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -182,11 +172,6 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) { } void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -212,11 +197,6 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) { } void hashmap_insert(HashMap* hm, const char* key, void* value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -242,11 +222,6 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) { } void hashmap_insert(HashMap* hm, const char* key, f32 value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -272,11 +247,6 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) { } void hashmap_insert(HashMap* hm, const char* key, const char* value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -304,11 +274,6 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) { } void hashmap_insert(HashMap* hm, const char* key, byte* value) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return; - } - uint64 index = hash_djb2(key) % hm->buf.count; int64 element = chunk_reserve(&hm->buf, 1); @@ -337,11 +302,6 @@ void hashmap_insert(HashMap* hm, const char* key, byte* value) { } HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) { - // @performance Do we really want to do this check every time? - if (hm->buf.count == 0) { - return NULL; - } - uint64 index = hash_djb2(key) % hm->buf.count; HashEntry* entry = (HashEntry *) hm->table[index]; @@ -359,10 +319,6 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) { // This function only saves one step (omission of the hash function) // The reason for this is in some cases we can use compile time hashing HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 index) { - if (hm->buf.count == 0) { - return NULL; - } - index %= hm->buf.count; HashEntry* entry = (HashEntry *) hm->table[index]; @@ -378,7 +334,7 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 index) { } void hashmap_delete_entry(HashMap* hm, const char* key) { - uint64 index = hash_djb2(key); + uint64 index = hash_djb2(key) % hm->buf.count; HashEntry* entry = (HashEntry *) hm->table[index]; HashEntry* prev = NULL; diff --git a/stdlib/ThreadedHashMap.h b/stdlib/ThreadedHashMap.h new file mode 100644 index 0000000..9eaf54a --- /dev/null +++ b/stdlib/ThreadedHashMap.h @@ -0,0 +1,134 @@ +/** + * Jingga + * + * @copyright Jingga + * @license OMS License 2.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef TOS_STDLIB_THREADED_HASH_MAP_H +#define TOS_STDLIB_THREADED_HASH_MAP_H + +#include "../stdlib/Types.h" +#include "HashMap.h" + +#if _WIN32 + #include "../platform/win32/threading/Thread.h" + #include "../platform/win32/threading/Semaphore.h" + #include "../platform/win32/threading/Atomic.h" +#elif __linux__ + #include "../platform/linux/threading/Thread.h" + #include "../platform/linux/threading/Semaphore.h" + #include "../platform/linux/threading/Atomic.h" +#endif + +struct ThreadedHashMap { + void** table; + ChunkMemory buf; + + pthread_mutex_t mutex; +}; + +// WARNING: element_size = element size + remaining HashEntry data size +inline +void threaded_hashmap_create(ThreadedHashMap* hm, int32 count, int32 element_size, RingMemory* ring) +{ + hashmap_create((HashMap *) hm, count, element_size, ring); + pthread_mutex_init(&hm->mutex, NULL); +} + +// WARNING: element_size = element size + remaining HashEntry data size +inline +void threaded_hashmap_create(ThreadedHashMap* hm, int32 count, int32 element_size, BufferMemory* buf) +{ + hashmap_create((HashMap *) hm, count, element_size, buf); + pthread_mutex_init(&hm->mutex, NULL); +} + +// WARNING: element_size = element size + remaining HashEntry data size +inline +void threaded_hashmap_create(ThreadedHashMap* hm, int32 count, int32 element_size, byte* buf) +{ + hashmap_create((HashMap *) hm, count, element_size, buf); + pthread_mutex_init(&hm->mutex, NULL); +} + +inline +void threaded_hashmap_free(ThreadedHashMap* hm) +{ + pthread_mutex_destroy(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, int32 value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, int64 value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, uintptr_t value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, void* value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, f32 value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, const char* value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_insert(ThreadedHashMap* hm, const char* key, byte* value) { + pthread_mutex_lock(&hm->mutex); + hashmap_insert((HashMap *) hm, key, value); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_get_entry(ThreadedHashMap* hm, HashEntry* entry, const char* key) { + pthread_mutex_lock(&hm->mutex); + HashEntry* temp = hashmap_get_entry((HashMap *) hm, key); + memcpy(entry, temp, hm->buf.chunk_size); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_get_entry(ThreadedHashMap* hm, HashEntry* entry, const char* key, uint64 index) { + pthread_mutex_lock(&hm->mutex); + HashEntry* temp = hashmap_get_entry((HashMap *) hm, key, index); + memcpy(entry, temp, hm->buf.chunk_size); + pthread_mutex_unlock(&hm->mutex); +} + +inline +void threaded_hashmap_delete_entry(ThreadedHashMap* hm, const char* key) { + pthread_mutex_lock(&hm->mutex); + hashmap_delete_entry((HashMap *) hm, key); + pthread_mutex_unlock(&hm->mutex); +} + +#endif \ No newline at end of file diff --git a/stdlib/Types.h b/stdlib/Types.h index 030a06e..65d16ab 100644 --- a/stdlib/Types.h +++ b/stdlib/Types.h @@ -47,8 +47,21 @@ typedef intptr_t smm; #define GIGABYTE 1073741824 #define MAX_BYTE 0xFF -#define MAX_INT16 0xFFFF -#define MAX_INT32 0xFFFFFFFF +#define MAX_UINT16 0xFFFF +#define MAX_UINT32 0xFFFFFFFF +#define MAX_UINT64 0xFFFFFFFFFFFFFFFF + +#define MAX_CHAR 0x7F +#define MAX_INT16 0x7FFF +#define MAX_INT32 0x7FFFFFFF +#define MAX_INT64 0x7FFFFFFFFFFFFFFF + +#define MIN_CHAR 0x80 +#define MIN_INT16 0x8000 +#define MIN_INT32 0x80000000 +#define MIN_INT64 0x8000000000000000 + +#define MILLI_MICRO 1000 #define internal static // only allows local "file" access #define local_persist static diff --git a/ui/UITheme.h b/ui/UITheme.h index 2e5b949..9e48dc1 100644 --- a/ui/UITheme.h +++ b/ui/UITheme.h @@ -12,9 +12,9 @@ #include "UIElementType.h" #if _WIN32 - #include "../platform/win32/UtilsWin32.h" + #include "../platform/win32/FileUtils.cpp" #else - #include "../platform/linux/UtilsLinux.h" + #include "../platform/linux/FileUtils.cpp" #endif #define UI_THEME_VERSION 1 diff --git a/utils/MathUtils.h b/utils/MathUtils.h index ab4bfa3..926998b 100644 --- a/utils/MathUtils.h +++ b/utils/MathUtils.h @@ -15,7 +15,7 @@ #define OMS_PI 3.14159265358979323846f #define OMS_PI_OVER_TWO (OMS_PI / 2.0f) #define OMS_PI_OVER_FOUR (OMS_PI / 4.0f) -#define OMS_TWO_PI (2 * OMS_PI) +#define OMS_TWO_PI (2.0f * OMS_PI) #define OMS_MAX(a, b) ((a) > (b) ? (a) : (b)) #define OMS_MIN(a, b) ((a) > (b) ? (b) : (a)) diff --git a/utils/Utils.h b/utils/Utils.h index 0444fc8..0457c17 100644 --- a/utils/Utils.h +++ b/utils/Utils.h @@ -14,7 +14,7 @@ #include "../stdlib/Types.h" struct FileBody { - uint64 size = 0; // doesn't include null termination (same as strlen) + uint64 size; // doesn't include null termination (same as strlen) byte* content; }; @@ -84,4 +84,28 @@ int random_weighted_index(const int32* arr, int32 array_count) return item_rarity; } +bool is_equal_aligned(const byte* region1, const byte* region2, uint64 size) +{ + while (size > 4) { + if (*(const int32_t*) region1 != *(const int32_t*) region2) { + return false; + } + + region1 += 4; + region2 += 4; + size -= 4; + } + + for (; size > 0; --size) { + if (region1 != region2) { + return false; + } + + ++region1; + ++region2; + } + + return true; +} + #endif \ No newline at end of file