continued with UI re-impl. utterly broken!!!

This commit is contained in:
Dennis Eichhorn 2025-01-25 23:37:28 +01:00
parent 2263d6f018
commit fd47385162
79 changed files with 2984 additions and 4797 deletions

18
architecture/Intrinsics.h Normal file
View 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

View File

@ -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

View 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

View File

@ -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)

View 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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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
View 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
View 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

View 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

View 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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View File

@ -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

View File

@ -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);

View File

@ -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
);

View File

@ -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) {

View File

@ -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__

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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: {

View File

@ -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 {

View File

@ -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;

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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]))

View File

@ -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

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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
View 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

View File

@ -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

View File

@ -2,12 +2,13 @@
#define TOS_UI_PANEL_H
#include "../stdlib/Types.h"
#include "attribute/UIAttributeDimension.h"
struct UIPanelState {
};
struct UIPanel {
UIAttributeDimension dimension;
};
#endif

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -14,6 +14,7 @@ struct UIAttributeFont {
f32 weight;
UIAttributeShadow shadow_outer;
byte decoration;
byte alignment;
// @todo family?
};

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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