mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
continued with UI re-impl. utterly broken!!!
This commit is contained in:
parent
2263d6f018
commit
fd47385162
18
architecture/Intrinsics.h
Normal file
18
architecture/Intrinsics.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_ARCHITECTURE_INTRINSICS_H
|
||||
#define TOS_ARCHITECTURE_INTRINSICS_H
|
||||
|
||||
#if ARM
|
||||
#include "arm/Intrinsics.h"
|
||||
#else
|
||||
#include "x86/Intrinsics.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -36,9 +36,9 @@ uint64 cpu_info_features() {
|
|||
return feature_bitfield;
|
||||
}
|
||||
|
||||
void cpu_info_cache(int32_t level, CpuCacheInfo* cache) {
|
||||
uint32_t ccsidr; // Cache Size ID Register
|
||||
uint32_t line_size, associativity, num_sets;
|
||||
void cpu_info_cache(int32 level, CpuCacheInfo* cache) {
|
||||
uint32 ccsidr; // Cache Size ID Register
|
||||
uint32 line_size, associativity, num_sets;
|
||||
|
||||
cache->level = level;
|
||||
cache->size = 0;
|
||||
|
|
@ -48,9 +48,9 @@ void cpu_info_cache(int32_t level, CpuCacheInfo* cache) {
|
|||
cache->line_size = 0;
|
||||
|
||||
// Select cache level and type in CSSELR
|
||||
uint32_t csselr = level << 1; // Data or unified cache
|
||||
__asm__ volatile("msr csselr_el1, %0" : : "r" (csselr));
|
||||
__asm__ volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr));
|
||||
uint32 csselr = level << 1; // Data or unified cache
|
||||
asm volatile("msr csselr_el1, %0" : : "r" (csselr));
|
||||
asm volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr));
|
||||
|
||||
// Extract cache details from CCSIDR
|
||||
line_size = (ccsidr & 0x7) + 4; // Line size in log2 words
|
||||
|
|
|
|||
40
architecture/arm/Intrinsics.h
Normal file
40
architecture/arm/Intrinsics.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_ARCHITECTURE_ARM_INTRINSICS_ARM_H
|
||||
#define TOS_ARCHITECTURE_ARM_INTRINSICS_ARM_H
|
||||
|
||||
#include <arm_sve.h>
|
||||
#include <arm_acle.h>
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
#define intrin_sqrt_f32(a) svget1_f32(svsqrt_f32(svdup_f32((a))))
|
||||
#define intrin_sqrt_f64(a) svget1_f64(svsqrt_f64(svdup_f64((a))))
|
||||
|
||||
#define intrin_rsqrt_f32(a) svget1_f32(svrsqrte_f32(svdup_f32((a))))
|
||||
#define intrin_rsqrt_f64(a) svget1_f64(svrsqrte_f64(svdup_f64((a))))
|
||||
|
||||
#define intrin_round_f32(a) svget1_f32(svrndn_f32(svdup_f32((a))))
|
||||
#define intrin_round_to_int32(a) svget1_s32(svcvtn_f32_s32(svdup_f32((a)), SVE_32B))
|
||||
#define intrin_floor_f32(a) svget1_f32(svfloor_f32(svdup_f32((a))))
|
||||
#define intrin_ceil_f32(a) svget1_f32(svceil_f32(svdup_f32((a))))
|
||||
|
||||
#define intrin_fence_memory() __dmb(0xF)
|
||||
#define intrin_fence_write() __dmb(0xB)
|
||||
#define intrin_fence_load() __dmb(0x7)
|
||||
#define intrin_invalidate_cache() asm volatile("dc ivac, %0" : : "r"(address) : "memory")
|
||||
|
||||
#define intrin_crc32_u8(crc, data) __crc32b((crc), (data))
|
||||
#define intrin_crc32_u16(crc, data) __crc32h((crc), (data))
|
||||
#define intrin_crc32_u32(crc, data) __crc32w((crc), (data))
|
||||
#define intrin_crc32_u64(crc, data) __crc32d((crc), (data))
|
||||
|
||||
#define intrin_timestamp_counter() ({ uint64_t cntvct; asm volatile("mrs %0, cntvct_el0" : "=r"(cntvct)); cntvct; })
|
||||
|
||||
#endif
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
static inline
|
||||
void cpuid(int32 cpuInfo[4], int32 function_id) {
|
||||
__asm__ volatile(
|
||||
asm volatile(
|
||||
"cpuid"
|
||||
: "=a" (cpuInfo[0]), "=b" (cpuInfo[1]), "=c" (cpuInfo[2]), "=d" (cpuInfo[3])
|
||||
: "a" (function_id)
|
||||
|
|
|
|||
56
architecture/x86/Intrinsics.h
Normal file
56
architecture/x86/Intrinsics.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_ARCHITECTURE_X86_INTRINSICS_H
|
||||
#define TOS_ARCHITECTURE_X86_INTRINSICS_H
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#if _WIN32
|
||||
#include <intrin.h>
|
||||
#elif __linux__
|
||||
#include <x86intrin.h>
|
||||
#include <x86gprintrin.h>
|
||||
#endif
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define intrin_sqrt_f32(a) _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss((a))))
|
||||
#else
|
||||
#define intrin_sqrt_f32(a) ({ float res; asm volatile("sqrtss %0, %1" : "=x"(res) : "x"(a)); res; })
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define intrin_sqrt_f64(a) _mm_cvtsd_f64(_mm_sqrt_sd(_mm_set_sd((a)), _mm_set_sd((a))))
|
||||
#else
|
||||
#define intrin_sqrt_f64(a) ({ double res; asm volatile("sqrtsd %0, %1" : "=x" (res) : "x" (a)); res; })
|
||||
#endif
|
||||
|
||||
#define intrin_rsqrt_f32(a) _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss((a))))
|
||||
#define intrin_rsqrt_f64(a) _mm_cvtsd_f64(_mm_div_sd(_mm_set_sd(1.0), _mm_sqrt_sd(_mm_set_sd((a)), _mm_set_sd((a)))))
|
||||
|
||||
#define intrin_round_f32(a) _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss((a)), (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)))
|
||||
#define intrin_round_to_int32(a) _mm_cvtss_si32(_mm_set_ss((a)))
|
||||
#define intrin_floor_f32(a) _mm_cvtss_f32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss((a))))
|
||||
#define intrin_ceil_f32(a) _mm_cvtss_f32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss((a))))
|
||||
|
||||
#define intrin_fence_memory() _mm_mfence()
|
||||
#define intrin_fence_write() _mm_sfence()
|
||||
#define intrin_fence_load() _mm_lfence()
|
||||
#define intrin_invalidate_cache() _mm_clflush()
|
||||
|
||||
#define intrin_crc32_u8(crc, data) _mm_crc32_u8((crc), (data))
|
||||
#define intrin_crc32_u16(crc, data) _mm_crc32_u16((crc), (data))
|
||||
#define intrin_crc32_u32(crc, data) _mm_crc32_u32((crc), (data))
|
||||
#define intrin_crc32_u64(crc, data) _mm_crc32_u64((crc), (data))
|
||||
|
||||
#define intrin_timestamp_counter() __rdtsc()
|
||||
|
||||
#endif
|
||||
|
|
@ -23,8 +23,7 @@ struct Asset {
|
|||
// Could be 0 if there is no official id
|
||||
uint32 official_id;
|
||||
|
||||
// @performance We would like to use a bool but windows only supports 32bit atomic values as smallest value
|
||||
// Maybe if we would set the IS_LOADED_STATE in the enum as the highest bit we could use the state variable and check it with >=
|
||||
// @performance Maybe if we would set the IS_LOADED_STATE in the enum as the highest bit we could use the state variable and check it with >=
|
||||
int32 is_loaded;
|
||||
|
||||
// Describes how much ram/vram the asset uses
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "AssetManagementSystem.h"
|
||||
#include "../system/FileUtils.cpp"
|
||||
#include "../stdlib/Simd.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
#define ASSET_ARCHIVE_VERSION 1
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ struct AssetArchive {
|
|||
};
|
||||
|
||||
// Calculates how large the header memory has to be to hold all its information
|
||||
int32 asset_archive_header_size(AssetArchive* archive, byte* data)
|
||||
int32 asset_archive_header_size(AssetArchive* __restrict archive, const byte* __restrict data)
|
||||
{
|
||||
data += sizeof(archive->header.version);
|
||||
|
||||
|
|
@ -88,7 +89,7 @@ int32 asset_archive_header_size(AssetArchive* archive, byte* data)
|
|||
+ asset_dependency_count * sizeof(int32);
|
||||
}
|
||||
|
||||
void asset_archive_header_load(AssetArchiveHeader* header, byte* data, int32 steps = 8)
|
||||
void asset_archive_header_load(AssetArchiveHeader* __restrict header, const byte* __restrict data, int32 steps = 8)
|
||||
{
|
||||
header->version = SWAP_ENDIAN_LITTLE(*((int32 *) data));
|
||||
data += sizeof(header->version);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
#include "AudioSetting.h"
|
||||
#include "Wav.h"
|
||||
|
||||
void audio_from_file(Audio* audio, const char* path, RingMemory* ring)
|
||||
void audio_from_file(Audio* __restrict audio, const char* __restrict path, RingMemory* __restrict ring)
|
||||
{
|
||||
FileBody file;
|
||||
file_read(path, &file, ring);
|
||||
|
|
@ -40,7 +40,7 @@ int32 audio_data_size(const Audio* audio)
|
|||
}
|
||||
|
||||
inline
|
||||
uint32 audio_header_from_data(const byte* data, Audio* audio)
|
||||
uint32 audio_header_from_data(const byte* __restrict data, Audio* __restrict audio)
|
||||
{
|
||||
const byte* start = data;
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ uint32 audio_header_from_data(const byte* data, Audio* audio)
|
|||
}
|
||||
|
||||
inline
|
||||
uint32 audio_header_to_data(const Audio* audio, byte* data)
|
||||
uint32 audio_header_to_data(const Audio* __restrict audio, byte* __restrict data)
|
||||
{
|
||||
byte* start = data;
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ uint32 audio_header_to_data(const Audio* audio, byte* data)
|
|||
return (int32) (data - start);
|
||||
}
|
||||
|
||||
uint32 audio_from_data(const byte* data, Audio* audio)
|
||||
uint32 audio_from_data(const byte* __restrict data, Audio* __restrict audio)
|
||||
{
|
||||
data += audio_header_from_data(data, audio);
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ uint32 audio_from_data(const byte* data, Audio* audio)
|
|||
return audio_data_size(audio);
|
||||
}
|
||||
|
||||
uint32 audio_to_data(const Audio* audio, byte* data)
|
||||
uint32 audio_to_data(const Audio* __restrict audio, byte* __restrict data)
|
||||
{
|
||||
data += audio_header_to_data(audio, data);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ enum AudioMixerState {
|
|||
struct AudioMixer {
|
||||
ChunkMemory audio_instances;
|
||||
AudioMixerState state_old;
|
||||
AudioMixerState state_new;
|
||||
int32 state_new;
|
||||
|
||||
uint64 effect;
|
||||
|
||||
|
|
@ -98,13 +98,13 @@ struct AudioMixer {
|
|||
|
||||
bool audio_mixer_is_active(AudioMixer* mixer) {
|
||||
if (mixer->state_old == AUDIO_MIXER_STATE_ACTIVE
|
||||
&& atomic_get_relaxed((int32 *) &mixer->state_new) == AUDIO_MIXER_STATE_ACTIVE
|
||||
&& atomic_get_relaxed(&mixer->state_new) == AUDIO_MIXER_STATE_ACTIVE
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AudioMixerState mixer_state;
|
||||
if ((mixer_state = (AudioMixerState) atomic_get_relaxed((int32 *) &mixer->state_new)) != mixer->state_old) {
|
||||
if ((mixer_state = (AudioMixerState) atomic_get_relaxed(&mixer->state_new)) != mixer->state_old) {
|
||||
if (mixer->state_old == AUDIO_MIXER_STATE_UNINITIALIZED) {
|
||||
audio_load(
|
||||
mixer->window,
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@
|
|||
#define TOS_CAMERA_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
|
||||
#include "../math/matrix/MatrixFloat32.h"
|
||||
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
#include "CameraMovement.h"
|
||||
|
||||
#define CAMERA_MAX_INPUTS 4
|
||||
|
|
@ -40,6 +39,8 @@ struct Camera {
|
|||
f32 sensitivity;
|
||||
f32 zoom;
|
||||
|
||||
// @question Consider to make these f32 values.
|
||||
// Yes, this uses obviously more space BUT we use these values very often in vertex calculations and always have to cast it
|
||||
uint16 viewport_width;
|
||||
uint16 viewport_height;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@
|
|||
#include "AppCmdBuffer.h"
|
||||
#include "../camera/Camera.h"
|
||||
#include "../ui/UILayout.h"
|
||||
#include "../ui/UILayout.cpp"
|
||||
#include "../ui/UITheme.h"
|
||||
#include "../system/FileUtils.cpp"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
// @todo Move the different functions to their own respective files (e.g. CmdAsset.cpp, CmdLayout.cpp)
|
||||
|
||||
|
|
@ -464,7 +466,7 @@ UIThemeStyle* cmd_theme_load_sync(
|
|||
inline
|
||||
void cmd_layout_populate_sync(
|
||||
AppCmdBuffer* cb,
|
||||
UILayout* layout, UIThemeStyle* theme,
|
||||
UILayout* layout, const UIThemeStyle* theme,
|
||||
const Camera* camera
|
||||
) {
|
||||
layout_from_theme(layout, theme, camera);
|
||||
|
|
@ -496,7 +498,7 @@ UILayout* cmd_ui_load(AppCmdBuffer* cb, Command* cmd)
|
|||
pos += sizeof(uintptr_t);
|
||||
|
||||
char* layout_path = (char *) pos;
|
||||
str_move_to((char **) &pos, '\0'); ++pos;
|
||||
str_move_to((const char **) &pos, '\0'); ++pos;
|
||||
|
||||
UIThemeStyle* general_theme = (UIThemeStyle *) pos;
|
||||
pos += sizeof(uintptr_t);
|
||||
|
|
@ -505,7 +507,7 @@ UILayout* cmd_ui_load(AppCmdBuffer* cb, Command* cmd)
|
|||
pos += sizeof(uintptr_t);
|
||||
|
||||
char* theme_path = (char *) pos;
|
||||
str_move_to((char **) &pos, '\0'); ++pos;
|
||||
str_move_to((const char **) &pos, '\0'); ++pos;
|
||||
|
||||
Camera* camera = (Camera *) pos;
|
||||
|
||||
|
|
|
|||
20
compiler/CompilerUtils.h
Normal file
20
compiler/CompilerUtils.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_COMPILER_COMPILER_UTILS_H
|
||||
#define TOS_COMPILER_COMPILER_UTILS_H
|
||||
|
||||
#include "../utils/TestUtils.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#include "msvc/CompilerUtils.h"
|
||||
#elif __GNUC__
|
||||
#include "gcc/CompilerUtils.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
212
compiler/gcc/Atomic.h
Normal file
212
compiler/gcc/Atomic.h
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_COMPILER_GCC_ATOMIC_H
|
||||
#define TOS_COMPILER_GCC_ATOMIC_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
#include <stdatomic.h>
|
||||
|
||||
inline void atomic_set_relaxed(void** target, void* value) { __atomic_store_n(target, value, __ATOMIC_RELAXED); }
|
||||
inline void* atomic_get_relaxed(void** target) { return __atomic_load_n(target, __ATOMIC_RELAXED); }
|
||||
inline void atomic_set_relaxed(volatile int32* value, int32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline void atomic_set_relaxed(volatile int64* value, int64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline int32 atomic_fetch_set_relaxed(volatile int32* value, int32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline int64 atomic_fetch_set_relaxed(volatile int64* value, int64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline int32 atomic_get_relaxed(volatile int32* value) { return __atomic_load_n((int32 *) value, __ATOMIC_RELAXED); }
|
||||
inline int64 atomic_get_relaxed(volatile int64* value) { return __atomic_load_n((int64 *) value, __ATOMIC_RELAXED); }
|
||||
inline void atomic_increment_relaxed(volatile int32* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_decrement_relaxed(volatile int32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_increment_relaxed(volatile int64* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_decrement_relaxed(volatile int64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_add_relaxed(volatile int32* value, int32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_RELAXED); }
|
||||
inline void atomic_sub_relaxed(volatile int32* value, int32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_RELAXED); }
|
||||
inline void atomic_add_relaxed(volatile int64* value, int64 increment) { __atomic_add_fetch(value, increment, __ATOMIC_RELAXED); }
|
||||
inline void atomic_sub_relaxed(volatile int64* value, int64 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_RELAXED); }
|
||||
inline int32 atomic_compare_exchange_weak_relaxed(volatile int32* value, int32* expected, int32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); return *expected; }
|
||||
inline int32 atomic_fetch_add_relaxed(volatile int32* value, int32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline int32 atomic_fetch_sub_relaxed(volatile int32* value, int32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline int64 atomic_fetch_add_relaxed(volatile int64* value, int64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline int64 atomic_fetch_sub_relaxed(volatile int64* value, int64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline void atomic_set_relaxed(volatile uint32* value, uint32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline void atomic_set_relaxed(volatile uint64* value, uint64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline uint32 atomic_fetch_set_relaxed(volatile uint32* value, uint32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline uint64 atomic_fetch_set_relaxed(volatile uint64* value, uint64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELAXED); }
|
||||
inline uint32 atomic_get_relaxed(volatile uint32* value) { return __atomic_load_n((uint32 *) value, __ATOMIC_RELAXED); }
|
||||
inline uint64 atomic_get_relaxed(volatile uint64* value) { return __atomic_load_n((uint64 *) value, __ATOMIC_RELAXED); }
|
||||
inline void atomic_increment_relaxed(volatile uint32* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_decrement_relaxed(volatile uint32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_increment_relaxed(volatile uint64* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_decrement_relaxed(volatile uint64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELAXED); }
|
||||
inline void atomic_add_relaxed(volatile uint32* value, uint32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_RELAXED); }
|
||||
inline void atomic_sub_relaxed(volatile uint32* value, uint32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_RELAXED); }
|
||||
inline uint32 atomic_compare_exchange_weak_relaxed(volatile uint32* value, uint32* expected, uint32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); return *expected; }
|
||||
inline uint32 atomic_fetch_add_relaxed(volatile uint32* value, uint32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline uint32 atomic_fetch_sub_relaxed(volatile uint32* value, uint32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline uint64 atomic_fetch_add_relaxed(volatile uint64* value, uint64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline uint64 atomic_fetch_sub_relaxed(volatile uint64* value, uint64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELAXED); }
|
||||
inline void atomic_and_relaxed(volatile uint32* value, uint32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_and_relaxed(volatile int32* value, int32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_and_relaxed(volatile uint64* value, uint64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_and_relaxed(volatile int64* value, int64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_or_relaxed(volatile uint32* value, uint32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_or_relaxed(volatile int32* value, int32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_or_relaxed(volatile uint64* value, uint64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELAXED); }
|
||||
inline void atomic_or_relaxed(volatile int64* value, int64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELAXED); }
|
||||
|
||||
inline void atomic_set_acquire(void** target, void* value) { __atomic_store_n(target, value, __ATOMIC_ACQUIRE); }
|
||||
inline void* atomic_get_acquire(void** target) { return __atomic_load_n(target, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_set_acquire(volatile int32* value, int32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_set_acquire(volatile int64* value, int64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline int32 atomic_fetch_set_acquire(volatile int32* value, int32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline int64 atomic_fetch_set_acquire(volatile int64* value, int64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline int32 atomic_get_acquire(volatile int32* value) { return __atomic_load_n((int32 *) value, __ATOMIC_ACQUIRE); }
|
||||
inline int64 atomic_get_acquire(volatile int64* value) { return __atomic_load_n((int64 *) value, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_increment_acquire(volatile int32* value) { __atomic_add_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_decrement_acquire(volatile int32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_increment_acquire(volatile int64* value) { __atomic_add_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_decrement_acquire(volatile int64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_add_acquire(volatile int32* value, int32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_sub_acquire(volatile int32* value, int32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_add_acquire(volatile int64* value, int64 increment) { __atomic_add_fetch(value, increment, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_sub_acquire(volatile int64* value, int64 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_ACQUIRE); }
|
||||
inline int32 atomic_compare_exchange_weak_acquire(volatile int32* value, int32* expected, int32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); return *expected; }
|
||||
inline int32 atomic_fetch_add_acquire(volatile int32* value, int32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline int32 atomic_fetch_sub_acquire(volatile int32* value, int32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline int64 atomic_fetch_add_acquire(volatile int64* value, int64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline int64 atomic_fetch_sub_acquire(volatile int64* value, int64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_set_acquire(volatile uint32* value, uint32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_set_acquire(volatile uint64* value, uint64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline uint32 atomic_fetch_set_acquire(volatile uint32* value, uint32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline uint64 atomic_fetch_set_acquire(volatile uint64* value, uint64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_ACQUIRE); }
|
||||
inline uint32 atomic_get_acquire(volatile uint32* value) { return __atomic_load_n((uint32 *) value, __ATOMIC_ACQUIRE); }
|
||||
inline uint64 atomic_get_acquire(volatile uint64* value) { return __atomic_load_n((uint64 *) value, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_increment_acquire(volatile uint32* value) { __atomic_add_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_decrement_acquire(volatile uint32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_increment_acquire(volatile uint64* value) { __atomic_add_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_decrement_acquire(volatile uint64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_add_acquire(volatile uint32* value, uint32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_sub_acquire(volatile uint32* value, uint32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_ACQUIRE); }
|
||||
inline uint32 atomic_compare_exchange_weak_acquire(volatile uint32* value, uint32* expected, uint32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); return *expected; }
|
||||
inline uint32 atomic_fetch_add_acquire(volatile uint32* value, uint32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline uint32 atomic_fetch_sub_acquire(volatile uint32* value, uint32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline uint64 atomic_fetch_add_acquire(volatile uint64* value, uint64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline uint64 atomic_fetch_sub_acquire(volatile uint64* value, uint64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_and_acquire(volatile uint32* value, uint32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_and_acquire(volatile int32* value, int32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_and_acquire(volatile uint64* value, uint64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_and_acquire(volatile int64* value, int64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_or_acquire(volatile uint32* value, uint32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_or_acquire(volatile int32* value, int32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_or_acquire(volatile uint64* value, uint64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_ACQUIRE); }
|
||||
inline void atomic_or_acquire(volatile int64* value, int64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_ACQUIRE); }
|
||||
|
||||
inline void atomic_set_release(void** target, void* value) { __atomic_store_n(target, value, __ATOMIC_RELEASE); }
|
||||
inline void* atomic_get_release(void** target) { return __atomic_load_n(target, __ATOMIC_RELEASE); }
|
||||
inline void atomic_set_release(volatile int32* value, int32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline void atomic_set_release(volatile int64* value, int64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline int32 atomic_fetch_set_release(volatile int32* value, int32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline int64 atomic_fetch_set_release(volatile int64* value, int64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline int32 atomic_get_release(volatile int32* value) { return __atomic_load_n((int32 *) value, __ATOMIC_RELEASE); }
|
||||
inline int64 atomic_get_release(volatile int64* value) { return __atomic_load_n((int64 *) value, __ATOMIC_RELEASE); }
|
||||
inline void atomic_increment_release(volatile int32* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_decrement_release(volatile int32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_increment_release(volatile int64* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_decrement_release(volatile int64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_add_release(volatile int32* value, int32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_RELEASE); }
|
||||
inline void atomic_sub_release(volatile int32* value, int32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_RELEASE); }
|
||||
inline void atomic_add_release(volatile int64* value, int64 increment) { __atomic_add_fetch(value, increment, __ATOMIC_RELEASE); }
|
||||
inline void atomic_sub_release(volatile int64* value, int64 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_RELEASE); }
|
||||
inline int32 atomic_compare_exchange_weak_release(volatile int32* value, int32* expected, int32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_RELEASE, __ATOMIC_RELEASE); return *expected; }
|
||||
inline int32 atomic_fetch_add_release(volatile int32* value, int32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline int32 atomic_fetch_sub_release(volatile int32* value, int32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline int64 atomic_fetch_add_release(volatile int64* value, int64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline int64 atomic_fetch_sub_release(volatile int64* value, int64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline void atomic_set_release(volatile uint32* value, uint32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline void atomic_set_release(volatile uint64* value, uint64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline uint32 atomic_fetch_set_release(volatile uint32* value, uint32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline uint64 atomic_fetch_set_release(volatile uint64* value, uint64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_RELEASE); }
|
||||
inline uint32 atomic_get_release(volatile uint32* value) { return __atomic_load_n((uint32 *) value, __ATOMIC_RELEASE); }
|
||||
inline uint64 atomic_get_release(volatile uint64* value) { return __atomic_load_n((uint64 *) value, __ATOMIC_RELEASE); }
|
||||
inline void atomic_increment_release(volatile uint32* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_decrement_release(volatile uint32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_increment_release(volatile uint64* value) { __atomic_add_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_decrement_release(volatile uint64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_RELEASE); }
|
||||
inline void atomic_add_release(volatile uint32* value, uint32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_RELEASE); }
|
||||
inline void atomic_sub_release(volatile uint32* value, uint32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_RELEASE); }
|
||||
inline uint32 atomic_compare_exchange_weak_release(volatile uint32* value, uint32* expected, uint32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_RELEASE, __ATOMIC_RELEASE); return *expected; }
|
||||
inline uint32 atomic_fetch_add_release(volatile uint32* value, uint32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline uint32 atomic_fetch_sub_release(volatile uint32* value, uint32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline uint64 atomic_fetch_add_release(volatile uint64* value, uint64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline uint64 atomic_fetch_sub_release(volatile uint64* value, uint64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_RELEASE); }
|
||||
inline void atomic_and_release(volatile uint32* value, uint32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_and_release(volatile int32* value, int32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_and_release(volatile uint64* value, uint64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_and_release(volatile int64* value, int64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_or_release(volatile uint32* value, uint32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_or_release(volatile int32* value, int32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_or_release(volatile uint64* value, uint64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELEASE); }
|
||||
inline void atomic_or_release(volatile int64* value, int64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_RELEASE); }
|
||||
|
||||
inline void atomic_set_acquire_release(void** target, void* value) { __atomic_store_n(target, value, __ATOMIC_SEQ_CST); }
|
||||
inline void* atomic_get_acquire_release(void** target) { return __atomic_load_n(target, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_set_acquire_release(volatile int32* value, int32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_set_acquire_release(volatile int64* value, int64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline int32 atomic_fetch_set_acquire_release(volatile int32* value, int32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline int64 atomic_fetch_set_acquire_release(volatile int64* value, int64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline int32 atomic_get_acquire_release(volatile int32* value) { return __atomic_load_n((int32 *) value, __ATOMIC_SEQ_CST); }
|
||||
inline int64 atomic_get_acquire_release(volatile int64* value) { return __atomic_load_n((int64 *) value, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_increment_acquire_release(volatile int32* value) { __atomic_add_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_decrement_acquire_release(volatile int32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_increment_acquire_release(volatile int64* value) { __atomic_add_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_decrement_acquire_release(volatile int64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_add_acquire_release(volatile int32* value, int32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_sub_acquire_release(volatile int32* value, int32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_add_acquire_release(volatile int64* value, int64 increment) { __atomic_add_fetch(value, increment, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_sub_acquire_release(volatile int64* value, int64 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_SEQ_CST); }
|
||||
inline int32 atomic_compare_exchange_weak_acquire_release(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_acquire_release(volatile int32* value, int32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline int32 atomic_fetch_sub_acquire_release(volatile int32* value, int32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline int64 atomic_fetch_add_acquire_release(volatile int64* value, int64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline int64 atomic_fetch_sub_acquire_release(volatile int64* value, int64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_set_acquire_release(volatile uint32* value, uint32 new_value) { __atomic_store_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_set_acquire_release(volatile uint64* value, uint64 new_value) { __atomic_store_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline uint32 atomic_fetch_set_acquire_release(volatile uint32* value, uint32 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline uint64 atomic_fetch_set_acquire_release(volatile uint64* value, uint64 new_value) { return __atomic_exchange_n(value, new_value, __ATOMIC_SEQ_CST); }
|
||||
inline uint32 atomic_get_acquire_release(volatile uint32* value) { return __atomic_load_n((uint32 *) value, __ATOMIC_SEQ_CST); }
|
||||
inline uint64 atomic_get_acquire_release(volatile uint64* value) { return __atomic_load_n((uint64 *) value, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_increment_acquire_release(volatile uint32* value) { __atomic_add_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_decrement_acquire_release(volatile uint32* value) { __atomic_sub_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_increment_acquire_release(volatile uint64* value) { __atomic_add_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_decrement_acquire_release(volatile uint64* value) { __atomic_sub_fetch(value, 1, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_add_acquire_release(volatile uint32* value, uint32 increment) { __atomic_add_fetch(value, increment, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_sub_acquire_release(volatile uint32* value, uint32 decrement) { __atomic_sub_fetch(value, decrement, __ATOMIC_SEQ_CST); }
|
||||
inline uint32 atomic_compare_exchange_weak_acquire_release(volatile uint32* value, uint32* expected, uint32 desired) { __atomic_compare_exchange_n(value, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return *expected; }
|
||||
inline uint32 atomic_fetch_add_acquire_release(volatile uint32* value, uint32 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline uint32 atomic_fetch_sub_acquire_release(volatile uint32* value, uint32 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline uint64 atomic_fetch_add_acquire_release(volatile uint64* value, uint64 operand) { return __atomic_add_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline uint64 atomic_fetch_sub_acquire_release(volatile uint64* value, uint64 operand) { return __atomic_sub_fetch(value, operand, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_and_acquire_release(volatile uint32* value, uint32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_and_acquire_release(volatile int32* value, int32 mask) { __atomic_fetch_and(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_and_acquire_release(volatile uint64* value, uint64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_and_acquire_release(volatile int64* value, int64 mask) { __atomic_fetch_and(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_or_acquire_release(volatile uint32* value, uint32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_or_acquire_release(volatile int32* value, int32 mask) { __atomic_fetch_or(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_or_acquire_release(volatile uint64* value, uint64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_SEQ_CST); }
|
||||
inline void atomic_or_acquire_release(volatile int64* value, int64 mask) { __atomic_fetch_or(value, mask, __ATOMIC_SEQ_CST); }
|
||||
|
||||
// Check out the intrinsic functions fence_memory and fence_write
|
||||
// These are much faster and could accomplish what you are doing
|
||||
#define atomic_fence_acquire() __atomic_thread_fence(__ATOMIC_ACQUIRE)
|
||||
|
||||
// Check out the intrinsic functions fence_memory and fence_write
|
||||
// These are much faster and could accomplish what you are doing
|
||||
#define atomic_fence_release() __atomic_thread_fence(__ATOMIC_RELEASE)
|
||||
|
||||
|
||||
#endif
|
||||
25
compiler/gcc/CompilerUtils.h
Normal file
25
compiler/gcc/CompilerUtils.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_COMPILER_GCC_COMPILER_UTILS_H
|
||||
#define TOS_COMPILER_GCC_COMPILER_UTILS_H
|
||||
|
||||
#include "../../utils/TestUtils.h"
|
||||
|
||||
#define PACKED_STRUCT __attribute__((__packed__))
|
||||
#define UNPACKED_STRUCT ((void) 0)
|
||||
|
||||
#define EXPORT_LIB extern "C" __attribute__((visibility("default")))
|
||||
|
||||
#if DEBUG
|
||||
#define UNREACHABLE() ASSERT_SIMPLE(false)
|
||||
#else
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
28
compiler/msvc/CompilerUtils.h
Normal file
28
compiler/msvc/CompilerUtils.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_COMPILER_MSVC_COMPILER_UTILS_H
|
||||
#define TOS_COMPILER_MSVC_COMPILER_UTILS_H
|
||||
|
||||
#include "../../utils/TestUtils.h"
|
||||
#include <basetsd.h>
|
||||
|
||||
#define PACKED_STRUCT __pragma(pack(push, 1))
|
||||
#define UNPACKED_STRUCT __pragma(pack(pop))
|
||||
|
||||
#define EXPORT_LIB extern "C" __declspec(dllexport)
|
||||
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
||||
#if DEBUG
|
||||
#define UNREACHABLE() ASSERT_SIMPLE(false)
|
||||
#else
|
||||
#define UNREACHABLE() __assume(0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
24
font/Font.h
24
font/Font.h
|
|
@ -17,10 +17,8 @@ struct GlyphMetrics {
|
|||
};
|
||||
|
||||
struct GlyphTextureCoords {
|
||||
f32 x1;
|
||||
f32 y1;
|
||||
f32 x2;
|
||||
f32 y2;
|
||||
v2_f32 start;
|
||||
v2_f32 end;
|
||||
};
|
||||
|
||||
#define GLYPH_SIZE 40
|
||||
|
|
@ -147,14 +145,14 @@ void font_from_file_txt(
|
|||
{strtof(++pos, &pos), strtof(++pos, &pos), strtof(++pos, &pos), strtof(++pos, &pos)}
|
||||
};
|
||||
|
||||
font->glyphs[glyph_index].metrics.width = font->glyphs[glyph_index].coords.x2 - font->glyphs[glyph_index].coords.x1;
|
||||
font->glyphs[glyph_index].metrics.height = font->glyphs[glyph_index].coords.y2 - font->glyphs[glyph_index].coords.y1;
|
||||
font->glyphs[glyph_index].metrics.width = font->glyphs[glyph_index].coords.end.x - font->glyphs[glyph_index].coords.start.x;
|
||||
font->glyphs[glyph_index].metrics.height = font->glyphs[glyph_index].coords.end.y - font->glyphs[glyph_index].coords.start.y;
|
||||
|
||||
font->glyphs[glyph_index].coords.x1 /= image_width;
|
||||
font->glyphs[glyph_index].coords.x2 /= image_width;
|
||||
font->glyphs[glyph_index].coords.start.x /= image_width;
|
||||
font->glyphs[glyph_index].coords.end.x /= image_width;
|
||||
|
||||
font->glyphs[glyph_index].coords.y1 /= image_height;
|
||||
font->glyphs[glyph_index].coords.y2 /= image_height;
|
||||
font->glyphs[glyph_index].coords.start.y /= image_height;
|
||||
font->glyphs[glyph_index].coords.end.y /= image_height;
|
||||
|
||||
++glyph_index;
|
||||
|
||||
|
|
@ -263,9 +261,9 @@ void font_invert_coordinates(Font* font)
|
|||
{
|
||||
// @todo Implement y-offset correction
|
||||
for (uint32 i = 0; i < font->glyph_count; ++i) {
|
||||
float temp = font->glyphs[i].coords.y1;
|
||||
font->glyphs[i].coords.y1 = 1.0f - font->glyphs[i].coords.y2;
|
||||
font->glyphs[i].coords.y2 = 1.0f - temp;
|
||||
float temp = font->glyphs[i].coords.start.y;
|
||||
font->glyphs[i].coords.start.y = 1.0f - font->glyphs[i].coords.end.y;
|
||||
font->glyphs[i].coords.end.y = 1.0f - temp;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,267 +13,112 @@
|
|||
#include <string.h>
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include "../math/matrix/MatrixFloat32.h"
|
||||
#include "../font/Font.h"
|
||||
#include "../object/Vertex.h"
|
||||
#include "../ui/UITheme.h"
|
||||
#include "../ui/UIElement.h"
|
||||
#include "../ui/UIAlignment.h"
|
||||
|
||||
// @performance Create improved vertex generation for components (input + button, chat, ...) where we don't use as many
|
||||
// degenerate triangle
|
||||
|
||||
// @todo in many places we use ->value_int. We should load it as a value_float and also define it as float in the theme.
|
||||
// This way we wouldn't have to convert the value
|
||||
#include "../architecture/Intrinsics.h"
|
||||
|
||||
inline
|
||||
void vertex_degenerate_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
int32 vertex_degenerate_create(
|
||||
Vertex3DTextureColor* __restrict vertices, f32 zindex,
|
||||
f32 x, f32 y
|
||||
) {
|
||||
// Degenerate triangles
|
||||
// They are alternating every loop BUT since we use references they look the same in code
|
||||
// WARNING: Before using we must make sure that the 0 index is defined
|
||||
// The easiest way is to just define a "degenerate" starting point
|
||||
vertices[*index] = {{vertices[*index - 1].position.x, vertices[*index - 1].position.y, zindex}, {0, 0}, 0};
|
||||
++(*index);
|
||||
vertices[0] = {{vertices[0 - 1].position.x, vertices[0 - 1].position.y, zindex}, {0, 0}};
|
||||
vertices[1] = {{x, y, zindex}, {0, 0}};
|
||||
|
||||
vertices[*index] = {{x, y, zindex}, {0, 0}, 0};
|
||||
++(*index);
|
||||
return 2;
|
||||
}
|
||||
|
||||
inline
|
||||
void vertex_line_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
f32 x1, f32 y1, f32 x2, f32 y2, f32 thickness, int32 align_h, int32 align_v,
|
||||
f32 color_index = 0, f32 tex_x1 = 0.0f, f32 tex_y1 = 0.0f, f32 tex_x2 = 0.0f, f32 tex_y2 = 0.0f
|
||||
void adjust_aligned_position(
|
||||
f32* __restrict x, f32* __restrict y,
|
||||
f32 width, f32 height,
|
||||
byte alignment
|
||||
)
|
||||
{
|
||||
if (alignment & UI_ALIGN_H_RIGHT) {
|
||||
*x -= width;
|
||||
} else if (alignment & UI_ALIGN_H_CENTER) {
|
||||
*x -= width / 2;
|
||||
}
|
||||
|
||||
if (alignment & UI_ALIGN_V_TOP) {
|
||||
*y -= height;
|
||||
} else if (alignment & UI_ALIGN_V_CENTER) {
|
||||
*y -= height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
int32 vertex_line_create(
|
||||
Vertex3DTextureColor* __restrict vertices, f32 zindex,
|
||||
v2_f32 start, v2_f32 end, f32 thickness, byte alignment,
|
||||
uint32 rgba = 0
|
||||
) {
|
||||
if (align_h == UI_ALIGN_H_RIGHT) {
|
||||
x1 -= thickness;
|
||||
x2 -= thickness;
|
||||
} else if (align_h == UI_ALIGN_H_CENTER) {
|
||||
x1 -= thickness / 2;
|
||||
x2 -= thickness / 2;
|
||||
if (alignment & UI_ALIGN_H_RIGHT) {
|
||||
start.x -= thickness;
|
||||
end.x -= thickness;
|
||||
} else if (alignment & UI_ALIGN_H_CENTER) {
|
||||
start.x -= thickness / 2;
|
||||
end.x -= thickness / 2;
|
||||
}
|
||||
|
||||
if (align_v == UI_ALIGN_V_TOP) {
|
||||
y1 -= thickness;
|
||||
y2 -= thickness;
|
||||
} else if (align_v == UI_ALIGN_V_CENTER) {
|
||||
y1 -= thickness / 2;
|
||||
y2 -= thickness / 2;
|
||||
if (alignment & UI_ALIGN_V_TOP) {
|
||||
start.y -= thickness;
|
||||
end.y -= thickness;
|
||||
} else if (alignment & UI_ALIGN_V_CENTER) {
|
||||
start.y -= thickness / 2;
|
||||
end.y -= thickness / 2;
|
||||
}
|
||||
|
||||
f32 n1 = -(y2 - y1);
|
||||
f32 n2 = x2 - x1;
|
||||
f32 n_ = oms_rsqrt(n2 * n2 + n1 * n1);
|
||||
f32 n1 = -(end.y - start.y);
|
||||
f32 n2 = end.x - start.x;
|
||||
f32 n_ = intrin_rsqrt_f32(n2 * n2 + n1 * n1);
|
||||
f32 norm1 = n1 * n_;
|
||||
f32 norm2 = n2 * n_;
|
||||
|
||||
vertex_degenerate_create(vertices, index, zindex, x1, y1);
|
||||
int32 idx = vertex_degenerate_create(vertices, zindex, start.x, start.y);
|
||||
|
||||
int32 idx = *index;
|
||||
vertices[idx++] = {{start.x, start.y, zindex}, {-((f32) rgba), 0.0f}};
|
||||
vertices[idx++] = {{start.x + thickness * norm1, start.y + thickness * norm2, zindex}, {-((f32) rgba), 0.0f}};
|
||||
vertices[idx++] = {{end.x, end.y, zindex}, {-((f32) rgba), 0.0f}};
|
||||
vertices[idx++] = {{end.x + thickness * norm1, end.y + thickness * norm2, zindex}, {-((f32) rgba), 0.0f}};
|
||||
|
||||
vertices[idx++] = {{x1, y1, zindex}, {tex_x1, tex_y1}, color_index};
|
||||
vertices[idx++] = {{x1 + thickness * norm1, y1 + thickness * norm2, zindex}, {tex_x1, tex_y2}, color_index};
|
||||
vertices[idx++] = {{x2, y2, zindex}, {tex_x2, tex_y1}, color_index};
|
||||
vertices[idx++] = {{x2 + thickness * norm1, y2 + thickness * norm2, zindex}, {tex_x2, tex_y2}, color_index};
|
||||
|
||||
*index = idx;
|
||||
return idx;
|
||||
}
|
||||
|
||||
// @performance Do we really want to create the UI as one continuous mesh?
|
||||
// Individual meshes without degenerates might be faster
|
||||
inline
|
||||
void vertex_rect_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
|
||||
f32 color_index = 0, f32 tex_x1 = 0.0f, f32 tex_y1 = 0.0f, f32 tex_x2 = 0.0f, f32 tex_y2 = 0.0f
|
||||
int32 vertex_rect_create(
|
||||
Vertex3DTextureColor* __restrict vertices, f32 zindex,
|
||||
v4_f32 dimension, byte alignment,
|
||||
uint32 rgba = 0, v2_f32 tex1 = {0}, v2_f32 tex2 = {0}
|
||||
) {
|
||||
if (align_h == UI_ALIGN_H_RIGHT) {
|
||||
x -= width;
|
||||
} else if (align_h == UI_ALIGN_H_CENTER) {
|
||||
x -= width / 2;
|
||||
if (alignment) {
|
||||
adjust_aligned_position(&dimension.x, &dimension.y, dimension.width, dimension.height, alignment);
|
||||
}
|
||||
|
||||
if (align_v == UI_ALIGN_V_TOP) {
|
||||
y -= height;
|
||||
} else if (align_v == UI_ALIGN_V_CENTER) {
|
||||
y -= height / 2;
|
||||
if (rgba) {
|
||||
tex1.x = -((f32) rgba);
|
||||
tex2.x = -((f32) rgba);
|
||||
}
|
||||
|
||||
vertex_degenerate_create(vertices, index, zindex, x, y);
|
||||
int32 idx = vertex_degenerate_create(vertices, zindex, dimension.x, dimension.y);
|
||||
|
||||
f32 y_height = y + height;
|
||||
f32 x_width = x + width;
|
||||
f32 y_height = dimension.y + dimension.height;
|
||||
f32 x_width = dimension.x + dimension.width;
|
||||
|
||||
// Rectangle
|
||||
int32 idx = *index;
|
||||
vertices[idx++] = {{dimension.x, dimension.y, zindex}, {tex1.x, tex1.y}};
|
||||
vertices[idx++] = {{dimension.x, y_height, zindex}, {tex1.x, tex2.y}};
|
||||
vertices[idx++] = {{x_width, dimension.y, zindex}, {tex2.x, tex1.y}};
|
||||
vertices[idx++] = {{x_width, y_height, zindex}, {tex2.x, tex2.y}};
|
||||
|
||||
vertices[idx++] = {{x, y, zindex}, {tex_x1, tex_y1}, color_index};
|
||||
vertices[idx++] = {{x, y_height, zindex}, {tex_x1, tex_y2}, color_index};
|
||||
vertices[idx++] = {{x_width, y, zindex}, {tex_x2, tex_y1}, color_index};
|
||||
vertices[idx++] = {{x_width, y_height, zindex}, {tex_x2, tex_y2}, color_index};
|
||||
|
||||
*index = idx;
|
||||
}
|
||||
|
||||
// @todo also allow background -> we can benefit from reduced vertex count
|
||||
// All we have to do is add 3 more vertices (= inside vertices)
|
||||
inline
|
||||
void vertex_rect_border_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
f32 x, f32 y, f32 width, f32 height, f32 thickness, int32 align_h, int32 align_v,
|
||||
f32 color_index = 0, f32 tex_x1 = 0.0f, f32 tex_y1 = 0.0f, f32 tex_x2 = 0.0f, f32 tex_y2 = 0.0f
|
||||
) {
|
||||
if (align_h == UI_ALIGN_H_RIGHT) {
|
||||
x -= width;
|
||||
} else if (align_h == UI_ALIGN_H_CENTER) {
|
||||
x -= width / 2;
|
||||
}
|
||||
|
||||
if (align_v == UI_ALIGN_V_TOP) {
|
||||
y -= height;
|
||||
} else if (align_v == UI_ALIGN_V_CENTER) {
|
||||
y -= height / 2;
|
||||
}
|
||||
|
||||
vertex_degenerate_create(vertices, index, zindex, x, y);
|
||||
|
||||
// @bug While this works for the whole rectangle it doesn't work for individual borders
|
||||
// @todo We need a version where you can define individual borders
|
||||
|
||||
f32 y_height = y + height;
|
||||
f32 y_thickness = y + thickness;
|
||||
f32 x_width = x + width;
|
||||
f32 x_thickness = x + thickness;
|
||||
|
||||
// Rectangle
|
||||
// Top border
|
||||
vertices[*index].position.x = x;
|
||||
vertices[*index].position.y = y;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x1;
|
||||
vertices[*index].tex_coord.y = tex_y1;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x;
|
||||
vertices[*index].position.y = y_thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x1;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x_width;
|
||||
vertices[*index].position.y = y;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y1;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x_width;
|
||||
vertices[*index].position.y = y_thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
// Right border
|
||||
vertices[*index].position.x = x_width - thickness;
|
||||
vertices[*index].position.y = y_thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x_width;
|
||||
vertices[*index].position.y = y_height;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x1;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x_width - thickness;
|
||||
vertices[*index].position.y = y_height;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y1;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
// Bottom border
|
||||
vertices[*index].position.x = x_width - thickness;
|
||||
vertices[*index].position.y = y_height - thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x;
|
||||
vertices[*index].position.y = y_height;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x1;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x;
|
||||
vertices[*index].position.y = y_height - thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y1;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
// Left border
|
||||
vertices[*index].position.x = x_thickness;
|
||||
vertices[*index].position.y = y_height - thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x;
|
||||
vertices[*index].position.y = y_thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x1;
|
||||
vertices[*index].tex_coord.y = tex_y2;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
|
||||
vertices[*index].position.x = x_thickness;
|
||||
vertices[*index].position.y = y_thickness;
|
||||
vertices[*index].position.z = zindex;
|
||||
vertices[*index].tex_coord.x = tex_x2;
|
||||
vertices[*index].tex_coord.y = tex_y1;
|
||||
vertices[*index].color = color_index;
|
||||
++(*index);
|
||||
}
|
||||
|
||||
void vertex_input(Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
|
||||
f32 color_index = 0, f32 tex_x1 = 0.0f, f32 tex_y1 = 0.0f, f32 tex_x2 = 0.0f, f32 tex_y2 = 0.0f
|
||||
)
|
||||
{
|
||||
vertex_rect_border_create(
|
||||
vertices, index, zindex,
|
||||
x, y, width, height, 1, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
12, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
x + 1, y + 1, width - 2, height - 2, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
14, 0.0f, 0.0f
|
||||
);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline
|
||||
|
|
@ -365,36 +210,30 @@ void text_calculate_dimensions(
|
|||
// we might want to implement distance field font atlas
|
||||
// @todo We should be able to cut off text at an arbitrary position, not just at a line_height incremental
|
||||
// we could probably get the MIN of the glyph height and the remaining window height
|
||||
v2_f32 vertex_text_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
|
||||
const Font* __restrict font, const char* __restrict text, f32 size, f32 color_index = 0
|
||||
v3_int32 vertex_text_create(
|
||||
Vertex3DTextureColor* __restrict vertices, f32 zindex,
|
||||
v4_f32 dimension, byte alignment,
|
||||
const Font* __restrict font, const char* __restrict text,
|
||||
f32 size, uint32 rgba = 0,
|
||||
f32 font_weight = 1.0f
|
||||
) {
|
||||
int32 length = utf8_strlen(text);
|
||||
bool is_ascii = (int32) strlen(text) == length;
|
||||
f32 scale = size / font->size;
|
||||
|
||||
// If we do a different alignment we need to pre-calculate the width and height
|
||||
if (align_h != 0 || align_v != 0) {
|
||||
if (align_h != 0 && align_v != 0) {
|
||||
text_calculate_dimensions(&width, &height, font, text, is_ascii, scale, length);
|
||||
} else if (align_h != 0) {
|
||||
width = text_calculate_dimensions_width(font, text, is_ascii, scale, length);
|
||||
if (alignment & (UI_ALIGN_H_RIGHT | UI_ALIGN_H_CENTER | UI_ALIGN_V_TOP | UI_ALIGN_V_CENTER)) {
|
||||
if ((alignment & (UI_ALIGN_H_RIGHT | UI_ALIGN_H_CENTER))
|
||||
&& (alignment & (UI_ALIGN_V_TOP | UI_ALIGN_V_CENTER))
|
||||
) {
|
||||
text_calculate_dimensions(&dimension.width, &dimension.height, font, text, is_ascii, scale, length);
|
||||
} else if (alignment & (UI_ALIGN_H_RIGHT | UI_ALIGN_H_CENTER)) {
|
||||
dimension.width = text_calculate_dimensions_width(font, text, is_ascii, scale, length);
|
||||
} else {
|
||||
height = text_calculate_dimensions_height(font, text, scale, length);
|
||||
dimension.height = text_calculate_dimensions_height(font, text, scale, length);
|
||||
}
|
||||
|
||||
if (align_h == UI_ALIGN_H_RIGHT) {
|
||||
x -= width;
|
||||
} else if (align_h == UI_ALIGN_H_CENTER) {
|
||||
x -= width / 2;
|
||||
}
|
||||
|
||||
if (align_v == UI_ALIGN_V_TOP) {
|
||||
y -= height;
|
||||
} else if (align_v == UI_ALIGN_V_CENTER) {
|
||||
y -= height / 2;
|
||||
}
|
||||
adjust_aligned_position(&dimension.x, &dimension.y, dimension.width, dimension.height, alignment);
|
||||
}
|
||||
|
||||
f32 line_height_scaled = font->line_height * scale;
|
||||
|
|
@ -402,15 +241,17 @@ v2_f32 vertex_text_create(
|
|||
f32 rendered_width = 0;
|
||||
f32 rendered_height = line_height_scaled;
|
||||
|
||||
f32 offset_x = x;
|
||||
int32 idx = 0;
|
||||
|
||||
f32 offset_x = dimension.x;
|
||||
for (int32 i = 0; i < length; ++i) {
|
||||
int32 character = is_ascii ? text[i] : utf8_get_char_at(text, i);
|
||||
if (character == '\n') {
|
||||
rendered_height += line_height_scaled;
|
||||
rendered_width = OMS_MAX(rendered_width, offset_x - x);
|
||||
rendered_width = OMS_MAX(rendered_width, offset_x - dimension.x);
|
||||
|
||||
y -= line_height_scaled;
|
||||
offset_x = x;
|
||||
dimension.y -= line_height_scaled;
|
||||
offset_x = dimension.x;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
|
@ -420,15 +261,17 @@ v2_f32 vertex_text_create(
|
|||
continue;
|
||||
}
|
||||
|
||||
f32 offset_y = y + glyph->metrics.offset_y * scale;
|
||||
f32 offset_y = dimension.y + glyph->metrics.offset_y * scale;
|
||||
offset_x += glyph->metrics.offset_x * scale;
|
||||
|
||||
// @performance Consider to handle whitespaces just by offsetting
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
offset_x, offset_y, glyph->metrics.width * scale, glyph->metrics.height * scale, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
color_index, glyph->coords.x1, glyph->coords.y1, glyph->coords.x2, glyph->coords.y2
|
||||
);
|
||||
if (character != ' ' && character != '\t') {
|
||||
// @todo We should probably inline the code here, we might be able to even optimize it then
|
||||
idx += vertex_rect_create(
|
||||
vertices, zindex,
|
||||
{offset_x, offset_y, glyph->metrics.width * scale, glyph->metrics.height * scale}, 0,
|
||||
rgba, glyph->coords.start, glyph->coords.end
|
||||
);
|
||||
}
|
||||
|
||||
offset_x += (glyph->metrics.width + glyph->metrics.advance_x) * scale;
|
||||
}
|
||||
|
|
@ -439,150 +282,7 @@ v2_f32 vertex_text_create(
|
|||
// This way we can ensure no overflow easily
|
||||
// @todo implement line alignment, currently only total alignment is considered
|
||||
|
||||
return {rendered_width, rendered_height};
|
||||
return {(int32) rendered_width, (int32) rendered_height, idx};
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_world_space(f32* world_space, const f32* local_space, const f32* model_mat)
|
||||
{
|
||||
mat4vec4_mult(model_mat, local_space, world_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_view_space(f32* view_space, const f32* world_space, const f32* view_mat)
|
||||
{
|
||||
mat4vec4_mult(view_mat, world_space, view_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_clip_space(f32* clip_space, const f32* view_space, const f32* projection_mat)
|
||||
{
|
||||
mat4vec4_mult(projection_mat, view_space, clip_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_clip_space_mat(f32* result_mat, const f32* model_mat, const f32* view_mat, const f32* projection_mat)
|
||||
{
|
||||
f32 temp[16];
|
||||
mat4mat4_mult(projection_mat, view_mat, temp);
|
||||
mat4mat4_mult(temp, model_mat, result_mat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the matrix used to transform from local space to clip space
|
||||
*
|
||||
* This allows us to transform multiple objects with the same matrix
|
||||
*
|
||||
* Vclip = Mprojection * Mview * Mmodel * Vlocal
|
||||
*/
|
||||
void entity_clip_space_mat_sse(f32* result_mat, const f32* model_mat, const f32* view_mat, const f32* projection_mat)
|
||||
{
|
||||
__m128 temp[4];
|
||||
|
||||
__m128 a[4];
|
||||
__m128 b[4];
|
||||
|
||||
a[0] = _mm_load_ps(projection_mat);
|
||||
a[1] = _mm_load_ps(&projection_mat[4]);
|
||||
a[2] = _mm_load_ps(&projection_mat[8]);
|
||||
a[3] = _mm_load_ps(&projection_mat[12]);
|
||||
|
||||
b[0] = _mm_load_ps(view_mat);
|
||||
b[1] = _mm_load_ps(&view_mat[4]);
|
||||
b[2] = _mm_load_ps(&view_mat[8]);
|
||||
b[3] = _mm_load_ps(&view_mat[12]);
|
||||
_MM_TRANSPOSE4_PS(b[0], b[1], b[2], b[3]);
|
||||
|
||||
mat4mat4_mult_sse(a, b, temp);
|
||||
|
||||
a[0] = temp[0];
|
||||
a[1] = temp[1];
|
||||
a[2] = temp[2];
|
||||
a[3] = temp[3];
|
||||
|
||||
b[0] = _mm_load_ps(model_mat);
|
||||
b[1] = _mm_load_ps(&model_mat[4]);
|
||||
b[2] = _mm_load_ps(&model_mat[8]);
|
||||
b[3] = _mm_load_ps(&model_mat[12]);
|
||||
_MM_TRANSPOSE4_PS(b[0], b[1], b[2], b[3]);
|
||||
|
||||
mat4mat4_mult_sse(a, b, temp);
|
||||
_mm_store_ps(&result_mat[0], temp[0]);
|
||||
_mm_store_ps(&result_mat[4], temp[1]);
|
||||
_mm_store_ps(&result_mat[8], temp[2]);
|
||||
_mm_store_ps(&result_mat[12], temp[3]);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_clip_space_from_local(f32* clip_space, const f32* local_space, const f32* mat)
|
||||
{
|
||||
mat4vec4_mult(mat, local_space, clip_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_clip_space_from_local_sse(f32* clip_space, const f32* local_space, const f32* mat)
|
||||
{
|
||||
mat4vec4_mult_sse(mat, local_space, clip_space);
|
||||
}
|
||||
|
||||
/*
|
||||
inline
|
||||
void entity_screen_space(f32* screen_space, const f32* clip_space, const f32* viewport_mat)
|
||||
{
|
||||
// @todo implement
|
||||
}
|
||||
*/
|
||||
|
||||
inline
|
||||
void entity_world_space_sse(f32* world_space, const f32* local_space, const f32* model_mat)
|
||||
{
|
||||
mat4vec4_mult_sse(model_mat, local_space, world_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_view_space_sse(f32* view_space, const f32* world_space, const f32* view_mat)
|
||||
{
|
||||
mat4vec4_mult_sse(view_mat, world_space, view_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_clip_space_sse(f32* clip_space, const f32* view_space, const f32* projection_mat)
|
||||
{
|
||||
mat4vec4_mult_sse(projection_mat, view_space, clip_space);
|
||||
}
|
||||
|
||||
/*
|
||||
inline
|
||||
void entity_screen_space_sse(f32* screen_space, const f32* clip_space, const f32* viewport_mat)
|
||||
{
|
||||
// @todo implement
|
||||
}
|
||||
*/
|
||||
|
||||
inline
|
||||
void entity_world_space_sse(__m128* world_space, const __m128* local_space, const __m128* model_mat)
|
||||
{
|
||||
mat4vec4_mult_sse(model_mat, local_space, world_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_view_space_sse(__m128* view_space, const __m128* world_space, const __m128* view_mat)
|
||||
{
|
||||
mat4vec4_mult_sse(view_mat, world_space, view_space);
|
||||
}
|
||||
|
||||
inline
|
||||
void entity_clip_space_sse(__m128* clip_space, const __m128* view_space, const __m128* projection_mat)
|
||||
{
|
||||
mat4vec4_mult_sse(projection_mat, view_space, clip_space);
|
||||
}
|
||||
|
||||
/*
|
||||
inline
|
||||
void entity_screen_space_sse(__m128* screen_space, const __m128* clip_space, const __m128* viewport_mat)
|
||||
{
|
||||
// @todo implement
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
252
gpuapi/UIUtils.h
252
gpuapi/UIUtils.h
|
|
@ -1,252 +0,0 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_GPUAPI_UI_UTILS_H
|
||||
#define TOS_GPUAPI_UI_UTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void ui_input_create(Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
f32 x, f32 y, f32 width, f32 height, int32 align_h, int32 align_v,
|
||||
uint32 color_index = 0, f32 tex_x1 = 0.0f, f32 tex_y1 = 0.0f, f32 tex_x2 = 0.0f, f32 tex_y2 = 0.0f
|
||||
)
|
||||
{
|
||||
vertex_rect_border_create(
|
||||
vertices, index, zindex,
|
||||
x, y, width, height, 1, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
12, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
x + 1, y + 1, width - 2, height - 2, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
14, 0.0f, 0.0f
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// @todo implement shadow (offset + angle + diffuse) or should this be a shader only thing? if so this would be a problem for us since we are handling text in the same shader as simple shapes
|
||||
// we might want to implement distance field font atlas
|
||||
f32 ui_text_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
UIThemeStyle* theme, UIElement* element
|
||||
) {
|
||||
if (element->vertex_count > 0) {
|
||||
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
|
||||
return vertices[element->vertex_count - 1].position.x;
|
||||
}
|
||||
|
||||
// @performance see comment for setup_theme()
|
||||
|
||||
// Load element data
|
||||
HashEntryVoidP* element_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, element->name, element->id);
|
||||
UIAttributeGroup* element_group = (UIAttributeGroup *) element_entry->value;
|
||||
|
||||
// Load general style
|
||||
UIAttribute* style = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_STYLE);
|
||||
HashEntryVoidP* style_entry = NULL;
|
||||
UIAttributeGroup* style_group = NULL;
|
||||
|
||||
if (style) {
|
||||
style_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, style->value_str);
|
||||
style_group = (UIAttributeGroup *) style_entry->value;
|
||||
}
|
||||
|
||||
UIAttribute* x;
|
||||
UIAttribute* y;
|
||||
|
||||
// Load parent data (for position data)
|
||||
UIAttribute* parent = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||
if (parent) {
|
||||
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, parent->value_str);
|
||||
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||
|
||||
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
|
||||
// @question Do we have more values which can be inherited from the parent?
|
||||
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||
} else {
|
||||
x = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
}
|
||||
|
||||
UIAttribute* width = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||
UIAttribute* height = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||
UIAttribute* align_h = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||
UIAttribute* align_v = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||
UIAttribute* text = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||
UIAttribute* size = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||
UIAttribute* color_index = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||
|
||||
int32 length = utf8_strlen(text->value_str);
|
||||
bool is_ascii = strlen(text->value_str) == length;
|
||||
f32 scale = size->value_float / theme->font->size;
|
||||
|
||||
// If we do a different alignment we need to pre-calculate the width and height
|
||||
if (align_h != NULL || align_v != NULL) {
|
||||
f32 tmp_width = (f32) width->value_int;
|
||||
f32 tmp_height = (f32) height->value_int;
|
||||
|
||||
if (align_h != NULL && align_v != NULL) {
|
||||
text_calculate_dimensions(&tmp_width, &tmp_height, theme->font, text->value_str, is_ascii, scale, length);
|
||||
} else if (align_h != NULL) {
|
||||
tmp_width = text_calculate_dimensions_width(theme->font, text->value_str, is_ascii, scale, length);
|
||||
} else {
|
||||
tmp_height = text_calculate_dimensions_height(theme->font, text->value_str, scale, length);
|
||||
}
|
||||
|
||||
if (align_h->value_int == UI_ALIGN_H_RIGHT) {
|
||||
x -= width->value_int;
|
||||
} else if (align_h->value_int == UI_ALIGN_H_CENTER) {
|
||||
x -= width->value_int / 2;
|
||||
}
|
||||
|
||||
if (align_v->value_int == UI_ALIGN_V_TOP) {
|
||||
y -= height->value_int;
|
||||
} else if (align_v->value_int == UI_ALIGN_V_CENTER) {
|
||||
y -= height->value_int / 2;
|
||||
}
|
||||
}
|
||||
|
||||
int32 start = *index;
|
||||
f32 offset_x = (f32) x->value_int;
|
||||
f32 offset_y = (f32) y->value_int;
|
||||
|
||||
int32 first_char = is_ascii ? text->value_str[0] : utf8_get_char_at(text->value_str, 0);
|
||||
for (int32 i = (first_char == '\n' ? 1 : 0); i < length; ++i) {
|
||||
int32 character = is_ascii ? text->value_str[i] : utf8_get_char_at(text->value_str, i);
|
||||
|
||||
if (character == '\n') {
|
||||
offset_y += theme->font->line_height * scale;
|
||||
offset_x = (f32) x->value_int;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Glyph* glyph = font_glyph_find(theme->font, character);
|
||||
if (!glyph) {
|
||||
continue;
|
||||
}
|
||||
|
||||
f32 offset_y2 = offset_y + glyph->metrics.offset_y * scale;
|
||||
offset_x += glyph->metrics.offset_x * scale;
|
||||
|
||||
// @performance Consider to handle whitespaces just by offsetting
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
offset_x, offset_y2, glyph->metrics.width * scale, glyph->metrics.height * scale, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
color_index->value_float, glyph->coords.x1, glyph->coords.y1, glyph->coords.x2, glyph->coords.y2
|
||||
);
|
||||
|
||||
offset_x += (glyph->metrics.width + glyph->metrics.advance_x) * scale;
|
||||
}
|
||||
|
||||
element->vertex_count = *index - start;
|
||||
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
|
||||
// @todo See todo of vertex_text function
|
||||
// @performance use elements->vertices and also cache result in there
|
||||
|
||||
return offset_x;
|
||||
}
|
||||
|
||||
void ui_button_create(
|
||||
Vertex3DTextureColorIndex* __restrict vertices, uint32* __restrict index, f32 zindex,
|
||||
UIThemeStyle* theme, UIElement* element
|
||||
)
|
||||
{
|
||||
// @todo handle different states and ongoing animations
|
||||
// We cannot return early in such cases
|
||||
if (element->vertex_count > 0) {
|
||||
memcpy(vertices + *index, element->vertices, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// @performance see comment for setup_theme()
|
||||
|
||||
// Load element data
|
||||
HashEntryVoidP* element_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, element->name, element->id);
|
||||
UIAttributeGroup* element_group = (UIAttributeGroup *) element_entry->value;
|
||||
|
||||
// Load general style
|
||||
UIAttribute* style = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_STYLE);
|
||||
HashEntryVoidP* style_entry = NULL;
|
||||
UIAttributeGroup* style_group = NULL;
|
||||
|
||||
if (style) {
|
||||
style_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, style->value_str);
|
||||
style_group = (UIAttributeGroup *) style_entry->value;
|
||||
}
|
||||
|
||||
UIAttribute* x;
|
||||
UIAttribute* y;
|
||||
|
||||
// Load parent data (for position data)
|
||||
UIAttribute* parent = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_PARENT);
|
||||
if (parent) {
|
||||
HashEntryVoidP* parent_entry = (HashEntryVoidP *) hashmap_get_entry(&theme->hash_map, parent->value_str);
|
||||
UIAttributeGroup* parent_group = (UIAttributeGroup *) parent_entry->value;
|
||||
|
||||
x = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(parent_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
|
||||
// @question Do we have more values which can be inherited from the parent?
|
||||
// We don't want to inherit implicit stuff like size, background etc. These things should be defined explicitly
|
||||
} else {
|
||||
x = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
y = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_POSITION_Y);
|
||||
}
|
||||
|
||||
UIAttribute* width = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH);
|
||||
UIAttribute* height = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT);
|
||||
UIAttribute* align_h = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_H);
|
||||
UIAttribute* align_v = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_ALIGN_V);
|
||||
UIAttribute* text = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_CONTENT);
|
||||
UIAttribute* size = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_SIZE);
|
||||
UIAttribute* color_index = ui_attribute_from_group(element_group, UI_ATTRIBUTE_TYPE_FONT_COLOR);
|
||||
|
||||
// @todo Above we only handle the default values, what about state dependent values like hover, active?
|
||||
// Simply check the state here and load the child_entries based on the state
|
||||
// However, for that we need the current state of the button... should this be in a separate button object,
|
||||
// that also holds position information for hover checks etc. Or should that state be stored in the theme data?
|
||||
// Right now we could make these checks right here anyway but in the future we don't want to update the rendering data
|
||||
// every frame if we don't have to. We don't want immediate mode! We only want to update the UI if there is a change.
|
||||
// If a change (or state change like hover) triggers a complete update of all elements or just a sub region update
|
||||
// remains TBD
|
||||
|
||||
int32 start = *index;
|
||||
|
||||
vertex_rect_border_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float, y->value_float, width->value_float, height->value_float, 1, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
12, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
vertex_rect_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float + 1, y->value_float + 1, width->value_float - 2, height->value_float - 2, UI_ALIGN_H_LEFT, UI_ALIGN_V_BOTTOM,
|
||||
14, 0.0f, 0.0f
|
||||
);
|
||||
|
||||
zindex = nextafterf(zindex, INFINITY);
|
||||
|
||||
vertex_text_create(
|
||||
vertices, index, zindex,
|
||||
x->value_float, y->value_float, width->value_float, height->value_float, align_h->value_int, align_v->value_int,
|
||||
theme->font, text->value_str, size->value_float, color_index->value_float
|
||||
);
|
||||
|
||||
element->vertex_count = *index - start;
|
||||
memcpy(element->vertices, vertices + start, sizeof(Vertex3DTextureColorIndex) * element->vertex_count);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -858,6 +858,7 @@ void opengl_init(Window* window, int32 multisample = 0)
|
|||
|
||||
opengl_init_wgl();
|
||||
|
||||
// @question Why do we do the GetDC here? Couldn't we do it in UtilsWindows.h
|
||||
window->hdc = GetDC(window->hwnd);
|
||||
set_pixel_format(window->hdc, multisample);
|
||||
|
||||
|
|
|
|||
76
hash/Crc.h
Normal file
76
hash/Crc.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_HASH_CRC_H
|
||||
#define TOS_HASH_CRC_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../architecture/Intrinsics.h"
|
||||
|
||||
inline
|
||||
uint32 crc32_software_u8(const byte* data, size_t length) {
|
||||
uint32 crc = 0xFFFFFFFF;
|
||||
|
||||
// Standard CRC-32 polynomial
|
||||
uint32 polynomial = 0xEDB88320;
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
crc ^= data[i];
|
||||
for (byte bit = 0; bit < 8; ++bit) {
|
||||
crc = (crc >> 1);
|
||||
|
||||
if (crc & 1) {
|
||||
crc ^= polynomial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 crc32_intrin_u8(const byte* data, size_t length) {
|
||||
uint32 crc = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
crc = intrin_crc32_u8(crc, data[i]);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 crc32_intrin_u16(const uint16* data, size_t length) {
|
||||
uint32 crc = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
crc = intrin_crc32_u16(crc, data[i]);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 crc32_intrin_u32(const uint32* data, size_t length) {
|
||||
uint32 crc = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
crc = intrin_crc32_u32(crc, data[i]);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 crc32_intrin_u64(const uint64* data, size_t length) {
|
||||
uint64 crc = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
crc = intrin_crc32_u64(crc, data[i]);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -222,4 +222,18 @@ uint64 hash_ejb_seeded(const char* str, int32 seed)
|
|||
return (hash % PRIME2) ^ (seed + (seed << 6) + (seed >> 2));;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 intrin_hash(uint64 a, uint64 b = 0)
|
||||
{
|
||||
uint8 seed[16] = {
|
||||
0xaa, 0x9b, 0xbd, 0xb8, 0xa1, 0x98, 0xac, 0x3f, 0x1f, 0x94, 0x07, 0xb3, 0x8c, 0x27, 0x93, 0x69,
|
||||
};
|
||||
|
||||
__m128i hash = _mm_set_epi64x(a, b);
|
||||
hash = _mm_aesdec_si128(hash, _mm_loadu_si128((__m128i *) seed));
|
||||
hash = _mm_aesdec_si128(hash, _mm_loadu_si128((__m128i *) seed));
|
||||
|
||||
return _mm_extract_epi32(hash, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
#include "Bitmap.h"
|
||||
#include "Png.h"
|
||||
|
||||
void image_from_file(Image* image, const char* path, RingMemory* ring)
|
||||
void image_from_file(Image* __restrict image, const char* __restrict path, RingMemory* __restrict ring)
|
||||
{
|
||||
FileBody file;
|
||||
file_read(path, &file, ring);
|
||||
|
|
@ -32,7 +32,7 @@ void image_from_file(Image* image, const char* path, RingMemory* ring)
|
|||
}
|
||||
}
|
||||
|
||||
void image_flip_vertical(RingMemory* ring, Image* image)
|
||||
void image_flip_vertical(RingMemory* __restrict ring, Image* __restrict image)
|
||||
{
|
||||
uint32 stride = image->width * sizeof(uint32);
|
||||
byte* temp = ring_get_memory(ring, image->pixel_count * sizeof(uint32));
|
||||
|
|
@ -66,7 +66,7 @@ int32 image_data_size(const Image* image)
|
|||
}
|
||||
|
||||
inline
|
||||
uint32 image_header_from_data(const byte* data, Image* image)
|
||||
uint32 image_header_from_data(const byte* __restrict data, Image* __restrict image)
|
||||
{
|
||||
const byte* start = data;
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ uint32 image_header_from_data(const byte* data, Image* image)
|
|||
return (int32) (data - start);
|
||||
}
|
||||
|
||||
uint32 image_from_data(const byte* data, Image* image)
|
||||
uint32 image_from_data(const byte* __restrict data, Image* __restrict image)
|
||||
{
|
||||
const byte* pos = data;
|
||||
pos += image_header_from_data(data, image);
|
||||
|
|
@ -97,7 +97,7 @@ uint32 image_from_data(const byte* data, Image* image)
|
|||
}
|
||||
|
||||
inline
|
||||
uint32 image_header_to_data(const Image* image, byte* data)
|
||||
uint32 image_header_to_data(const Image* __restrict image, byte* __restrict data)
|
||||
{
|
||||
byte* start = data;
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ uint32 image_header_to_data(const Image* image, byte* data)
|
|||
return (int32) (data - start);
|
||||
}
|
||||
|
||||
uint32 image_to_data(const Image* image, byte* data)
|
||||
uint32 image_to_data(const Image* __restrict image, byte* __restrict data)
|
||||
{
|
||||
byte* pos = data;
|
||||
pos += image_header_to_data(image, data);
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@
|
|||
|
||||
#define LANGUAGE_VERSION 1
|
||||
|
||||
// Size is limited to 4GB
|
||||
struct Language {
|
||||
// WARNING: the actual start of data is data -= sizeof(count); see file loading below
|
||||
// WARNING: the actual start of data is data -= sizeof(Language); see file loading below
|
||||
// The reason for this is we store the Language struct in the beginning of the data/file
|
||||
byte* data;
|
||||
|
||||
int32 count;
|
||||
int64 size;
|
||||
int32 size;
|
||||
char** lang;
|
||||
};
|
||||
|
||||
|
|
@ -27,7 +29,7 @@ void language_from_file_txt(
|
|||
|
||||
// count elements
|
||||
language->count = 1;
|
||||
int64 len = 0;
|
||||
int32 len = 0;
|
||||
|
||||
byte* data = file.content;
|
||||
|
||||
|
|
@ -65,7 +67,7 @@ int32 language_data_size(const Language* language)
|
|||
return (int32) (language->size
|
||||
+ sizeof(language->count)
|
||||
+ sizeof(language->size)
|
||||
+ language->count * sizeof(uint64)
|
||||
+ language->count * sizeof(uint32)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -92,12 +94,12 @@ int32 language_from_data(
|
|||
|
||||
// Load pointers/offsets
|
||||
for (int32 i = 0; i < language->count; ++i) {
|
||||
*pos_lang++ = (char *) (start + SWAP_ENDIAN_LITTLE(*((uint64 *) pos)));
|
||||
pos += sizeof(uint64);
|
||||
*pos_lang++ = (char *) (start + SWAP_ENDIAN_LITTLE(*((uint32 *) pos)));
|
||||
pos += sizeof(uint32);
|
||||
}
|
||||
|
||||
memcpy(
|
||||
language->data + language->count * sizeof(uint64),
|
||||
language->data + language->count * sizeof(uint32),
|
||||
pos,
|
||||
language->size
|
||||
);
|
||||
|
|
@ -123,14 +125,14 @@ int32 language_to_data(
|
|||
|
||||
// Save pointers
|
||||
for (int32 i = 0; i < language->count; ++i) {
|
||||
*((uint64 *) pos) = SWAP_ENDIAN_LITTLE(pos - start);
|
||||
pos += sizeof(uint64);
|
||||
*((uint32 *) pos) = SWAP_ENDIAN_LITTLE((uint32) ((uintptr_t) pos - (uintptr_t) start));
|
||||
pos += sizeof(uint32);
|
||||
}
|
||||
|
||||
// Save actual strings
|
||||
memcpy(
|
||||
pos,
|
||||
language->data + language->count * sizeof(uint64),
|
||||
language->data + language->count * sizeof(uint32),
|
||||
language->size
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,8 @@
|
|||
#include "../utils/StringUtils.h"
|
||||
#include "../utils/TestUtils.h"
|
||||
#include "../thread/Atomic.h"
|
||||
|
||||
// Required for rdtsc();
|
||||
#if _WIN32
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
#include "../architecture/Intrinsics.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
global_persist DebugContainer* debug_container = NULL;
|
||||
|
||||
|
|
@ -93,7 +88,7 @@ void log_to_file()
|
|||
inline
|
||||
void update_timing_stat(uint32 stat, const char* function)
|
||||
{
|
||||
uint64 new_tick_count = __rdtsc();
|
||||
uint64 new_tick_count = intrin_timestamp_counter();
|
||||
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
|
|
@ -109,14 +104,14 @@ inline
|
|||
void update_timing_stat_start(uint32 stat, const char*)
|
||||
{
|
||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||
debug_container->perf_stats[stat].old_tick_count = __rdtsc();
|
||||
debug_container->perf_stats[stat].old_tick_count = intrin_timestamp_counter();
|
||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||
}
|
||||
|
||||
inline
|
||||
void update_timing_stat_end(uint32 stat, const char* function)
|
||||
{
|
||||
uint64 new_tick_count = __rdtsc();
|
||||
uint64 new_tick_count = intrin_timestamp_counter();
|
||||
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
|
|
@ -131,7 +126,7 @@ void update_timing_stat_end(uint32 stat, const char* function)
|
|||
inline
|
||||
void update_timing_stat_end_continued(uint32 stat, const char* function)
|
||||
{
|
||||
uint64 new_tick_count = __rdtsc();
|
||||
uint64 new_tick_count = intrin_timestamp_counter();
|
||||
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
|
|
@ -241,7 +236,7 @@ void debug_memory_log(uintptr_t start, uint64 size, int32 type, const char* func
|
|||
dmr->start = start - mem->start;
|
||||
dmr->size = size;
|
||||
|
||||
dmr->time = __rdtsc();
|
||||
dmr->time = intrin_timestamp_counter();
|
||||
dmr->function_name = function;
|
||||
|
||||
if (type < 0 && mem->usage < size * -type) {
|
||||
|
|
@ -273,7 +268,7 @@ void debug_memory_reserve(uintptr_t start, uint64 size, int32 type, const char*
|
|||
dmr->start = start - mem->start;
|
||||
dmr->size = size;
|
||||
|
||||
dmr->time = __rdtsc();
|
||||
dmr->time = intrin_timestamp_counter();
|
||||
dmr->function_name = function;
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +302,7 @@ void debug_memory_reset()
|
|||
}
|
||||
|
||||
// We remove debug information that are "older" than 1GHz
|
||||
uint64 time = __rdtsc() - 1 * GHZ;
|
||||
uint64 time = intrin_timestamp_counter() - 1 * GHZ;
|
||||
|
||||
for (uint64 i = 0; i < debug_container->dmc.memory_element_idx; ++i) {
|
||||
for (int32 j = 0; j < DEBUG_MEMORY_RANGE_MAX; ++j) {
|
||||
|
|
|
|||
14
log/Debug.h
14
log/Debug.h
|
|
@ -32,6 +32,18 @@
|
|||
|
||||
#define LOG_DATA_ARRAY 5
|
||||
|
||||
#ifndef DEBUG_COUNTER
|
||||
#define DEBUG_COUNTER 1
|
||||
enum DebugCounter {
|
||||
DEBUG_COUNTER_MEM_ALLOC,
|
||||
|
||||
DEBUG_COUNTER_DRIVE_READ,
|
||||
DEBUG_COUNTER_DRIVE_WRITE,
|
||||
|
||||
DEBUG_COUNTER_SIZE
|
||||
};
|
||||
#endif
|
||||
|
||||
enum LogDataType {
|
||||
LOG_DATA_NONE,
|
||||
LOG_DATA_VOID,
|
||||
|
|
@ -86,6 +98,8 @@ struct DebugContainer {
|
|||
// Used to log general int values (e.g. counter for draw calls etc.)
|
||||
int64* counter;
|
||||
|
||||
// We are not using FileHandle here since that would require us to include more files
|
||||
// These files in return require Debug.h
|
||||
#if _WIN32
|
||||
HANDLE log_fp;
|
||||
#elif __linux__
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
|
@ -106,6 +107,7 @@ f32 evaluator_apply_operator(char op, f32 a, f32 b) {
|
|||
return a / b;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,7 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "../../utils/TestUtils.h"
|
||||
|
||||
#if ARM
|
||||
#include "../../stdlib/IntrinsicsArm.h"
|
||||
#else
|
||||
#include "../../stdlib/Intrinsics.h"
|
||||
#endif
|
||||
#include "../../architecture/Intrinsics.h"
|
||||
|
||||
// @todo Implement intrinsic versions!
|
||||
|
||||
|
|
@ -29,7 +24,7 @@
|
|||
inline
|
||||
void vec2_normalize(f32* __restrict x, f32* __restrict y)
|
||||
{
|
||||
f32 d = oms_rsqrt((*x) * (*x) + (*y) * (*y));
|
||||
f32 d = intrin_rsqrt_f32((*x) * (*x) + (*y) * (*y));
|
||||
|
||||
*x *= d;
|
||||
*y *= d;
|
||||
|
|
@ -113,7 +108,7 @@ f32 vec3_length(v3_f32* vec)
|
|||
inline
|
||||
void vec3_normalize(f32* __restrict x, f32* __restrict y, f32* __restrict z)
|
||||
{
|
||||
f32 d = oms_rsqrt((*x) * (*x) + (*y) * (*y) + (*z) * (*z));
|
||||
f32 d = intrin_rsqrt_f32((*x) * (*x) + (*y) * (*y) + (*z) * (*z));
|
||||
|
||||
*x *= d;
|
||||
*y *= d;
|
||||
|
|
@ -123,7 +118,7 @@ void vec3_normalize(f32* __restrict x, f32* __restrict y, f32* __restrict z)
|
|||
inline
|
||||
void vec3_normalize(v3_f32* vec)
|
||||
{
|
||||
f32 d = oms_rsqrt(vec->x * vec->x + vec->y * vec->y + vec->z * vec->z);
|
||||
f32 d = intrin_rsqrt_f32(vec->x * vec->x + vec->y * vec->y + vec->z * vec->z);
|
||||
|
||||
vec->x *= d;
|
||||
vec->y *= d;
|
||||
|
|
@ -204,7 +199,7 @@ f32 vec3_dot(const v3_f32* a, const v3_f32* b) {
|
|||
|
||||
void vec4_normalize(f32* __restrict x, f32* __restrict y, f32* __restrict z, f32* __restrict w)
|
||||
{
|
||||
f32 d = oms_rsqrt((*x) * (*x) + (*y) * (*y) + (*z) * (*z) + (*w) * (*w));
|
||||
f32 d = intrin_rsqrt_f32((*x) * (*x) + (*y) * (*y) + (*z) * (*z) + (*w) * (*w));
|
||||
|
||||
*x *= d;
|
||||
*y *= d;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "../stdlib/Types.h"
|
||||
#include "../utils/EndianUtils.h"
|
||||
#include "../utils/TestUtils.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/DebugMemory.h"
|
||||
#include "../system/Allocator.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "../utils/TestUtils.h"
|
||||
#include "../utils/EndianUtils.h"
|
||||
#include "../utils/BitUtils.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/DebugMemory.h"
|
||||
#include "BufferMemory.h"
|
||||
#include "../system/Allocator.h"
|
||||
|
|
@ -125,7 +126,7 @@ void chunk_free(ChunkMemory* buf)
|
|||
}
|
||||
|
||||
inline
|
||||
uint32 chunk_id_from_memory(ChunkMemory* buf, byte* pos) {
|
||||
uint32 chunk_id_from_memory(const ChunkMemory* buf, const byte* pos) {
|
||||
return (uint32) ((uintptr_t) pos - (uintptr_t) buf->memory) / buf->chunk_size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ bool queue_dequeue(Queue* queue, byte* data)
|
|||
inline
|
||||
bool queue_dequeue_atomic(Queue* queue, byte* data)
|
||||
{
|
||||
if (atomic_get_acquire_release((volatile uint64 *) &queue->head) == (uint64) queue->tail) {
|
||||
if ((uint64) atomic_get_acquire_release((void **) &queue->head) == (uint64) queue->tail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "../utils/TestUtils.h"
|
||||
|
||||
#include "BufferMemory.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/DebugMemory.h"
|
||||
#include "../thread/Atomic.h"
|
||||
#include "../thread/Semaphore.h"
|
||||
|
|
@ -270,7 +271,7 @@ bool ring_commit_safe_atomic(const RingMemory* ring, uint64 size, uint32 aligned
|
|||
uint64 max_mem_required = size + aligned * 2;
|
||||
|
||||
// @todo consider to switch to uintptr_t
|
||||
uint64 tail = atomic_get_relaxed((uint64 *) &ring->tail);
|
||||
uint64 tail = (uint64) atomic_get_relaxed((void **) &ring->tail);
|
||||
|
||||
// This doesn't have to be atomic since we assume single producer/consumer and a commit is performed by the consumer
|
||||
uint64 head = (uint64) ring->head;
|
||||
|
|
|
|||
|
|
@ -9,18 +9,24 @@
|
|||
#ifndef TOS_MODELS_CHAT_TYPE_H
|
||||
#define TOS_MODELS_CHAT_TYPE_H
|
||||
|
||||
enum ChatType {
|
||||
CHAT_TYPE_MIXED = 1,
|
||||
CHAT_TYPE_LOCAL = 2,
|
||||
CHAT_TYPE_GLOBAL = 4,
|
||||
CHAT_TYPE_PLAYER = 8,
|
||||
CHAT_TYPE_GROUP = 16,
|
||||
CHAT_TYPE_GUILD = 32,
|
||||
CHAT_TYPE_AUCTION_HOUSE = 64,
|
||||
CHAT_TYPE_TRADE = 128,
|
||||
CHAT_TYPE_PRIVATE = 256, // e.g. direct messages, also used in raid finders etc.
|
||||
CHAT_TYPE_FRIENDS = 512,
|
||||
CHAT_TYPE_CHAT_ROOM = 1024
|
||||
#include "../../stdlib/Types.h"
|
||||
|
||||
// NOT the message type, just where the message is sent to/from
|
||||
enum ChatType : uint16 {
|
||||
CHAT_TYPE_MIXED = 1 << 0,
|
||||
CHAT_TYPE_LOCAL = 1 << 1,
|
||||
CHAT_TYPE_GLOBAL = 1 << 2,
|
||||
CHAT_TYPE_SERVER = 1 << 3,
|
||||
CHAT_TYPE_PLAYER = 1 << 4,
|
||||
CHAT_TYPE_GROUP = 1 << 5,
|
||||
CHAT_TYPE_GUILD = 1 << 6,
|
||||
CHAT_TYPE_AUCTION_HOUSE = 1 << 7,
|
||||
CHAT_TYPE_TRADE = 1 << 8, // When trading with someone
|
||||
CHAT_TYPE_PRIVATE = 1 << 9, // When chatting with someone in private (DM)
|
||||
CHAT_TYPE_FRIENDS = 1 << 10,
|
||||
CHAT_TYPE_CHAT_ROOM = 1 << 11, // Specific chat channels
|
||||
CHAT_TYPE_CHAT_LFG = 1 << 12, // LFG finder
|
||||
CHAT_TYPE_PVP = 1 << 13, // Also Guild vs. Guild
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
#include "../utils/EndianUtils.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include "../stdlib/Simd.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
#define MESH_VERSION 1
|
||||
|
||||
|
|
@ -72,13 +73,13 @@ void mesh_from_file_txt(
|
|||
file_read(path, &file, ring);
|
||||
ASSERT_SIMPLE(file.size);
|
||||
|
||||
char* pos = (char *) file.content;
|
||||
const char* pos = (char *) file.content;
|
||||
|
||||
// move past the version string
|
||||
pos += 8;
|
||||
|
||||
// @todo us version for different handling
|
||||
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||
int32 version = strtol(pos, (char **) &pos, 10); ++pos;
|
||||
|
||||
int32 object_index = 0;
|
||||
int32 group_index = 0;
|
||||
|
|
@ -179,9 +180,9 @@ void mesh_from_file_txt(
|
|||
mesh->vertex_type |= VERTEX_TYPE_POSITION;
|
||||
}
|
||||
|
||||
vertices[vertex_count * 3] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 3 + 1] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 3 + 2] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 3] = strtof(pos, (char **) &pos); ++pos;
|
||||
vertices[vertex_count * 3 + 1] = strtof(pos, (char **) &pos); ++pos;
|
||||
vertices[vertex_count * 3 + 2] = strtof(pos, (char **) &pos); ++pos;
|
||||
|
||||
// has color information
|
||||
// @todo Move to own case statement // 'co'
|
||||
|
|
@ -190,13 +191,13 @@ void mesh_from_file_txt(
|
|||
mesh->vertex_type |= VERTEX_TYPE_COLOR;
|
||||
}
|
||||
|
||||
vertices[vertex_count * 12 + 8] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 12 + 9] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 12 + 10] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 12 + 8] = strtof(pos, (char **) &pos); ++pos;
|
||||
vertices[vertex_count * 12 + 9] = strtof(pos, (char **) &pos); ++pos;
|
||||
vertices[vertex_count * 12 + 10] = strtof(pos, (char **) &pos); ++pos;
|
||||
|
||||
// handle optional alpha [a]
|
||||
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
||||
vertices[vertex_count * 12 + 11] = strtof(pos, &pos); ++pos;
|
||||
vertices[vertex_count * 12 + 11] = strtof(pos, (char **) &pos); ++pos;
|
||||
} else {
|
||||
vertices[vertex_count * 12 + 11] = 1.0f;
|
||||
}
|
||||
|
|
@ -208,30 +209,30 @@ void mesh_from_file_txt(
|
|||
} break;
|
||||
case 2: {
|
||||
// 'vn'
|
||||
normals[normal_count * 3] = strtof(pos, &pos); ++pos;
|
||||
normals[normal_count * 3 + 1] = strtof(pos, &pos); ++pos;
|
||||
normals[normal_count * 3 + 2] = strtof(pos, &pos); ++pos;
|
||||
normals[normal_count * 3] = strtof(pos, (char **) &pos); ++pos;
|
||||
normals[normal_count * 3 + 1] = strtof(pos, (char **) &pos); ++pos;
|
||||
normals[normal_count * 3 + 2] = strtof(pos, (char **) &pos); ++pos;
|
||||
|
||||
++normal_count;
|
||||
} break;
|
||||
case 3: {
|
||||
// 'vt'
|
||||
tex_coords[tex_coord_count * 2] = strtof(pos, &pos); ++pos;
|
||||
tex_coords[tex_coord_count * 2 + 1] = strtof(pos, &pos); ++pos;
|
||||
tex_coords[tex_coord_count * 2] = strtof(pos, (char **) &pos); ++pos;
|
||||
tex_coords[tex_coord_count * 2 + 1] = strtof(pos, (char **) &pos); ++pos;
|
||||
|
||||
++tex_coord_count;
|
||||
} break;
|
||||
case 4: {
|
||||
// 'vp'
|
||||
strtof(pos, &pos); ++pos;
|
||||
strtof(pos, (char **) &pos); ++pos;
|
||||
|
||||
// handle optional [v]
|
||||
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
||||
strtof(pos, &pos); ++pos;
|
||||
strtof(pos, (char **) &pos); ++pos;
|
||||
|
||||
// handle optional [w]
|
||||
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
||||
strtof(pos, &pos); ++pos;
|
||||
strtof(pos, (char **) &pos); ++pos;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
|
@ -248,12 +249,12 @@ void mesh_from_file_txt(
|
|||
} break;
|
||||
case 6: {
|
||||
// 's'
|
||||
strtol(pos, &pos, 10); ++pos;
|
||||
strtol(pos, (char **) &pos, 10); ++pos;
|
||||
} break;
|
||||
case 7: {
|
||||
// 'f'
|
||||
int32 ftype = 0;
|
||||
char* tmp = pos;
|
||||
const char* tmp = pos;
|
||||
while (*tmp != ' ') {
|
||||
if (*tmp++ == '/') {
|
||||
++ftype;
|
||||
|
|
@ -275,33 +276,33 @@ void mesh_from_file_txt(
|
|||
face_type = VERTEX_TYPE_POSITION;
|
||||
}
|
||||
|
||||
faces[(face_count * max_blocks * 1) + block] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 1) + block] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
} else if (ftype == 1) {
|
||||
// v1/vt1 v2/vt2 v3/vt3 ...
|
||||
if (face_count == 0) {
|
||||
face_type = VERTEX_TYPE_POSITION | VERTEX_TYPE_TEXTURE_COORD;
|
||||
}
|
||||
|
||||
faces[(face_count * max_blocks * 2) + block * 2] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 2) + block * 2] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
} else if (ftype == 2) {
|
||||
// v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
|
||||
if (face_count == 0) {
|
||||
face_type = VERTEX_TYPE_POSITION | VERTEX_TYPE_TEXTURE_COORD | VERTEX_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
faces[(face_count * max_blocks * 3) + block * 3] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 3) + block * 3 + 1] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 3) + block * 3 + 2] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 3) + block * 3] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 3) + block * 3 + 1] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 3) + block * 3 + 2] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
} else if (ftype == 3) {
|
||||
// v1//vn1 v2//vn2 v3//vn3 ...
|
||||
if (face_count == 0) {
|
||||
face_type = VERTEX_TYPE_POSITION | VERTEX_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
faces[(face_count * max_blocks * 2) + block * 2] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 2) + block * 2] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
++pos;
|
||||
faces[(face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, &pos, 10) - 1; ++pos;
|
||||
faces[(face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, (char **) &pos, 10) - 1; ++pos;
|
||||
}
|
||||
|
||||
++block;
|
||||
|
|
@ -323,7 +324,7 @@ void mesh_from_file_txt(
|
|||
case 9: {
|
||||
//l
|
||||
while (*pos != '\0' && *pos != '\n') {
|
||||
strtol(pos, &pos, 10); ++pos;
|
||||
strtol(pos, (char **) &pos, 10); ++pos;
|
||||
}
|
||||
} break;
|
||||
case 10: {
|
||||
|
|
|
|||
|
|
@ -25,14 +25,9 @@ struct Vertex3DNormal {
|
|||
|
||||
struct Vertex3DTextureColor {
|
||||
v3_f32 position;
|
||||
v2_f32 tex_coord;
|
||||
v4_f32 color;
|
||||
};
|
||||
|
||||
struct Vertex3DTextureColorIndex {
|
||||
v3_f32 position;
|
||||
v2_f32 tex_coord;
|
||||
f32 color;
|
||||
// If negative = color, positive = texture
|
||||
v2_f32 texture_color;
|
||||
};
|
||||
|
||||
struct Vertex3DColorIndex {
|
||||
|
|
|
|||
|
|
@ -224,29 +224,33 @@ void file_read(const char* path, FileBody* file, RingMemory* ring) {
|
|||
return;
|
||||
}
|
||||
|
||||
struct stat file_stat;
|
||||
if (fstat(fp, &file_stat) == -1) {
|
||||
close(fp);
|
||||
file->size = 0;
|
||||
file->content = NULL;
|
||||
if (file->size == 0) {
|
||||
struct stat file_stat;
|
||||
if (fstat(fp, &file_stat) == -1) {
|
||||
close(fp);
|
||||
file->size = 0;
|
||||
file->content = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_stat.st_size > MAX_UINT32) {
|
||||
close(fp);
|
||||
file->size = 0;
|
||||
file->content = NULL;
|
||||
if (file_stat.st_size > MAX_UINT32) {
|
||||
close(fp);
|
||||
file->size = 0;
|
||||
file->content = NULL;
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
file->size = file_stat.st_size + 1;
|
||||
}
|
||||
|
||||
if (ring != NULL) {
|
||||
file->content = ring_get_memory(ring, file_stat.st_size);
|
||||
file->content = ring_get_memory(ring, file->size);
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(fp, file->content, file_stat.st_size);
|
||||
if (bytes_read != file_stat.st_size) {
|
||||
ssize_t bytes_read = read(fp, file->content, file->size - 1);
|
||||
if (bytes_read != file->size) {
|
||||
close(fp);
|
||||
file->content = NULL;
|
||||
file->size = 0;
|
||||
|
|
@ -262,6 +266,59 @@ void file_read(const char* path, FileBody* file, RingMemory* ring) {
|
|||
close(fp);
|
||||
}
|
||||
|
||||
// This function uses a couple of temporary/internal variables to keep track of state and data for consecutive calls
|
||||
// The alternative would be to correct the file position after almost every call using seek which is very inefficient.
|
||||
// Since the mentality of this function is to be called consecutively we do it this way.
|
||||
bool file_read_line(
|
||||
FileHandle fp,
|
||||
char* line_buffer, size_t buffer_size,
|
||||
char internal_buffer[512], ssize_t* internal_buffer_size, char** internal_pos
|
||||
) {
|
||||
if (!(*internal_pos)) {
|
||||
*internal_pos = internal_buffer;
|
||||
}
|
||||
|
||||
size_t line_filled = 0;
|
||||
|
||||
while (line_filled < buffer_size - 1) {
|
||||
// Refill the internal buffer if empty
|
||||
if (*internal_pos == internal_buffer + *internal_buffer_size) {
|
||||
*internal_buffer_size = read(fp, internal_buffer, 512);
|
||||
if (*internal_buffer_size <= 0) {
|
||||
line_buffer[line_filled] = '\0';
|
||||
|
||||
return line_filled > 0;
|
||||
}
|
||||
|
||||
*internal_pos = internal_buffer;
|
||||
}
|
||||
|
||||
char current_char = **internal_pos;
|
||||
++(*internal_pos);
|
||||
|
||||
// Handle line endings (\n, \r, \r\n, \n\r)
|
||||
if (current_char == '\n' || current_char == '\r') {
|
||||
if ((*internal_pos < internal_buffer + *internal_buffer_size)
|
||||
&& (**internal_pos == '\n' || **internal_pos == '\r')
|
||||
&& **internal_pos != current_char
|
||||
) {
|
||||
++(*internal_pos);
|
||||
}
|
||||
|
||||
line_buffer[line_filled] = '\0';
|
||||
|
||||
// Successfully read a line
|
||||
return true;
|
||||
}
|
||||
|
||||
line_buffer[line_filled++] = current_char;
|
||||
}
|
||||
|
||||
line_buffer[line_filled] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool file_write(const char* path, const FileBody* file) {
|
||||
int32 fd;
|
||||
|
|
|
|||
|
|
@ -17,10 +17,8 @@
|
|||
|
||||
#include "../../stdlib/Types.h"
|
||||
#include "../../utils/StringUtils.h"
|
||||
#include "UtilsLinux.h"
|
||||
#include "../../system/Library.h"
|
||||
|
||||
// @todo Rename file to Library.cpp
|
||||
#include "UtilsLinux.h"
|
||||
|
||||
inline
|
||||
bool library_load(Library* lib)
|
||||
|
|
@ -28,7 +26,8 @@ bool library_load(Library* lib)
|
|||
char dst[PATH_MAX];
|
||||
str_concat_new(dst, lib->dir, lib->dst);
|
||||
|
||||
#if DEBUG
|
||||
// In debug mode, we create a copy at runtime, so we can recompile & reload it
|
||||
#if DEBUG || INTERNAL
|
||||
char src[PATH_MAX];
|
||||
size_t dst_len = strlen(dst);
|
||||
|
||||
|
|
@ -43,7 +42,7 @@ bool library_load(Library* lib)
|
|||
if (lib->handle) {
|
||||
dlclose(lib->handle);
|
||||
lib->handle = NULL;
|
||||
usleep(100000); // 100 ms
|
||||
usleep(100000); // 100 ms
|
||||
}
|
||||
|
||||
// @question we might want RTLD_NOW?
|
||||
|
|
|
|||
14
platform/linux/Library.h
Normal file
14
platform/linux/Library.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_PLATFORM_LINUX_LIBRARY_H
|
||||
#define TOS_PLATFORM_LINUX_LIBRARY_H
|
||||
|
||||
typedef void* LibraryHandle;
|
||||
|
||||
#endif
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
#include "../../stdlib/Types.h"
|
||||
#include "../../system/SystemInfo.h"
|
||||
#include "../../architecture/CpuInfo.cpp"
|
||||
#include "../../system/FileUtils.cpp"
|
||||
|
||||
#include <locale.h>
|
||||
#include <sys/resource.h>
|
||||
|
|
@ -34,14 +35,15 @@ uint64 system_private_memory_usage()
|
|||
|
||||
uint64 system_app_memory_usage()
|
||||
{
|
||||
FILE* fp = fopen("/proc/self/smaps", "r");
|
||||
if (!fp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64 total_size = 0;
|
||||
|
||||
FileHandle fp = file_read_handle("/proc/self/smaps");
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
char internal_buffer[512];
|
||||
ssize_t internal_buffer_size = 0;
|
||||
char* internal_pos = NULL;
|
||||
|
||||
while (file_read_line(fp, line, sizeof(line), internal_buffer, &internal_buffer_size, &internal_pos)) {
|
||||
if (str_compare(line, "Private_Dirty:", sizeof("Private_Dirty:") - 1) == 0) {
|
||||
uint64 private_dirty;
|
||||
if (sscanf(line, "Private_Dirty: %lu kB", &private_dirty) == 1) {
|
||||
|
|
@ -50,7 +52,7 @@ uint64 system_app_memory_usage()
|
|||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
file_close_handle(fp);
|
||||
|
||||
return total_size;
|
||||
}
|
||||
|
|
@ -70,22 +72,18 @@ uint16 system_country_code()
|
|||
}
|
||||
|
||||
void mainboard_info_get(MainboardInfo* info) {
|
||||
info->name[63] = '\0';
|
||||
info->serial_number[63] = '\0';
|
||||
FileBody file;
|
||||
|
||||
FILE *fp;
|
||||
file.content = info->name;
|
||||
file.size = sizeof(info->name);
|
||||
file_read("/sys/class/dmi/id/board_name", &file);
|
||||
|
||||
fp = fopen("/sys/class/dmi/id/board_name", "r");
|
||||
if (fp) {
|
||||
fgets(info->name, 64, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
file.content = info->serial_number;
|
||||
file.size = sizeof(info->serial_number);
|
||||
file_read("/sys/class/dmi/id/board_serial", &file);
|
||||
|
||||
fp = fopen("/sys/class/dmi/id/board_serial", "r");
|
||||
if (fp) {
|
||||
fgets(info->serial_number, 64, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
info->name[sizeof(info->name) - 1] = '\0';
|
||||
info->serial_number[sizeof(info->serial_number) - 1] = '\0';
|
||||
|
||||
info->name[strcspn(info->name, "\n")] = '\0';
|
||||
info->serial_number[strcspn(info->serial_number, "\n")] = '\0';
|
||||
|
|
@ -96,28 +94,23 @@ int32 network_info_get(NetworkInfo* info) {
|
|||
struct stat st;
|
||||
int32 i = 0;
|
||||
|
||||
FileBody file;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf_fast(path, "/sys/class/net/eth%d", i);
|
||||
|
||||
if (stat(path, &st) == 0) {
|
||||
// Read MAC address
|
||||
sprintf_fast(path, "/sys/class/net/eth%d/address", i);
|
||||
FILE *mac_file = fopen(path, "r");
|
||||
if (mac_file) {
|
||||
fscanf(mac_file, "%s", info[i].mac);
|
||||
fclose(mac_file);
|
||||
}
|
||||
file.content = info[i].mac;
|
||||
file.size = sizeof(info[i].mac);
|
||||
file_read(path, &file);
|
||||
|
||||
// Read interface name
|
||||
sprintf_fast(path, "/sys/class/net/eth%d/ifindex", i);
|
||||
FILE *index_file = fopen(path, "r");
|
||||
if (index_file) {
|
||||
fscanf(index_file, "%s", info[i].slot);
|
||||
fclose(index_file);
|
||||
}
|
||||
|
||||
info[i].mac[23] = '\0';
|
||||
info[i].slot[63] = '\0';
|
||||
file.content = info[i].slot;
|
||||
file.size = sizeof(info[i].slot);
|
||||
file_read(path, &file);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,14 +118,13 @@ int32 network_info_get(NetworkInfo* info) {
|
|||
}
|
||||
|
||||
void cpu_info_get(CpuInfo* info) {
|
||||
FILE* fp = fopen("/proc/cpuinfo", "r");
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileHandle fp = file_read_handle("/proc/cpuinfo");
|
||||
char line[256];
|
||||
char internal_buffer[512];
|
||||
ssize_t internal_buffer_size = 0;
|
||||
char* internal_pos = NULL;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
while (file_read_line(fp, line, sizeof(line), internal_buffer, &internal_buffer_size, &internal_pos)) {
|
||||
if (str_compare(line, "vendor_id", 9) == 0) {
|
||||
sscanf(line, "vendor_id : %s", info->vendor);
|
||||
} else if (str_compare(line, "model", 5) == 0) {
|
||||
|
|
@ -146,7 +138,7 @@ void cpu_info_get(CpuInfo* info) {
|
|||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
file_close_handle(fp);
|
||||
|
||||
info->family = 0;
|
||||
info->page_size = 4096; // Assuming standard page size of 4KB in Linux
|
||||
|
|
@ -160,21 +152,21 @@ void os_info_get(OSInfo* info) {
|
|||
}
|
||||
|
||||
void ram_info_get(RamInfo* info) {
|
||||
FILE* fp = fopen("/proc/meminfo", "r");
|
||||
if (fp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
uint32 total_memory = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
FileHandle fp = file_read_handle("/proc/meminfo");
|
||||
char line[256];
|
||||
char internal_buffer[512];
|
||||
ssize_t internal_buffer_size = 0;
|
||||
char* internal_pos = NULL;
|
||||
|
||||
while (file_read_line(fp, line, sizeof(line), internal_buffer, &internal_buffer_size, &internal_pos)) {
|
||||
if (sscanf(line, "MemTotal: %u kB", &total_memory) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
file_close_handle(fp);
|
||||
|
||||
// Convert memory from kB to MB
|
||||
info->memory = total_memory / 1024;
|
||||
|
|
@ -209,32 +201,6 @@ uint32 gpu_info_get(GpuInfo* info) {
|
|||
return count;
|
||||
}
|
||||
|
||||
|
||||
void display_info_get_primary(DisplayInfo* info) {
|
||||
FILE* fp = popen("xrandr --current", "r");
|
||||
if (fp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (strstr(line, "primary")) {
|
||||
// Example of a line containing display info: "HDMI-1 connected 1920x1080+0+0 60.00*+"
|
||||
char name[64];
|
||||
uint32 width, height, hz;
|
||||
if (sscanf(line, "%s connected %dx%d+%*d+%*d %d", name, &width, &height, &hz) == 4) {
|
||||
str_copy_short(info->name, name);
|
||||
info->width = width;
|
||||
info->height = height;
|
||||
info->hz = hz;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
uint32 display_info_get(DisplayInfo* info) {
|
||||
FILE* fp = popen("xrandr --current", "r");
|
||||
if (fp == NULL) {
|
||||
|
|
@ -254,6 +220,7 @@ uint32 display_info_get(DisplayInfo* info) {
|
|||
info[count].width = width;
|
||||
info[count].height = height;
|
||||
info[count].hz = hz;
|
||||
info[count].is_primary = strstr(line, "primary");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -176,20 +176,24 @@ file_read(const char* path, FileBody* file, RingMemory* ring = NULL)
|
|||
return;
|
||||
}
|
||||
|
||||
LARGE_INTEGER size;
|
||||
if (!GetFileSizeEx(fp, &size)) {
|
||||
CloseHandle(fp);
|
||||
file->content = NULL;
|
||||
if (file->size == 0) {
|
||||
LARGE_INTEGER size;
|
||||
if (!GetFileSizeEx(fp, &size)) {
|
||||
CloseHandle(fp);
|
||||
file->content = NULL;
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
file->size = size.QuadPart + 1;
|
||||
}
|
||||
|
||||
if (ring != NULL) {
|
||||
file->content = ring_get_memory(ring, size.QuadPart + 1);
|
||||
file->content = ring_get_memory(ring, file->size);
|
||||
}
|
||||
|
||||
DWORD bytes;
|
||||
if (!ReadFile(fp, file->content, (uint32) size.QuadPart, &bytes, NULL)) {
|
||||
DWORD bytes_read;
|
||||
if (!ReadFile(fp, file->content, (uint32) file->size - 1, &bytes_read, NULL)) {
|
||||
CloseHandle(fp);
|
||||
file->content = NULL;
|
||||
|
||||
|
|
@ -198,12 +202,13 @@ file_read(const char* path, FileBody* file, RingMemory* ring = NULL)
|
|||
|
||||
CloseHandle(fp);
|
||||
|
||||
file->content[bytes] = '\0';
|
||||
file->size = size.QuadPart;
|
||||
file->content[bytes_read] = '\0';
|
||||
file->size = bytes_read + 1;
|
||||
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_DRIVE_READ, bytes);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_DRIVE_READ, bytes_read);
|
||||
}
|
||||
|
||||
// @question Do we really need length? we have file.size we could use as we do in a function above
|
||||
inline
|
||||
void file_read(const char* path, FileBody* file, uint64 offset, uint64 length = MAX_UINT64, RingMemory* ring = NULL)
|
||||
{
|
||||
|
|
@ -272,8 +277,8 @@ void file_read(const char* path, FileBody* file, uint64 offset, uint64 length =
|
|||
return;
|
||||
}
|
||||
|
||||
DWORD bytes;
|
||||
if (!ReadFile(fp, file->content, (uint32) read_length, &bytes, NULL)) {
|
||||
DWORD bytes_read;
|
||||
if (!ReadFile(fp, file->content, (uint32) read_length, &bytes_read, NULL)) {
|
||||
CloseHandle(fp);
|
||||
file->content = NULL;
|
||||
|
||||
|
|
@ -282,10 +287,10 @@ void file_read(const char* path, FileBody* file, uint64 offset, uint64 length =
|
|||
|
||||
CloseHandle(fp);
|
||||
|
||||
file->content[bytes] = '\0';
|
||||
file->size = bytes;
|
||||
file->content[bytes_read] = '\0';
|
||||
file->size = bytes_read;
|
||||
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_DRIVE_READ, bytes);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_DRIVE_READ, bytes_read);
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
@ -326,18 +331,70 @@ void file_read(FileHandle fp, FileBody* file, uint64 offset = 0, uint64 length =
|
|||
return;
|
||||
}
|
||||
|
||||
DWORD bytes;
|
||||
if (!ReadFile(fp, file->content, (uint32) read_length, &bytes, NULL)) {
|
||||
DWORD bytes_read;
|
||||
if (!ReadFile(fp, file->content, (uint32) read_length, &bytes_read, NULL)) {
|
||||
file->content = NULL;
|
||||
ASSERT_SIMPLE(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
file->content[bytes] = '\0';
|
||||
file->size = bytes;
|
||||
file->content[bytes_read] = '\0';
|
||||
file->size = bytes_read;
|
||||
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_DRIVE_READ, bytes);
|
||||
LOG_INCREMENT_BY(DEBUG_COUNTER_DRIVE_READ, bytes_read);
|
||||
}
|
||||
|
||||
inline
|
||||
bool file_read_line(
|
||||
FileHandle fp,
|
||||
char* line_buffer, size_t buffer_size,
|
||||
char internal_buffer[512], ssize_t* internal_buffer_size, char** internal_pos
|
||||
) {
|
||||
if (!(*internal_pos)) {
|
||||
*internal_pos = internal_buffer;
|
||||
}
|
||||
|
||||
size_t line_filled = 0;
|
||||
|
||||
while (line_filled < buffer_size - 1) {
|
||||
// Refill the internal buffer if empty
|
||||
if (*internal_pos == internal_buffer + *internal_buffer_size) {
|
||||
if (!ReadFile(fp, internal_buffer, 512, (DWORD *) internal_buffer_size, NULL)
|
||||
|| *internal_buffer_size == 0
|
||||
) {
|
||||
line_buffer[line_filled] = '\0';
|
||||
|
||||
return line_filled > 0;
|
||||
}
|
||||
|
||||
*internal_pos = internal_buffer;
|
||||
}
|
||||
|
||||
char current_char = **internal_pos;
|
||||
++(*internal_pos);
|
||||
|
||||
// Handle line endings (\n, \r, \r\n, \n\r)
|
||||
if (current_char == '\n' || current_char == '\r') {
|
||||
if ((*internal_pos < internal_buffer + *internal_buffer_size)
|
||||
&& (**internal_pos == '\n' || **internal_pos == '\r')
|
||||
&& **internal_pos != current_char
|
||||
) {
|
||||
++(*internal_pos);
|
||||
}
|
||||
|
||||
line_buffer[line_filled] = '\0';
|
||||
|
||||
// Successfully read a line
|
||||
return true;
|
||||
}
|
||||
|
||||
line_buffer[line_filled++] = current_char;
|
||||
}
|
||||
|
||||
line_buffer[line_filled] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline uint64
|
||||
|
|
|
|||
|
|
@ -18,15 +18,14 @@
|
|||
#include "../../utils/StringUtils.h"
|
||||
#include "../../system/Library.h"
|
||||
|
||||
// @todo Rename file to Library.cpp
|
||||
|
||||
inline
|
||||
bool library_load(Library* lib)
|
||||
{
|
||||
char dst[MAX_PATH];
|
||||
str_concat_new(dst, lib->dir, lib->dst);
|
||||
|
||||
#if DEBUG
|
||||
// In debug mode, we create a copy at runtime, so we can recompile & reload it
|
||||
#if DEBUG || INTERNAL
|
||||
char src[MAX_PATH];
|
||||
size_t dst_len = strlen(dst);
|
||||
|
||||
|
|
|
|||
16
platform/win32/Library.h
Normal file
16
platform/win32/Library.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_PLATFORM_WIN32_LIBRARY_H
|
||||
#define TOS_PLATFORM_WIN32_LIBRARY_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef HMODULE LibraryHandle;
|
||||
|
||||
#endif
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
#include <hidsdi.h>
|
||||
|
||||
// @performance Do we really need all these libs, can't we simplify that?!
|
||||
// At least we should dynamically load them, this way the application won't crash if the lib doesn't exist
|
||||
#include <intrin.h>
|
||||
#pragma comment(lib, "Advapi32.lib")
|
||||
#pragma comment(lib, "wbemuuid.lib")
|
||||
|
|
@ -94,8 +95,8 @@ uint16 system_country_code()
|
|||
}
|
||||
|
||||
void mainboard_info_get(MainboardInfo* info) {
|
||||
info->name[63] = '\0';
|
||||
info->serial_number[63] = '\0';
|
||||
info->name[sizeof(info->name) - 1] = '\0';
|
||||
info->serial_number[sizeof(info->serial_number) - 1] = '\0';
|
||||
|
||||
HRESULT hres;
|
||||
|
||||
|
|
@ -214,8 +215,6 @@ void mainboard_info_get(MainboardInfo* info) {
|
|||
if (SUCCEEDED(hr)) {
|
||||
wchar_to_char(vtProp.bstrVal);
|
||||
sprintf_fast(info->serial_number, sizeof(info->serial_number), "%s", vtProp.bstrVal);
|
||||
info->serial_number[64] = '\0';
|
||||
|
||||
VariantClear(&vtProp);
|
||||
}
|
||||
|
||||
|
|
@ -227,6 +226,9 @@ void mainboard_info_get(MainboardInfo* info) {
|
|||
pLoc->Release();
|
||||
pEnumerator->Release();
|
||||
CoUninitialize();
|
||||
|
||||
info->name[sizeof(info->name) - 1] = '\0';
|
||||
info->serial_number[sizeof(info->serial_number) - 1] = '\0';
|
||||
}
|
||||
|
||||
int32 network_info_get(NetworkInfo* info) {
|
||||
|
|
@ -368,7 +370,7 @@ uint32 gpu_info_get(GpuInfo* info) {
|
|||
}
|
||||
|
||||
uint32 i = 0;
|
||||
while (pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND && i < 2) {
|
||||
while (pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND && i < 3) {
|
||||
hr = pAdapter->GetDesc(&adapterDesc);
|
||||
if (FAILED(hr)) {
|
||||
pAdapter->Release();
|
||||
|
|
@ -388,33 +390,6 @@ uint32 gpu_info_get(GpuInfo* info) {
|
|||
return i;
|
||||
}
|
||||
|
||||
void display_info_get_primary(DisplayInfo* info) {
|
||||
DISPLAY_DEVICEA device;
|
||||
DEVMODEA mode;
|
||||
|
||||
device.cb = sizeof(DISPLAY_DEVICEA);
|
||||
|
||||
uint32_t i = 0;
|
||||
while (EnumDisplayDevicesA(NULL, i, &device, 0)) {
|
||||
++i;
|
||||
|
||||
if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mode.dmSize = sizeof(mode);
|
||||
|
||||
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||
str_copy_short(info->name, device.DeviceName);
|
||||
info->width = mode.dmPelsWidth;
|
||||
info->height = mode.dmPelsHeight;
|
||||
info->hz = mode.dmDisplayFrequency;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 display_info_get(DisplayInfo* info) {
|
||||
DISPLAY_DEVICEA device;
|
||||
DEVMODEA mode;
|
||||
|
|
@ -431,6 +406,7 @@ uint32 display_info_get(DisplayInfo* info) {
|
|||
info[i].width = mode.dmPelsWidth;
|
||||
info[i].height = mode.dmPelsHeight;
|
||||
info[i].hz = mode.dmDisplayFrequency;
|
||||
info[i].is_primary = (bool) (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE);
|
||||
}
|
||||
|
||||
++i;
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ void window_open(Window* window)
|
|||
void window_close(Window* window)
|
||||
{
|
||||
CloseWindow(window->hwnd);
|
||||
DestroyWindow(window->hwnd);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
401
stdlib/HashMap.h
401
stdlib/HashMap.h
|
|
@ -15,59 +15,54 @@
|
|||
#include "../memory/BufferMemory.h"
|
||||
#include "../memory/ChunkMemory.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include "../stdlib/Simd.h"
|
||||
|
||||
// WARNING Length of 28 used to ensure perfect padding with element_id and key
|
||||
#define HASH_MAP_MAX_KEY_LENGTH 28
|
||||
// If a hash key is longer than the max key length, we use the last N characters of that key
|
||||
// The key length is currently chosen to result in 32 byte size for the common case: HashEntryInt32
|
||||
#define HASH_MAP_MAX_KEY_LENGTH 26
|
||||
|
||||
/////////////////////////////
|
||||
// string key
|
||||
/////////////////////////////
|
||||
struct HashEntryInt32 {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntryInt32* next;
|
||||
uint16 next;
|
||||
int32 value;
|
||||
};
|
||||
|
||||
struct HashEntryInt64 {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntryInt64* next;
|
||||
uint16 next;
|
||||
int64 value;
|
||||
};
|
||||
|
||||
struct HashEntryUIntPtr {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntryUIntPtr* next;
|
||||
uint16 next;
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
struct HashEntryVoidP {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntryVoidP* next;
|
||||
uint16 next;
|
||||
void* value;
|
||||
};
|
||||
|
||||
struct HashEntryFloat {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntryFloat* next;
|
||||
uint16 next;
|
||||
f32 value;
|
||||
};
|
||||
|
||||
struct HashEntryStr {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntryStr* next;
|
||||
uint16 next;
|
||||
char value[HASH_MAP_MAX_KEY_LENGTH];
|
||||
};
|
||||
|
||||
struct HashEntry {
|
||||
uint32 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
HashEntry* next;
|
||||
uint16 next;
|
||||
byte* value;
|
||||
};
|
||||
|
||||
|
|
@ -75,56 +70,53 @@ struct HashEntry {
|
|||
// int key
|
||||
/////////////////////////////
|
||||
struct HashEntryInt32KeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryInt32KeyInt32* next;
|
||||
uint16 next;
|
||||
int32 value;
|
||||
};
|
||||
|
||||
struct HashEntryInt64KeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryInt64KeyInt32* next;
|
||||
uint16 next;
|
||||
int64 value;
|
||||
};
|
||||
|
||||
struct HashEntryUIntPtrKeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryUIntPtrKeyInt32* next;
|
||||
uint16 next;
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
struct HashEntryVoidPKeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryVoidPKeyInt32* next;
|
||||
uint16 next;
|
||||
void* value;
|
||||
};
|
||||
|
||||
struct HashEntryFloatKeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryFloatKeyInt32* next;
|
||||
uint16 next;
|
||||
f32 value;
|
||||
};
|
||||
|
||||
struct HashEntryStrKeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryStrKeyInt32* next;
|
||||
uint16 next;
|
||||
char value[HASH_MAP_MAX_KEY_LENGTH];
|
||||
};
|
||||
|
||||
struct HashEntryKeyInt32 {
|
||||
uint32 element_id;
|
||||
int32 key;
|
||||
HashEntryKeyInt32* next;
|
||||
uint16 next;
|
||||
byte* value;
|
||||
};
|
||||
|
||||
// HashMaps are limited to 4GB in total size
|
||||
struct HashMap {
|
||||
void** table;
|
||||
// Values are 1-indexed/offset since 0 means not used/found
|
||||
uint16* table;
|
||||
|
||||
// @todo We might want to align the ChunkMemory memory to 8byte, currently it's either 4 or 8 byte depending on the length
|
||||
ChunkMemory buf;
|
||||
};
|
||||
|
||||
|
|
@ -133,13 +125,12 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ri
|
|||
{
|
||||
byte* data = ring_get_memory(
|
||||
ring,
|
||||
count * (sizeof(void *) + element_size)
|
||||
+ CEIL_DIV(count, 64) * sizeof(hm->buf.free),
|
||||
0, true
|
||||
count * (sizeof(uint16) + element_size)
|
||||
+ CEIL_DIV(count, 64) * sizeof(hm->buf.free)
|
||||
);
|
||||
|
||||
hm->table = (void **) data;
|
||||
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 8);
|
||||
hm->table = (uint16 *) data;
|
||||
chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8);
|
||||
|
||||
LOG_LEVEL_2("Created HashMap for %n elements with %n B per element = %n B", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}, {LOG_DATA_UINT64, &hm->buf.size}});
|
||||
}
|
||||
|
|
@ -149,12 +140,12 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory*
|
|||
{
|
||||
byte* data = buffer_get_memory(
|
||||
buf,
|
||||
count * (sizeof(void *) + element_size)
|
||||
count * (sizeof(uint16) + element_size)
|
||||
+ CEIL_DIV(count, 64) * sizeof(hm->buf.free)
|
||||
);
|
||||
|
||||
hm->table = (void **) data;
|
||||
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 8);
|
||||
hm->table = (uint16 *) data;
|
||||
chunk_init(&hm->buf, data + sizeof(uint16) * count, count, element_size, 8);
|
||||
|
||||
LOG_LEVEL_2("Created HashMap for %n elements with %n B per element = %n B", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}, {LOG_DATA_UINT64, &hm->buf.size}});
|
||||
}
|
||||
|
|
@ -162,12 +153,18 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory*
|
|||
// WARNING: element_size = element size + remaining HashEntry data size
|
||||
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf)
|
||||
{
|
||||
hm->table = (void **) buf;
|
||||
chunk_init(&hm->buf, buf + sizeof(void *) * count, count, element_size, 8);
|
||||
hm->table = (uint16 *) buf;
|
||||
chunk_init(&hm->buf, buf + sizeof(uint16) * count, count, element_size, 8);
|
||||
|
||||
LOG_LEVEL_2("Created HashMap for %n elements with %n B per element = %n B", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}, {LOG_DATA_UINT64, &hm->buf.size}});
|
||||
}
|
||||
|
||||
void hashmap_update_data_pointer(HashMap* hm, byte* data)
|
||||
{
|
||||
hm->table = (uint16 *) data;
|
||||
hm->buf.memory = data + sizeof(uint16) * hm->buf.count;
|
||||
}
|
||||
|
||||
// Calculates how large a hashmap will be
|
||||
inline
|
||||
int64 hashmap_size(int count, int32 element_size)
|
||||
|
|
@ -180,7 +177,7 @@ int64 hashmap_size(int count, int32 element_size)
|
|||
inline
|
||||
int64 hashmap_size(const HashMap* hm)
|
||||
{
|
||||
return hm->buf.count * sizeof(hm->table) + hm->buf.size;
|
||||
return hm->buf.count * sizeof(uint16) + hm->buf.size;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
|
@ -191,7 +188,6 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
|
|
@ -199,17 +195,17 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
|||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt32* tmp = (HashEntryInt32 *) hm->table[index];
|
||||
HashEntryInt32* tmp = (HashEntryInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +214,6 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
|
|
@ -226,17 +221,17 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
|||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt64* tmp = (HashEntryInt64 *) hm->table[index];
|
||||
HashEntryInt64* tmp = (HashEntryInt64 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryInt64 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +240,6 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryUIntPtr* entry = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
|
|
@ -253,17 +247,17 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
|||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryUIntPtr* tmp = (HashEntryUIntPtr *) hm->table[index];
|
||||
HashEntryUIntPtr* tmp = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +266,6 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryVoidP* entry = (HashEntryVoidP *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
|
|
@ -280,17 +273,17 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
|||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryVoidP* tmp = (HashEntryVoidP *) hm->table[index];
|
||||
HashEntryVoidP* tmp = (HashEntryVoidP *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryVoidP *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -299,7 +292,6 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryFloat* entry = (HashEntryFloat *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
|
|
@ -307,17 +299,17 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
|||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryFloat* tmp = (HashEntryFloat *) hm->table[index];
|
||||
HashEntryFloat* tmp = (HashEntryFloat *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryFloat *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +318,6 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryStr* entry = (HashEntryStr *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
// Ensure key length
|
||||
str_move_to_pos(&key, -HASH_MAP_MAX_KEY_LENGTH);
|
||||
|
|
@ -336,17 +327,17 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
|||
str_copy_short(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->value[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryStr* tmp = (HashEntryStr *) hm->table[index];
|
||||
HashEntryStr* tmp = (HashEntryStr *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryStr *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +346,6 @@ HashEntry* hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->value = (byte *) entry + sizeof(HashEntry);
|
||||
|
||||
|
|
@ -366,17 +356,17 @@ HashEntry* hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
|||
|
||||
memcpy(entry->value, value, hm->buf.chunk_size - sizeof(HashEntry));
|
||||
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntry* tmp = (HashEntry *) hm->table[index];
|
||||
HashEntry* tmp = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntry *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
return entry;
|
||||
|
|
@ -387,7 +377,6 @@ HashEntry* hashmap_reserve(HashMap* hm, const char* key) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->value = (byte *) entry + sizeof(HashEntry);
|
||||
|
||||
|
|
@ -396,17 +385,17 @@ HashEntry* hashmap_reserve(HashMap* hm, const char* key) {
|
|||
str_copy_short(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntry* tmp = (HashEntry *) hm->table[index];
|
||||
HashEntry* tmp = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntry *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
return entry;
|
||||
|
|
@ -416,7 +405,7 @@ HashEntry* hashmap_reserve(HashMap* hm, const char* key) {
|
|||
HashEntry* hashmap_get_reserve(HashMap* hm, const char* key)
|
||||
{
|
||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
|
||||
while (entry != NULL) {
|
||||
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||
|
|
@ -424,16 +413,15 @@ HashEntry* hashmap_get_reserve(HashMap* hm, const char* key)
|
|||
return entry;
|
||||
}
|
||||
|
||||
if (((HashEntry *) entry->next) == NULL) {
|
||||
if (!entry->next) {
|
||||
break;
|
||||
}
|
||||
|
||||
entry = (HashEntry *) entry->next;
|
||||
entry = (HashEntry *) chunk_get_element(&hm->buf, entry->next - 1, false);
|
||||
}
|
||||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntry* entry_new = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
||||
entry_new->element_id = element;
|
||||
|
||||
entry_new->value = (byte *) entry_new + sizeof(HashEntry);
|
||||
|
||||
|
|
@ -443,17 +431,29 @@ HashEntry* hashmap_get_reserve(HashMap* hm, const char* key)
|
|||
entry_new->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
if (entry) {
|
||||
entry->next = entry_new;
|
||||
entry->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry_new;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
return entry_new;
|
||||
}
|
||||
|
||||
HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
|
||||
inline
|
||||
HashEntry* hashmap_get_entry_by_index(HashMap* hm, uint32 index)
|
||||
{
|
||||
return hm->table[index] ? (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false) : NULL;
|
||||
}
|
||||
|
||||
inline
|
||||
HashEntry* hashmap_get_entry_by_element(HashMap* hm, uint32 element)
|
||||
{
|
||||
return (HashEntry *) chunk_get_element(&hm->buf, element - 1, false);
|
||||
}
|
||||
|
||||
HashEntry* hashmap_get_entry(HashMap* hm, const char* key) {
|
||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
|
||||
while (entry != NULL) {
|
||||
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||
|
|
@ -461,17 +461,42 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
|
|||
return entry;
|
||||
}
|
||||
|
||||
entry = (HashEntry *) entry->next;
|
||||
entry = (HashEntry *) chunk_get_element(&hm->buf, entry->next - 1, false);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32 hashmap_get_element(const HashMap* hm, const char* key) {
|
||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||
const HashEntry* entry = (const HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, hm->table[index] - 1, false);
|
||||
|
||||
uint32 element_id = hm->table[index];
|
||||
|
||||
while (entry != NULL) {
|
||||
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||
DEBUG_MEMORY_READ((uintptr_t) entry, sizeof(HashEntry));
|
||||
return element_id;
|
||||
}
|
||||
|
||||
element_id = entry->next;
|
||||
entry = (const HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, entry->next - 1, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 hashmap_get_element_by_entry(const HashMap* hm, const HashEntry* entry)
|
||||
{
|
||||
return chunk_id_from_memory(&hm->buf, (byte *) entry) + 1;
|
||||
}
|
||||
|
||||
// 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 hash) {
|
||||
HashEntry* hashmap_get_entry(HashMap* hm, const char* key, uint64 hash) {
|
||||
hash %= hm->buf.count;
|
||||
HashEntry* entry = (HashEntry *) hm->table[hash];
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, hm->table[hash] - 1, false);
|
||||
|
||||
while (entry != NULL) {
|
||||
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||
|
|
@ -479,7 +504,7 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 hash) {
|
|||
return entry;
|
||||
}
|
||||
|
||||
entry = (HashEntry *) entry->next;
|
||||
entry = (HashEntry *) chunk_get_element(&hm->buf, entry->next - 1, false);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -490,9 +515,11 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 hash) {
|
|||
// Maybe we create a nother hashmap that is doubly linked
|
||||
void hashmap_remove(HashMap* hm, const char* key) {
|
||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
HashEntry* prev = NULL;
|
||||
|
||||
uint32 element_id = hm->table[index];
|
||||
|
||||
while (entry != NULL) {
|
||||
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||
if (prev == NULL) {
|
||||
|
|
@ -501,13 +528,14 @@ void hashmap_remove(HashMap* hm, const char* key) {
|
|||
prev->next = entry->next;
|
||||
}
|
||||
|
||||
chunk_free_elements(&hm->buf, entry->element_id);
|
||||
chunk_free_elements(&hm->buf, element_id - 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
element_id = entry->next;
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
entry = (HashEntry *) chunk_get_element(&hm->buf, entry->next - 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -519,21 +547,20 @@ void hashmap_insert(HashMap* hm, int32 key, int32 value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryInt32KeyInt32* entry = (HashEntryInt32KeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt32KeyInt32* tmp = (HashEntryInt32KeyInt32 *) hm->table[index];
|
||||
HashEntryInt32KeyInt32* tmp = (HashEntryInt32KeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryInt32KeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,21 +569,20 @@ void hashmap_insert(HashMap* hm, int32 key, int64 value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryInt64KeyInt32* entry = (HashEntryInt64KeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt64KeyInt32* tmp = (HashEntryInt64KeyInt32 *) hm->table[index];
|
||||
HashEntryInt64KeyInt32* tmp = (HashEntryInt64KeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryInt64KeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -565,21 +591,20 @@ void hashmap_insert(HashMap* hm, int32 key, uintptr_t value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryUIntPtrKeyInt32* entry = (HashEntryUIntPtrKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryUIntPtrKeyInt32* tmp = (HashEntryUIntPtrKeyInt32 *) hm->table[index];
|
||||
HashEntryUIntPtrKeyInt32* tmp = (HashEntryUIntPtrKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryUIntPtrKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -588,21 +613,20 @@ void hashmap_insert(HashMap* hm, int32 key, void* value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryVoidPKeyInt32* entry = (HashEntryVoidPKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryVoidPKeyInt32* tmp = (HashEntryVoidPKeyInt32 *) hm->table[index];
|
||||
HashEntryVoidPKeyInt32* tmp = (HashEntryVoidPKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryVoidPKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -611,21 +635,20 @@ void hashmap_insert(HashMap* hm, int32 key, f32 value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryFloatKeyInt32* entry = (HashEntryFloatKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryFloatKeyInt32* tmp = (HashEntryFloatKeyInt32 *) hm->table[index];
|
||||
HashEntryFloatKeyInt32* tmp = (HashEntryFloatKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryFloatKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -634,24 +657,23 @@ void hashmap_insert(HashMap* hm, int32 key, const char* value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryStrKeyInt32* entry = (HashEntryStrKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
|
||||
str_copy_short(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
entry->value[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryStrKeyInt32* tmp = (HashEntryStrKeyInt32 *) hm->table[index];
|
||||
HashEntryStrKeyInt32* tmp = (HashEntryStrKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryStrKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -660,30 +682,29 @@ void hashmap_insert(HashMap* hm, int32 key, byte* value) {
|
|||
|
||||
int32 element = chunk_reserve(&hm->buf, 1);
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||
entry->element_id = element;
|
||||
|
||||
entry->key = key;
|
||||
entry->value = (byte *) entry + sizeof(HashEntryKeyInt32);
|
||||
|
||||
memcpy(entry->value, value, hm->buf.chunk_size - sizeof(HashEntryKeyInt32));
|
||||
|
||||
entry->next = NULL;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryKeyInt32* tmp = (HashEntryKeyInt32 *) hm->table[index];
|
||||
HashEntryKeyInt32* tmp = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = tmp->next;
|
||||
tmp = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = entry;
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = entry;
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
}
|
||||
}
|
||||
|
||||
HashEntryKeyInt32* hashmap_get_entry(const HashMap* hm, int32 key) {
|
||||
HashEntryKeyInt32* hashmap_get_entry(HashMap* hm, int32 key) {
|
||||
uint64 index = key % hm->buf.count;
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) hm->table[index];
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
|
||||
while (entry != NULL) {
|
||||
if (entry->key == key) {
|
||||
|
|
@ -699,9 +720,9 @@ HashEntryKeyInt32* hashmap_get_entry(const HashMap* hm, int32 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
|
||||
HashEntryKeyInt32* hashmap_get_entry(const HashMap* hm, int32 key, uint64 hash) {
|
||||
HashEntryKeyInt32* hashmap_get_entry(HashMap* hm, int32 key, uint64 hash) {
|
||||
hash %= hm->buf.count;
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) hm->table[hash];
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, hm->table[hash] - 1, false);
|
||||
|
||||
while (entry != NULL) {
|
||||
if (entry->key == key) {
|
||||
|
|
@ -720,9 +741,11 @@ HashEntryKeyInt32* hashmap_get_entry(const HashMap* hm, int32 key, uint64 hash)
|
|||
// Maybe we create a nother hashmap that is doubly linked
|
||||
void hashmap_remove(HashMap* hm, int32 key) {
|
||||
uint64 index = key % hm->buf.count;
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) hm->table[index];
|
||||
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
HashEntryKeyInt32* prev = NULL;
|
||||
|
||||
uint32 element_id = hm->table[index];
|
||||
|
||||
while (entry != NULL) {
|
||||
if (entry->key == key) {
|
||||
if (prev == NULL) {
|
||||
|
|
@ -731,13 +754,14 @@ void hashmap_remove(HashMap* hm, int32 key) {
|
|||
prev->next = entry->next;
|
||||
}
|
||||
|
||||
chunk_free_elements(&hm->buf, entry->element_id);
|
||||
chunk_free_elements(&hm->buf, element_id - 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
element_id = entry->next;
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
entry = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, entry->next - 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -746,26 +770,27 @@ int32 hashmap_value_size(const HashMap* hm)
|
|||
{
|
||||
return (uint32) (
|
||||
hm->buf.chunk_size
|
||||
- sizeof(uint32) // element id
|
||||
- sizeof(char) * HASH_MAP_MAX_KEY_LENGTH // key
|
||||
- sizeof(uintptr_t) // next pointer
|
||||
- sizeof(uint16) // next element
|
||||
);
|
||||
}
|
||||
|
||||
// @question Shouldn't we also store the hashmap count, chunk size etc? Currently not done and expected to be correctly initialized.
|
||||
// @question Shouldn't we also store the chunk size etc? Currently not done and expected to be correctly initialized.
|
||||
inline
|
||||
int64 hashmap_dump(const HashMap* hm, byte* data)
|
||||
int64 hashmap_dump(const HashMap* hm, byte* data, int32 steps = 8)
|
||||
{
|
||||
*((uint32 *) data) = SWAP_ENDIAN_LITTLE(hm->buf.count);
|
||||
data += sizeof(hm->buf.count);
|
||||
|
||||
// Dump the table content where the elements are relative indices/pointers
|
||||
for (int32 i = 0; i < hm->buf.count; ++i) {
|
||||
*((uint64 *) data) = hm->table[i]
|
||||
? SWAP_ENDIAN_LITTLE((uintptr_t) hm->table[i] - (uintptr_t) hm->buf.memory)
|
||||
: 0ULL;
|
||||
}
|
||||
data += sizeof(uint64) * hm->buf.count;
|
||||
// Dump the table content
|
||||
memcpy(data, hm->table, sizeof(uint16) * hm->buf.count);
|
||||
SWAP_ENDIAN_LITTLE_SIMD(
|
||||
(uint16 *) data,
|
||||
(uint16 *) data,
|
||||
sizeof(uint16) * hm->buf.count / 2, // everything is 2 bytes -> super easy to swap
|
||||
steps
|
||||
);
|
||||
data += sizeof(uint16) * hm->buf.count;
|
||||
|
||||
// @bug what if Int32 key?
|
||||
int32 value_size = hashmap_value_size(hm);
|
||||
|
|
@ -778,21 +803,13 @@ int64 hashmap_dump(const HashMap* hm, byte* data)
|
|||
if (hm->buf.free[free_index] & (1ULL << bit_index)) {
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, i);
|
||||
|
||||
// element_id
|
||||
*((uint32 *) data) = SWAP_ENDIAN_LITTLE(entry->element_id);
|
||||
data += sizeof(entry->element_id);
|
||||
|
||||
// key
|
||||
memcpy(data, entry->key, sizeof(entry->key));
|
||||
data += sizeof(entry->key);
|
||||
|
||||
// next pointer
|
||||
if (entry->next) {
|
||||
*((uint64 *) data) = SWAP_ENDIAN_LITTLE((uintptr_t) entry->next - (uintptr_t) hm->buf.memory);
|
||||
} else {
|
||||
memset(data, 0, sizeof(uint64));
|
||||
}
|
||||
data += sizeof(uint64);
|
||||
// next "pointer"
|
||||
*((uint16 *) data) = SWAP_ENDIAN_LITTLE(entry->next);
|
||||
data += sizeof(entry->next);
|
||||
|
||||
// We just assume that 4 or 8 bytes = int -> endian handling
|
||||
if (value_size == 4) {
|
||||
|
|
@ -820,25 +837,26 @@ int64 hashmap_dump(const HashMap* hm, byte* data)
|
|||
memcpy(data, hm->buf.free, sizeof(uint64) * CEIL_DIV(hm->buf.count, 64));
|
||||
|
||||
return sizeof(hm->buf.count) // hash map count = buffer count
|
||||
+ hm->buf.count * sizeof(uint64) // table content
|
||||
+ hm->buf.count * sizeof(uint16) // table content
|
||||
+ hm->buf.size; // hash map content + free array
|
||||
}
|
||||
|
||||
// WARNING: Requires hashmap_create first
|
||||
inline
|
||||
int64 hashmap_load(HashMap* hm, const byte* data)
|
||||
int64 hashmap_load(HashMap* hm, const byte* data, int32 steps = 8)
|
||||
{
|
||||
uint64 count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
|
||||
data += sizeof(uint32);
|
||||
data += sizeof(uint16);
|
||||
|
||||
// Load the table content
|
||||
for (uint32 i = 0; i < count; ++i) {
|
||||
uint64 offset = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
||||
data += sizeof(offset);
|
||||
|
||||
// the first element has no offset!
|
||||
hm->table[i] = offset || i == 0 ? hm->buf.memory + offset : NULL;
|
||||
}
|
||||
memcpy(hm->table, data, sizeof(uint16) * count);
|
||||
SWAP_ENDIAN_LITTLE_SIMD(
|
||||
(uint32 *) hm->table,
|
||||
(uint32 *) hm->table,
|
||||
sizeof(uint16) * count / 4, // everything is 4 bytes -> super easy to swap
|
||||
steps
|
||||
);
|
||||
data += sizeof(uint16) * count;
|
||||
|
||||
// This loop here is why it is important to already have an initialized hashmap
|
||||
// @question Do we maybe want to change this and not require an initalized hashmap?
|
||||
|
|
@ -856,16 +874,11 @@ int64 hashmap_load(HashMap* hm, const byte* data)
|
|||
chunk_iterate_start(&hm->buf, chunk_id)
|
||||
HashEntry* entry = (HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, chunk_id);
|
||||
|
||||
// element id
|
||||
entry->element_id = SWAP_ENDIAN_LITTLE(entry->element_id);
|
||||
|
||||
// key is already loaded with the memcpy
|
||||
// @question Do we even want to use memcpy? We are re-checking all the values here anyways
|
||||
|
||||
// next pointer
|
||||
if (entry->next) {
|
||||
entry->next = (HashEntry *) (hm->buf.memory + SWAP_ENDIAN_LITTLE((uint64) entry->next));
|
||||
}
|
||||
// next "pointer"
|
||||
entry->next = SWAP_ENDIAN_LITTLE(entry->next);
|
||||
|
||||
if (value_size == 4) {
|
||||
((HashEntryInt32 *) entry)->value = SWAP_ENDIAN_LITTLE(((HashEntryInt32 *) entry)->value);
|
||||
|
|
@ -878,7 +891,7 @@ int64 hashmap_load(HashMap* hm, const byte* data)
|
|||
|
||||
// How many bytes was read from data
|
||||
return sizeof(hm->buf.count) // hash map count = buffer count
|
||||
+ hm->buf.count * sizeof(uint64) // table content
|
||||
+ hm->buf.count * sizeof(uint16) // table content
|
||||
+ hm->buf.size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_STDLIB_INTRINSICS_H
|
||||
#define TOS_STDLIB_INTRINSICS_H
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#if __linux__
|
||||
#include <x86intrin.h>
|
||||
#include <x86gprintrin.h>
|
||||
#endif
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
inline f32 oms_sqrt(f32 a) { return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(a))); }
|
||||
inline f64 oms_sqrt(f64 a)
|
||||
{
|
||||
__m128d temp =_mm_set_sd(a);
|
||||
|
||||
return _mm_cvtsd_f64(_mm_sqrt_sd(temp, temp));
|
||||
}
|
||||
|
||||
inline f32 oms_rsqrt(f32 a) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(a))); }
|
||||
inline f64 oms_rsqrt(f64 a)
|
||||
{
|
||||
__m128d temp =_mm_set_sd(a);
|
||||
|
||||
return _mm_cvtsd_f64(
|
||||
_mm_div_sd(
|
||||
_mm_set_sd(1.0),
|
||||
_mm_sqrt_sd(temp, temp)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
inline f32 oms_round(f32 a)
|
||||
{
|
||||
return _mm_cvtss_f32(
|
||||
_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(a), (_MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC)));
|
||||
}
|
||||
|
||||
inline uint32 round_to_int(f32 a) { return (uint32) _mm_cvtss_si32(_mm_set_ss(a)); }
|
||||
|
||||
inline f32 oms_floor(f32 a) { return _mm_cvtss_f32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(a))); }
|
||||
|
||||
inline f32 oms_ceil(f32 a) { return _mm_cvtss_f32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(a))); }
|
||||
|
||||
inline uint32 oms_hash(uint64 a, uint64 b = 0)
|
||||
{
|
||||
uint8 seed[16] = {
|
||||
0xaa, 0x9b, 0xbd, 0xb8, 0xa1, 0x98, 0xac, 0x3f, 0x1f, 0x94, 0x07, 0xb3, 0x8c, 0x27, 0x93, 0x69,
|
||||
};
|
||||
|
||||
__m128i hash = _mm_set_epi64x(a, b);
|
||||
hash = _mm_aesdec_si128(hash, _mm_loadu_si128((__m128i *) seed));
|
||||
hash = _mm_aesdec_si128(hash, _mm_loadu_si128((__m128i *) seed));
|
||||
|
||||
return _mm_extract_epi32(hash, 0);
|
||||
}
|
||||
|
||||
inline void oms_fence_memory()
|
||||
{
|
||||
_mm_mfence();
|
||||
}
|
||||
|
||||
inline void oms_fence_write()
|
||||
{
|
||||
_mm_sfence();
|
||||
}
|
||||
|
||||
inline void oms_fence_load()
|
||||
{
|
||||
_mm_lfence();
|
||||
}
|
||||
|
||||
inline
|
||||
void oms_invalidate_cache(void* address)
|
||||
{
|
||||
_mm_clflush(address);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_STDLIB_INTRINSICS_ARM_H
|
||||
#define TOS_STDLIB_INTRINSICS_ARM_H
|
||||
|
||||
#include <arm_sve.h>
|
||||
#include <arm_acle.h>
|
||||
|
||||
inline float oms_sqrt(float a) {
|
||||
svfloat32_t input = svdup_f32(a);
|
||||
svfloat32_t result = svsqrt_f32(input);
|
||||
|
||||
return svget1_f32(result);
|
||||
}
|
||||
|
||||
inline double oms_sqrt(double a) {
|
||||
svfloat64_t input = svdup_f64(a);
|
||||
svfloat64_t result = svsqrt_f64(input);
|
||||
|
||||
return svget1_f64(result);
|
||||
}
|
||||
|
||||
inline float oms_rsqrt(float a) {
|
||||
svfloat32_t input = svdup_f32(a);
|
||||
svfloat32_t result = svrsqrte_f32(input);
|
||||
|
||||
return svget1_f32(result);
|
||||
}
|
||||
|
||||
inline double oms_rsqrt(double a) {
|
||||
svfloat64_t input = svdup_f64(a);
|
||||
svfloat64_t result = svrsqrte_f64(input);
|
||||
|
||||
return svget1_f64(result);
|
||||
}
|
||||
|
||||
inline float oms_round(float a) {
|
||||
svfloat32_t input = svdup_f32(a);
|
||||
svfloat32_t result = svrndn_f32(input);
|
||||
|
||||
return svget1_f32(result);
|
||||
}
|
||||
|
||||
inline uint32_t round_to_int(float a) {
|
||||
svfloat32_t input = svdup_f32(a);
|
||||
svint32_t result = svcvtn_f32_s32(input, SVE_32B);
|
||||
|
||||
return svget1_s32(result);
|
||||
}
|
||||
|
||||
inline float oms_floor(float a) {
|
||||
svfloat32_t input = svdup_f32(a);
|
||||
svfloat32_t result = svfloor_f32(input);
|
||||
|
||||
return svget1_f32(result);
|
||||
}
|
||||
|
||||
inline float oms_ceil(float a) {
|
||||
svfloat32_t input = svdup_f32(a);
|
||||
svfloat32_t result = svceil_f32(input);
|
||||
|
||||
return svget1_f32(result);
|
||||
}
|
||||
|
||||
inline void oms_fence_memory()
|
||||
{
|
||||
__dmb(0xF);
|
||||
}
|
||||
|
||||
inline void oms_fence_write()
|
||||
{
|
||||
__dmb(0xB);
|
||||
}
|
||||
|
||||
inline void oms_fence_load()
|
||||
{
|
||||
__dmb(0x7);
|
||||
}
|
||||
|
||||
inline
|
||||
void oms_invalidate_cache(void* address)
|
||||
{
|
||||
asm volatile("dc ivac, %0" : : "r"(address) : "memory");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
#include "../hash/GeneralHash.h"
|
||||
#include "../memory/RingMemory.h"
|
||||
|
||||
typedef uint64 (*perfect_hash_function)(const char* key, int32 seed);
|
||||
typedef uint64 (*PerfectHashFunction)(const char* key, int32 seed);
|
||||
|
||||
const perfect_hash_function PERFECT_HASH_FUNCTIONS[] = {
|
||||
const PerfectHashFunction PERFECT_HASH_FUNCTIONS[] = {
|
||||
hash_djb2_seeded,
|
||||
hash_sdbm_seeded,
|
||||
hash_lose_lose_seeded,
|
||||
|
|
@ -27,91 +27,50 @@ const perfect_hash_function PERFECT_HASH_FUNCTIONS[] = {
|
|||
};
|
||||
|
||||
struct PerfectHashEntryInt32 {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
int32 value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryInt64 {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
int64 value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryUIntPtr {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryVoidP {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
void* value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryFloat {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
f32 value;
|
||||
};
|
||||
|
||||
struct PerfectHashEntryStr {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
char value[HASH_MAP_MAX_KEY_LENGTH];
|
||||
};
|
||||
|
||||
struct PerfectHashEntry {
|
||||
int64 element_id;
|
||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||
byte* value;
|
||||
};
|
||||
|
||||
struct PerfectHashMap {
|
||||
int32 hash_seed;
|
||||
perfect_hash_function hash_function;
|
||||
int32 entry_size;
|
||||
PerfectHashFunction hash_function;
|
||||
uint32 entry_size;
|
||||
|
||||
int32 map_size;
|
||||
uint32 map_size;
|
||||
byte* hash_entries;
|
||||
};
|
||||
|
||||
bool set_perfect_hashmap(PerfectHashMap* hm, const char** keys, int32 key_count, perfect_hash_function hash_func, int32 seed_tries, RingMemory* ring)
|
||||
{
|
||||
int32* indices = (int32 *) ring_get_memory(ring, hm->map_size * sizeof(int32), 4);
|
||||
bool is_unique = false;
|
||||
|
||||
int32 seed;
|
||||
int32 c = 0;
|
||||
|
||||
while (!is_unique && c < seed_tries) {
|
||||
is_unique = true;
|
||||
seed = rand();
|
||||
memset(indices, 0, hm->map_size * sizeof(int32));
|
||||
|
||||
for (int32 j = 0; j < key_count; ++j) {
|
||||
int32 index = hash_func(keys[j], seed) % hm->map_size;
|
||||
if (indices[index]) {
|
||||
is_unique = false;
|
||||
break;
|
||||
} else {
|
||||
indices[index] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
++c;
|
||||
}
|
||||
|
||||
if (is_unique) {
|
||||
hm->hash_seed = seed;
|
||||
hm->hash_function = hash_func;
|
||||
}
|
||||
|
||||
return is_unique;
|
||||
}
|
||||
|
||||
bool perfect_hashmap_find_perfect_hash(PerfectHashMap* hm, const char** keys, int32 key_count, int32 seed_trys, RingMemory* ring)
|
||||
PerfectHashMap* perfect_hashmap_prepare(PerfectHashMap* hm, const char** keys, int32 key_count, int32 seed_trys, RingMemory* ring)
|
||||
{
|
||||
int32* indices = (int32 *) ring_get_memory(ring, hm->map_size * sizeof(int32), 4);
|
||||
bool is_unique = false;
|
||||
|
|
@ -141,22 +100,12 @@ bool perfect_hashmap_find_perfect_hash(PerfectHashMap* hm, const char** keys, in
|
|||
if (is_unique) {
|
||||
hm->hash_seed = seed;
|
||||
hm->hash_function = PERFECT_HASH_FUNCTIONS[i];
|
||||
|
||||
return hm;
|
||||
}
|
||||
}
|
||||
|
||||
return is_unique;
|
||||
}
|
||||
|
||||
// WARNING: element_size = element size + remaining HashEntry data size
|
||||
void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size, RingMemory* ring)
|
||||
{
|
||||
hm->map_size = count;
|
||||
hm->entry_size = element_size;
|
||||
hm->hash_entries = ring_get_memory(
|
||||
ring,
|
||||
count * element_size,
|
||||
0, true
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// WARNING: element_size = element size + remaining HashEntry data size
|
||||
|
|
@ -169,6 +118,8 @@ void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size,
|
|||
count * element_size,
|
||||
0, true
|
||||
);
|
||||
|
||||
LOG_LEVEL_2("Created PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}});
|
||||
}
|
||||
|
||||
// WARNING: element_size = element size + remaining HashEntry data size
|
||||
|
|
@ -177,6 +128,8 @@ void perfect_hashmap_create(PerfectHashMap* hm, int32 count, int32 element_size,
|
|||
hm->map_size = count;
|
||||
hm->entry_size = element_size;
|
||||
hm->hash_entries = buf;
|
||||
|
||||
LOG_LEVEL_2("Created PerfectHashMap for %n elements with %n B per element", {{LOG_DATA_INT32, &count}, {LOG_DATA_INT32, &element_size}});
|
||||
}
|
||||
|
||||
// Calculates how large a hashmap will be
|
||||
|
|
@ -196,7 +149,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, int32 value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryInt32* entry = (PerfectHashEntryInt32 *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
entry->value = value;
|
||||
}
|
||||
|
|
@ -205,7 +157,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, int64 value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryInt64* entry = (PerfectHashEntryInt64 *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
entry->value = value;
|
||||
}
|
||||
|
|
@ -214,7 +165,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, uintptr_t value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryUIntPtr* entry = (PerfectHashEntryUIntPtr *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
entry->value = value;
|
||||
}
|
||||
|
|
@ -223,7 +173,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, void* value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryVoidP* entry = (PerfectHashEntryVoidP *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
entry->value = value;
|
||||
}
|
||||
|
|
@ -232,7 +181,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, f32 value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryFloat* entry = (PerfectHashEntryFloat *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
entry->value = value;
|
||||
}
|
||||
|
|
@ -241,7 +189,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, const char* value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
memcpy(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||
}
|
||||
|
|
@ -250,7 +197,6 @@ inline
|
|||
void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, byte* value) {
|
||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
||||
entry->element_id = index;
|
||||
str_copy_short(entry->key, key);
|
||||
memcpy(entry->value, value, hm->entry_size - sizeof(PerfectHashEntry));
|
||||
}
|
||||
|
|
@ -260,7 +206,7 @@ PerfectHashEntry* perfect_hashmap_get_entry(const PerfectHashMap* hm, const char
|
|||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntry* entry = (PerfectHashEntry *) (hm->hash_entries + hm->entry_size * index);
|
||||
|
||||
return *entry->key == '\0' ? NULL : entry;
|
||||
return str_compare(entry->key, key) == 0 ? entry : NULL;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
@ -268,7 +214,12 @@ void perfect_hashmap_delete_entry(PerfectHashMap* hm, const char* key) {
|
|||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||
PerfectHashEntry* entry = (PerfectHashEntry *) (hm->hash_entries + hm->entry_size * index);
|
||||
|
||||
// This depends on where we check if an element exists (if we change perfect_hashmap_get_entry this also needs changing)
|
||||
if (str_compare(entry->key, key) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This depends on where we check if an element exists
|
||||
// If we change perfect_hashmap_get_entry this also needs changing
|
||||
*entry->key = '\0';
|
||||
}
|
||||
|
||||
|
|
@ -329,30 +280,30 @@ int64 perfect_hashmap_load(PerfectHashMap* hm, const byte* data)
|
|||
inline
|
||||
bool perfect_hashmap_from_hashmap(PerfectHashMap* phm, const HashMap* hm, int32 seed_trys, RingMemory* ring)
|
||||
{
|
||||
char** keys = (char **) ring_get_memory(ring, sizeof(char *) * hm->buf.count, 8);
|
||||
const char** keys = (char **) ring_get_memory(ring, sizeof(char *) * hm->buf.count, 8);
|
||||
|
||||
// Find all keys
|
||||
int32 key_index = 0;
|
||||
for (int32 i = 0; i < hm->buf.count; ++i) {
|
||||
HashEntry* entry = (HashEntry *) hm->table[i];
|
||||
const HashEntry* entry = (HashEntry *) hashmap_get_entry_by_index((HashMap *) hm, i);
|
||||
while (entry != NULL) {
|
||||
keys[key_index++] = entry->key;
|
||||
entry = (HashEntry *) entry->next;
|
||||
entry = (HashEntry *) hashmap_get_entry_by_element((HashMap *) hm, entry->next);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we can turn it into a perfect hash map
|
||||
bool is_perfect = perfect_hashmap_find_perfect_hash(phm, (char **) keys, key_index, seed_trys, ring);
|
||||
PerfectHashMap* is_perfect = perfect_hashmap_prepare(phm, keys, key_index, seed_trys, ring);
|
||||
if (!is_perfect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill perfect hash map
|
||||
for (int32 i = 0; i < hm->buf.count; ++i) {
|
||||
HashEntry* entry = (HashEntry *) hm->table[i];
|
||||
const HashEntry* entry = (HashEntry *) hashmap_get_entry_by_index((HashMap *) hm, i);
|
||||
while (entry != NULL) {
|
||||
perfect_hashmap_insert(phm, entry->key, entry->value);
|
||||
entry = (HashEntry *) entry->next;
|
||||
entry = (HashEntry *) hashmap_get_entry_by_element((HashMap *) hm, entry->next);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#if __aarch64__
|
||||
|
||||
#else
|
||||
// @todo Should get moved to architecture/x86/simd directory
|
||||
#include "simd/SIMD_F32.h"
|
||||
#include "simd/SIMD_F64.h"
|
||||
#include "simd/SIMD_I8.h"
|
||||
|
|
|
|||
|
|
@ -6,30 +6,14 @@
|
|||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_TYPES_H
|
||||
#define TOS_TYPES_H
|
||||
#ifndef TOS_STDLIB_TYPES_H
|
||||
#define TOS_STDLIB_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <windows.h>
|
||||
|
||||
#define PACKED_STRUCT __pragma(pack(push, 1))
|
||||
#define UNPACKED_STRUCT __pragma(pack(pop))
|
||||
typedef SSIZE_T ssize_t;
|
||||
|
||||
#define UNREACHABLE() __assume(0)
|
||||
#else
|
||||
#define PACKED_STRUCT __attribute__((__packed__))
|
||||
#define UNPACKED_STRUCT ((void) 0)
|
||||
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
#define EXPORT_LIB extern "C" __declspec(dllexport)
|
||||
#elif __linux__
|
||||
#define EXPORT_LIB extern "C" __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#define ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
|
|
|||
|
|
@ -12,22 +12,20 @@
|
|||
#include "../stdlib/Types.h"
|
||||
|
||||
#if _WIN32
|
||||
#include <windows.h>
|
||||
#include "../platform/win32/Library.h"
|
||||
#elif __linux__
|
||||
#include "../platform/linux/Library.h"
|
||||
#endif
|
||||
|
||||
struct Library {
|
||||
#if _WIN32
|
||||
HMODULE handle;
|
||||
#elif __linux__
|
||||
void* handle;
|
||||
#endif
|
||||
LibraryHandle handle;
|
||||
|
||||
bool is_valid;
|
||||
|
||||
char dir[MAX_PATH];
|
||||
char dst[64];
|
||||
|
||||
#if DEBUG
|
||||
#if DEBUG || INTERNAL
|
||||
uint64 last_load;
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -53,15 +53,16 @@ void system_info_render(char* buf, const SystemInfo* info) {
|
|||
"==============\n"
|
||||
"Name: %s\n" "VRAM: %d\n"
|
||||
"Name: %s\n" "VRAM: %d\n"
|
||||
"Name: %s\n" "VRAM: %d\n"
|
||||
"\n"
|
||||
"Display:\n"
|
||||
"==============\n"
|
||||
"Name: %s\n" "Width: %d\n" "Height: %d\n" "Hz: %d\n"
|
||||
"Name: %s\n" "Width: %d\n" "Height: %d\n" "Hz: %d\n"
|
||||
"Name: %s\n" "Width: %d\n" "Height: %d\n" "Hz: %d\n"
|
||||
"Name: %s\n" "Width: %d\n" "Height: %d\n" "Hz: %d\n"
|
||||
"Name: %s\n" "Width: %d\n" "Height: %d\n" "Hz: %d\n"
|
||||
"Name: %s\n" "Width: %d\n" "Height: %d\n" "Hz: %d\n"
|
||||
"Name: %s " "Width: %d " "Height: %d " "Hz: %d " "Primary: %d\n"
|
||||
"Name: %s " "Width: %d " "Height: %d " "Hz: %d " "Primary: %d\n"
|
||||
"Name: %s " "Width: %d " "Height: %d " "Hz: %d " "Primary: %d\n"
|
||||
"Name: %s " "Width: %d " "Height: %d " "Hz: %d " "Primary: %d\n"
|
||||
"Name: %s " "Width: %d " "Height: %d " "Hz: %d " "Primary: %d\n"
|
||||
"Name: %s " "Width: %d " "Height: %d " "Hz: %d " "Primary: %d\n"
|
||||
"\n"
|
||||
"RAM:\n"
|
||||
"==============\n"
|
||||
|
|
@ -80,12 +81,13 @@ void system_info_render(char* buf, const SystemInfo* info) {
|
|||
info->cpu.features,
|
||||
info->gpu[0].name, info->gpu[0].vram,
|
||||
info->gpu_count < 2 ? "" : info->gpu[1].name, info->gpu_count < 2 ? 0 : info->gpu[1].vram,
|
||||
info->display[0].name, info->display[0].width, info->display[0].height, info->display[0].hz,
|
||||
info->display_count < 2 ? "" : info->display[1].name, info->display_count < 2 ? 0 : info->display[1].width, info->display_count < 2 ? 0 : info->display[1].height, info->display_count < 2 ? 0 : info->display[1].hz,
|
||||
info->display_count < 3 ? "" : info->display[2].name, info->display_count < 3 ? 0 : info->display[2].width, info->display_count < 3 ? 0 : info->display[2].height, info->display_count < 3 ? 0 : info->display[2].hz,
|
||||
info->display_count < 4 ? "" : info->display[3].name, info->display_count < 4 ? 0 : info->display[3].width, info->display_count < 4 ? 0 : info->display[3].height, info->display_count < 4 ? 0 : info->display[3].hz,
|
||||
info->display_count < 5 ? "" : info->display[4].name, info->display_count < 5 ? 0 : info->display[4].width, info->display_count < 5 ? 0 : info->display[4].height, info->display_count < 5 ? 0 : info->display[4].hz,
|
||||
info->display_count < 6 ? "" : info->display[5].name, info->display_count < 6 ? 0 : info->display[5].width, info->display_count < 6 ? 0 : info->display[5].height, info->display_count < 6 ? 0 : info->display[5].hz,
|
||||
info->gpu_count < 3 ? "" : info->gpu[2].name, info->gpu_count < 3 ? 0 : info->gpu[2].vram,
|
||||
info->display[0].name, info->display[0].width, info->display[0].height, info->display[0].hz, info->display[0].is_primary,
|
||||
info->display_count < 2 ? "" : info->display[1].name, info->display_count < 2 ? 0 : info->display[1].width, info->display_count < 2 ? 0 : info->display[1].height, info->display_count < 2 ? 0 : info->display[1].hz, info->display_count < 2 ? 0 : info->display[1].is_primary,
|
||||
info->display_count < 3 ? "" : info->display[2].name, info->display_count < 3 ? 0 : info->display[2].width, info->display_count < 3 ? 0 : info->display[2].height, info->display_count < 3 ? 0 : info->display[2].hz, info->display_count < 3 ? 0 : info->display[2].is_primary,
|
||||
info->display_count < 4 ? "" : info->display[3].name, info->display_count < 4 ? 0 : info->display[3].width, info->display_count < 4 ? 0 : info->display[3].height, info->display_count < 4 ? 0 : info->display[3].hz, info->display_count < 4 ? 0 : info->display[3].is_primary,
|
||||
info->display_count < 5 ? "" : info->display[4].name, info->display_count < 5 ? 0 : info->display[4].width, info->display_count < 5 ? 0 : info->display[4].height, info->display_count < 5 ? 0 : info->display[4].hz, info->display_count < 5 ? 0 : info->display[4].is_primary,
|
||||
info->display_count < 6 ? "" : info->display[5].name, info->display_count < 6 ? 0 : info->display[5].width, info->display_count < 6 ? 0 : info->display[5].height, info->display_count < 6 ? 0 : info->display[5].hz, info->display_count < 6 ? 0 : info->display[5].is_primary,
|
||||
info->ram.memory
|
||||
);
|
||||
}
|
||||
|
|
@ -99,7 +101,6 @@ void system_info_get(SystemInfo* info)
|
|||
ram_info_get(&info->ram);
|
||||
info->gpu_count = gpu_info_get(info->gpu);
|
||||
info->display_count = display_info_get(info->display);
|
||||
display_info_get_primary(&info->display_primary);
|
||||
info->language = system_language_code();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ struct DisplayInfo {
|
|||
int32 width;
|
||||
int32 height;
|
||||
int32 hz;
|
||||
bool is_primary;
|
||||
};
|
||||
|
||||
struct SystemInfo {
|
||||
|
|
@ -57,10 +58,9 @@ struct SystemInfo {
|
|||
CpuInfo cpu;
|
||||
RamInfo ram;
|
||||
|
||||
GpuInfo gpu[2];
|
||||
GpuInfo gpu[3];
|
||||
int32 gpu_count;
|
||||
|
||||
DisplayInfo display_primary;
|
||||
DisplayInfo display[6];
|
||||
int32 display_count;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ typedef void (*ThreadPoolJobFunc)(void*);
|
|||
|
||||
struct PoolWorker {
|
||||
int32 id;
|
||||
volatile int32 state;
|
||||
int32 state;
|
||||
void* arg;
|
||||
void* result;
|
||||
RingMemory ring;
|
||||
|
|
@ -29,7 +29,7 @@ struct PoolWorker {
|
|||
};
|
||||
|
||||
struct Worker {
|
||||
volatile int32 state;
|
||||
int32 state;
|
||||
pthread_t thread;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ struct ThreadPool {
|
|||
int32 size;
|
||||
int32 state;
|
||||
|
||||
uint32 id_counter;
|
||||
int32 id_counter;
|
||||
};
|
||||
|
||||
static THREAD_RETURN thread_pool_worker(void* arg)
|
||||
|
|
@ -120,7 +120,7 @@ void thread_pool_wait(ThreadPool* pool)
|
|||
void thread_pool_destroy(ThreadPool* pool)
|
||||
{
|
||||
// This sets the queue to empty
|
||||
atomic_set_acquire((void **) &pool->work_queue.tail, (void **) &pool->work_queue.head);
|
||||
atomic_set_acquire((void **) &pool->work_queue.tail, pool->work_queue.head);
|
||||
|
||||
// This sets the state to "shutdown"
|
||||
atomic_set_release(&pool->state, 1);
|
||||
|
|
|
|||
|
|
@ -4,20 +4,38 @@
|
|||
#include "../stdlib/Types.h"
|
||||
#include "../animation/AnimationEaseType.h"
|
||||
|
||||
#define UI_ANIMATION_ACTIVE_FLAG 0x80000000
|
||||
|
||||
struct UIAnimationState {
|
||||
uint64 start;
|
||||
uint32 duration;
|
||||
// When did the animation start?
|
||||
uint32 start;
|
||||
|
||||
// Element specific use (e.g. cursor uses 0/1 for visible/invisible for blinking)
|
||||
// The highest bit indicates if the animation is active or not
|
||||
int32 state;
|
||||
AnimationEaseType anim_type;
|
||||
int16 state;
|
||||
|
||||
// What is our last keyframe for reference?
|
||||
byte keyframe;
|
||||
|
||||
// Currently active animation
|
||||
byte active_animation;
|
||||
};
|
||||
|
||||
struct UIAnimation {
|
||||
uint32 duration;
|
||||
// For which transition is this animation used?
|
||||
UIStyleType style_old;
|
||||
UIStyleType style_new;
|
||||
|
||||
// This allows for a maximum duration of 65536ms = 65.536s
|
||||
uint16 duration;
|
||||
AnimationEaseType anim_type;
|
||||
|
||||
// The last keyframe is always the style associated with style_new
|
||||
// This means that with 0 keyframes and a duration of 'Xs' the animation is interpolated between style_old and style_new
|
||||
// In other words the keyframes below only define ADDITIONAL steps in between style_old and style_new
|
||||
// The keyframes are defined in the theme file
|
||||
byte keyframe_count;
|
||||
|
||||
// The dynamic array containing the offsets into the keyframes come directly after UIAnimation
|
||||
// UIElementDetail* keyframes;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,12 @@
|
|||
#define TOS_UI_BUTTON_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "attribute/UIAttribute.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UIStyleType.h"
|
||||
#include "UILayout.h"
|
||||
#include "UIElement.h"
|
||||
#include "../math/Evaluator.h"
|
||||
|
||||
struct UIButtonState {
|
||||
};
|
||||
|
|
@ -10,4 +16,42 @@ struct UIButton {
|
|||
|
||||
};
|
||||
|
||||
void ui_button_state_serialize(const UIButtonState* __restrict state, byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_button_state_unserialize(UIButtonState* __restrict state, const byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_button_state_populate(const UIAttributeGroup* __restrict group, UIButtonState* __restrict state)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_button_element_serialize(const UIButton* __restrict details, byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_button_element_unserialize(UIButton* __restrict details, const byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_button_element_populate(
|
||||
const UIAttributeGroup* __restrict group,
|
||||
UIElement* __restrict element,
|
||||
UIStyleType style_type,
|
||||
EvaluatorVariable* __restrict variables
|
||||
) {
|
||||
}
|
||||
|
||||
int32 ui_button_element_update(UILayout* layout, UIElement* element)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -2,12 +2,24 @@
|
|||
#define TOS_UI_CURSOR_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UILayout.h"
|
||||
#include "UIElement.h"
|
||||
|
||||
struct UICursorState {
|
||||
};
|
||||
|
||||
struct UICursor {
|
||||
|
||||
UIAttributeDimension dimension;
|
||||
byte opacity; // 1 byte alpha channel
|
||||
};
|
||||
|
||||
int32 ui_cursor_element_update(UILayout* layout, UIElement* element)
|
||||
{
|
||||
UICursor* input = (UICursor *) (layout->data + element->style_types[element->style_new]);
|
||||
UICursorState* state = (UICursorState *) (layout->data + element->state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -2,11 +2,14 @@
|
|||
#define TOS_UI_ELEMENT_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "UIStyleType.h"
|
||||
#include "UIAnimation.h"
|
||||
|
||||
enum UIElementType : byte {
|
||||
UI_ELEMENT_TYPE_BUTTON,
|
||||
UI_ELEMENT_TYPE_SELECT,
|
||||
UI_ELEMENT_TYPE_INPUT,
|
||||
UI_ELEMENT_TYPE_LABEL,
|
||||
UI_ELEMENT_TYPE_TEXTAREA,
|
||||
UI_ELEMENT_TYPE_IMAGE,
|
||||
UI_ELEMENT_TYPE_TEXT,
|
||||
|
|
@ -24,22 +27,92 @@ enum UIElementState : byte {
|
|||
UI_ELEMENT_STATE_VISIBLE = 1 << 1,
|
||||
UI_ELEMENT_STATE_FOCUSED = 1 << 2,
|
||||
UI_ELEMENT_STATE_CLICKABLE = 1 << 3,
|
||||
|
||||
// Are we currently in an animation?
|
||||
// @question Do we even need this? Can't we just use the animation state (start == 0)
|
||||
UI_ELEMENT_STATE_ANIMATION = 1 << 4,
|
||||
|
||||
// Flag to indicate that the element changed
|
||||
// Just checking style_old and style_new is not enough, since we may have an ongoing animation
|
||||
// We also need to be able to check the parent element in case the parent element changed position
|
||||
// -> we also need to change position of all child elements (parent sets this flag for all child elements)
|
||||
UI_ELEMENT_STATE_CHANGED = 1 << 5,
|
||||
};
|
||||
|
||||
// All pointer like variables are uint32 offset values
|
||||
// This allows us to copy the data around temp memory without loss of reference
|
||||
// For arrays we position the data after the UIElement
|
||||
struct UIElement {
|
||||
// @see UIElementState
|
||||
byte state_flag;
|
||||
UIElementType type;
|
||||
|
||||
// Used to keep track of the current state (= _old) and the next state or state we are transitioning into
|
||||
UIStyleType style_old;
|
||||
UIStyleType style_new;
|
||||
UIElement* parent;
|
||||
void* state;
|
||||
void* details[UI_STYLE_TYPE_SIZE];
|
||||
|
||||
// Used for grouping ui elements (e.g. const ui elements, fairly static elements, dynamic elements)
|
||||
byte category;
|
||||
|
||||
f32 zindex;
|
||||
|
||||
// Some elements need information from their parent element
|
||||
// @question Do we even need this? When rendering we always start at the root and go down
|
||||
// This means we could easily pass the parent element as a parameter in the function call
|
||||
// Maybe we still want this in case we ever want to call individual elements?
|
||||
uint32 parent;
|
||||
|
||||
//////////////////////////////////////
|
||||
// Sub info
|
||||
//////////////////////////////////////
|
||||
|
||||
// Every element can have a fixed state, which is element type specific
|
||||
// This state is shared across all style_type (see below)
|
||||
uint32 state;
|
||||
|
||||
// Every element can have multiple implementations for different states (e.g. hover, focus, ...)
|
||||
// These implementations are also element type specific and can be found in here
|
||||
uint32 style_types[UI_STYLE_TYPE_SIZE];
|
||||
|
||||
// A UIElement can have child elements (e.g. a Window has labels, buttons, etc...)
|
||||
uint16 children_count;
|
||||
UIElement** children;
|
||||
|
||||
// The children array comes directly after UIElement
|
||||
// uint32* children;
|
||||
|
||||
//////////////////////////////////////
|
||||
// Animations
|
||||
//////////////////////////////////////
|
||||
|
||||
// A UIElement can have animations
|
||||
// e.g. when switching between styles/states or simply repeating animations like hover animations
|
||||
// @question Should this even be here or should this be in the state of the element?
|
||||
UIAnimationState animation_state;
|
||||
|
||||
// The animation count is defined in the theme file
|
||||
uint16 animation_count;
|
||||
|
||||
// Location where the animation information is stored
|
||||
// We cannot put it at the end of this UIElement since the length is only known by the theme
|
||||
uint32 animations;
|
||||
|
||||
// The animations array comes directly after the children array
|
||||
// This means: UIElement + children_count * sizeof(uint32)
|
||||
// uint32* animations;
|
||||
|
||||
//////////////////////////////////////
|
||||
// Cache
|
||||
//////////////////////////////////////
|
||||
|
||||
// We cache the last UI element rendering for re-use in the next frame
|
||||
// @question There might be custom UI elements which need more than 2^16 vertices
|
||||
uint16 vertex_count;
|
||||
|
||||
// The max vertex count is defined in the theme file
|
||||
uint16 vertex_count_max;
|
||||
|
||||
// Offset into the vertex array (NOT in bytes but in vertices)
|
||||
uint32 vertices_active;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
#include "UIButton.h"
|
||||
#include "UISelect.h"
|
||||
#include "UIInput.h"
|
||||
|
|
@ -46,6 +47,7 @@ int32 ui_element_type_size(UIElementType e)
|
|||
return sizeof(UICursor);
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -80,6 +82,7 @@ int32 ui_element_state_size(UIElementType e)
|
|||
return sizeof(UICursorState);
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
210
ui/UIInput.h
210
ui/UIInput.h
|
|
@ -3,15 +3,21 @@
|
|||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../camera/Camera.h"
|
||||
#include "attribute/UIAttribute.h"
|
||||
#include "attribute/UIAttributeBorder.h"
|
||||
#include "attribute/UIAttributeShadow.h"
|
||||
#include "attribute/UIAttributeFont.h"
|
||||
#include "attribute/UIAttributeBackground.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UIAnimation.h"
|
||||
#include "UIStyleType.h"
|
||||
#include "UIElement.h"
|
||||
#include "UICursor.h"
|
||||
#include "UIWindow.h"
|
||||
#include "UIPanel.h"
|
||||
#include "UILayout.h"
|
||||
#include "../math/Evaluator.h"
|
||||
#include "../gpuapi/RenderUtils.h"
|
||||
#include "../object/Vertex.h"
|
||||
|
||||
enum UIInputType : byte {
|
||||
UI_INPUT_TYPE_TEXT,
|
||||
|
|
@ -22,11 +28,8 @@ enum UIInputType : byte {
|
|||
};
|
||||
|
||||
struct UIInputState {
|
||||
char content[512];
|
||||
char* content_ref;
|
||||
uint16 cursor_pos_x;
|
||||
uint16 cursor_pos_y;
|
||||
UIAnimationState animation;
|
||||
UIInputType type;
|
||||
int32 min_value;
|
||||
int32 max_value;
|
||||
|
|
@ -38,66 +41,213 @@ struct UIInput {
|
|||
byte opacity; // 1 byte alpha channel
|
||||
byte padding;
|
||||
|
||||
// Animation used to get into this style
|
||||
UIAnimation animation;
|
||||
|
||||
UIAttributeFont font;
|
||||
UIAttributeBackground background;
|
||||
|
||||
UIAttributeBorder border;
|
||||
UIAttributeShadow shadow_outer;
|
||||
UIAttributeShadow shadow_inner;
|
||||
};
|
||||
|
||||
void ui_input_state_populate(const UIAttributeGroup* group, UIInputState* state) {
|
||||
for (int32 i = 0; i < group->attribute_size; ++i) {
|
||||
switch (group->attributes[i].attribute_id) {
|
||||
void ui_input_state_serialize(const UIInputState* __restrict state, byte** __restrict pos)
|
||||
{
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(state->cursor_pos_x);
|
||||
*pos += sizeof(state->cursor_pos_x);
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(state->cursor_pos_y);
|
||||
*pos += sizeof(state->cursor_pos_y);
|
||||
|
||||
**pos = state->type;
|
||||
*pos += sizeof(state->type);
|
||||
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(state->min_value);
|
||||
*pos += sizeof(state->min_value);
|
||||
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(state->max_value);
|
||||
*pos += sizeof(state->max_value);
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(state->max_input_length);
|
||||
*pos += sizeof(state->max_input_length);
|
||||
}
|
||||
|
||||
void ui_input_state_unserialize(UIInputState* __restrict state, const byte** __restrict pos)
|
||||
{
|
||||
state->cursor_pos_x = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(state->cursor_pos_x);
|
||||
|
||||
state->cursor_pos_y = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(state->cursor_pos_y);
|
||||
|
||||
state->type = (UIInputType) **pos;
|
||||
*pos += sizeof(state->type);
|
||||
|
||||
state->min_value = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(state->min_value);
|
||||
|
||||
state->max_value = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(state->max_value);
|
||||
|
||||
state->max_input_length = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(state->max_input_length);
|
||||
}
|
||||
|
||||
void ui_input_state_populate(const UIAttributeGroup* __restrict group, UIInputState* __restrict state) {
|
||||
|
||||
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
||||
for (int32 i = 0; i < group->attribute_count; ++i) {
|
||||
switch (attributes[i].attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_TYPE: {
|
||||
state->type = (UIInputType) group->attributes[i].value_int;
|
||||
state->type = (UIInputType) attributes[i].value_int;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_MIN_VALUE: {
|
||||
state->min_value = group->attributes[i].value_int;
|
||||
state->min_value = attributes[i].value_int;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_MAX_VALUE: {
|
||||
state->max_value = group->attributes[i].value_int;
|
||||
state->max_value = attributes[i].value_int;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_MAX_INPUT_LENGTH: {
|
||||
state->max_input_length = (uint16) group->attributes[i].value_int;
|
||||
state->max_input_length = (uint16) attributes[i].value_int;
|
||||
} break;
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_input_element_serialize(const UIInput* __restrict details, byte** __restrict pos)
|
||||
{
|
||||
ui_attr_dimension_serialize(&details->dimension, pos);
|
||||
|
||||
**pos = details->opacity;
|
||||
*pos += sizeof(details->opacity);
|
||||
|
||||
**pos = details->padding;
|
||||
*pos += sizeof(details->padding);
|
||||
|
||||
ui_attr_background_serialize(&details->background, pos);
|
||||
ui_attr_border_serialize(&details->border, pos);
|
||||
ui_attr_shadow_serialize(&details->shadow_outer, pos);
|
||||
ui_attr_shadow_serialize(&details->shadow_inner, pos);
|
||||
}
|
||||
|
||||
void ui_input_element_unserialize(UIInput* __restrict details, const byte** __restrict pos)
|
||||
{
|
||||
ui_attr_dimension_unserialize(&details->dimension, pos);
|
||||
|
||||
details->opacity = **pos;
|
||||
*pos += sizeof(details->opacity);
|
||||
|
||||
details->padding = **pos;
|
||||
*pos += sizeof(details->padding);
|
||||
|
||||
ui_attr_background_unserialize(&details->background, pos);
|
||||
ui_attr_border_unserialize(&details->border, pos);
|
||||
ui_attr_shadow_unserialize(&details->shadow_outer, pos);
|
||||
ui_attr_shadow_unserialize(&details->shadow_inner, pos);
|
||||
}
|
||||
|
||||
void ui_input_element_populate(
|
||||
const UIAttributeGroup* group,
|
||||
UIElement* element,
|
||||
UIStyleType style_type,
|
||||
EvaluatorVariable* variables
|
||||
UILayout* layout,
|
||||
const UIAttributeGroup* __restrict group,
|
||||
UIInput* __restrict input,
|
||||
UIElement* parent,
|
||||
EvaluatorVariable* __restrict variables
|
||||
) {
|
||||
if (element->parent) {
|
||||
if (parent) {
|
||||
// @bug How to ensure that the parent is initialized before the child element
|
||||
// Currently the order of the initialization depends on the theme file, NOT the layout file
|
||||
// We could fix it by loading the style based on the layout order but this would result in many misses when looking up styles
|
||||
// The reason for these misses are, that often only 1-2 style_types exist per element
|
||||
UIInput* parent = (UIInput *) element->parent->details[UI_STYLE_TYPE_DEFAULT];
|
||||
variables[2].value = parent->dimension.dimension.x;
|
||||
variables[3].value = parent->dimension.dimension.y;
|
||||
variables[4].value = parent->dimension.dimension.width;
|
||||
variables[5].value = parent->dimension.dimension.height;
|
||||
|
||||
v4_f32* parent_dimension;
|
||||
switch (parent->type) {
|
||||
case UI_ELEMENT_TYPE_VIEW_WINDOW: {
|
||||
UIWindow* parent_window = (UIWindow *) (layout->data + parent->style_types[UI_STYLE_TYPE_ACTIVE]);
|
||||
parent_dimension = &parent_window->dimension.dimension;
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_VIEW_PANEL: {
|
||||
UIPanel* parent_window = (UIPanel *) (layout->data + parent->style_types[UI_STYLE_TYPE_ACTIVE]);
|
||||
parent_dimension = &parent_window->dimension.dimension;
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
parent_dimension = NULL;
|
||||
}
|
||||
|
||||
variables[2].value = parent_dimension->x;
|
||||
variables[3].value = parent_dimension->y;
|
||||
variables[4].value = parent_dimension->width;
|
||||
variables[5].value = parent_dimension->height;
|
||||
}
|
||||
|
||||
UIInput* e = (UIInput *) element->details[style_type];
|
||||
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
||||
|
||||
// First set all values, which we can set immediately
|
||||
for (int32 i = 0; i < group->attribute_size; ++i) {
|
||||
switch (group->attributes[i].attribute_id) {
|
||||
for (int32 i = 0; i < group->attribute_count; ++i) {
|
||||
switch (attributes->attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_X:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_Y:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
||||
ui_theme_assign_dimension(&e->dimension, &group->attributes[i], 6, variables);
|
||||
ui_theme_assign_dimension(&input->dimension, attributes, 6, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 ui_input_element_update(UILayout* layout, UIElement* element)
|
||||
{
|
||||
UIInput* input = (UIInput *) (layout->data + element->style_types[element->style_new]);
|
||||
UIInputState* state = (UIInputState *) (layout->data + element->state);
|
||||
|
||||
int32 idx = 0;
|
||||
f32 zindex = element->zindex;
|
||||
|
||||
v4_f32 dimension = input->dimension.dimension;
|
||||
|
||||
// Border
|
||||
if (input->border.thickness) {
|
||||
idx += vertex_rect_create(
|
||||
layout->vertices_active + element->vertices_active, zindex,
|
||||
dimension, input->dimension.alignment,
|
||||
input->border.color
|
||||
);
|
||||
|
||||
// Adjusting dimension based on border
|
||||
// border is part of width/height
|
||||
dimension.x += input->border.thickness;
|
||||
dimension.y += input->border.thickness;
|
||||
dimension.width -= input->border.thickness;
|
||||
dimension.height -= input->border.thickness;
|
||||
|
||||
zindex = nextafterf(zindex, INFINITY);
|
||||
}
|
||||
|
||||
// Background
|
||||
if (input->background.background_color) {
|
||||
idx += vertex_rect_create(
|
||||
layout->vertices_active + element->vertices_active + idx, zindex,
|
||||
dimension, input->dimension.alignment,
|
||||
input->background.background_color
|
||||
);
|
||||
|
||||
zindex = nextafterf(zindex, INFINITY);
|
||||
}
|
||||
|
||||
// Cursor
|
||||
HashEntryInt32* cursor_entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, "cursor");
|
||||
if (cursor_entry) {
|
||||
UIElement* cursor_element = (UIElement *) (layout->data + cursor_entry->value);
|
||||
// This requires the cursor position is already updated wherever we handle the input update (on_enter, on_leave)
|
||||
idx += ui_cursor_element_update(layout, cursor_element);
|
||||
|
||||
memcpy(
|
||||
layout->vertices_active + element->vertices_active + idx,
|
||||
layout->vertices_active + cursor_element->vertices_active,
|
||||
cursor_element->vertex_count * sizeof(*(layout->vertices_active))
|
||||
);
|
||||
}
|
||||
|
||||
element->vertex_count = (uint16) idx;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
#endif
|
||||
59
ui/UILabel.h
59
ui/UILabel.h
|
|
@ -2,12 +2,69 @@
|
|||
#define TOS_UI_LABEL_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "attribute/UIAttributeFont.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UIStyleType.h"
|
||||
#include "UIElement.h"
|
||||
#include "UITheme.h"
|
||||
#include "UILayout.h"
|
||||
#include "../object/Vertex.h"
|
||||
|
||||
struct UILabelState {
|
||||
char* content;
|
||||
};
|
||||
|
||||
struct UILabel {
|
||||
|
||||
UIAttributeDimension dimension;
|
||||
byte opacity; // 1 byte alpha channel
|
||||
UIAttributeFont font;
|
||||
};
|
||||
|
||||
void ui_label_state_serialize(const UILabelState* __restrict state, byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_label_state_unserialize(UILabelState* __restrict state, const byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_label_state_populate(const UIAttributeGroup* __restrict group, UILabelState* __restrict state)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_label_element_serialize(const UILabel* __restrict details, byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_label_element_unserialize(UILabel* __restrict details, const byte** __restrict pos)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_label_element_populate(
|
||||
const UIAttributeGroup* __restrict group,
|
||||
UIElement* __restrict element,
|
||||
UIStyleType style_type,
|
||||
EvaluatorVariable* __restrict variables
|
||||
) {
|
||||
}
|
||||
|
||||
int32 ui_label_element_update(UILayout* layout, UIElement* element)
|
||||
{
|
||||
UILabel* label = (UILabel *) (layout->data + element->style_types[element->style_new]);
|
||||
UILabelState* state = (UILabelState *) (layout->data + element->state);
|
||||
|
||||
return vertex_text_create(
|
||||
layout->vertices_active + element->vertices_active, element->zindex,
|
||||
label->dimension.dimension, label->font.alignment,
|
||||
layout->font, state->content,
|
||||
label->font.size, label->font.color,
|
||||
label->font.weight
|
||||
).z;
|
||||
}
|
||||
|
||||
#endif
|
||||
847
ui/UILayout.cpp
Normal file
847
ui/UILayout.cpp
Normal file
|
|
@ -0,0 +1,847 @@
|
|||
#ifndef TOS_UI_LAYOUT_C
|
||||
#define TOS_UI_LAYOUT_C
|
||||
|
||||
#include <string.h>
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../stdlib/HashMap.h"
|
||||
#include "../asset/Asset.h"
|
||||
#include "../camera/Camera.h"
|
||||
#include "../system/FileUtils.cpp"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
#include "UILayout.h"
|
||||
#include "UITheme.h"
|
||||
#include "UIElement.h"
|
||||
#include "UIElementType.h"
|
||||
#include "UIInput.h"
|
||||
#include "UILabel.h"
|
||||
|
||||
// Doesn't change the position of pos outside of the function, since lookahead
|
||||
static
|
||||
void ui_layout_count_direct_children(UIElement* __restrict element, const char* __restrict pos, int32 parent_level)
|
||||
{
|
||||
// Find amount of child elements
|
||||
// We have to perform a lookahead since this determins the size of our children array
|
||||
uint16 direct_child_elements = 0;
|
||||
|
||||
int32 level = 0;
|
||||
while (*pos != '\0') {
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (level > parent_level + 4) {
|
||||
// This element is a childrens child and not a direct child
|
||||
continue;
|
||||
} else if (level <= parent_level || !str_is_alphanum(*pos)) {
|
||||
// We are no longer inside of element
|
||||
break;
|
||||
}
|
||||
|
||||
++direct_child_elements;
|
||||
str_move_past(&pos, '\n');
|
||||
}
|
||||
|
||||
element->children_count = direct_child_elements;
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_assign_children(
|
||||
UILayout* __restrict layout,
|
||||
UIElement* __restrict element,
|
||||
const char* __restrict pos,
|
||||
int32 parent_level
|
||||
) {
|
||||
int32 current_child_pos = 0;
|
||||
|
||||
char block_name[28];
|
||||
|
||||
int32 level = 0;
|
||||
while (*pos != '\0') {
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (level > parent_level + 4) {
|
||||
// This element is a childrens child and not a direct child
|
||||
continue;
|
||||
} else if (level <= parent_level) {
|
||||
// We are no longer inside of element
|
||||
break;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
// Children array (located after the UIElement)
|
||||
uint32* children = (uint32 *) (element + 1);
|
||||
|
||||
// Set child offset
|
||||
HashEntryInt32* child_entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name);
|
||||
children[current_child_pos] = child_entry->value;
|
||||
|
||||
// Create a reference to the parent element for the child element
|
||||
UIElement* child_element = (UIElement *) (layout->data + child_entry->value);
|
||||
child_element->parent = (uint32) ((uintptr_t) element - (uintptr_t) layout->data);
|
||||
|
||||
++current_child_pos;
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: theme needs to have memory already reserved and assigned to data
|
||||
void layout_from_file_txt(
|
||||
UILayout* __restrict layout,
|
||||
const char* __restrict path,
|
||||
RingMemory* ring
|
||||
) {
|
||||
FileBody file;
|
||||
file_read(path, &file, ring);
|
||||
ASSERT_SIMPLE(file.size);
|
||||
|
||||
const char* pos = (char *) file.content;
|
||||
|
||||
// move past the version string
|
||||
pos += 8;
|
||||
|
||||
// Use version for different handling
|
||||
int32 version = strtol(pos, (char **) &pos, 10); ++pos;
|
||||
|
||||
// 1. Iteration: We have to find how many elements are defined in the layout file.
|
||||
// Therefore we have to do an initial iteration
|
||||
// We start at 1 since we always have a root element
|
||||
int32 temp_element_count = 1;
|
||||
while (*pos != '\0') {
|
||||
// Skip all white spaces
|
||||
str_skip_empty(&pos);
|
||||
|
||||
++temp_element_count;
|
||||
|
||||
// Go to the next line
|
||||
str_move_past(&pos, '\n');
|
||||
}
|
||||
|
||||
// 2. Iteration: Fill HashMap
|
||||
// @performance This is probably horrible since we are not using a perfect hashing function (1 hash -> 1 index)
|
||||
// I wouldn't be surprised if we have a 50% hash overlap (2 hashes -> 1 index)
|
||||
hashmap_create(&layout->hash_map, temp_element_count, sizeof(HashEntryInt32), layout->data);
|
||||
int64 hm_size = hashmap_size(&layout->hash_map);
|
||||
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
char block_name[28];
|
||||
char block_type[28];
|
||||
|
||||
// We store the UIElement and associated data after the hashmap
|
||||
byte* element_data = layout->data + hm_size;
|
||||
|
||||
// Create root element
|
||||
UIElement* root = (UIElement *) element_data;
|
||||
hashmap_insert(&layout->hash_map, ":root", element_data - layout->data);
|
||||
ui_layout_count_direct_children(root, pos, -4);
|
||||
|
||||
// NOTE: The root element cannot have any animations or vertices
|
||||
element_data += sizeof(UIElement) + sizeof(uint32) * root->children_count;
|
||||
|
||||
int32 level;
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (*pos == '\n' || *pos == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":"); ++pos;
|
||||
str_copy_move_until(&pos, block_type, " \r\n");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
// Insert new element
|
||||
UIElement* element = (UIElement *) element_data;
|
||||
hashmap_insert(&layout->hash_map, block_name, (int32) ((uintptr_t) element_data - (uintptr_t) layout->data));
|
||||
|
||||
element->type = (UIElementType) ui_element_type_to_id(block_type);
|
||||
|
||||
// The children array is dynamic in size and comes directly after the UIElement
|
||||
ui_layout_count_direct_children(element, pos, level);
|
||||
|
||||
element_data += sizeof(UIElement)
|
||||
+ sizeof(uint32) * element->children_count; // Children offsets come after the UIElement
|
||||
|
||||
// We put the state data after this element
|
||||
element->state = (uint32) ((uintptr_t) element_data - (uintptr_t) layout->data);
|
||||
element_data += ui_element_state_size(element->type);
|
||||
|
||||
// We put the active element data after this element
|
||||
element->style_types[UI_STYLE_TYPE_ACTIVE] = (uint32) ((uintptr_t) element_data - (uintptr_t) layout->data);
|
||||
// @performance We should probably make sure the data is nicely aligned here
|
||||
element_data += ui_element_type_size(element->type);
|
||||
|
||||
// We put the default element data after this element
|
||||
// Depending on the theme we will have also additional styles (e.g. :active, :hidden, ...)
|
||||
element->style_types[UI_STYLE_TYPE_DEFAULT] = (uint32) ((uintptr_t) element_data - (uintptr_t) layout->data);
|
||||
// @performance We should probably make sure the data is nicely aligned here
|
||||
element_data += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
// 3. Iteration: Create child references
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (*pos == '\n' || *pos == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
UIElement* element = (UIElement *) (layout->data + ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value);
|
||||
ui_layout_assign_children(layout, element, pos, level);
|
||||
|
||||
// ui_layout_assign_children doesn't move the pos pointer
|
||||
str_move_past(&pos, '\n');
|
||||
}
|
||||
|
||||
// 4. Iteration: Create root child references
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
uint32* root_children = (uint32 *) (root + 1);
|
||||
|
||||
int32 child = 0;
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*pos == ' ' || *pos == '\t') {
|
||||
str_move_past(&pos, '\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
root_children[child++] = ((HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, block_name))->value;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_serialize_element_state(UIElementType type, const void* __restrict state, byte** __restrict pos) {
|
||||
switch (type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_state_serialize((UIInputState *) state, pos);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_serialize_element_detail(UIElementType type, const void* __restrict details, byte** __restrict pos) {
|
||||
switch (type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_serialize((UIInput *) details, pos);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_serialize_element(
|
||||
HashEntryInt32* entry,
|
||||
byte* data,
|
||||
byte** pos,
|
||||
const byte* start
|
||||
) {
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of layout->data
|
||||
UIElement* element = (UIElement *) (data + entry->value);
|
||||
|
||||
**pos = element->state_flag;
|
||||
*pos += sizeof(element->state_flag);
|
||||
|
||||
**pos = element->type;
|
||||
*pos += sizeof(element->type);
|
||||
|
||||
**pos = element->style_old;
|
||||
*pos += sizeof(element->style_old);
|
||||
|
||||
**pos = element->style_new;
|
||||
*pos += sizeof(element->style_new);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->parent);
|
||||
*pos += sizeof(element->parent);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->state);
|
||||
*pos += sizeof(element->state);
|
||||
|
||||
// Details
|
||||
for (int32 i = 0; i < UI_STYLE_TYPE_SIZE; ++i) {
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->style_types[i]);
|
||||
*pos += sizeof(element->style_types[i]);
|
||||
}
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->children_count);
|
||||
*pos += sizeof(element->children_count);
|
||||
|
||||
/* We don't save the animation state since that is always 0 in the file
|
||||
memset(*pos, 0, sizeof(element->animation_state));
|
||||
*pos += sizeof(element->animation_state);
|
||||
*/
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->animation_count);
|
||||
*pos += sizeof(element->animation_count);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->animations);
|
||||
*pos += sizeof(element->animations);
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->vertex_count);
|
||||
*pos += sizeof(element->vertex_count);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->vertices_active);
|
||||
*pos += sizeof(element->vertices_active);
|
||||
|
||||
// Output dynamic length content directly after UIElement
|
||||
//
|
||||
// WARNING: The data ordering in our output data is not necessarily the same as in memory ESPECIALLY for animations
|
||||
// However, we can simply reconstruct the memory order by reversing the logic
|
||||
//
|
||||
// @todo We could optimize the memory layout of our data 9e.g. ->style_types, children, ... to be more packed
|
||||
// At this point we may have this data available now (if we save a cached version = layout+theme)
|
||||
// Obviously, this won't have an effect on the current run-tim but would make the memory layout nicer on the next load
|
||||
// It would be kind of a self-optimizing ui layout system :).
|
||||
// Of course, updating the reference values (uint32) will be challenging since the file pos will still not be the same as the offset due to alignment and padding
|
||||
// We would probably need a helper_offset value that gets passed around also as parameter of this function
|
||||
//////////////////////////////////////
|
||||
|
||||
// Children array
|
||||
uint32* children = (uint32 *) (element + 1);
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(children[i]);
|
||||
*pos += sizeof(*children);
|
||||
}
|
||||
|
||||
// State element data e.g. UIInputState
|
||||
ui_layout_serialize_element_state(element->type, data + element->state, pos);
|
||||
|
||||
// detailed element data/style_types e.g. UIInput
|
||||
// When you create a layout this is should only contain the default style type
|
||||
// BUT we also support layout caching where a fully parsed layout+theme can be saved and loaded
|
||||
// This is very fast since now we don't need to build the layout based on the theme as long as the theme and window dimensions didn't change
|
||||
for (int32 i = 0; i < UI_STYLE_TYPE_SIZE; ++i) {
|
||||
if (!element->style_types[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui_layout_serialize_element_detail(element->type, data + element->style_types[i], pos);
|
||||
}
|
||||
|
||||
UIAnimation* animations = (UIAnimation *) (data + element->animations);
|
||||
int32 element_style_type_size = ui_element_type_size(element->type);
|
||||
|
||||
for (int32 i = 0; i < element->animation_count; ++i) {
|
||||
**pos = animations[i].style_old;
|
||||
*pos += sizeof(animations[i].style_old);
|
||||
|
||||
**pos = animations[i].style_new;
|
||||
*pos += sizeof(animations[i].style_new);
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(animations[i].duration);
|
||||
*pos += sizeof(animations[i].duration);
|
||||
|
||||
**pos = animations[i].anim_type;
|
||||
*pos += sizeof(animations[i].anim_type);
|
||||
|
||||
**pos = animations[i].keyframe_count;
|
||||
*pos += sizeof(animations[i].keyframe_count);
|
||||
|
||||
// The keyframes are the element detail information (e.g. UIInput) and they are located after the respective Animation definition
|
||||
byte* keyframes = (byte *) (&animations[i] + 1);
|
||||
for (int32 j = 0; j < animations[i].keyframe_count; ++j) {
|
||||
ui_layout_serialize_element_detail(element->type, keyframes + j * element_style_type_size, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 layout_to_data(
|
||||
const UILayout* __restrict layout,
|
||||
byte* __restrict data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
|
||||
// version
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_LAYOUT_VERSION);
|
||||
pos += sizeof(int32);
|
||||
|
||||
// hashmap
|
||||
byte* start = pos;
|
||||
pos += hashmap_dump(&layout->hash_map, pos);
|
||||
|
||||
// UIElement data
|
||||
for (uint32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry_by_index((HashMap *) &layout->hash_map, i);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = (HashEntryInt32 *) hashmap_get_entry_by_element((HashMap *) &layout->hash_map, entry->next);
|
||||
}
|
||||
}
|
||||
|
||||
return (int32) (max_pos - data);
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_unserialize_element_state(UIElementType type, void* __restrict state, const byte** __restrict pos) {
|
||||
switch (type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_state_unserialize((UIInputState *) state, pos);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_unserialize_element_detail(UIElementType type, void* __restrict details, const byte** __restrict pos) {
|
||||
switch (type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_unserialize((UIInput *) details, pos);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void ui_layout_parse_element(HashEntryInt32* entry, byte* data, const byte** pos)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of layout->data
|
||||
UIElement* element = (UIElement *) (data + entry->value);
|
||||
|
||||
element->state_flag = **pos;
|
||||
*pos += sizeof(element->state_flag);
|
||||
|
||||
element->type = (UIElementType) **pos;
|
||||
*pos += sizeof(element->type);
|
||||
|
||||
element->style_old = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_old);
|
||||
|
||||
element->style_new = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_new);
|
||||
|
||||
element->parent = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->parent);
|
||||
|
||||
element->state = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->state);
|
||||
|
||||
// Details
|
||||
for (int32 i = 0; i < UI_STYLE_TYPE_SIZE; ++i) {
|
||||
element->style_types[i] = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->style_types[i]);
|
||||
}
|
||||
|
||||
element->children_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->children_count);
|
||||
|
||||
// @question Do we really have to do that? Shouldn't the animation_state data be 0 anyways or could there be garbage values?
|
||||
memset(&element->animation_state, 0, sizeof(element->animation_state));
|
||||
|
||||
element->animation_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->animation_count);
|
||||
|
||||
element->animations = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->animations);
|
||||
|
||||
element->vertex_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->vertex_count);
|
||||
|
||||
// @bug this needs to be changed?
|
||||
element->vertices_active = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->vertices_active);
|
||||
|
||||
// Load dynamic length content
|
||||
// Some of the content belongs directly after the element but some of it belongs at very specific offsets
|
||||
// The reason for that is that the offsets are stored e.g. in element->state
|
||||
// The memory is fragmented since a lot of the information is split up in different files (layout file and theme file)
|
||||
// Therefor, we cannot create a nice memory layout when loading a layout+theme
|
||||
//
|
||||
// @question Can we optimize the memory layout to a less fragmented version?
|
||||
// One solution could be to combine layout file and theme file. In that case we always know the correct element count
|
||||
// Or see the serialization function for more comments
|
||||
//////////////////////////////////////
|
||||
|
||||
// Children array
|
||||
uint32* children = (uint32 *) (element + 1);
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
children[i] = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(*children);
|
||||
}
|
||||
|
||||
// State element data e.g. UIInputState
|
||||
ui_layout_unserialize_element_state(element->type, data + element->state, pos);
|
||||
|
||||
// detailed element data/style_types e.g. UIInput
|
||||
for (int32 i = 0; i < UI_STYLE_TYPE_SIZE; ++i) {
|
||||
if (!element->style_types[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui_layout_unserialize_element_detail(element->type, data + element->style_types[i], pos);
|
||||
}
|
||||
|
||||
UIAnimation* animations = (UIAnimation *) (data + element->animations);
|
||||
int32 element_style_type_size = ui_element_type_size(element->type);
|
||||
|
||||
for (int32 i = 0; i < element->animation_count; ++i) {
|
||||
animations[i].style_old = (UIStyleType) **pos;
|
||||
*pos += sizeof(animations[i].style_old);
|
||||
|
||||
animations[i].style_new = (UIStyleType) **pos;
|
||||
*pos += sizeof(animations[i].style_new);
|
||||
|
||||
animations[i].duration = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(animations[i].duration);
|
||||
|
||||
animations[i].anim_type = (AnimationEaseType) **pos;
|
||||
*pos += sizeof(animations[i].anim_type);
|
||||
|
||||
animations[i].keyframe_count = **pos;
|
||||
*pos += sizeof(animations[i].keyframe_count);
|
||||
|
||||
// The keyframes are the element detail information (e.g. UIInput) and they are located after the respective Animation definition
|
||||
byte* keyframes = (byte *) (&animations[i] + 1);
|
||||
for (int32 j = 0; j < animations[i].keyframe_count; ++j) {
|
||||
ui_layout_unserialize_element_detail(element->type, keyframes + j * element_style_type_size, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The size of layout->data should be the file size + a bunch of additional data for additional theme dependent "UIElements->style_types".
|
||||
// Yes, this means we have a little too much data but not by a lot
|
||||
int32 layout_from_data(
|
||||
const byte* __restrict data,
|
||||
UILayout* __restrict layout
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = pos;
|
||||
|
||||
int32 version = *((int32 *) pos);
|
||||
pos += sizeof(version);
|
||||
|
||||
// Prepare hashmap (incl. reserve memory) by initializing it the same way we originally did
|
||||
// Of course we still need to populate the data using hashmap_load()
|
||||
hashmap_create(&layout->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint32 *) pos)), sizeof(HashEntryInt32), layout->data);
|
||||
|
||||
const byte* start = data;
|
||||
pos += hashmap_load(&layout->hash_map, pos);
|
||||
|
||||
// layout data
|
||||
for (int32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry_by_index(&layout->hash_map, i);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = (HashEntryInt32 *) hashmap_get_entry_by_element(&layout->hash_map, entry->next);
|
||||
}
|
||||
}
|
||||
|
||||
layout->layout_size = (uint32) (max_pos - data);
|
||||
|
||||
return (int32) layout->layout_size;
|
||||
}
|
||||
|
||||
// @performance Implement a way to only load a specific element and all its children
|
||||
// This way we can re-load specific elements on change and we could also greatly reduce the setup time by ignoring ui elements that are rarely visible
|
||||
|
||||
void layout_from_theme(
|
||||
UILayout* __restrict layout,
|
||||
const UIThemeStyle* __restrict theme,
|
||||
const Camera* __restrict camera
|
||||
) {
|
||||
EvaluatorVariable variables[] = {
|
||||
{ "vw", (f32) camera->viewport_width },
|
||||
{ "vh", (f32) camera->viewport_height },
|
||||
{ "px", 0.0 }, // Placeholder for parent values
|
||||
{ "py", 0.0 }, // Placeholder for parent values
|
||||
{ "pw", 0.0 }, // Placeholder for parent values
|
||||
{ "ph", 0.0 }, // Placeholder for parent values
|
||||
};
|
||||
|
||||
// @todo Handle animations
|
||||
// @todo Handle vertices_active offset
|
||||
|
||||
layout->font = theme->font;
|
||||
|
||||
// Current position where we can the different sub elements (e.g. :hover, :active, ...)
|
||||
// We make sure that the offset is a multiple of 8 bytes for better alignment
|
||||
uint32 dynamic_pos = ROUND_TO_NEAREST(layout->layout_size, 8);
|
||||
|
||||
// We first need to handle the default element -> iterate all elements but only handle the default style
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
HashEntryInt32* style_entry = (HashEntryInt32 *) hashmap_get_entry_by_index((HashMap *) &theme->hash_map, i);
|
||||
if (!style_entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't handle special styles here, only the default one
|
||||
if (strchr(style_entry->key, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, style_entry->key);
|
||||
if (!entry) {
|
||||
// Couldn't find the base element
|
||||
continue;
|
||||
}
|
||||
|
||||
// Populate default element
|
||||
UIElement* element = (UIElement *) (layout->data + entry->value);
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) (theme->data + style_entry->value);
|
||||
UIElement* parent = element->parent ? (UIElement *) (layout->data + element->parent) : NULL;
|
||||
|
||||
// @todo Continue implementation
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_state_populate(group, (UIInputState *) (layout->data + element->state));
|
||||
ui_input_element_populate(
|
||||
layout,
|
||||
group,
|
||||
(UIInput *) (layout->data + element->style_types[UI_STYLE_TYPE_DEFAULT]),
|
||||
parent,
|
||||
variables
|
||||
);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// We iterate every style
|
||||
// 1. Fill default element if it is default style
|
||||
// 2. Create and fill new element if it isn't default style (e.g. :hover)
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
HashEntryInt32* style_entry = (HashEntryInt32 *) hashmap_get_entry_by_index((HashMap *) &theme->hash_map, i);
|
||||
if (!style_entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We only handle special styles here, not the default one
|
||||
char* special = strchr(style_entry->key, ':');
|
||||
if (!special) {
|
||||
// The default element was already handled outside this loop
|
||||
continue;
|
||||
}
|
||||
|
||||
UIStyleType style_type = (UIStyleType) ui_style_type_to_id(special);
|
||||
|
||||
char pure_name[HASH_MAP_MAX_KEY_LENGTH];
|
||||
str_copy_until(style_entry->key, pure_name, ':');
|
||||
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, pure_name);
|
||||
if (!entry) {
|
||||
// Couldn't find the base element
|
||||
continue;
|
||||
}
|
||||
|
||||
UIElement* element = (UIElement *) (layout->data + entry->value);
|
||||
|
||||
// Doesn't exist (usually the first load, but exists when we resize our window)
|
||||
if (!element->style_types[style_type]) {
|
||||
element->style_types[style_type] = dynamic_pos;
|
||||
dynamic_pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
// The style inherits from the default style/element
|
||||
memcpy(
|
||||
layout->data + element->style_types[style_type],
|
||||
layout->data + element->style_types[UI_STYLE_TYPE_DEFAULT],
|
||||
ui_element_type_size(element->type)
|
||||
);
|
||||
|
||||
// Populate element style_types
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) (theme->data + style_entry->value);
|
||||
UIElement* parent = element->parent ? (UIElement *) (layout->data + element->parent) : NULL;
|
||||
|
||||
// @todo Continue implementation
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_populate(
|
||||
layout,
|
||||
group,
|
||||
(UIInput *) (layout->data + element->style_types[style_type]),
|
||||
parent,
|
||||
variables
|
||||
);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_layout_update(UILayout* layout, UIElement* element) {
|
||||
if (element->style_new != element->style_old
|
||||
&& (element->state_flag & UI_ELEMENT_STATE_CHANGED)
|
||||
&& (element->state_flag & UI_ELEMENT_STATE_ANIMATION)
|
||||
) {
|
||||
// @todo Even if an animation is ongoing we might not want to update if the last step is < n ms ago
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_BUTTON: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_SELECT: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_update(layout, element);
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_LABEL: {
|
||||
ui_label_element_update(layout, element);
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_TEXT: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_TEXTAREA: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_IMAGE: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_LINK: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_TABLE: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_VIEW_WINDOW: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_VIEW_PANEL: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_VIEW_TAB: {
|
||||
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_CURSOR: {
|
||||
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_layout_update_dfs(UILayout* layout, UIElement* element, byte category = 0) {
|
||||
if (element->category == category) {
|
||||
ui_layout_update(layout, element);
|
||||
}
|
||||
|
||||
uint32* children = (uint32 *) (element + 1);
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
ui_layout_update(layout, (UIElement *) (layout->data + children[i]));
|
||||
}
|
||||
}
|
||||
|
||||
uint32 ui_layout_render_dfs(
|
||||
UILayout* layout,
|
||||
UIElement* element, Vertex3DTextureColor* __restrict vertices,
|
||||
byte category = 0
|
||||
) {
|
||||
uint32 vertex_count = 0;
|
||||
|
||||
if (element->category == category) {
|
||||
memcpy(vertices, layout->vertices_active + element->vertices_active, sizeof(*vertices) * element->vertex_count);
|
||||
vertices += element->vertex_count;
|
||||
vertex_count += element->vertex_count;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
uint32 child_vertex_count = ui_layout_render_dfs(layout, element, vertices, category);
|
||||
vertices += child_vertex_count;
|
||||
vertex_count += child_vertex_count;
|
||||
}
|
||||
|
||||
return vertex_count;
|
||||
}
|
||||
|
||||
uint32 ui_layout_update_render_dfs(
|
||||
UILayout* layout,
|
||||
UIElement* __restrict element, Vertex3DTextureColor* __restrict vertices,
|
||||
byte category = 0
|
||||
) {
|
||||
uint32 vertex_count = 0;
|
||||
|
||||
if (element->category == category) {
|
||||
ui_layout_update(layout, element);
|
||||
|
||||
memcpy(vertices, layout->vertices_active + element->vertices_active, sizeof(*vertices) * element->vertex_count);
|
||||
vertices += element->vertex_count;
|
||||
vertex_count += element->vertex_count;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
uint32 child_vertex_count = ui_layout_update_render_dfs(layout, element, vertices, category);
|
||||
vertices += child_vertex_count;
|
||||
vertex_count += child_vertex_count;
|
||||
}
|
||||
|
||||
return vertex_count;
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 layout_element_from_location(UILayout* layout, uint16 x, uint16 y)
|
||||
{
|
||||
return layout->ui_chroma_codes[layout->width * y / 4 + x / 4];
|
||||
}
|
||||
|
||||
#endif
|
||||
555
ui/UILayout.h
555
ui/UILayout.h
|
|
@ -5,13 +5,8 @@
|
|||
#include "../stdlib/Types.h"
|
||||
#include "../stdlib/HashMap.h"
|
||||
#include "../asset/Asset.h"
|
||||
#include "../camera/Camera.h"
|
||||
|
||||
#include "../system/FileUtils.cpp"
|
||||
|
||||
#include "UITheme.h"
|
||||
#include "UIElement.h"
|
||||
#include "UIElementType.h"
|
||||
#include "../font/Font.h"
|
||||
#include "../object/Vertex.h"
|
||||
|
||||
#define UI_LAYOUT_VERSION 1
|
||||
|
||||
|
|
@ -27,6 +22,9 @@ struct UILayout {
|
|||
// Contains all UI elements also dynamic ones (e.g. movable windows)
|
||||
uint32* ui_chroma_codes;
|
||||
|
||||
// @question Maybe we should have an array of fonts (e.g. allow up to 3 fonts per layout?)
|
||||
Font* font;
|
||||
|
||||
// Used to directly find element by name
|
||||
// The values are pointers to the UIElements
|
||||
// @todo Should be a perfect hash map
|
||||
|
|
@ -36,7 +34,7 @@ struct UILayout {
|
|||
// Only the size of the fixed layout, doesn't include the theme specific data
|
||||
uint32 layout_size;
|
||||
|
||||
// Total possible size
|
||||
// Total possible size (hard limited to 4 GB)
|
||||
uint32 data_size;
|
||||
|
||||
// Holds the ui elements
|
||||
|
|
@ -44,11 +42,16 @@ struct UILayout {
|
|||
// 1. HashMap data (points to 2.a.)
|
||||
// 2. UIElements (default)
|
||||
// a. General UIElement
|
||||
// b. Element specific state
|
||||
// c. Element specific style
|
||||
// b. Children array (uint32)
|
||||
// c. Element specific state
|
||||
// d. Element specific active style (very important for animations)
|
||||
// e. Element specific default style (not the other styles)
|
||||
// 3. Additional UI styles (see c.), dynamically created when the theme is loaded
|
||||
// We effectively create a tree in data where the individual elements can get directly accessed through the hashmap
|
||||
byte* data;
|
||||
// WARNING: This memory is shared between different layouts
|
||||
// 1. When we load a new layout we assign a temp memory buffer to this pointer
|
||||
// 2. Once we are ready to switch the scene we copy the temporary memory into this data pointer
|
||||
byte* data; // Owner of the actual data
|
||||
|
||||
// Changes on a as needed basis
|
||||
uint32 vertex_size_static;
|
||||
|
|
@ -67,527 +70,15 @@ struct UILayout {
|
|||
uint32 vertex_size;
|
||||
|
||||
// @question Should we maybe also hold the font atlas asset here AND the color palette?
|
||||
|
||||
// Cache for elements to be used for rendering
|
||||
// This is very similar to the currently rendered UI output but may have some empty space between elements
|
||||
// The reason for this is that some elements may need different vertex counts for different states (e.g. input field)
|
||||
// WARNING: This memory is shared between different layouts
|
||||
// @performance Maybe we could use this also for rendering by setting free vertices and elements currently hidden to 0
|
||||
// This would allow us to effectively remove the ui_asset
|
||||
uint32 active_vertex_size;
|
||||
Vertex3DTextureColor* vertices_active; // Not the data owner (see data above)
|
||||
};
|
||||
|
||||
void count_direct_child_elements(UIElement* element, const char* pos, int32 parent_level)
|
||||
{
|
||||
// Find amount of child elements
|
||||
// We have to perform a lookahead since this determins the since this determines the size of our current element
|
||||
uint16 direct_child_elements = 0;
|
||||
|
||||
int32 level = 0;
|
||||
while (*pos != '\0') {
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (level > parent_level + 4) {
|
||||
// This element is a childrens child and not a direct child
|
||||
continue;
|
||||
} else if (level <= parent_level) {
|
||||
// We are no longer inside of element
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
element->children_count = direct_child_elements;
|
||||
}
|
||||
|
||||
void assign_child_elements(UILayout* layout, UIElement* element, char* pos, int32 parent_level) {
|
||||
int32 current_child_pos = 0;
|
||||
|
||||
char block_name[28];
|
||||
|
||||
int32 level = 0;
|
||||
while (*pos != '\0') {
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (level > parent_level + 4) {
|
||||
// This element is a childrens child and not a direct child
|
||||
continue;
|
||||
} else if (level <= parent_level) {
|
||||
// We are no longer inside of element
|
||||
break;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
element->children[current_child_pos] = (UIElement *) (
|
||||
layout->data
|
||||
+ ((HashEntryInt64 *) hashmap_get_entry(&layout->hash_map, block_name))->value
|
||||
);
|
||||
|
||||
element->children[current_child_pos]->parent = element;
|
||||
|
||||
++current_child_pos;
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING: theme needs to have memory already reserved and assigned to data
|
||||
void layout_from_file_txt(
|
||||
UILayout* layout,
|
||||
const char* path,
|
||||
RingMemory* ring
|
||||
) {
|
||||
FileBody file;
|
||||
file_read(path, &file, ring);
|
||||
ASSERT_SIMPLE(file.size);
|
||||
|
||||
char* pos = (char *) file.content;
|
||||
|
||||
// move past the version string
|
||||
pos += 8;
|
||||
|
||||
// Use version for different handling
|
||||
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||
|
||||
// 1. Iteration: We have to find how many elements are defined in the layout file.
|
||||
// Therefore we have to do an initial iteration
|
||||
int32 temp_element_count = 0;
|
||||
while (*pos != '\0') {
|
||||
// Skip all white spaces
|
||||
str_skip_empty(&pos);
|
||||
|
||||
++temp_element_count;
|
||||
|
||||
// Go to the next line
|
||||
str_move_past(&pos, '\n');
|
||||
}
|
||||
|
||||
// 2. Iteration: Fill HashMap
|
||||
// @performance This is probably horrible since we are not using a perfect hashing function (1 hash -> 1 index)
|
||||
// I wouldn't be surprised if we have a 50% hash overlap (2 hashes -> 1 index)
|
||||
hashmap_create(&layout->hash_map, temp_element_count, sizeof(HashEntryVoidP), layout->data);
|
||||
int64 hm_size = hashmap_size(&layout->hash_map);
|
||||
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
char block_name[28];
|
||||
char block_type[28];
|
||||
|
||||
byte* element_data = layout->data + hm_size;
|
||||
|
||||
int32 level;
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (*pos == '\n' || *pos == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":"); ++pos;
|
||||
str_copy_move_until(&pos, block_type, " \r\n");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
UIElement* element = (UIElement *) element_data;
|
||||
element->type = (UIElementType) ui_element_type_to_id(block_type);
|
||||
element->children = (UIElement **) (element_data + sizeof(UIElement));
|
||||
count_direct_child_elements(element, pos, level);
|
||||
|
||||
// Every UIElement can have child elements, we need to store the pointers to those elements
|
||||
element_data += sizeof(UIElement) + sizeof(UIElement*) * element->children_count;
|
||||
|
||||
// We also put the state data after this element
|
||||
element->state = element_data;
|
||||
element_data += ui_element_state_size(element->type);
|
||||
|
||||
// We also put the default element data after this element
|
||||
// Depending on the theme we will have also additional styles (e.g. :active, :hidden, ...)
|
||||
element->details[0] = element_data;
|
||||
|
||||
// @performance We should probably make sure the data is nicely aligned here
|
||||
element_data += ui_element_type_size(element->type);
|
||||
|
||||
// Insert new element
|
||||
hashmap_insert(&layout->hash_map, block_name, element_data - layout->data);
|
||||
}
|
||||
|
||||
// 3. Iteration: Create child references
|
||||
pos = (char *) file.content;
|
||||
|
||||
// move past version string
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\n') {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
level = 0;
|
||||
while (*pos == ' ' || *pos == '\t') {
|
||||
++pos;
|
||||
++level;
|
||||
}
|
||||
|
||||
if (*pos == '\n' || *pos == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_copy_move_until(&pos, block_name, ":");
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
UIElement* element = (UIElement *) (layout->data + ((HashEntryInt64 *) hashmap_get_entry(&layout->hash_map, block_name))->value);
|
||||
assign_child_elements(layout, element, pos, level);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
void ui_layout_serialize_element(HashEntryInt64* entry, byte* data, byte** pos, const byte* start)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of layout->data
|
||||
UIElement* element = (UIElement *) (data + entry->value);
|
||||
|
||||
**pos = element->state_flag;
|
||||
*pos += sizeof(element->state_flag);
|
||||
|
||||
**pos = element->type;
|
||||
*pos += sizeof(element->type);
|
||||
|
||||
**pos = element->style_old;
|
||||
*pos += sizeof(element->style_old);
|
||||
|
||||
**pos = element->style_new;
|
||||
*pos += sizeof(element->style_new);
|
||||
|
||||
// Parent
|
||||
if (element->parent) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->parent - (uintptr_t) data));
|
||||
} else {
|
||||
memset(*pos, 0, sizeof(uint64));
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// State
|
||||
if (element->state) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->state - (uintptr_t) data));
|
||||
} else {
|
||||
memset(*pos, 0, sizeof(uint64));
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// Details
|
||||
for (int32 j = 0; j < UI_STYLE_TYPE_SIZE; ++j) {
|
||||
if (element->details[j]) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->details[j] - (uintptr_t) data));
|
||||
} else {
|
||||
memset(*pos, 0, sizeof(uint64));
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->children_count);
|
||||
*pos += sizeof(element->children_count);
|
||||
|
||||
for (int32 j = 0; j < element->children_count; ++j) {
|
||||
*((uint64 *) *pos) = SWAP_ENDIAN_LITTLE((uint64) ((uintptr_t) element->children[j] - (uintptr_t) data));
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
// @performance, shouldn't it just be memset for both elements?
|
||||
// state element data e.g. UIInputState
|
||||
memcpy(*pos, element->state, ui_element_state_size(element->type));
|
||||
*pos += ui_element_state_size(element->type);
|
||||
|
||||
// detailed element data e.g. UIInput
|
||||
memcpy(*pos, element->details[0], ui_element_type_size(element->type));
|
||||
*pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
int32 layout_to_data(
|
||||
const UILayout* layout,
|
||||
byte* data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
|
||||
// version
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_LAYOUT_VERSION);
|
||||
pos += sizeof(int32);
|
||||
|
||||
// hashmap
|
||||
byte* start = pos;
|
||||
pos += hashmap_dump(&layout->hash_map, pos);
|
||||
|
||||
// UIElement data
|
||||
for (uint32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
if (!layout->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) layout->hash_map.table[i];
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
return (int32) (max_pos - data);
|
||||
}
|
||||
|
||||
static inline
|
||||
void ui_layout_parse_element(HashEntryInt64* entry, byte* data, const byte** pos)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of layout->data
|
||||
|
||||
// Change offset to pointer
|
||||
entry->value = (uintptr_t) data + entry->value;
|
||||
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
|
||||
element->state_flag = **pos;
|
||||
*pos += sizeof(element->state_flag);
|
||||
|
||||
element->type = (UIElementType) **pos;
|
||||
*pos += sizeof(element->type);
|
||||
|
||||
element->style_old = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_old);
|
||||
|
||||
element->style_new = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_new);
|
||||
|
||||
// Parent
|
||||
uint64 temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->parent = temp
|
||||
? (UIElement *) (data + temp)
|
||||
: NULL;
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// State
|
||||
temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->state = temp
|
||||
? data + temp
|
||||
: NULL;
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
|
||||
// Details
|
||||
for (int32 j = 0; j < UI_STYLE_TYPE_SIZE; ++j) {
|
||||
temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->details[j] = temp
|
||||
? data + temp
|
||||
: NULL;
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
element->children_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->children_count);
|
||||
|
||||
for (int32 j = 0; j < element->children_count; ++j) {
|
||||
temp = SWAP_ENDIAN_LITTLE(*((uint64 *) *pos));
|
||||
element->children[j] = temp
|
||||
? (UIElement *) (data + temp)
|
||||
: NULL; // this should never happen since the children_count only gets incremented if it has a child
|
||||
|
||||
if (element->children[j]) {
|
||||
element->children[j]->parent = element;
|
||||
}
|
||||
|
||||
*pos += sizeof(uint64);
|
||||
}
|
||||
|
||||
// @performance, shouldn't it just be memset for both elements?
|
||||
// state element data e.g. UIInput
|
||||
memcpy(element->state, *pos, ui_element_state_size(element->type));
|
||||
*pos += ui_element_state_size(element->type);
|
||||
|
||||
// detailed element data e.g. UIInput
|
||||
memcpy(element->details[0], *pos, ui_element_type_size(element->type));
|
||||
*pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
// The size of layout->data should be the file size + a bunch of additional data for additional theme dependent "UIElements->details".
|
||||
// Yes, this means we have a little too much data but not by a lot
|
||||
int32 layout_from_data(
|
||||
const byte* data,
|
||||
UILayout* layout
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = pos;
|
||||
|
||||
int32 version = *((int32 *) pos);
|
||||
pos += sizeof(version);
|
||||
|
||||
// Prepare hashmap (incl. reserve memory) by initializing it the same way we originally did
|
||||
// Of course we still need to populate the data using hashmap_load()
|
||||
// The value is a int64 (because this is the value of the chunk buffer size but the hashmap only allows int32)
|
||||
hashmap_create(&layout->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint64 *) pos)), sizeof(HashEntryInt64), layout->data);
|
||||
|
||||
const byte* start = data;
|
||||
pos += hashmap_load(&layout->hash_map, pos);
|
||||
|
||||
// layout data
|
||||
for (int32 i = 0; i < layout->hash_map.buf.count; ++i) {
|
||||
if (!layout->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) layout->hash_map.table[i];
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
|
||||
// save all the next elements
|
||||
while (entry->next) {
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
layout->layout_size = (uint32) (max_pos - data);
|
||||
|
||||
return (int32) layout->layout_size;
|
||||
}
|
||||
|
||||
// @performance Implement a way to only load a specific element and all its children
|
||||
// This way we can re-load specific elements on change and we could also greatly reduce the setup time by ignoring ui elements that are rarely visible
|
||||
|
||||
void layout_from_theme(
|
||||
UILayout* layout,
|
||||
const UIThemeStyle* theme,
|
||||
const Camera* camera
|
||||
) {
|
||||
EvaluatorVariable variables[] = {
|
||||
{ "vw", (f32) camera->viewport_width },
|
||||
{ "vh", (f32) camera->viewport_height },
|
||||
{ "px", 0.0 }, // Placeholder for parent values
|
||||
{ "py", 0.0 }, // Placeholder for parent values
|
||||
{ "pw", 0.0 }, // Placeholder for parent values
|
||||
{ "ph", 0.0 }, // Placeholder for parent values
|
||||
};
|
||||
|
||||
// Current position where we can the different sub elements (e.g. :hover, :active, ...)
|
||||
byte* dynamic_pos = layout->data + layout->layout_size;
|
||||
|
||||
// We first need to handle the default element -> iterate all elements but only handle the default style
|
||||
// @todo do here
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
if (!theme->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryUIntPtr* style_entry = (HashEntryUIntPtr *) theme->hash_map.table[i];
|
||||
|
||||
// We don't handle special styles here, only the default one
|
||||
if (strchr(style_entry->key, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntry* entry = hashmap_get_entry(&layout->hash_map, style_entry->key);
|
||||
if (!entry) {
|
||||
// Couldn't even find the base element
|
||||
continue;
|
||||
}
|
||||
|
||||
// Populate default element
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) style_entry->value;
|
||||
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_state_populate(group, (UIInputState *) element->state);
|
||||
ui_input_element_populate(group, element, UI_STYLE_TYPE_DEFAULT, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// We iterate every style
|
||||
// 1. Fill default element if it is default style
|
||||
// 2. Create and fill new element if it isn't default style (e.g. :hover)
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
if (!theme->hash_map.table[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryUIntPtr* style_entry = (HashEntryUIntPtr *) theme->hash_map.table[i];
|
||||
|
||||
// We only handle special styles here, not the default one
|
||||
char* special = strchr(style_entry->key, ':');
|
||||
if (!special) {
|
||||
// The default element was already handled outside this loop
|
||||
continue;
|
||||
}
|
||||
|
||||
UIStyleType style_type = (UIStyleType) ui_style_type_to_id(special);
|
||||
|
||||
char pure_name[HASH_MAP_MAX_KEY_LENGTH];
|
||||
str_copy_until(style_entry->key, pure_name, ':');
|
||||
|
||||
HashEntry* entry = hashmap_get_entry(&layout->hash_map, pure_name);
|
||||
if (!entry) {
|
||||
// Couldn't even find the base element
|
||||
continue;
|
||||
}
|
||||
|
||||
UIElement* element = (UIElement *) entry->value;
|
||||
|
||||
// Doesn't exist (usually the first load, but exists when we resize our window)
|
||||
if (!element->details[style_type]) {
|
||||
element->details[style_type] = dynamic_pos;
|
||||
dynamic_pos += ui_element_type_size(element->type);
|
||||
}
|
||||
|
||||
// The style inherits from the default element
|
||||
memcpy(element->details[style_type], element->details[0], ui_element_type_size(element->type));
|
||||
|
||||
// Populate element details
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) style_entry->value;
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_populate(group, element, style_type, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
uint32 layout_element_from_location(UILayout* layout, uint16 x, uint16 y)
|
||||
{
|
||||
return layout->ui_chroma_codes[layout->width * y / 4 + x / 4];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -2,12 +2,13 @@
|
|||
#define TOS_UI_PANEL_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
|
||||
struct UIPanelState {
|
||||
};
|
||||
|
||||
struct UIPanel {
|
||||
|
||||
UIAttributeDimension dimension;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -4,9 +4,14 @@
|
|||
#include "../stdlib/Types.h"
|
||||
|
||||
enum UIStyleType : byte {
|
||||
UI_STYLE_TYPE_DEFAULT, // = :visible
|
||||
UI_STYLE_TYPE_HIDDEN,
|
||||
UI_STYLE_TYPE_ACTIVE, // e.g. input
|
||||
// This can be the same as one of the style_types below but doesn't have to be
|
||||
// The most common situation where this is different is for animations
|
||||
// In case of animations we often perform interpolations so the active style is different from ANY defined style since it is dynamically calculated based on the time
|
||||
UI_STYLE_TYPE_ACTIVE,
|
||||
|
||||
UI_STYLE_TYPE_DEFAULT,
|
||||
UI_STYLE_TYPE_HIDDEN, // e.g. useful as starting position for slide-in/blend-in etc. animations
|
||||
UI_STYLE_TYPE_FOCUS, // e.g. input
|
||||
UI_STYLE_TYPE_DISABLED, // disabled form elements
|
||||
UI_STYLE_TYPE_HOVER, // e.g. button
|
||||
UI_STYLE_TYPE_MANUAL,
|
||||
|
|
@ -16,19 +21,19 @@ enum UIStyleType : byte {
|
|||
constexpr
|
||||
int32 ui_style_type_to_id(const char* str)
|
||||
{
|
||||
if (str_compare(":hidden", str) == 0) {
|
||||
if (*str == '\0') {
|
||||
return UI_STYLE_TYPE_DEFAULT;
|
||||
} else if (str_compare(":hid", str, 4) == 0) {
|
||||
return UI_STYLE_TYPE_HIDDEN;
|
||||
} else if (str_compare(":active", str) == 0) {
|
||||
return UI_STYLE_TYPE_ACTIVE;
|
||||
} else if (str_compare(":diabled", str) == 0) {
|
||||
} else if (str_compare(":foc", str, 4) == 0) {
|
||||
return UI_STYLE_TYPE_FOCUS;
|
||||
} else if (str_compare(":dis", str, 4) == 0) {
|
||||
return UI_STYLE_TYPE_DISABLED;
|
||||
} else if (str_compare(":hover", str) == 0) {
|
||||
} else if (str_compare(":hov", str, 4) == 0) {
|
||||
return UI_STYLE_TYPE_HOVER;
|
||||
} else if (str_compare(":manual", str) == 0) {
|
||||
} else {
|
||||
return UI_STYLE_TYPE_MANUAL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
168
ui/UITheme.h
168
ui/UITheme.h
|
|
@ -24,34 +24,37 @@ struct UIThemeStyle {
|
|||
// The hashmap contains the offset where the respective style can be found
|
||||
// @performance Switch to perfect hash map
|
||||
HashMap hash_map;
|
||||
|
||||
uint32 data_size;
|
||||
byte* data;
|
||||
|
||||
// It feels weird that this is here, especially considering we could have multiple fonts
|
||||
// @question It feels weird that this is here, especially considering we could have multiple fonts
|
||||
// Maybe we should have an array of fonts (e.g. allow up to 3 fonts per theme?)
|
||||
Font* font;
|
||||
};
|
||||
|
||||
inline
|
||||
UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name)
|
||||
{
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) hashmap_get_entry(&theme->hash_map, group_name);
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&theme->hash_map, group_name);
|
||||
if (!entry) {
|
||||
ASSERT_SIMPLE(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (UIAttributeGroup *) entry->value;
|
||||
return (UIAttributeGroup *) (theme->data + entry->value);
|
||||
}
|
||||
|
||||
inline
|
||||
UIAttributeGroup* theme_style_group(UIThemeStyle* theme, const char* group_name, int32 group_id)
|
||||
{
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) hashmap_get_entry(&theme->hash_map, group_name, group_id);
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&theme->hash_map, group_name, group_id);
|
||||
if (!entry) {
|
||||
ASSERT_SIMPLE(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (UIAttributeGroup *) entry->value;
|
||||
return (UIAttributeGroup *) (theme->data + entry->value);
|
||||
}
|
||||
|
||||
static inline
|
||||
|
|
@ -83,13 +86,13 @@ void theme_from_file_txt(
|
|||
file_read(path, &file, ring);
|
||||
ASSERT_SIMPLE(file.size);
|
||||
|
||||
char* pos = (char *) file.content;
|
||||
const char* pos = (char *) file.content;
|
||||
|
||||
// move past the version string
|
||||
pos += 8;
|
||||
|
||||
// Use version for different handling
|
||||
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||
int32 version = strtol(pos, (char **) &pos, 10); ++pos;
|
||||
|
||||
// We have to find how many groups are defined in the theme file.
|
||||
// Therefore we have to do an initial iteration
|
||||
|
|
@ -109,7 +112,7 @@ void theme_from_file_txt(
|
|||
|
||||
// @performance This is probably horrible since we are not using a perfect hashing function (1 hash -> 1 index)
|
||||
// I wouldn't be surprised if we have a 50% hash overlap (2 hashes -> 1 index)
|
||||
hashmap_create(&theme->hash_map, temp_group_count, sizeof(HashEntryInt64), theme->data);
|
||||
hashmap_create(&theme->hash_map, temp_group_count, sizeof(HashEntryInt32), theme->data);
|
||||
int64 data_offset = hashmap_size(&theme->hash_map);
|
||||
|
||||
UIAttributeGroup* temp_group = NULL;
|
||||
|
|
@ -153,15 +156,14 @@ void theme_from_file_txt(
|
|||
if (temp_group) {
|
||||
// Before we insert a new group we have to sort the attributes
|
||||
// since this makes searching them later on more efficient.
|
||||
qsort(temp_group->attributes, temp_group->attribute_size, sizeof(UIAttribute), compare_by_attribute_id);
|
||||
qsort(temp_group + 1, temp_group->attribute_count, sizeof(UIAttribute), compare_by_attribute_id);
|
||||
}
|
||||
|
||||
// Insert new group
|
||||
hashmap_insert(&theme->hash_map, block_name, data_offset);
|
||||
|
||||
temp_group = (UIAttributeGroup *) (theme->data + data_offset);
|
||||
temp_group->attribute_size = 0;
|
||||
temp_group->attributes = (UIAttribute *) (theme->data + data_offset + sizeof(UIAttributeGroup));
|
||||
temp_group->attribute_count = 0;
|
||||
data_offset += sizeof(UIAttributeGroup);
|
||||
}
|
||||
|
||||
|
|
@ -182,65 +184,65 @@ void theme_from_file_txt(
|
|||
// Again, currently this if check is redundant but it wasn't in the past and we may need it again in the future.
|
||||
if (block_name[0] == '#' || block_name[0] == '.') {
|
||||
// Named block
|
||||
UIAttribute* attribute_reference = (UIAttribute *) (temp_group + 1);
|
||||
memcpy(
|
||||
temp_group->attributes + temp_group->attribute_size,
|
||||
attribute_reference + temp_group->attribute_count,
|
||||
&attribute,
|
||||
sizeof(attribute)
|
||||
);
|
||||
|
||||
data_offset += sizeof(attribute);
|
||||
++temp_group->attribute_size;
|
||||
++temp_group->attribute_count;
|
||||
}
|
||||
|
||||
str_move_to(&pos, '\n');
|
||||
}
|
||||
|
||||
// We still need to sort the last group
|
||||
qsort(temp_group->attributes, temp_group->attribute_size, sizeof(UIAttribute), compare_by_attribute_id);
|
||||
qsort(temp_group + 1, temp_group->attribute_count, sizeof(UIAttribute), compare_by_attribute_id);
|
||||
}
|
||||
|
||||
static inline
|
||||
void ui_theme_parse_group(HashEntryInt64* entry, byte* data, const byte** pos)
|
||||
void ui_theme_parse_group(HashEntryInt32* entry, byte* data, const byte** pos)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of theme->data
|
||||
|
||||
// Change offset to pointer
|
||||
entry->value = (uintptr_t) data + entry->value;
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) data + entry->value;
|
||||
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) entry->value;
|
||||
group->attribute_count = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(group->attribute_count);
|
||||
|
||||
group->attribute_size = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(group->attribute_size);
|
||||
UIAttribute* attribute_reference = (UIAttribute *) (group + 1);
|
||||
|
||||
for (uint32 j = 0; j < group->attribute_size; ++j) {
|
||||
group->attributes[j].attribute_id = (UIAttributeType) SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].attribute_id);
|
||||
for (uint32 j = 0; j < group->attribute_count; ++j) {
|
||||
attribute_reference[j].attribute_id = (UIAttributeType) SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].attribute_id);
|
||||
|
||||
group->attributes[j].datatype = *((UIAttributeDataType *) *pos);
|
||||
*pos += sizeof(group->attributes[j].datatype);
|
||||
attribute_reference[j].datatype = *((UIAttributeDataType *) *pos);
|
||||
*pos += sizeof(attribute_reference[j].datatype);
|
||||
|
||||
if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
group->attributes[j].value_int = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_int);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
group->attributes[j].value_float = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_float);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
memcpy(group->attributes[j].value_str, *pos, sizeof(group->attributes[j].value_str));
|
||||
*pos += sizeof(group->attributes[j].value_str);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_V4_F32) {
|
||||
group->attributes[j].value_v4_f32.x = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.x);
|
||||
if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
attribute_reference[j].value_int = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_int);
|
||||
} else if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
attribute_reference[j].value_float = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_float);
|
||||
} else if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
memcpy(attribute_reference[j].value_str, *pos, sizeof(attribute_reference[j].value_str));
|
||||
*pos += sizeof(attribute_reference[j].value_str);
|
||||
} else if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_V4_F32) {
|
||||
attribute_reference[j].value_v4_f32.x = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.x);
|
||||
|
||||
group->attributes[j].value_v4_f32.y = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.y);
|
||||
attribute_reference[j].value_v4_f32.y = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.y);
|
||||
|
||||
group->attributes[j].value_v4_f32.z = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.z);
|
||||
attribute_reference[j].value_v4_f32.z = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.z);
|
||||
|
||||
group->attributes[j].value_v4_f32.w = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.w);
|
||||
attribute_reference[j].value_v4_f32.w = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -261,8 +263,8 @@ void ui_theme_parse_group(HashEntryInt64* entry, byte* data, const byte** pos)
|
|||
// The size of theme->data should be the file size.
|
||||
// Yes, this means we have a little too much data but not by a lot
|
||||
int32 theme_from_data(
|
||||
const byte* data,
|
||||
UIThemeStyle* theme
|
||||
const byte* __restrict data,
|
||||
UIThemeStyle* __restrict theme
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = data;
|
||||
|
|
@ -273,7 +275,7 @@ int32 theme_from_data(
|
|||
// Prepare hashmap (incl. reserve memory) by initializing it the same way we originally did
|
||||
// Of course we still need to populate the data using hashmap_load()
|
||||
// The value is a int64 (because this is the value of the chunk buffer size but the hashmap only allows int32)
|
||||
hashmap_create(&theme->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint64 *) pos)), sizeof(HashEntryInt64), theme->data);
|
||||
hashmap_create(&theme->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint32 *) pos)), sizeof(HashEntryInt32), theme->data);
|
||||
|
||||
const byte* start = theme->hash_map.buf.memory;
|
||||
pos += hashmap_load(&theme->hash_map, pos);
|
||||
|
|
@ -281,12 +283,11 @@ int32 theme_from_data(
|
|||
// theme data
|
||||
// Layout: first load the size of the group, then load the individual attributes
|
||||
for (int32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
if (!theme->hash_map.table[i]) {
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry_by_index((HashMap *) &theme->hash_map, i);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) theme->hash_map.table[i];
|
||||
|
||||
// This way we now could access the data directly without the silly theme->data + entry->value calc.
|
||||
pos = start + entry->value;
|
||||
ui_theme_parse_group(entry, theme->data, &pos);
|
||||
|
|
@ -301,7 +302,7 @@ int32 theme_from_data(
|
|||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
entry = (HashEntryInt32 *) hashmap_get_entry_by_element((HashMap *) &theme->hash_map, entry->next);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +313,7 @@ int32 theme_from_data(
|
|||
// Not every group has all the attributes (most likely only a small subset)
|
||||
// However, an accurate calculation is probably too slow and not needed most of the time
|
||||
inline
|
||||
int64 theme_data_size(const UIThemeStyle* theme)
|
||||
int64 theme_data_size_max(const UIThemeStyle* theme)
|
||||
{
|
||||
return hashmap_size(&theme->hash_map)
|
||||
+ theme->hash_map.buf.count * UI_ATTRIBUTE_TYPE_SIZE * sizeof(UIAttribute);
|
||||
|
|
@ -320,43 +321,45 @@ int64 theme_data_size(const UIThemeStyle* theme)
|
|||
|
||||
// @todo Why do even need **pos, shouldn't it just be *pos
|
||||
static inline
|
||||
void ui_theme_serialize_group(HashEntryInt64* entry, byte* data, byte** pos, const byte* start)
|
||||
void ui_theme_serialize_group(const HashEntryInt32* entry, byte* data, byte** pos, const byte* start)
|
||||
{
|
||||
// @performance Are we sure the data is nicely aligned?
|
||||
// Probably depends on the from_txt function and the start of theme->data
|
||||
UIAttributeGroup* group = (UIAttributeGroup *) (data + entry->value);
|
||||
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attribute_size);
|
||||
*pos += sizeof(group->attribute_size);
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attribute_count);
|
||||
*pos += sizeof(group->attribute_count);
|
||||
|
||||
for (uint32 j = 0; j < group->attribute_size; ++j) {
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].attribute_id);
|
||||
*pos += sizeof(group->attributes[j].attribute_id);
|
||||
UIAttribute* attribute_reference = (UIAttribute *) (group + 1);
|
||||
|
||||
*((byte *) *pos) = group->attributes[j].datatype;
|
||||
*pos += sizeof(group->attributes[j].datatype);
|
||||
for (uint32 j = 0; j < group->attribute_count; ++j) {
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].attribute_id);
|
||||
*pos += sizeof(attribute_reference[j].attribute_id);
|
||||
|
||||
if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_int);
|
||||
*pos += sizeof(group->attributes[j].value_int);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_float);
|
||||
*pos += sizeof(group->attributes[j].value_float);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
memcpy(*pos, group->attributes[j].value_str, sizeof(group->attributes[j].value_str));
|
||||
*pos += sizeof(group->attributes[j].value_str);
|
||||
} else if (group->attributes[j].datatype == UI_ATTRIBUTE_DATA_TYPE_V4_F32) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.x);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.x);
|
||||
*((byte *) *pos) = attribute_reference[j].datatype;
|
||||
*pos += sizeof(attribute_reference[j].datatype);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.y);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.y);
|
||||
if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_int);
|
||||
*pos += sizeof(attribute_reference[j].value_int);
|
||||
} else if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_float);
|
||||
*pos += sizeof(attribute_reference[j].value_float);
|
||||
} else if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
memcpy(*pos, attribute_reference[j].value_str, sizeof(attribute_reference[j].value_str));
|
||||
*pos += sizeof(attribute_reference[j].value_str);
|
||||
} else if (attribute_reference[j].datatype == UI_ATTRIBUTE_DATA_TYPE_V4_F32) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.x);
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.x);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.z);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.z);
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.y);
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.y);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attributes[j].value_v4_f32.w);
|
||||
*pos += sizeof(group->attributes[j].value_v4_f32.w);
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.z);
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.z);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.w);
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -377,8 +380,8 @@ void ui_theme_serialize_group(HashEntryInt64* entry, byte* data, byte** pos, con
|
|||
// attributes ...
|
||||
|
||||
int32 theme_to_data(
|
||||
const UIThemeStyle* theme,
|
||||
byte* data
|
||||
const UIThemeStyle* __restrict theme,
|
||||
byte* __restrict data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
|
|
@ -394,12 +397,11 @@ int32 theme_to_data(
|
|||
// theme data
|
||||
// Layout: first save the size of the group, then save the individual attributes
|
||||
for (uint32 i = 0; i < theme->hash_map.buf.count; ++i) {
|
||||
if (!theme->hash_map.table[i]) {
|
||||
const HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry_by_index((HashMap *) &theme->hash_map, i);
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt64* entry = (HashEntryInt64 *) theme->hash_map.table[i];
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_theme_serialize_group(entry, theme->data, &pos, start);
|
||||
if (pos > max_pos) {
|
||||
|
|
@ -413,7 +415,7 @@ int32 theme_to_data(
|
|||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
entry = entry->next;
|
||||
entry = (HashEntryInt32 *) hashmap_get_entry_by_element((HashMap *) &theme->hash_map, entry->next);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "attribute/UIAttributeShadow.h"
|
||||
#include "attribute/UIAttributeFont.h"
|
||||
#include "attribute/UIAttributeBackground.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UIAnimation.h"
|
||||
#include "UIStyleType.h"
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ struct UIWindowState {
|
|||
};
|
||||
|
||||
struct UIWindow {
|
||||
v4_int16 dimension;
|
||||
UIAttributeDimension dimension;
|
||||
UIAnimation animation;
|
||||
byte padding;
|
||||
byte alignment;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "../../stdlib/Types.h"
|
||||
#include "../../utils/StringUtils.h"
|
||||
#include "../../math/Evaluator.h"
|
||||
#include "../../compiler/CompilerUtils.h"
|
||||
#include "UIAttributeType.h"
|
||||
#include "UIAttributeDimension.h"
|
||||
|
||||
|
|
@ -29,27 +30,31 @@ struct UIAttribute {
|
|||
};
|
||||
|
||||
struct UIAttributeGroup {
|
||||
int32 attribute_size;
|
||||
UIAttribute* attributes;
|
||||
int32 attribute_count;
|
||||
// We don't use a pointer since this would prevent us from copying around the main data owner
|
||||
// The UIAttribute values come directly after UIAttributeGroup (e.g. group + 1 in memory)
|
||||
//UIAttribute* attributes;
|
||||
};
|
||||
|
||||
UIAttribute* ui_attribute_from_group(UIAttributeGroup* group, UIAttributeType type)
|
||||
{
|
||||
if (!group->attributes) {
|
||||
if (!group->attribute_count) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32 left = 0;
|
||||
int32 right = type;
|
||||
|
||||
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
||||
|
||||
// Binary search since attributes are sorted by attribute_id
|
||||
// @performance Consider Eytzinger
|
||||
while (left <= right) {
|
||||
int32 mid = left + (right - left) / 2;
|
||||
|
||||
if (group->attributes[mid].attribute_id == type) {
|
||||
return &group->attributes[mid];
|
||||
} else if (group->attributes[mid].attribute_id < type) {
|
||||
if (attributes[mid].attribute_id == type) {
|
||||
return &attributes[mid];
|
||||
} else if (attributes[mid].attribute_id < type) {
|
||||
left = mid + 1;
|
||||
} else {
|
||||
right = mid - 1;
|
||||
|
|
@ -70,6 +75,8 @@ int32 ui_attribute_type_to_id(const char* attribute_name)
|
|||
return UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH;
|
||||
} else if (str_compare(attribute_name, "height") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT;
|
||||
} else if (str_compare(attribute_name, "overflow") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_DIMENSION_OVERFLOW;
|
||||
} else if (str_compare(attribute_name, "font_name") == 0) {
|
||||
return UI_ATTRIBUTE_TYPE_FONT_NAME;
|
||||
} else if (str_compare(attribute_name, "font_color") == 0) {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,29 @@ enum UIBackgroundStyle : byte {
|
|||
struct UIAttributeBackground {
|
||||
UIBackgroundStyle background_style;
|
||||
union {
|
||||
void* background_image;
|
||||
uint32 background_image;
|
||||
uint32 background_color;
|
||||
};
|
||||
};
|
||||
|
||||
inline
|
||||
void ui_attr_background_serialize(const UIAttributeBackground* __restrict bg, byte** __restrict pos)
|
||||
{
|
||||
**pos = bg->background_style;
|
||||
*pos += sizeof(bg->background_style);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(bg->background_color);
|
||||
*pos += sizeof(bg->background_color);
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_attr_background_unserialize(UIAttributeBackground* __restrict bg, const byte** __restrict pos)
|
||||
{
|
||||
bg->background_style = (UIBackgroundStyle) **pos;
|
||||
*pos += sizeof(bg->background_style);
|
||||
|
||||
bg->background_color = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(bg->background_color);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -9,4 +9,24 @@ struct UIAttributeBorder {
|
|||
uint32 color;
|
||||
};
|
||||
|
||||
inline
|
||||
void ui_attr_border_serialize(const UIAttributeBorder* __restrict border, byte** __restrict pos)
|
||||
{
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(border->thickness);
|
||||
*pos += sizeof(border->thickness);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(border->color);
|
||||
*pos += sizeof(border->color);
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_attr_border_unserialize(UIAttributeBorder* __restrict border, const byte** __restrict pos)
|
||||
{
|
||||
border->thickness = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(border->thickness);
|
||||
|
||||
border->color = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(border->color);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -53,4 +53,34 @@ struct UIAttributeDimension {
|
|||
*/
|
||||
};
|
||||
|
||||
inline
|
||||
void ui_attr_dimension_serialize(const UIAttributeDimension* __restrict dim, byte** __restrict pos)
|
||||
{
|
||||
**pos = dim->flag;
|
||||
*pos += sizeof(dim->flag);
|
||||
|
||||
**pos = dim->alignment;
|
||||
*pos += sizeof(dim->alignment);
|
||||
|
||||
for (int32 i = 0; i < 4; ++i) {
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(dim->dimension.vec[i]);
|
||||
*pos += sizeof(dim->dimension.vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_attr_dimension_unserialize(UIAttributeDimension* __restrict dim, const byte** __restrict pos)
|
||||
{
|
||||
dim->flag = **pos;
|
||||
*pos += sizeof(dim->flag);
|
||||
|
||||
dim->alignment = **pos;
|
||||
*pos += sizeof(dim->alignment);
|
||||
|
||||
for (int32 i = 0; i < 4; ++i) {
|
||||
dim->dimension.vec[i] = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(dim->dimension.vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -14,6 +14,7 @@ struct UIAttributeFont {
|
|||
f32 weight;
|
||||
UIAttributeShadow shadow_outer;
|
||||
byte decoration;
|
||||
byte alignment;
|
||||
|
||||
// @todo family?
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,4 +10,36 @@ struct UIAttributeShadow {
|
|||
byte offset;
|
||||
};
|
||||
|
||||
inline
|
||||
void ui_attr_shadow_serialize(const UIAttributeShadow* __restrict shadow, byte** __restrict pos)
|
||||
{
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(shadow->angle);
|
||||
*pos += sizeof(shadow->angle);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(shadow->color);
|
||||
*pos += sizeof(shadow->color);
|
||||
|
||||
**pos = shadow->fade;
|
||||
*pos += sizeof(shadow->fade);
|
||||
|
||||
**pos = shadow->offset;
|
||||
*pos += sizeof(shadow->offset);
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_attr_shadow_unserialize(UIAttributeShadow* __restrict shadow, const byte** __restrict pos)
|
||||
{
|
||||
shadow->angle = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(shadow->angle);
|
||||
|
||||
shadow->color = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(shadow->color);
|
||||
|
||||
shadow->fade = **pos;
|
||||
*pos += sizeof(shadow->fade);
|
||||
|
||||
shadow->offset = **pos;
|
||||
*pos += sizeof(shadow->offset);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -17,6 +17,10 @@ enum UIAttributeType : uint16 {
|
|||
UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH,
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT,
|
||||
|
||||
// Allows elements to overflow their parent while still positioned relative to their parent element
|
||||
// e.g. Text in a button (e.g. a cooldown timer of a skill could be positioned below a button)
|
||||
UI_ATTRIBUTE_TYPE_DIMENSION_OVERFLOW,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_CONTENT,
|
||||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_H,
|
||||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_V,
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <x86intrin.h>
|
||||
#include "../architecture/Intrinsics.h"
|
||||
|
||||
// @todo consider to log/ignore outliers
|
||||
uint64_t measure_cycles(int count, size_t (*func)(const char *), const char *str) {
|
||||
uint64_t start = __rdtsc();
|
||||
for (int = 0; i < count; ++i) {
|
||||
uint64_t start = intrin_timestamp_counter();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
func(str);
|
||||
}
|
||||
uint64_t end = __rdtsc();
|
||||
uint64_t end = intrin_timestamp_counter();
|
||||
|
||||
return end - start;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,8 +546,7 @@ void str_copy_until(const char* __restrict src, char* __restrict dest, const cha
|
|||
}
|
||||
}
|
||||
|
||||
*dest++ = *src;
|
||||
++src;
|
||||
*dest++ = *src++;
|
||||
}
|
||||
|
||||
*dest = '\0';
|
||||
|
|
@ -605,7 +604,7 @@ void str_copy_long(char* __restrict dest, const char* __restrict src)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_copy_move_until(char** __restrict src, char* __restrict dest, char delim)
|
||||
void str_copy_move_until(const char** __restrict src, char* __restrict dest, char delim)
|
||||
{
|
||||
while (**src != delim && **src != '\0') {
|
||||
*dest++ = **src;
|
||||
|
|
@ -616,7 +615,7 @@ void str_copy_move_until(char** __restrict src, char* __restrict dest, char deli
|
|||
}
|
||||
|
||||
inline
|
||||
void str_copy_move_until(char** __restrict src, char* __restrict dest, const char* __restrict delim)
|
||||
void str_copy_move_until(const char** __restrict src, char* __restrict dest, const char* __restrict delim)
|
||||
{
|
||||
size_t len = strlen(delim);
|
||||
|
||||
|
|
@ -1019,13 +1018,14 @@ int32 str_to(const char* str, char delim)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_move_to(char** str, char delim)
|
||||
void str_move_to(const char** str, char delim)
|
||||
{
|
||||
while (**str != delim && **str != '\0') {
|
||||
++(*str);
|
||||
}
|
||||
}
|
||||
|
||||
// Negative pos counts backwards
|
||||
inline
|
||||
void str_move_to_pos(const char** str, int32 pos)
|
||||
{
|
||||
|
|
@ -1037,7 +1037,7 @@ void str_move_to_pos(const char** str, int32 pos)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_move_past(char** str, char delim)
|
||||
void str_move_past(const char** str, char delim)
|
||||
{
|
||||
while (**str != delim && **str != '\0') {
|
||||
++(*str);
|
||||
|
|
@ -1049,7 +1049,7 @@ void str_move_past(char** str, char delim)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_move_past_alpha_num(char** str)
|
||||
void str_move_past_alpha_num(const char** str)
|
||||
{
|
||||
while ((**str >= 48 && **str <= 57)
|
||||
|| (**str >= 65 && **str <= 90)
|
||||
|
|
@ -1061,13 +1061,13 @@ void str_move_past_alpha_num(char** str)
|
|||
}
|
||||
|
||||
inline
|
||||
bool str_is_comment(char* str)
|
||||
bool str_is_comment(const char* str)
|
||||
{
|
||||
return (*str == '/' && str[1] == '/') || (*str == '/' && str[1] == '*');
|
||||
}
|
||||
|
||||
inline
|
||||
void str_skip(char** str, char delim)
|
||||
void str_skip(const char** str, char delim)
|
||||
{
|
||||
while (**str && **str == delim) {
|
||||
++(*str);
|
||||
|
|
@ -1075,7 +1075,7 @@ void str_skip(char** str, char delim)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_skip_whitespace(char** str)
|
||||
void str_skip_whitespace(const char** str)
|
||||
{
|
||||
while (**str && (**str == ' ' || **str == '\t')) {
|
||||
++(*str);
|
||||
|
|
@ -1083,7 +1083,7 @@ void str_skip_whitespace(char** str)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_skip_empty(char** str)
|
||||
void str_skip_empty(const char** str)
|
||||
{
|
||||
while (**str == ' ' || **str == '\t' || **str == '\n' || **str == '\r') {
|
||||
++(*str);
|
||||
|
|
@ -1091,7 +1091,7 @@ void str_skip_empty(char** str)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_skip_non_empty(char** str)
|
||||
void str_skip_non_empty(const char** str)
|
||||
{
|
||||
while (**str != ' ' && **str != '\t' && **str != '\n' && **str != '\0') {
|
||||
++(*str);
|
||||
|
|
@ -1099,7 +1099,7 @@ void str_skip_non_empty(char** str)
|
|||
}
|
||||
|
||||
inline
|
||||
void str_skip_list(char** __restrict str, const char* __restrict delim, int32 len)
|
||||
void str_skip_list(const char** __restrict str, const char* __restrict delim, int32 len)
|
||||
{
|
||||
bool run = true;
|
||||
while (run && **str != '\0') {
|
||||
|
|
@ -1117,7 +1117,7 @@ void str_skip_list(char** __restrict str, const char* __restrict delim, int32 le
|
|||
}
|
||||
|
||||
inline
|
||||
void str_skip_until_list(char** __restrict str, const char* __restrict delim)
|
||||
void str_skip_until_list(const char** __restrict str, const char* __restrict delim)
|
||||
{
|
||||
while (**str != '\0') {
|
||||
const char* delim_temp = delim;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#define TOS_UTILS_TEST_UTILS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../architecture/Intrinsics.h"
|
||||
|
||||
#define ASSERT_EQUALS(a, b, t1, t2) \
|
||||
({ \
|
||||
|
|
@ -118,12 +119,12 @@
|
|||
|
||||
#define ASSERT_PERFORMANCE_START(time_start) \
|
||||
({ \
|
||||
time_start = __rdtsc(); \
|
||||
time_start = intrin_timestamp_counter(); \
|
||||
})
|
||||
|
||||
#define ASSERT_PERFORMANCE_END(time_start, max_duration) \
|
||||
({ \
|
||||
ASSERT_TRUE(__rdtsc() - (time_start) <= (max_duration)); \
|
||||
#define ASSERT_PERFORMANCE_END(time_start, max_duration) \
|
||||
({ \
|
||||
ASSERT_TRUE(intrin_timestamp_counter() - (time_start) <= (max_duration)); \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user