mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
getting closer to our first rendering with the UILayout, app working.
This commit is contained in:
parent
a8191a239a
commit
c7b881e6b7
|
|
@ -40,6 +40,9 @@
|
|||
#define intrin_bits_count_64(data) compiler_popcount_64((data))
|
||||
|
||||
#define intrin_prefetch(mem) compiler_prefetch((mem))
|
||||
#define intrin_prefetch_l1(mem) compiler_prefetch_l1((mem))
|
||||
#define intrin_prefetch_l2(mem) compiler_prefetch_l2((mem))
|
||||
#define intrin_prefetch_l3(mem) compiler_prefetch_l3((mem))
|
||||
|
||||
#if _WIN32
|
||||
#define intrin_timestamp_counter() ({ uint64_t cntvct; asm volatile("mrs %0, cntvct_el0" : "=r"(cntvct)); cntvct; })
|
||||
|
|
|
|||
|
|
@ -54,7 +54,9 @@
|
|||
#define intrin_bits_count_32(data) _mm_popcnt_u32((data))
|
||||
#define intrin_bits_count_64(data) _mm_popcnt_u64((data))
|
||||
|
||||
#define intrin_prefetch(mem) _mm_prefetch((mem))
|
||||
#define intrin_prefetch_l1(mem) _mm_prefetch((mem), _MM_HINT_T0)
|
||||
#define intrin_prefetch_l2(mem) _mm_prefetch((mem), _MM_HINT_T1)
|
||||
#define intrin_prefetch_l3(mem) _mm_prefetch((mem), _MM_HINT_T2)
|
||||
|
||||
inline
|
||||
uint64 intrin_timestamp_counter() {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
#define __restrict __restrict__
|
||||
|
||||
#define compiler_prefetch(mem) __builtin_prefetch((mem), 0, 3)
|
||||
#define compiler_prefetch_l1(mem) __builtin_prefetch((mem), 0, 3)
|
||||
#define compiler_prefetch_l2(mem) __builtin_prefetch((mem), 0, 2)
|
||||
#define compiler_prefetch_l3(mem) __builtin_prefetch((mem), 0, 1)
|
||||
|
||||
int32 compiler_find_first_bit_r2l(uint64 mask) {
|
||||
if (!mask) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ typedef SSIZE_T ssize_t;
|
|||
#define compiler_popcount_64(data) __popcnt64((data))
|
||||
|
||||
#define compiler_prefetch(mem) __prefetch((mem))
|
||||
#define compiler_prefetch_l1(mem) __prefetch((mem))
|
||||
#define compiler_prefetch_l2(mem) __prefetch((mem))
|
||||
#define compiler_prefetch_l3(mem) __prefetch((mem))
|
||||
|
||||
inline
|
||||
int32 compiler_find_first_bit_r2l(uint64 mask) {
|
||||
|
|
@ -39,9 +42,7 @@ int32 compiler_find_first_bit_r2l(uint64 mask) {
|
|||
}
|
||||
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, mask);
|
||||
|
||||
return index;
|
||||
return _BitScanForward64(&index, mask) ? index : -1;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
@ -51,9 +52,7 @@ int32 compiler_find_first_bit_r2l(uint32 mask) {
|
|||
}
|
||||
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, mask);
|
||||
|
||||
return index;
|
||||
return _BitScanForward(&index, mask) ? index : -1;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
@ -63,9 +62,7 @@ int32 compiler_find_first_bit_l2r(uint64 mask) {
|
|||
}
|
||||
|
||||
unsigned long index;
|
||||
_BitScanReverse64(&index, mask);
|
||||
|
||||
return index;
|
||||
return _BitScanReverse64(&index, mask) ? index : -1;
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
@ -75,9 +72,7 @@ int32 compiler_find_first_bit_l2r(uint32 mask) {
|
|||
}
|
||||
|
||||
unsigned long index;
|
||||
_BitScanReverse(&index, mask);
|
||||
|
||||
return index;
|
||||
return _BitScanReverse(&index, mask) ? index : -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -114,6 +114,175 @@ uint32 hash_ejb(const char* str)
|
|||
return hash % PRIME2;
|
||||
}
|
||||
|
||||
#define ROTL32(x, r) ((x) << (r)) | ((x) >> (32 - (r)))
|
||||
inline constexpr
|
||||
uint32 hash_murmur3_32(const byte* key, size_t len, uint32 seed = 0) {
|
||||
uint32 h = seed;
|
||||
uint32 k;
|
||||
|
||||
// Process 32-bit blocks (4 bytes at a time)
|
||||
for (size_t i = 0; i < len / 4; ++i) {
|
||||
// Get 4 bytes from the key
|
||||
k = *(uint32 *) (key + i * 4);
|
||||
|
||||
// Mix the 4 bytes into the hash
|
||||
k *= 0xcc9e2d51;
|
||||
k = ROTL32(k, 15);
|
||||
k *= 0x1b873593;
|
||||
|
||||
h ^= k;
|
||||
h = ROTL32(h, 13);
|
||||
h = h * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
// Handle the remaining bytes (less than 4 bytes)
|
||||
k = 0;
|
||||
switch (len & 3) {
|
||||
case 3:
|
||||
k ^= key[len / 4 * 4 + 2] << 16;
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
k ^= key[len / 4 * 4 + 1] << 8;
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
k ^= key[len / 4 * 4];
|
||||
k *= 0xcc9e2d51;
|
||||
k = ROTL32(k, 15);
|
||||
k *= 0x1b873593;
|
||||
h ^= k;
|
||||
}
|
||||
|
||||
// Finalize the hash
|
||||
h ^= len;
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#define ROTL64(x, r) ((x) << (r)) | ((x) >> (64 - (r)))
|
||||
uint64 hash_murmur3_64(const void* key, size_t len, uint64 seed = 0) {
|
||||
const uint64 c1 = 0x87c37b91114253d5ULL;
|
||||
const uint64 c2 = 0x4cf5ad432745937fULL;
|
||||
|
||||
const byte* data = (const byte*) key;
|
||||
const size_t nblocks = len / 16;
|
||||
|
||||
uint64 h1 = seed;
|
||||
uint64 h2 = seed;
|
||||
|
||||
// Process 128-bit blocks (16 bytes at a time)
|
||||
for (size_t i = 0; i < nblocks; ++i) {
|
||||
uint64 k1 = *(uint64 *) (data + i * 16);
|
||||
uint64 k2 = *(uint64 *) (data + i * 16 + 8);
|
||||
|
||||
k1 *= c1;
|
||||
k1 = ROTL64(k1, 31);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
|
||||
h1 = ROTL64(h1, 27);
|
||||
h1 += h2;
|
||||
h1 = h1 * 5 + 0x52dce729;
|
||||
|
||||
k2 *= c2;
|
||||
k2 = ROTL64(k2, 33);
|
||||
k2 *= c1;
|
||||
h2 ^= k2;
|
||||
|
||||
h2 = ROTL64(h2, 31);
|
||||
h2 += h1;
|
||||
h2 = h2 * 5 + 0x38495ab5;
|
||||
}
|
||||
|
||||
// Handle the remaining bytes (less than 16 bytes)
|
||||
const byte* tail = data + nblocks * 16;
|
||||
uint64 k1 = 0;
|
||||
uint64 k2 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15:
|
||||
k2 ^= ((uint64) tail[14]) << 48;
|
||||
[[fallthrough]];
|
||||
case 14:
|
||||
k2 ^= ((uint64) tail[13]) << 40;
|
||||
[[fallthrough]];
|
||||
case 13:
|
||||
k2 ^= ((uint64) tail[12]) << 32;
|
||||
[[fallthrough]];
|
||||
case 12:
|
||||
k2 ^= ((uint64) tail[11]) << 24;
|
||||
[[fallthrough]];
|
||||
case 11:
|
||||
k2 ^= ((uint64) tail[10]) << 16;
|
||||
[[fallthrough]];
|
||||
case 10:
|
||||
k2 ^= ((uint64) tail[9]) << 8;
|
||||
[[fallthrough]];
|
||||
case 9:
|
||||
k2 ^= ((uint64) tail[8]) << 0;
|
||||
k2 *= c2;
|
||||
k2 = ROTL64(k2, 33);
|
||||
k2 *= c1;
|
||||
h2 ^= k2;
|
||||
[[fallthrough]];
|
||||
case 8:
|
||||
k1 ^= ((uint64) tail[7]) << 56;
|
||||
[[fallthrough]];
|
||||
case 7:
|
||||
k1 ^= ((uint64) tail[6]) << 48;
|
||||
[[fallthrough]];
|
||||
case 6:
|
||||
k1 ^= ((uint64) tail[5]) << 40;
|
||||
[[fallthrough]];
|
||||
case 5:
|
||||
k1 ^= ((uint64) tail[4]) << 32;
|
||||
[[fallthrough]];
|
||||
case 4:
|
||||
k1 ^= ((uint64) tail[3]) << 24;
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
k1 ^= ((uint64) tail[2]) << 16;
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
k1 ^= ((uint64) tail[1]) << 8;
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
k1 ^= ((uint64) tail[0]) << 0;
|
||||
k1 *= c1;
|
||||
k1 = ROTL64(k1, 31);
|
||||
k1 *= c2;
|
||||
h1 ^= k1;
|
||||
}
|
||||
|
||||
// Finalize the hash
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 ^= h1 >> 33;
|
||||
h1 *= 0xff51afd7ed558ccdULL;
|
||||
h1 ^= h1 >> 33;
|
||||
h1 *= 0xc4ceb9fe1a85ec53ULL;
|
||||
h1 ^= h1 >> 33;
|
||||
|
||||
h2 ^= h2 >> 33;
|
||||
h2 *= 0xff51afd7ed558ccdULL;
|
||||
h2 ^= h2 >> 33;
|
||||
h2 *= 0xc4ceb9fe1a85ec53ULL;
|
||||
h2 ^= h2 >> 33;
|
||||
|
||||
h1 += h2;
|
||||
// h2 += h1;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// Seeded hash functions
|
||||
////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ void generate_default_tga_references(const FileBody* file, Tga* tga)
|
|||
void image_tga_generate(const FileBody* src_data, Image* image)
|
||||
{
|
||||
// @performance We are generating the struct and then filling the data.
|
||||
// There is some asignment/copy overhead
|
||||
// There is some assignment/copy overhead
|
||||
Tga src = {};
|
||||
generate_default_tga_references(src_data, &src);
|
||||
|
||||
|
|
|
|||
447
log/Debug.cpp
447
log/Debug.cpp
|
|
@ -1,447 +0,0 @@
|
|||
#ifndef TOS_LOG_DEBUG_MEMORY_C
|
||||
#define TOS_LOG_DEBUG_MEMORY_C
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "Debug.h"
|
||||
#include "DebugMemory.h"
|
||||
#include "Log.h"
|
||||
#include "TimingStat.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include "../utils/TestUtils.h"
|
||||
#include "../thread/Atomic.h"
|
||||
#include "../architecture/Intrinsics.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
global_persist DebugContainer* debug_container = NULL;
|
||||
|
||||
// WARNING: Spinlock uses TimeUtils which uses performance counter, which is part of DebugContainer
|
||||
// @todo The explanation above is insane. We did this so we only have to set the performance counter once but it is biting us now
|
||||
#include "../thread/Spinlock.cpp"
|
||||
|
||||
#if _WIN32
|
||||
#include <windows.h>
|
||||
void setup_performance_count() {
|
||||
if (!debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
LARGE_INTEGER perf_counter;
|
||||
QueryPerformanceFrequency(&perf_counter);
|
||||
debug_container->performance_count_frequency = perf_counter.QuadPart;
|
||||
}
|
||||
#elif __linux__
|
||||
void setup_performance_count() {
|
||||
if (!debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* fp = fopen("/proc/cpuinfo", "r");
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
char line[256];
|
||||
uint64 cpu_freq = 0;
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (sscanf(line, "cpu MHz%*[^0-9]%ld", &cpu_freq) == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
debug_container->performance_count_frequency = cpu_freq == 0 ? 1 : cpu_freq * 1000000;
|
||||
}
|
||||
#endif
|
||||
|
||||
void log_to_file()
|
||||
{
|
||||
// we don't log an empty log pool
|
||||
if (!debug_container || debug_container->log_memory.pos == 0 || !debug_container->log_fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
DWORD written;
|
||||
WriteFile(
|
||||
debug_container->log_fp,
|
||||
(char *) debug_container->log_memory.memory,
|
||||
(uint32) debug_container->log_memory.pos,
|
||||
&written,
|
||||
NULL
|
||||
);
|
||||
#else
|
||||
if (debug_container->log_fp < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
write(
|
||||
debug_container->log_fp,
|
||||
(char *) debug_container->log_memory.memory,
|
||||
(uint32) debug_container->log_memory.pos
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
// IMPORTANT: This function should only be called when you actually use this data
|
||||
// e.g. log to display or file
|
||||
inline
|
||||
void update_timing_stat(uint32 stat, const char* function)
|
||||
{
|
||||
uint64 new_tick_count = intrin_timestamp_counter();
|
||||
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||
timing_stat->function = function;
|
||||
timing_stat->delta_tick = (uint32) (new_tick_count - timing_stat->old_tick_count);
|
||||
timing_stat->delta_time = (f64) timing_stat->delta_tick / (f64) debug_container->performance_count_frequency;
|
||||
timing_stat->old_tick_count = new_tick_count;
|
||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||
}
|
||||
|
||||
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 = 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 = intrin_timestamp_counter();
|
||||
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||
timing_stat->function = function;
|
||||
timing_stat->delta_tick = (uint32) (new_tick_count - timing_stat->old_tick_count);
|
||||
timing_stat->delta_time = (f64) timing_stat->delta_tick / (f64) debug_container->performance_count_frequency;
|
||||
timing_stat->old_tick_count = new_tick_count;
|
||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||
}
|
||||
|
||||
inline
|
||||
void update_timing_stat_end_continued(uint32 stat, const char* function)
|
||||
{
|
||||
uint64 new_tick_count = intrin_timestamp_counter();
|
||||
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||
timing_stat->function = function;
|
||||
timing_stat->delta_tick = (uint32) ((uint32) (new_tick_count - timing_stat->old_tick_count) + timing_stat->delta_tick);
|
||||
timing_stat->delta_time = timing_stat->delta_time + (f64) timing_stat->delta_tick / (f64) debug_container->performance_count_frequency;
|
||||
timing_stat->old_tick_count = new_tick_count;
|
||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||
}
|
||||
|
||||
inline
|
||||
void update_timing_stat_reset(uint32 stat)
|
||||
{
|
||||
TimingStat* timing_stat = &debug_container->perf_stats[stat];
|
||||
|
||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||
timing_stat->function = NULL;
|
||||
timing_stat->delta_tick = 0;
|
||||
timing_stat->delta_time = 0;
|
||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||
}
|
||||
|
||||
inline
|
||||
void reset_counter(int32 id)
|
||||
{
|
||||
if (!debug_container || !debug_container->counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set_acquire(&debug_container->counter[id], 0);
|
||||
}
|
||||
|
||||
inline
|
||||
void log_increment(int32 id, int64 by = 1)
|
||||
{
|
||||
if (!debug_container || !debug_container->counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_add_acquire(&debug_container->counter[id], by);
|
||||
}
|
||||
|
||||
inline
|
||||
void log_counter(int32 id, int64 value)
|
||||
{
|
||||
if (!debug_container || !debug_container->counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set_acquire(&debug_container->counter[id], value);
|
||||
}
|
||||
|
||||
inline
|
||||
DebugMemory* debug_memory_find(uintptr_t start)
|
||||
{
|
||||
for (uint64 i = 0; i < debug_container->dmc.memory_size; ++i) {
|
||||
if (debug_container->dmc.memory_stats[i].start <= start
|
||||
&& debug_container->dmc.memory_stats[i].start + debug_container->dmc.memory_stats[i].size >= start
|
||||
) {
|
||||
return &debug_container->dmc.memory_stats[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void debug_memory_init(uintptr_t start, uint64 size)
|
||||
{
|
||||
if (!start || !debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DebugMemory* mem = debug_memory_find(start);
|
||||
if (mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemoryContainer* dmc = &debug_container->dmc;
|
||||
if (dmc->memory_size <= dmc->memory_element_idx) {
|
||||
DebugMemory* old = dmc->memory_stats;
|
||||
|
||||
dmc->memory_size += 3;
|
||||
dmc->memory_stats = (DebugMemory *) calloc(dmc->memory_size, sizeof(DebugMemory));
|
||||
|
||||
if (old) {
|
||||
memcpy(dmc->memory_stats, old, (dmc->memory_size - 3) * sizeof(DebugMemory));
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
|
||||
DebugMemory* debug_mem = &dmc->memory_stats[dmc->memory_element_idx];
|
||||
debug_mem->start = start;
|
||||
debug_mem->size = size;
|
||||
debug_mem->usage = 0;
|
||||
|
||||
++dmc->memory_element_idx;
|
||||
}
|
||||
|
||||
void debug_memory_log(uintptr_t start, uint64 size, int32 type, const char* function)
|
||||
{
|
||||
if (!start || !debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemory* mem = debug_memory_find(start);
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 idx = atomic_fetch_add_relaxed(&mem->action_idx, 1);
|
||||
if (idx >= ARRAY_COUNT(mem->last_action)) {
|
||||
atomic_set_acquire(&mem->action_idx, 1);
|
||||
idx %= ARRAY_COUNT(mem->last_action);
|
||||
}
|
||||
|
||||
DebugMemoryRange* dmr = &mem->last_action[idx];
|
||||
dmr->type = type;
|
||||
dmr->start = start - mem->start;
|
||||
dmr->size = size;
|
||||
|
||||
dmr->time = intrin_timestamp_counter();
|
||||
dmr->function_name = function;
|
||||
|
||||
if (type < 0 && mem->usage < size * -type) {
|
||||
mem->usage = 0;
|
||||
} else {
|
||||
mem->usage += size * type;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_memory_reserve(uintptr_t start, uint64 size, int32 type, const char* function)
|
||||
{
|
||||
if (!start || !debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemory* mem = debug_memory_find(start);
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 idx = atomic_fetch_add_relaxed(&mem->reserve_action_idx, 1);
|
||||
if (idx >= ARRAY_COUNT(mem->reserve_action)) {
|
||||
atomic_set_acquire(&mem->reserve_action_idx, 1);
|
||||
idx %= ARRAY_COUNT(mem->reserve_action);
|
||||
}
|
||||
|
||||
DebugMemoryRange* dmr = &mem->reserve_action[idx];
|
||||
dmr->type = type;
|
||||
dmr->start = start - mem->start;
|
||||
dmr->size = size;
|
||||
|
||||
dmr->time = intrin_timestamp_counter();
|
||||
dmr->function_name = function;
|
||||
}
|
||||
|
||||
// undo reserve
|
||||
void debug_memory_free(uintptr_t start)
|
||||
{
|
||||
if (!start || !debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemory* mem = debug_memory_find(start);
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < ARRAY_COUNT(mem->reserve_action); ++i) {
|
||||
DebugMemoryRange* dmr = &mem->reserve_action[i];
|
||||
if (dmr->start == start - mem->start) {
|
||||
dmr->size = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @bug This probably requires thread safety
|
||||
inline
|
||||
void debug_memory_reset()
|
||||
{
|
||||
if (!debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We remove debug information that are "older" than 1GHz
|
||||
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) {
|
||||
if (debug_container->dmc.memory_stats[i].last_action[j].time < time) {
|
||||
memset(&debug_container->dmc.memory_stats[i].last_action[j], 0, sizeof(DebugMemoryRange));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @bug This probably requires thread safety
|
||||
byte* log_get_memory()
|
||||
{
|
||||
LogMemory* log_mem = &debug_container->log_memory;
|
||||
if (log_mem->pos + MAX_LOG_LENGTH > log_mem->size) {
|
||||
log_mem->pos = 0;
|
||||
}
|
||||
|
||||
byte* offset = (byte *) (log_mem->memory + log_mem->pos);
|
||||
memset((void *) offset, 0, MAX_LOG_LENGTH);
|
||||
|
||||
log_mem->pos += MAX_LOG_LENGTH;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void log(const char* str, bool should_log, const char* file, const char* function, int32 line)
|
||||
{
|
||||
if (!should_log || !debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64 len = str_length(str);
|
||||
while (len > 0) {
|
||||
LogMessage* msg = (LogMessage *) log_get_memory();
|
||||
|
||||
// Fill file
|
||||
msg->file = file;
|
||||
msg->function = function;
|
||||
msg->line = line;
|
||||
msg->message = (char *) (msg + 1);
|
||||
msg->time = system_time();
|
||||
|
||||
int32 message_length = (int32) OMS_MIN(MAX_LOG_LENGTH - sizeof(LogMessage) - 1, len);
|
||||
|
||||
memcpy(msg->message, str, message_length);
|
||||
msg->message[message_length] = '\0';
|
||||
str += message_length;
|
||||
len -= MAX_LOG_LENGTH - sizeof(LogMessage);
|
||||
|
||||
if (debug_container->log_memory.size - debug_container->log_memory.pos < MAX_LOG_LENGTH) {
|
||||
log_to_file();
|
||||
debug_container->log_memory.pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void log(const char* format, LogDataArray data, bool should_log, const char* file, const char* function, int32 line)
|
||||
{
|
||||
ASSERT_SIMPLE(str_length(format) + str_length(file) + str_length(function) + 50 < MAX_LOG_LENGTH);
|
||||
|
||||
if (!should_log || !debug_container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.data[0].type == LOG_DATA_VOID) {
|
||||
log(format, should_log, file, function, line);
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage* msg = (LogMessage *) log_get_memory();
|
||||
msg->file = file;
|
||||
msg->function = function;
|
||||
msg->line = line;
|
||||
msg->message = (char *) (msg + 1);
|
||||
msg->time = system_time();
|
||||
|
||||
char temp_format[MAX_LOG_LENGTH];
|
||||
str_copy_short(msg->message, format);
|
||||
|
||||
for (int32 i = 0; i < LOG_DATA_ARRAY; ++i) {
|
||||
if (data.data[i].type == LOG_DATA_VOID) {
|
||||
break;
|
||||
}
|
||||
|
||||
str_copy_short(temp_format, msg->message);
|
||||
|
||||
switch (data.data[i].type) {
|
||||
case LOG_DATA_NONE: {
|
||||
} break;
|
||||
case LOG_DATA_BYTE: {
|
||||
sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_INT32: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_UINT32: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((uint32 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_INT64: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((int64 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_UINT64: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((uint64 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_CHAR: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((char *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_CHAR_STR: {
|
||||
sprintf_fast_iter(msg->message, temp_format, (const char *) data.data[i].value);
|
||||
} break;
|
||||
case LOG_DATA_FLOAT32: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((f32 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_FLOAT64: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((f64 *) data.data[i].value));
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_container->log_memory.size - debug_container->log_memory.pos < MAX_LOG_LENGTH) {
|
||||
log_to_file();
|
||||
debug_container->log_memory.pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
109
log/Debug.h
109
log/Debug.h
|
|
@ -1,109 +0,0 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_LOG_DEBUG_H
|
||||
#define TOS_LOG_DEBUG_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "DebugMemory.h"
|
||||
#include "TimingStat.h"
|
||||
#include "../thread/Spinlock.h"
|
||||
|
||||
#if _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LOG_LENGTH
|
||||
#define MAX_LOG_LENGTH 256
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LOG_MESSAGES
|
||||
#define MAX_LOG_MESSAGES 256
|
||||
#endif
|
||||
|
||||
#ifndef LOG_LEVEL
|
||||
#define LOG_LEVEL 0
|
||||
#endif
|
||||
|
||||
#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,
|
||||
LOG_DATA_BYTE,
|
||||
LOG_DATA_INT32,
|
||||
LOG_DATA_UINT32,
|
||||
LOG_DATA_INT64,
|
||||
LOG_DATA_UINT64,
|
||||
LOG_DATA_CHAR,
|
||||
LOG_DATA_CHAR_STR,
|
||||
LOG_DATA_FLOAT32,
|
||||
LOG_DATA_FLOAT64
|
||||
};
|
||||
|
||||
struct LogMemory {
|
||||
byte* memory;
|
||||
|
||||
uint64 size;
|
||||
uint64 pos;
|
||||
};
|
||||
|
||||
struct LogMessage {
|
||||
const char* file;
|
||||
const char* function;
|
||||
int32 line;
|
||||
uint64 time;
|
||||
char* message;
|
||||
};
|
||||
|
||||
struct LogData {
|
||||
LogDataType type;
|
||||
void* value;
|
||||
};
|
||||
|
||||
struct LogDataArray{
|
||||
LogData data[LOG_DATA_ARRAY];
|
||||
};
|
||||
|
||||
struct DebugContainer {
|
||||
// Used to log memory access (read, write)
|
||||
DebugMemoryContainer dmc;
|
||||
LogMemory log_memory;
|
||||
|
||||
// Used for logging timings for different sections
|
||||
TimingStat* perf_stats;
|
||||
spinlock32 perf_stats_spinlock;
|
||||
|
||||
// Used to log general int values (e.g. counter for draw calls etc.)
|
||||
int64* counter;
|
||||
|
||||
// Required to calculate the "fps"
|
||||
uint64 performance_count_frequency;
|
||||
|
||||
// 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__
|
||||
int32 log_fp;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
34
log/DebugContainer.h
Normal file
34
log/DebugContainer.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_LOG_DEBUG_CONTAINER_H
|
||||
#define TOS_LOG_DEBUG_CONTAINER_H
|
||||
|
||||
#include "Log.h"
|
||||
#include "DebugMemory.h"
|
||||
#include "PerformanceProfiler.h"
|
||||
#include "Stats.h"
|
||||
|
||||
struct DebugContainer {
|
||||
DebugMemoryContainer* dmc;
|
||||
|
||||
#if _WIN32
|
||||
HANDLE log_fp;
|
||||
#elif __linux__
|
||||
int32 log_fp;
|
||||
#endif
|
||||
LogMemory* log_memory;
|
||||
|
||||
PerformanceProfileResult* perf_stats;
|
||||
|
||||
atomic_64 int64* stats_counter;
|
||||
|
||||
PerformanceProfiler** perf_current_scope;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -10,9 +10,15 @@
|
|||
#define TOS_LOG_DEBUG_MEMORY_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../thread/Atomic.h"
|
||||
|
||||
#define DEBUG_MEMORY_RANGE_MAX 500
|
||||
#define DEBUG_MEMORY_RANGE_RES_MAX 100
|
||||
#ifndef DEBUG_MEMORY_RANGE_MAX
|
||||
#define DEBUG_MEMORY_RANGE_MAX 500
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_MEMORY_RANGE_RES_MAX
|
||||
#define DEBUG_MEMORY_RANGE_RES_MAX 100
|
||||
#endif
|
||||
|
||||
struct DebugMemoryRange {
|
||||
int32 type;
|
||||
|
|
@ -40,6 +46,7 @@ struct DebugMemoryContainer {
|
|||
uint64 memory_element_idx;
|
||||
DebugMemory* memory_stats;
|
||||
};
|
||||
static DebugMemoryContainer* _dmc = NULL;
|
||||
|
||||
enum MemoryDebugType {
|
||||
MEMORY_DEBUG_TYPE_DELETE = -1,
|
||||
|
|
@ -49,13 +56,151 @@ enum MemoryDebugType {
|
|||
MEMORY_DEBUG_TYPE_SUBREGION = 3,
|
||||
};
|
||||
|
||||
#if DEBUG || INTERNAL
|
||||
void debug_memory_init(uintptr_t, uint64);
|
||||
void debug_memory_log(uintptr_t, uint64, int32, const char*);
|
||||
void debug_memory_reserve(uintptr_t, uint64, int32, const char*);
|
||||
void debug_memory_free(uintptr_t);
|
||||
void debug_memory_reset();
|
||||
inline
|
||||
DebugMemory* debug_memory_find(uintptr_t start)
|
||||
{
|
||||
for (uint64 i = 0; i < _dmc->memory_size; ++i) {
|
||||
if (_dmc->memory_stats[i].start <= start
|
||||
&& _dmc->memory_stats[i].start + _dmc->memory_stats[i].size >= start
|
||||
) {
|
||||
return &_dmc->memory_stats[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void debug_memory_init(uintptr_t start, uint64 size)
|
||||
{
|
||||
if (!start || !_dmc) {
|
||||
return;
|
||||
}
|
||||
|
||||
const DebugMemory* mem = debug_memory_find(start);
|
||||
if (mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dmc->memory_size <= _dmc->memory_element_idx) {
|
||||
DebugMemory* old = _dmc->memory_stats;
|
||||
|
||||
_dmc->memory_size += 3;
|
||||
_dmc->memory_stats = (DebugMemory *) calloc(_dmc->memory_size, sizeof(DebugMemory));
|
||||
|
||||
if (old) {
|
||||
memcpy(_dmc->memory_stats, old, (_dmc->memory_size - 3) * sizeof(DebugMemory));
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
|
||||
DebugMemory* debug_mem = &_dmc->memory_stats[_dmc->memory_element_idx];
|
||||
debug_mem->start = start;
|
||||
debug_mem->size = size;
|
||||
debug_mem->usage = 0;
|
||||
|
||||
++_dmc->memory_element_idx;
|
||||
}
|
||||
|
||||
void debug_memory_log(uintptr_t start, uint64 size, int32 type, const char* function)
|
||||
{
|
||||
if (!start || !_dmc) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemory* mem = debug_memory_find(start);
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 idx = atomic_fetch_add_relaxed(&mem->action_idx, 1);
|
||||
if (idx >= ARRAY_COUNT(mem->last_action)) {
|
||||
atomic_set_acquire(&mem->action_idx, 1);
|
||||
idx %= ARRAY_COUNT(mem->last_action);
|
||||
}
|
||||
|
||||
DebugMemoryRange* dmr = &mem->last_action[idx];
|
||||
dmr->type = type;
|
||||
dmr->start = start - mem->start;
|
||||
dmr->size = size;
|
||||
|
||||
dmr->time = intrin_timestamp_counter();
|
||||
dmr->function_name = function;
|
||||
|
||||
if (type < 0 && mem->usage < size * -type) {
|
||||
mem->usage = 0;
|
||||
} else {
|
||||
mem->usage += size * type;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_memory_reserve(uintptr_t start, uint64 size, int32 type, const char* function)
|
||||
{
|
||||
if (!start || !_dmc) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemory* mem = debug_memory_find(start);
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64 idx = atomic_fetch_add_relaxed(&mem->reserve_action_idx, 1);
|
||||
if (idx >= ARRAY_COUNT(mem->reserve_action)) {
|
||||
atomic_set_acquire(&mem->reserve_action_idx, 1);
|
||||
idx %= ARRAY_COUNT(mem->reserve_action);
|
||||
}
|
||||
|
||||
DebugMemoryRange* dmr = &mem->reserve_action[idx];
|
||||
dmr->type = type;
|
||||
dmr->start = start - mem->start;
|
||||
dmr->size = size;
|
||||
|
||||
dmr->time = intrin_timestamp_counter();
|
||||
dmr->function_name = function;
|
||||
}
|
||||
|
||||
// undo reserve
|
||||
void debug_memory_free(uintptr_t start)
|
||||
{
|
||||
if (!start || !_dmc) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugMemory* mem = debug_memory_find(start);
|
||||
if (!mem) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < ARRAY_COUNT(mem->reserve_action); ++i) {
|
||||
DebugMemoryRange* dmr = &mem->reserve_action[i];
|
||||
if (dmr->start == start - mem->start) {
|
||||
dmr->size = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @bug This probably requires thread safety
|
||||
inline
|
||||
void debug_memory_reset()
|
||||
{
|
||||
if (!_dmc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We remove debug information that are "older" than 1GHz
|
||||
uint64 time = intrin_timestamp_counter() - 1 * GHZ;
|
||||
|
||||
for (uint64 i = 0; i < _dmc->memory_element_idx; ++i) {
|
||||
for (int32 j = 0; j < DEBUG_MEMORY_RANGE_MAX; ++j) {
|
||||
if (_dmc->memory_stats[i].last_action[j].time < time) {
|
||||
memset(&_dmc->memory_stats[i].last_action[j], 0, sizeof(DebugMemoryRange));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG || INTERNAL
|
||||
#define DEBUG_MEMORY_INIT(start, size) debug_memory_init((start), (size))
|
||||
#define DEBUG_MEMORY_READ(start, size) debug_memory_log((start), (size), MEMORY_DEBUG_TYPE_READ, __func__)
|
||||
#define DEBUG_MEMORY_WRITE(start, size) debug_memory_log((start), (size), MEMORY_DEBUG_TYPE_WRITE, __func__)
|
||||
|
|
|
|||
226
log/Log.h
226
log/Log.h
|
|
@ -10,13 +10,215 @@
|
|||
#define TOS_LOG_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "Debug.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
#include "../utils/StringUtils.h"
|
||||
#include "../platform/win32/TimeUtils.h"
|
||||
|
||||
void log_to_file();
|
||||
void log(const char* str, bool should_log, const char* file, const char* function, int32 line);
|
||||
void log(const char* format, LogDataArray data, bool should_log, const char* file, const char* function, int32 line);
|
||||
void log_increment(int32, int64);
|
||||
void log_counter(int32, int64);
|
||||
#define LOG_DATA_ARRAY 5
|
||||
|
||||
#ifndef LOG_LEVEL
|
||||
#define LOG_LEVEL 0
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LOG_LENGTH
|
||||
#define MAX_LOG_LENGTH 256
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LOG_MESSAGES
|
||||
#define MAX_LOG_MESSAGES 256
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
static HANDLE _log_fp;
|
||||
#elif __linux__
|
||||
static int32 _log_fp;
|
||||
#endif
|
||||
|
||||
struct LogMemory {
|
||||
byte* memory;
|
||||
|
||||
uint64 size;
|
||||
uint64 pos;
|
||||
};
|
||||
static LogMemory* _log_memory = NULL;
|
||||
|
||||
enum LogDataType {
|
||||
LOG_DATA_NONE,
|
||||
LOG_DATA_VOID,
|
||||
LOG_DATA_BYTE,
|
||||
LOG_DATA_INT32,
|
||||
LOG_DATA_UINT32,
|
||||
LOG_DATA_INT64,
|
||||
LOG_DATA_UINT64,
|
||||
LOG_DATA_CHAR,
|
||||
LOG_DATA_CHAR_STR,
|
||||
LOG_DATA_FLOAT32,
|
||||
LOG_DATA_FLOAT64
|
||||
};
|
||||
|
||||
struct LogMessage {
|
||||
const char* file;
|
||||
const char* function;
|
||||
int32 line;
|
||||
uint64 time;
|
||||
char* message;
|
||||
};
|
||||
|
||||
struct LogData {
|
||||
LogDataType type;
|
||||
void* value;
|
||||
};
|
||||
|
||||
struct LogDataArray{
|
||||
LogData data[LOG_DATA_ARRAY];
|
||||
};
|
||||
|
||||
// @bug This probably requires thread safety
|
||||
byte* log_get_memory()
|
||||
{
|
||||
if (_log_memory->pos + MAX_LOG_LENGTH > _log_memory->size) {
|
||||
_log_memory->pos = 0;
|
||||
}
|
||||
|
||||
byte* offset = (byte *) (_log_memory->memory + _log_memory->pos);
|
||||
memset((void *) offset, 0, MAX_LOG_LENGTH);
|
||||
|
||||
_log_memory->pos += MAX_LOG_LENGTH;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void log_to_file()
|
||||
{
|
||||
// we don't log an empty log pool
|
||||
if (!_log_memory || _log_memory->pos == 0 || !_log_fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
DWORD written;
|
||||
WriteFile(
|
||||
_log_fp,
|
||||
(char *) _log_memory->memory,
|
||||
(uint32) _log_memory->pos,
|
||||
&written,
|
||||
NULL
|
||||
);
|
||||
#else
|
||||
if (_log_fp < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
write(
|
||||
_log_fp,
|
||||
(char *) _log_memory->memory,
|
||||
(uint32) _log_memory->pos
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void log(const char* str, bool should_log, const char* file, const char* function, int32 line)
|
||||
{
|
||||
if (!should_log || !_log_memory) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64 len = str_length(str);
|
||||
while (len > 0) {
|
||||
LogMessage* msg = (LogMessage *) log_get_memory();
|
||||
|
||||
// Fill file
|
||||
msg->file = file;
|
||||
msg->function = function;
|
||||
msg->line = line;
|
||||
msg->message = (char *) (msg + 1);
|
||||
msg->time = system_time();
|
||||
|
||||
int32 message_length = (int32) OMS_MIN(MAX_LOG_LENGTH - sizeof(LogMessage) - 1, len);
|
||||
|
||||
memcpy(msg->message, str, message_length);
|
||||
msg->message[message_length] = '\0';
|
||||
str += message_length;
|
||||
len -= MAX_LOG_LENGTH - sizeof(LogMessage);
|
||||
|
||||
if (_log_memory->size - _log_memory->pos < MAX_LOG_LENGTH) {
|
||||
log_to_file();
|
||||
_log_memory->pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void log(const char* format, LogDataArray data, bool should_log, const char* file, const char* function, int32 line)
|
||||
{
|
||||
ASSERT_SIMPLE(str_length(format) + str_length(file) + str_length(function) + 50 < MAX_LOG_LENGTH);
|
||||
|
||||
if (!should_log || !_log_memory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.data[0].type == LOG_DATA_VOID) {
|
||||
log(format, should_log, file, function, line);
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage* msg = (LogMessage *) log_get_memory();
|
||||
msg->file = file;
|
||||
msg->function = function;
|
||||
msg->line = line;
|
||||
msg->message = (char *) (msg + 1);
|
||||
msg->time = system_time();
|
||||
|
||||
char temp_format[MAX_LOG_LENGTH];
|
||||
str_copy_short(msg->message, format);
|
||||
|
||||
for (int32 i = 0; i < LOG_DATA_ARRAY; ++i) {
|
||||
if (data.data[i].type == LOG_DATA_VOID) {
|
||||
break;
|
||||
}
|
||||
|
||||
str_copy_short(temp_format, msg->message);
|
||||
|
||||
switch (data.data[i].type) {
|
||||
case LOG_DATA_NONE: {
|
||||
} break;
|
||||
case LOG_DATA_BYTE: {
|
||||
sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_INT32: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_UINT32: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((uint32 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_INT64: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((int64 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_UINT64: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((uint64 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_CHAR: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((char *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_CHAR_STR: {
|
||||
sprintf_fast_iter(msg->message, temp_format, (const char *) data.data[i].value);
|
||||
} break;
|
||||
case LOG_DATA_FLOAT32: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((f32 *) data.data[i].value));
|
||||
} break;
|
||||
case LOG_DATA_FLOAT64: {
|
||||
sprintf_fast_iter(msg->message, temp_format, *((f64 *) data.data[i].value));
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_log_memory->size - _log_memory->pos < MAX_LOG_LENGTH) {
|
||||
log_to_file();
|
||||
_log_memory->pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define LOG(should_log, str) log((str), (should_log), __FILE__, __func__, __LINE__)
|
||||
#define LOG_FORMAT(should_log, format, ...) log((format), LogDataArray{__VA_ARGS__}, (should_log), __FILE__, __func__, __LINE__)
|
||||
|
|
@ -33,16 +235,4 @@ void log_counter(int32, int64);
|
|||
#define LOG_LEVEL_2(format, ...) ((void) 0)
|
||||
#endif
|
||||
|
||||
#if (!DEBUG && !INTERNAL) || RELEASE
|
||||
#define LOG_INCREMENT(a) ((void) 0)
|
||||
#define LOG_INCREMENT_BY(a, b) ((void) 0)
|
||||
#define LOG_COUNTER(a, b) ((void) 0)
|
||||
#define RESET_COUNTER(a) ((void) 0)
|
||||
#else
|
||||
#define LOG_INCREMENT(a) log_increment((a), 1)
|
||||
#define LOG_INCREMENT_BY(a, b) log_increment((a), (b))
|
||||
#define LOG_COUNTER(a, b) log_counter((a), (b))
|
||||
#define RESET_COUNTER(a) reset_counter((a))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
150
log/PerformanceProfiler.h
Normal file
150
log/PerformanceProfiler.h
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_LOG_PERFORMANCE_PROFILER_H
|
||||
#define TOS_LOG_PERFORMANCE_PROFILER_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../platform/win32/TimeUtils.h"
|
||||
#include "../architecture/Intrinsics.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
|
||||
struct PerformanceProfileResult {
|
||||
const char* name;
|
||||
atomic_64 int64 total_time;
|
||||
atomic_64 int64 total_cycle;
|
||||
atomic_64 int64 self_time;
|
||||
atomic_64 int64 self_cycle;
|
||||
|
||||
// Required for manual start/stop otherwise we would have to use one of the existing values above,
|
||||
// which corrupts them for rendering
|
||||
atomic_64 int64 tmp_time;
|
||||
atomic_64 int64 tmp_cycle;
|
||||
PerformanceProfileResult* parent;
|
||||
};
|
||||
static PerformanceProfileResult* _perf_stats = NULL;
|
||||
|
||||
struct PerformanceProfiler;
|
||||
static PerformanceProfiler** _perf_current_scope = NULL; // Used when sharing profiler across dlls and threads (threads unlikely)
|
||||
static PerformanceProfiler* _perf_current_scope_internal; // Used when in dll or thread and no shared pointer found
|
||||
struct PerformanceProfiler {
|
||||
const char* name;
|
||||
int32 id;
|
||||
|
||||
int64 start_time;
|
||||
int64 total_time;
|
||||
int64 self_time;
|
||||
|
||||
int64 start_cycle;
|
||||
int64 total_cycle;
|
||||
int64 self_cycle;
|
||||
|
||||
PerformanceProfiler* parent;
|
||||
|
||||
PerformanceProfiler(int32 id, const char* scope_name) : id(id) {
|
||||
name = scope_name;
|
||||
|
||||
start_time = time_mu();
|
||||
start_cycle = intrin_timestamp_counter();
|
||||
|
||||
total_time = 0;
|
||||
total_cycle = 0;
|
||||
|
||||
self_time = 0;
|
||||
self_cycle = 0;
|
||||
|
||||
if (_perf_current_scope) {
|
||||
parent = *_perf_current_scope;
|
||||
*_perf_current_scope = this;
|
||||
} else {
|
||||
parent = _perf_current_scope_internal;
|
||||
_perf_current_scope_internal = this;
|
||||
}
|
||||
}
|
||||
|
||||
~PerformanceProfiler() {
|
||||
uint64 end_time = time_mu();
|
||||
uint64 end_cycle = intrin_timestamp_counter();
|
||||
|
||||
total_time = OMS_MAX(end_time - start_time, 0);
|
||||
total_cycle = OMS_MAX(end_cycle - start_cycle, 0);
|
||||
|
||||
self_time += total_time;
|
||||
self_cycle += total_cycle;
|
||||
|
||||
if (parent) {
|
||||
parent->self_time -= total_time;
|
||||
parent->self_cycle -= total_cycle;
|
||||
}
|
||||
|
||||
// Store result
|
||||
PerformanceProfileResult* perf = &_perf_stats[id];
|
||||
perf->name = name;
|
||||
perf->total_time = total_time;
|
||||
perf->total_cycle = total_cycle;
|
||||
perf->self_time = self_time;
|
||||
perf->self_cycle = self_cycle;
|
||||
// @todo create reference to parent result
|
||||
|
||||
if (_perf_current_scope) {
|
||||
*_perf_current_scope = parent;
|
||||
} else {
|
||||
_perf_current_scope_internal = parent;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void performance_profiler_reset(uint32 id)
|
||||
{
|
||||
PerformanceProfileResult* perf = &_perf_stats[id];
|
||||
perf->total_time = 0;
|
||||
perf->total_cycle = 0;
|
||||
perf->self_time = 0;
|
||||
perf->self_cycle = 0;
|
||||
}
|
||||
|
||||
void performance_profiler_start(uint32 id, const char* name)
|
||||
{
|
||||
PerformanceProfileResult* perf = &_perf_stats[id];
|
||||
perf->name = name;
|
||||
|
||||
perf->tmp_time = time_mu();
|
||||
perf->tmp_cycle = intrin_timestamp_counter();
|
||||
}
|
||||
|
||||
void performance_profiler_end(uint32 id)
|
||||
{
|
||||
PerformanceProfileResult* perf = &_perf_stats[id];
|
||||
perf->total_time = time_mu() - perf->tmp_time;
|
||||
perf->total_cycle = intrin_timestamp_counter() - perf->tmp_cycle;
|
||||
perf->self_time = perf->total_time;
|
||||
perf->self_cycle = perf->self_cycle;
|
||||
}
|
||||
|
||||
#if (!DEBUG && !INTERNAL) || RELEASE
|
||||
#define PROFILE_SCOPE(id, name) ((void) 0)
|
||||
#define PROFILE(id) ((void) 0)
|
||||
|
||||
#define PROFILE_START(id, name) ((void) 0)
|
||||
#define PROFILE_END(id) ((void) 0)
|
||||
|
||||
#define PROFILE_RESET(id) ((void) 0)
|
||||
#else
|
||||
#define PROFILE_SCOPE(id, name) PerformanceProfiler __profile_scope_##id(id, name)
|
||||
|
||||
// Only this function can properly handle self-time calculation
|
||||
// Use this whenever you want to profile an entire function
|
||||
#define PROFILE(id) PROFILE_SCOPE(id, __func__)
|
||||
|
||||
#define PROFILE_START(id, name) performance_profiler_start(id, name)
|
||||
#define PROFILE_END(id) performance_profiler_end(id)
|
||||
|
||||
#define PROFILE_RESET(id) performance_profiler_reset((id))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
64
log/Stats.h
Normal file
64
log/Stats.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
#ifndef TOS_LOG_STATS_H
|
||||
#define TOS_LOG_STATS_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "../thread/Atomic.h"
|
||||
|
||||
#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
|
||||
|
||||
static atomic_64 int64* _stats_counter = NULL;
|
||||
|
||||
inline
|
||||
void reset_counter(int32 id)
|
||||
{
|
||||
if (!_stats_counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set_acquire(&_stats_counter[id], 0);
|
||||
}
|
||||
|
||||
inline
|
||||
void log_increment(int32 id, int64 by = 1)
|
||||
{
|
||||
if (!_stats_counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_add_acquire(&_stats_counter[id], by);
|
||||
}
|
||||
|
||||
inline
|
||||
void log_counter(int32 id, int64 value)
|
||||
{
|
||||
if (!_stats_counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set_acquire(&_stats_counter[id], value);
|
||||
}
|
||||
|
||||
#if (!DEBUG && !INTERNAL) || RELEASE
|
||||
#define LOG_INCREMENT(a) ((void) 0)
|
||||
#define LOG_INCREMENT_BY(a, b) ((void) 0)
|
||||
#define LOG_COUNTER(a, b) ((void) 0)
|
||||
#define RESET_COUNTER(a) ((void) 0)
|
||||
#else
|
||||
#define LOG_INCREMENT(a) log_increment((a), 1)
|
||||
#define LOG_INCREMENT_BY(a, b) log_increment((a), (b))
|
||||
#define LOG_COUNTER(a, b) log_counter((a), (b))
|
||||
#define RESET_COUNTER(a) reset_counter((a))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
* Jingga
|
||||
*
|
||||
* @copyright Jingga
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
#ifndef TOS_LOG_TIMING_STAT_H
|
||||
#define TOS_LOG_TIMING_STAT_H
|
||||
|
||||
#include "../stdlib/Types.h"
|
||||
#include "Debug.h"
|
||||
|
||||
#if _WIN32
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
struct TimingStat {
|
||||
const char* function;
|
||||
uint64 old_tick_count;
|
||||
f64 delta_time;
|
||||
uint32 delta_tick;
|
||||
};
|
||||
|
||||
// Sometimes we want to only do logging in debug mode.
|
||||
// In such cases use the following macro.
|
||||
#if DEBUG || INTERNAL
|
||||
void update_timing_stat(uint32, const char*);
|
||||
#define UPDATE_TIMING_STAT(stat) update_timing_stat(stat, __func__)
|
||||
|
||||
// These are only needed if we need to delay the overwrite by 1 frame (e.g. ui update)
|
||||
void update_timing_stat_start(uint32, const char*);
|
||||
#define UPDATE_TIMING_STAT_START(stat) update_timing_stat_start(stat, __func__)
|
||||
#define UPDATE_TIMING_STAT_CONTINUE(stat) update_timing_stat_start(stat, __func__)
|
||||
|
||||
void update_timing_stat_end(uint32, const char*);
|
||||
#define UPDATE_TIMING_STAT_END(stat) update_timing_stat_end(stat, __func__)
|
||||
|
||||
void update_timing_stat_end_continued(uint32, const char*);
|
||||
#define UPDATE_TIMING_STAT_END_CONTINUED(stat) update_timing_stat_end_continued(stat, __func__)
|
||||
|
||||
void update_timing_stat_reset(uint32);
|
||||
#define UPDATE_TIMING_STAT_RESET(stat) update_timing_stat_reset(stat)
|
||||
#else
|
||||
#define UPDATE_TIMING_STAT(stat) ((void) 0)
|
||||
#define UPDATE_TIMING_STAT_START(stat) ((void) 0)
|
||||
#define UPDATE_TIMING_STAT_END(stat) ((void) 0)
|
||||
#define UPDATE_TIMING_STAT_CONTINUE(stat) ((void) 0)
|
||||
#define UPDATE_TIMING_STAT_END_CONTINUED(stat) ((void) 0)
|
||||
#define UPDATE_TIMING_STAT_RESET(stat) ((void) 0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -267,6 +267,8 @@ f32 evaluator_evaluate_function(const char* name, const char* args) {
|
|||
}
|
||||
|
||||
f32 evaluator_evaluate(const char* expr, int32 variable_count = 0, const EvaluatorVariable* variables = NULL) {
|
||||
ASSERT_SIMPLE(str_length(expr) > 0);
|
||||
|
||||
// Handle variables
|
||||
char expr_internal[1024];
|
||||
char* dest = expr_internal;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "../utils/EndianUtils.h"
|
||||
#include "../utils/TestUtils.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/Stats.h"
|
||||
#include "../log/DebugMemory.h"
|
||||
#include "../system/Allocator.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@
|
|||
#include "../utils/TestUtils.h"
|
||||
#include "../utils/EndianUtils.h"
|
||||
#include "../utils/BitUtils.h"
|
||||
#include "../compiler/CompilerUtils.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/Debug.cpp"
|
||||
#include "../log/Stats.h"
|
||||
#include "../log/DebugMemory.h"
|
||||
#include "BufferMemory.h"
|
||||
#include "../system/Allocator.h"
|
||||
#include "../thread/Thread.h"
|
||||
|
|
@ -35,6 +37,7 @@ struct ChunkMemory {
|
|||
uint64* free;
|
||||
};
|
||||
|
||||
// INFO: A chunk count of 2^n is recommended for maximum performance
|
||||
inline
|
||||
void chunk_alloc(ChunkMemory* buf, uint32 count, uint32 chunk_size, int32 alignment = 64)
|
||||
{
|
||||
|
|
@ -153,26 +156,38 @@ byte* chunk_get_element(ChunkMemory* buf, uint64 element, bool zeroed = false)
|
|||
return offset;
|
||||
}
|
||||
|
||||
// @performance This is a very important function, revisit in the future for optimization (e.g. ABM)
|
||||
// @performance Is _BitScanForward faster?
|
||||
// @performance We could probably even reduce the number of iterations by only iterating until popcount is reached?
|
||||
int32 chunk_reserve(ChunkMemory* buf, uint32 elements = 1)
|
||||
{
|
||||
if ((uint32) (buf->last_pos + 1) >= buf->count) {
|
||||
buf->last_pos = -1;
|
||||
}
|
||||
|
||||
uint32 free_index = (buf->last_pos + 1) / 64;
|
||||
uint32 bit_index = (buf->last_pos + 1) & 63;
|
||||
int32 free_element = -1;
|
||||
|
||||
// Check standard simple solution
|
||||
if (elements == 1 && !IS_BIT_SET_64_R2L(buf->free[free_index], bit_index)) {
|
||||
buf->free[free_index] |= (1ULL << bit_index);
|
||||
++buf->last_pos;
|
||||
|
||||
return free_index * 64 + bit_index;
|
||||
}
|
||||
|
||||
int32 free_element = -1;
|
||||
uint32 i = 0;
|
||||
uint32 consecutive_free_bits = 0;
|
||||
|
||||
while (free_element < 0 && i++ <= buf->count) {
|
||||
// Skip fully filled ranges
|
||||
if (free_index * 64 + bit_index + elements - consecutive_free_bits > buf->count) {
|
||||
// Go to beginning after overflow
|
||||
i += buf->count - (free_index * 64 + bit_index);
|
||||
consecutive_free_bits = 0;
|
||||
free_index = 0;
|
||||
bit_index = 0;
|
||||
|
||||
continue;
|
||||
} else if (buf->free[free_index] == 0xFFFFFFFFFFFFFFFF) {
|
||||
// Skip fully filled ranges
|
||||
++free_index;
|
||||
bit_index = 0;
|
||||
i += 64;
|
||||
|
|
@ -182,28 +197,10 @@ int32 chunk_reserve(ChunkMemory* buf, uint32 elements = 1)
|
|||
}
|
||||
|
||||
// Find first free element
|
||||
while (IS_BIT_SET_64_R2L(buf->free[free_index], bit_index) && i <= buf->count) {
|
||||
consecutive_free_bits = 0;
|
||||
++bit_index;
|
||||
++i;
|
||||
// This MUST find a free element, otherwise we wouldn't have gotten here
|
||||
bit_index = compiler_find_first_bit_r2l(~buf->free[free_index]);
|
||||
|
||||
// We still need to check for overflow since our initial bit_index is based on buf->last_pos
|
||||
if (bit_index > 63) {
|
||||
bit_index = 0;
|
||||
++free_index;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The previous while may exit with an "overflow", that's why this check is required
|
||||
if (IS_BIT_SET_64_R2L(buf->free[free_index], bit_index) || i > buf->count) {
|
||||
consecutive_free_bits = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// We found our first free element, let's check if we have enough free space
|
||||
// Let's check if we have enough free space, we need more than just one free bit
|
||||
do {
|
||||
++i;
|
||||
++consecutive_free_bits;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
|
||||
#include "BufferMemory.h"
|
||||
#include "../log/Log.h"
|
||||
#include "../log/Debug.cpp"
|
||||
#include "../log/Stats.h"
|
||||
#include "../log/DebugMemory.h"
|
||||
#include "../thread/Atomic.h"
|
||||
#include "../thread/Semaphore.h"
|
||||
#include "../thread/ThreadDefines.h"
|
||||
|
|
|
|||
|
|
@ -44,4 +44,7 @@ typedef void pthread_mutexattr_t;
|
|||
typedef void pthread_condattr_t;
|
||||
typedef void pthread_rwlockattr_t;
|
||||
|
||||
// Thread local variable Already exists in c++11
|
||||
// #define thread_local __thread
|
||||
|
||||
#endif
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
#include <dbghelp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "../../log/Debug.cpp"
|
||||
#include "../../log/Log.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include "../../utils/Utils.h"
|
||||
#include "../../utils/TestUtils.h"
|
||||
#include "../../memory/RingMemory.h"
|
||||
#include "../../log/Log.h"
|
||||
#include "../../log/Stats.h"
|
||||
|
||||
typedef HANDLE FileHandle;
|
||||
typedef HANDLE MMFHandle;
|
||||
|
|
|
|||
|
|
@ -149,4 +149,22 @@ void window_close(Window* window)
|
|||
DestroyWindow(window->hwnd);
|
||||
}
|
||||
|
||||
HBITMAP CreateBitmapFromRGBA(HDC hdc, const byte* rgba, int32 width, int32 height) {
|
||||
BITMAPINFO bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = width;
|
||||
bmi.bmiHeader.biHeight = height;
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
void* pbits;
|
||||
HBITMAP hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pbits, NULL, 0);
|
||||
if (hBitmap) {
|
||||
memcpy(pbits, rgba, width * height * 4);
|
||||
}
|
||||
|
||||
return hBitmap;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -21,6 +21,9 @@ typedef void pthread_rwlockattr_t;
|
|||
typedef HANDLE pthread_t;
|
||||
typedef CONDITION_VARIABLE pthread_cond_t;
|
||||
|
||||
// Thread local variable Already exists in c++11
|
||||
// #define thread_local __declspec(thread)
|
||||
|
||||
struct pthread_rwlock_t {
|
||||
SRWLOCK lock;
|
||||
bool exclusive;
|
||||
|
|
|
|||
218
stdlib/HashMap.h
218
stdlib/HashMap.h
|
|
@ -225,16 +225,12 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt32* tmp = (HashEntryInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryInt32* tmp = (HashEntryInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
||||
|
|
@ -251,16 +247,12 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt64* tmp = (HashEntryInt64 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryInt64 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryInt64* tmp = (HashEntryInt64*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
||||
|
|
@ -277,16 +269,12 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryUIntPtr* tmp = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryUIntPtr* tmp = (HashEntryUIntPtr*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
||||
|
|
@ -303,16 +291,12 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryVoidP* tmp = (HashEntryVoidP *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryVoidP *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryVoidP* tmp = (HashEntryVoidP*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
||||
|
|
@ -329,16 +313,12 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryFloat* tmp = (HashEntryFloat *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryFloat *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryFloat* tmp = (HashEntryFloat*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
||||
|
|
@ -357,16 +337,12 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
|||
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryStr* tmp = (HashEntryStr *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryStr *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryStr* tmp = (HashEntryStr*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
HashEntry* hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
||||
|
|
@ -386,16 +362,12 @@ HashEntry* hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
|||
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntry* tmp = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntry *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntry* tmp = (HashEntry*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
|
@ -415,16 +387,12 @@ HashEntry* hashmap_reserve(HashMap* hm, const char* key) {
|
|||
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntry* tmp = (HashEntry *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntry *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntry* tmp = (HashEntry*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
|
@ -575,16 +543,12 @@ void hashmap_insert(HashMap* hm, int32 key, int32 value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt32KeyInt32* tmp = (HashEntryInt32KeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryInt32KeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryInt32KeyInt32* tmp = (HashEntryInt32KeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, int32 key, int64 value) {
|
||||
|
|
@ -597,16 +561,12 @@ void hashmap_insert(HashMap* hm, int32 key, int64 value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryInt64KeyInt32* tmp = (HashEntryInt64KeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryInt64KeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryInt64KeyInt32* tmp = (HashEntryInt64KeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, int32 key, uintptr_t value) {
|
||||
|
|
@ -619,16 +579,12 @@ void hashmap_insert(HashMap* hm, int32 key, uintptr_t value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryUIntPtrKeyInt32* tmp = (HashEntryUIntPtrKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryUIntPtrKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryUIntPtrKeyInt32* tmp = (HashEntryUIntPtrKeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, int32 key, void* value) {
|
||||
|
|
@ -641,16 +597,12 @@ void hashmap_insert(HashMap* hm, int32 key, void* value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryVoidPKeyInt32* tmp = (HashEntryVoidPKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryVoidPKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryVoidPKeyInt32* tmp = (HashEntryVoidPKeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, int32 key, f32 value) {
|
||||
|
|
@ -663,16 +615,12 @@ void hashmap_insert(HashMap* hm, int32 key, f32 value) {
|
|||
entry->value = value;
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryFloatKeyInt32* tmp = (HashEntryFloatKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryFloatKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryFloatKeyInt32* tmp = (HashEntryFloatKeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, int32 key, const char* value) {
|
||||
|
|
@ -688,16 +636,12 @@ void hashmap_insert(HashMap* hm, int32 key, const char* value) {
|
|||
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryStrKeyInt32* tmp = (HashEntryStrKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryStrKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryStrKeyInt32* tmp = (HashEntryStrKeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
void hashmap_insert(HashMap* hm, int32 key, byte* value) {
|
||||
|
|
@ -713,16 +657,12 @@ void hashmap_insert(HashMap* hm, int32 key, byte* value) {
|
|||
|
||||
entry->next = 0;
|
||||
|
||||
if (hm->table[index]) {
|
||||
HashEntryKeyInt32* tmp = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, hm->table[index] - 1, false);
|
||||
while(tmp->next) {
|
||||
tmp = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, tmp->next - 1, false);
|
||||
}
|
||||
|
||||
tmp->next = (uint16) (element + 1);
|
||||
} else {
|
||||
hm->table[index] = (uint16) (element + 1);
|
||||
uint16* target = &hm->table[index];
|
||||
while (*target) {
|
||||
HashEntryKeyInt32* tmp = (HashEntryKeyInt32*) chunk_get_element(&hm->buf, *target - 1, false);
|
||||
target = &tmp->next;
|
||||
}
|
||||
*target = (uint16) (element + 1);
|
||||
}
|
||||
|
||||
HashEntryKeyInt32* hashmap_get_entry(HashMap* hm, int32 key) {
|
||||
|
|
@ -869,14 +809,14 @@ inline
|
|||
int64 hashmap_load(HashMap* hm, const byte* data, [[maybe_unused]] int32 steps = 8)
|
||||
{
|
||||
uint64 count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
|
||||
data += sizeof(uint16);
|
||||
data += sizeof(uint32);
|
||||
|
||||
// Load the table content
|
||||
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
|
||||
(uint16 *) hm->table,
|
||||
(uint16 *) hm->table,
|
||||
sizeof(uint16) * count / 2, // everything is 2 bytes -> super easy to swap
|
||||
steps
|
||||
);
|
||||
data += sizeof(uint16) * count;
|
||||
|
|
|
|||
3
tests/.vscode/settings.json
vendored
3
tests/.vscode/settings.json
vendored
|
|
@ -230,7 +230,8 @@
|
|||
"uilayout.h": "c",
|
||||
"camera.h": "c",
|
||||
"stdio.h": "c",
|
||||
"uilabel.h": "c"
|
||||
"uilabel.h": "c",
|
||||
"uiattributedimension.h": "c"
|
||||
},
|
||||
"C_Cpp.default.cppStandard": "c++20",
|
||||
"todo-tree.general.tags": [
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
#define TEST_INIT(test_count) \
|
||||
do \
|
||||
{ \
|
||||
setvbuf(stdout, NULL, _IONBF, 0); \
|
||||
SetUnhandledExceptionFilter(test_exception_handler); \
|
||||
_test_assert_error_count = 0; \
|
||||
_test_count = 0; \
|
||||
|
|
@ -81,6 +82,7 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
#define TEST_INIT(test_count) \
|
||||
do \
|
||||
{ \
|
||||
setvbuf(stdout, NULL, _IONBF, 0); \
|
||||
signal(SIGSEGV, test_exception_handler); \
|
||||
signal(SIGABRT, test_exception_handler); \
|
||||
_test_assert_error_count = 0; \
|
||||
|
|
@ -103,19 +105,21 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
if (_test_assert_error_count) \
|
||||
{ \
|
||||
printf( \
|
||||
"[NG] %5d (%5d/%5d) %s\n", \
|
||||
"[NG] %5d (%5d/%5d) %s\n", \
|
||||
_test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, __FILE__); \
|
||||
for (int i = 0; i < _test_assert_error_count; ++i) \
|
||||
{ \
|
||||
printf(" %s\n", _test_log[i]); \
|
||||
printf(" %s\n", _test_log[i]); \
|
||||
fflush(stdout); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
printf( \
|
||||
"[OK] %5d (%5d/%5d) %s\n", \
|
||||
"[OK] %5d (%5d/%5d) %s\n", \
|
||||
_test_count, _test_assert_count - _test_assert_error_count, _test_assert_count, __FILE__); \
|
||||
} \
|
||||
fflush(stdout); \
|
||||
free(_test_log); \
|
||||
_test_log = NULL; \
|
||||
_test_assert_error_count = 0; \
|
||||
|
|
@ -156,18 +160,18 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_EQUALS_WITH_DELTA(a, b, delta) \
|
||||
do \
|
||||
{ \
|
||||
++_test_assert_count; \
|
||||
++_test_global_assert_count; \
|
||||
if (OMS_ABS((a) - (b)) > (delta)) \
|
||||
{ \
|
||||
++_test_global_assert_error_count; \
|
||||
snprintf( \
|
||||
_test_log[_test_assert_error_count++], 1024, \
|
||||
"%4i: %lld != %lld", __LINE__, (int64)(a), (int64)(b)); \
|
||||
} \
|
||||
#define ASSERT_EQUALS_WITH_DELTA(a, b, delta) \
|
||||
do \
|
||||
{ \
|
||||
++_test_assert_count; \
|
||||
++_test_global_assert_count; \
|
||||
if (OMS_ABS((a) - (b)) > (delta)) \
|
||||
{ \
|
||||
++_test_global_assert_error_count; \
|
||||
snprintf( \
|
||||
_test_log[_test_assert_error_count++], 1024, \
|
||||
"%4i: %f != %f", __LINE__, (f64)(a), (f64)(b)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_CONTAINS(a, b) \
|
||||
|
|
@ -240,6 +244,25 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_MEMORY_EQUALS(ptr1, ptr2, len) \
|
||||
do \
|
||||
{ \
|
||||
++_test_assert_count; \
|
||||
++_test_global_assert_count; \
|
||||
const uint8_t *p1 = (const uint8_t *)(ptr1); \
|
||||
const uint8_t *p2 = (const uint8_t *)(ptr2); \
|
||||
for (uint64_t i = 0; i < (uint64_t)(len); ++i) \
|
||||
{ \
|
||||
if (p1[i] != p2[i]) \
|
||||
{ \
|
||||
++_test_global_assert_error_count; \
|
||||
snprintf(_test_log[_test_global_assert_error_count], 1024, \
|
||||
"%4i: mismatch at offset %lld", __LINE__, i); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define COMPARE_FUNCTION_TEST_CYCLE(func1, func2, x_percent) \
|
||||
do \
|
||||
{ \
|
||||
|
|
@ -247,7 +270,7 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
++_test_global_assert_count; \
|
||||
uint64_t cycles_func1, cycles_func2; \
|
||||
uint64_t start, end; \
|
||||
int64_t a = 0, b = 0; \
|
||||
int64_t a = 0, b = 0; \
|
||||
\
|
||||
/* Measure func1 */ \
|
||||
start = intrin_timestamp_counter(); \
|
||||
|
|
@ -283,7 +306,7 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
++_test_global_assert_count; \
|
||||
uint64_t cycles_func; \
|
||||
uint64_t start, end; \
|
||||
int64_t para = 0; \
|
||||
int64_t para = 0; \
|
||||
\
|
||||
/* Measure func */ \
|
||||
start = intrin_timestamp_counter(); \
|
||||
|
|
@ -307,7 +330,7 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
++_test_assert_count; \
|
||||
++_test_global_assert_count; \
|
||||
double time_func1, time_func2; \
|
||||
int64_t a = 0, b = 0; \
|
||||
int64_t a = 0, b = 0; \
|
||||
\
|
||||
/* Measure func1 */ \
|
||||
time_func1 = test_measure_func_time_ns(func1, (void *)&a); \
|
||||
|
|
@ -336,7 +359,7 @@ double test_measure_func_time_ns(void (*func)(void *), void *para)
|
|||
++_test_assert_count; \
|
||||
++_test_global_assert_count; \
|
||||
double time_func; \
|
||||
int64_t para = 0; \
|
||||
int64_t para = 0; \
|
||||
\
|
||||
/* Measure func */ \
|
||||
time_func = test_measure_func_time_ns(func, (void *)¶); \
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "../TestFramework.h"
|
||||
#include "../../stdlib/HashMap.h"
|
||||
|
||||
static void test_hash_alloc() {
|
||||
static void test_hashmap_alloc() {
|
||||
HashMap hm = {};
|
||||
hashmap_alloc(&hm, 3, sizeof(HashEntryInt32));
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ static void test_hash_alloc() {
|
|||
ASSERT_EQUALS(hm.buf.memory, NULL);
|
||||
}
|
||||
|
||||
static void test_hash_insert_int32() {
|
||||
static void test_hashmap_insert_int32() {
|
||||
HashMap hm = {};
|
||||
hashmap_alloc(&hm, 3, sizeof(HashEntryInt32));
|
||||
|
||||
|
|
@ -40,6 +40,34 @@ static void test_hash_insert_int32() {
|
|||
hashmap_free(&hm);
|
||||
}
|
||||
|
||||
static void test_hashmap_dump_load() {
|
||||
RingMemory ring;
|
||||
ring_alloc(&ring, 10 * MEGABYTE, 64);
|
||||
|
||||
HashMap hm_dump = {};
|
||||
hashmap_alloc(&hm_dump, 3, sizeof(HashEntryInt32));
|
||||
|
||||
hashmap_insert(&hm_dump, "test1", 1);
|
||||
hashmap_insert(&hm_dump, "test2", 2);
|
||||
hashmap_insert(&hm_dump, "test3", 3);
|
||||
|
||||
HashMap hm_load = {};
|
||||
hashmap_alloc(&hm_load, 3, sizeof(HashEntryInt32));
|
||||
|
||||
byte* out = ring_get_memory(&ring, 1024 * 1024);
|
||||
|
||||
int64 dump_size = hashmap_dump(&hm_dump, out);
|
||||
int64 load_size = hashmap_load(&hm_load, out);
|
||||
ASSERT_EQUALS(dump_size, load_size);
|
||||
ASSERT_MEMORY_EQUALS(hm_dump.table, hm_load.table, sizeof(uint16) * hm_dump.buf.count);
|
||||
ASSERT_MEMORY_EQUALS(hm_dump.buf.memory, hm_load.buf.memory, hm_dump.buf.size);
|
||||
|
||||
hashmap_free(&hm_dump);
|
||||
hashmap_free(&hm_load);
|
||||
|
||||
ring_free(&ring);
|
||||
}
|
||||
|
||||
#ifdef UBER_TEST
|
||||
#ifdef main
|
||||
#undef main
|
||||
|
|
@ -50,8 +78,9 @@ static void test_hash_insert_int32() {
|
|||
int main() {
|
||||
TEST_INIT(25);
|
||||
|
||||
RUN_TEST(test_hash_alloc);
|
||||
RUN_TEST(test_hash_insert_int32);
|
||||
RUN_TEST(test_hashmap_alloc);
|
||||
RUN_TEST(test_hashmap_insert_int32);
|
||||
RUN_TEST(test_hashmap_dump_load);
|
||||
|
||||
TEST_FINALIZE();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,42 +4,61 @@
|
|||
#include "../../system/Allocator.h"
|
||||
|
||||
static void test_layout_from_file_txt() {
|
||||
RingMemory ui_layout_ring;
|
||||
ring_alloc(&ui_layout_ring, 10 * MEGABYTE, 64);
|
||||
RingMemory ring;
|
||||
ring_alloc(&ring, 10 * MEGABYTE, 64);
|
||||
|
||||
UILayout layout;
|
||||
layout.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
|
||||
layout_from_file_txt(&layout, "./../../GameEditor/assets/themes/default/scene1.layouttxt", &ui_layout_ring);
|
||||
layout_from_file_txt(&layout, "./../../GameEditor/assets/themes/default/scene1.layouttxt", &ring);
|
||||
|
||||
UIElement* element = layout_get_element(&layout, "cmd_window");
|
||||
ASSERT_NOT_EQUALS(element, NULL);
|
||||
|
||||
platform_free((void **) &layout.data);
|
||||
ring_free(&ui_layout_ring);
|
||||
ring_free(&ring);
|
||||
}
|
||||
|
||||
static void test_layout_to_data() {
|
||||
}
|
||||
static void test_layout_to_from_data() {
|
||||
RingMemory ring;
|
||||
ring_alloc(&ring, 10 * MEGABYTE, 64);
|
||||
|
||||
static void test_layout_from_data() {
|
||||
UILayout layout_dump;
|
||||
layout_dump.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
layout_from_file_txt(&layout_dump, "./../../GameEditor/assets/themes/default/scene1.layouttxt", &ring);
|
||||
|
||||
UILayout layout_load;
|
||||
layout_load.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
|
||||
byte* out = ring_get_memory(&ring, 1024 * 1024);
|
||||
|
||||
int64 dump_size = layout_to_data(&layout_dump, out);
|
||||
int64 load_size = layout_from_data(out, &layout_load);
|
||||
ASSERT_EQUALS(dump_size, load_size);
|
||||
ASSERT_MEMORY_EQUALS(layout_dump.data, layout_load.data, (uint32) (load_size * 0.8));
|
||||
|
||||
UIElement* element = layout_get_element(&layout_load, "cmd_window");
|
||||
ASSERT_NOT_EQUALS(element, NULL);
|
||||
|
||||
platform_free((void **) &layout_load.data);
|
||||
platform_free((void **) &layout_dump.data);
|
||||
ring_free(&ring);
|
||||
}
|
||||
|
||||
static void test_layout_from_theme() {
|
||||
RingMemory ui_layout_ring;
|
||||
ring_alloc(&ui_layout_ring, 10 * MEGABYTE, 64);
|
||||
RingMemory ring;
|
||||
ring_alloc(&ring, 10 * MEGABYTE, 64);
|
||||
|
||||
UILayout layout;
|
||||
layout.data = (byte *) platform_alloc(10 * MEGABYTE);
|
||||
layout_from_file_txt(&layout, "./../../GameEditor/assets/themes/default/scene1.layouttxt", &ui_layout_ring);
|
||||
layout_from_file_txt(&layout, "./../../GameEditor/assets/themes/default/scene1.layouttxt", &ring);
|
||||
|
||||
UIThemeStyle theme1;
|
||||
theme1.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
theme_from_file_txt(&theme1, "./../../GameEditor/assets/themes/default/general.themetxt", &ui_layout_ring);
|
||||
theme_from_file_txt(&theme1, "./../../GameEditor/assets/themes/default/general.themetxt", &ring);
|
||||
|
||||
UIThemeStyle theme2;
|
||||
theme2.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
theme_from_file_txt(&theme2, "./../../GameEditor/assets/themes/default/scene1.themetxt", &ui_layout_ring);
|
||||
theme_from_file_txt(&theme2, "./../../GameEditor/assets/themes/default/scene1.themetxt", &ring);
|
||||
|
||||
Camera camera = {};
|
||||
camera.viewport_width = 1024;
|
||||
|
|
@ -49,14 +68,16 @@ static void test_layout_from_theme() {
|
|||
|
||||
UIElement* element = layout_get_element(&layout, "cmd_window");
|
||||
ASSERT_NOT_EQUALS(element, NULL);
|
||||
ASSERT_TRUE(element->state > 0);
|
||||
ASSERT_TRUE(element->children_count > 0);
|
||||
ASSERT_EQUALS(element->type, UI_ELEMENT_TYPE_VIEW_WINDOW);
|
||||
|
||||
UIWindow* default_style = (UIWindow *) layout_get_element_style(&layout, element, UI_STYLE_TYPE_DEFAULT);
|
||||
ASSERT_EQUALS_WITH_DELTA(default_style->dimension.dimension.width, 1.0f, 0.001f);
|
||||
ASSERT_EQUALS_WITH_DELTA(default_style->dimension.dimension.height, 0.25f, 0.001f);
|
||||
|
||||
platform_free((void **) &layout.data);
|
||||
platform_free((void **) &theme1.data);
|
||||
platform_free((void **) &theme2.data);
|
||||
ring_free(&ui_layout_ring);
|
||||
ring_free(&ring);
|
||||
}
|
||||
|
||||
#ifdef UBER_TEST
|
||||
|
|
@ -67,11 +88,10 @@ static void test_layout_from_theme() {
|
|||
#endif
|
||||
|
||||
int main() {
|
||||
TEST_INIT(10);
|
||||
TEST_INIT(100);
|
||||
|
||||
RUN_TEST(test_layout_from_file_txt);
|
||||
RUN_TEST(test_layout_to_data);
|
||||
RUN_TEST(test_layout_from_data);
|
||||
RUN_TEST(test_layout_to_from_data);
|
||||
RUN_TEST(test_layout_from_theme);
|
||||
|
||||
TEST_FINALIZE();
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@
|
|||
#include "../../system/Allocator.h"
|
||||
|
||||
static void test_theme_from_file_txt() {
|
||||
RingMemory ui_theme_ring;
|
||||
ring_alloc(&ui_theme_ring, 10 * MEGABYTE, 64);
|
||||
RingMemory ring;
|
||||
ring_alloc(&ring, 10 * MEGABYTE, 64);
|
||||
|
||||
UIThemeStyle theme;
|
||||
theme.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
|
||||
theme_from_file_txt(&theme, "./../../GameEditor/assets/themes/default/scene1.themetxt", &ui_theme_ring);
|
||||
theme_from_file_txt(&theme, "./../../GameEditor/assets/themes/default/scene1.themetxt", &ring);
|
||||
|
||||
UIAttributeGroup* group = theme_style_group(&theme, "#cmd_window");
|
||||
ASSERT_NOT_EQUALS(group, NULL);
|
||||
|
|
@ -20,13 +19,40 @@ static void test_theme_from_file_txt() {
|
|||
ASSERT_EQUALS(attr->datatype, UI_ATTRIBUTE_DATA_TYPE_F32);
|
||||
ASSERT_EQUALS_WITH_DELTA(attr->value_float, 0.0f, 0.001f);
|
||||
|
||||
ring_free(&ui_theme_ring);
|
||||
platform_free((void **) &theme.data);
|
||||
ring_free(&ring);
|
||||
}
|
||||
|
||||
static void test_theme_to_data() {
|
||||
}
|
||||
static void test_theme_to_from_data() {
|
||||
RingMemory ring;
|
||||
ring_alloc(&ring, 10 * MEGABYTE, 64);
|
||||
|
||||
static void test_theme_from_data() {
|
||||
UIThemeStyle theme_dump;
|
||||
theme_dump.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
theme_from_file_txt(&theme_dump, "./../../GameEditor/assets/themes/default/scene1.themetxt", &ring);
|
||||
|
||||
UIThemeStyle theme_load;
|
||||
theme_load.data = (byte *) platform_alloc(2 * MEGABYTE);
|
||||
|
||||
byte* out = ring_get_memory(&ring, 1024 * 1024);
|
||||
|
||||
int64 dump_size = theme_to_data(&theme_dump, out);
|
||||
int64 load_size = theme_from_data(out, &theme_load);
|
||||
ASSERT_EQUALS(dump_size, load_size);
|
||||
ASSERT_MEMORY_EQUALS(theme_dump.data, theme_load.data, (uint32) (load_size * 0.8));
|
||||
|
||||
UIAttributeGroup* group = theme_style_group(&theme_load, "#cmd_window");
|
||||
ASSERT_NOT_EQUALS(group, NULL);
|
||||
ASSERT_TRUE(group->attribute_count > 0);
|
||||
|
||||
UIAttribute* attr = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_POSITION_X);
|
||||
ASSERT_NOT_EQUALS(attr, NULL);
|
||||
ASSERT_EQUALS(attr->datatype, UI_ATTRIBUTE_DATA_TYPE_F32);
|
||||
ASSERT_EQUALS_WITH_DELTA(attr->value_float, 0.0f, 0.001f);
|
||||
|
||||
platform_free((void **) &theme_load.data);
|
||||
platform_free((void **) &theme_dump.data);
|
||||
ring_free(&ring);
|
||||
}
|
||||
|
||||
#ifdef UBER_TEST
|
||||
|
|
@ -37,11 +63,10 @@ static void test_theme_from_data() {
|
|||
#endif
|
||||
|
||||
int main() {
|
||||
TEST_INIT(10);
|
||||
TEST_INIT(100);
|
||||
|
||||
RUN_TEST(test_theme_from_file_txt);
|
||||
RUN_TEST(test_theme_to_data);
|
||||
RUN_TEST(test_theme_from_data);
|
||||
RUN_TEST(test_theme_to_from_data);
|
||||
|
||||
TEST_FINALIZE();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ struct UIButtonState {
|
|||
};
|
||||
|
||||
struct UIButton {
|
||||
|
||||
UIAttributeDimension dimension;
|
||||
};
|
||||
|
||||
void ui_button_state_serialize(const UIButtonState* __restrict state, byte** __restrict pos)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@ enum UIElementType : byte {
|
|||
UI_ELEMENT_TYPE_VIEW_PANEL,
|
||||
UI_ELEMENT_TYPE_VIEW_TAB,
|
||||
UI_ELEMENT_TYPE_CURSOR,
|
||||
|
||||
// Uses a callback function for update/rendering
|
||||
UI_ELEMENT_TYPE_CUSTOM,
|
||||
|
||||
// Doesn't do anything is handled completely manual
|
||||
UI_ELEMENT_TYPE_MANUAL,
|
||||
|
||||
UI_ELEMENT_TYPE_SIZE,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ int32 ui_element_type_size(UIElementType e)
|
|||
return sizeof(UICursor);
|
||||
case UI_ELEMENT_TYPE_CUSTOM:
|
||||
return sizeof(UICustom);
|
||||
case UI_ELEMENT_TYPE_MANUAL:
|
||||
return sizeof(UICustom);
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
@ -89,6 +91,8 @@ int32 ui_element_state_size(UIElementType e)
|
|||
return sizeof(UICursorState);
|
||||
case UI_ELEMENT_TYPE_CUSTOM:
|
||||
return sizeof(UICustomState);
|
||||
case UI_ELEMENT_TYPE_MANUAL:
|
||||
return sizeof(UICustomState);
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
@ -126,8 +130,12 @@ int32 ui_element_type_to_id(const char* str)
|
|||
return UI_ELEMENT_TYPE_CURSOR;
|
||||
} else if (str_compare("custom", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_CUSTOM;
|
||||
} else if (str_compare("manual", str) == 0) {
|
||||
return UI_ELEMENT_TYPE_MANUAL;
|
||||
}
|
||||
|
||||
ASSERT_SIMPLE(false);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -180,12 +180,12 @@ void ui_input_element_populate(
|
|||
|
||||
// First set all values, which we can set immediately
|
||||
for (uint32 i = 0; i < group->attribute_count; ++i) {
|
||||
switch (attributes->attribute_id) {
|
||||
switch (attributes[i].attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_POSITION_X:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
||||
case UI_ATTRIBUTE_TYPE_POSITION_Y:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
||||
ui_theme_assign_dimension(&input->dimension, attributes, 6, variables);
|
||||
ui_theme_assign_dimension(&input->dimension, &attributes[i], 6, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
63
ui/UILabel.h
63
ui/UILabel.h
|
|
@ -51,15 +51,66 @@ void ui_label_element_unserialize(UILabel* __restrict details, const byte** __re
|
|||
}
|
||||
|
||||
void ui_label_element_populate(
|
||||
UILayout* layout,
|
||||
const UIAttributeGroup* __restrict group,
|
||||
UIElement* __restrict element,
|
||||
UIStyleType style_type,
|
||||
UILabel* __restrict label,
|
||||
UIElement* parent,
|
||||
EvaluatorVariable* __restrict variables
|
||||
) {
|
||||
(void *) group;
|
||||
(void *) element;
|
||||
(void) style_type;
|
||||
(void *) variables;
|
||||
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
|
||||
|
||||
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;
|
||||
case UI_ELEMENT_TYPE_BUTTON: {
|
||||
UIButton* parent_button = (UIButton *) (layout->data + parent->style_types[UI_STYLE_TYPE_ACTIVE]);
|
||||
parent_dimension = &parent_button->dimension.dimension;
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
UIInput* parent_input = (UIInput *) (layout->data + parent->style_types[UI_STYLE_TYPE_ACTIVE]);
|
||||
parent_dimension = &parent_input->dimension.dimension;
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
variables[2].value = parent_dimension->x;
|
||||
variables[3].value = parent_dimension->y;
|
||||
variables[4].value = parent_dimension->width;
|
||||
variables[5].value = parent_dimension->height;
|
||||
}
|
||||
|
||||
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
||||
|
||||
// First set all values, which we can set immediately
|
||||
for (uint32 i = 0; i < group->attribute_count; ++i) {
|
||||
switch (attributes[i].attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_POSITION_X:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
||||
case UI_ATTRIBUTE_TYPE_POSITION_Y:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
||||
ui_theme_assign_dimension(&label->dimension, &attributes[i], 6, variables);
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_FONT_NAME:
|
||||
case UI_ATTRIBUTE_TYPE_FONT_COLOR:
|
||||
case UI_ATTRIBUTE_TYPE_FONT_SIZE:
|
||||
case UI_ATTRIBUTE_TYPE_FONT_WEIGHT:
|
||||
case UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT: {
|
||||
ui_theme_assign_font(&label->font, &attributes[i]);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 ui_label_element_update(UILayout* layout, UIElement* element)
|
||||
|
|
|
|||
292
ui/UILayout.cpp
292
ui/UILayout.cpp
|
|
@ -15,6 +15,7 @@
|
|||
#include "UIElementType.h"
|
||||
#include "UIInput.h"
|
||||
#include "UILabel.h"
|
||||
#include "UIWindow.h"
|
||||
|
||||
// @todo We should add some asserts that ensure that the respective structs at least start at a 4byte memory alignment
|
||||
|
||||
|
|
@ -39,11 +40,14 @@ void ui_layout_count_direct_children(UIElement* __restrict element, const char*
|
|||
str_move_past(&pos, '\n');
|
||||
|
||||
continue;
|
||||
} else if (level <= parent_level || !str_is_alphanum(*pos)) {
|
||||
} else if (level <= parent_level) {
|
||||
// We are no longer inside of element
|
||||
str_move_past(&pos, '\n');
|
||||
|
||||
break;
|
||||
} else if (is_eol(pos)) {
|
||||
pos += is_eol(pos);
|
||||
continue;
|
||||
}
|
||||
|
||||
str_move_past(&pos, '\n');
|
||||
|
|
@ -127,6 +131,9 @@ void layout_from_file_txt(
|
|||
while (*pos != '\0') {
|
||||
// Skip all white spaces
|
||||
str_skip_empty(&pos);
|
||||
if (*pos == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
++temp_element_count;
|
||||
|
||||
|
|
@ -153,7 +160,7 @@ void layout_from_file_txt(
|
|||
|
||||
// Create root element
|
||||
UIElement* root = (UIElement *) element_data;
|
||||
hashmap_insert(&layout->hash_map, ":root", (int32) (element_data - layout->data));
|
||||
hashmap_insert(&layout->hash_map, "root", (int32) (element_data - layout->data));
|
||||
ui_layout_count_direct_children(root, pos, -4);
|
||||
|
||||
// NOTE: The root element cannot have any animations or vertices
|
||||
|
|
@ -288,55 +295,55 @@ static
|
|||
void ui_layout_serialize_element(
|
||||
HashEntryInt32* entry,
|
||||
byte* data,
|
||||
byte** pos
|
||||
byte** out
|
||||
) {
|
||||
// @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);
|
||||
**out = element->state_flag;
|
||||
*out += sizeof(element->state_flag);
|
||||
|
||||
**pos = element->type;
|
||||
*pos += sizeof(element->type);
|
||||
**out = element->type;
|
||||
*out += sizeof(element->type);
|
||||
|
||||
**pos = element->style_old;
|
||||
*pos += sizeof(element->style_old);
|
||||
**out = element->style_old;
|
||||
*out += sizeof(element->style_old);
|
||||
|
||||
**pos = element->style_new;
|
||||
*pos += sizeof(element->style_new);
|
||||
**out = element->style_new;
|
||||
*out += sizeof(element->style_new);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->parent);
|
||||
*pos += sizeof(element->parent);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(element->parent);
|
||||
*out += sizeof(element->parent);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->state);
|
||||
*pos += sizeof(element->state);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(element->state);
|
||||
*out += 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]);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(element->style_types[i]);
|
||||
*out += sizeof(element->style_types[i]);
|
||||
}
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->children_count);
|
||||
*pos += sizeof(element->children_count);
|
||||
*((uint16 *) *out) = SWAP_ENDIAN_LITTLE(element->children_count);
|
||||
*out += 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);
|
||||
memset(*out, 0, sizeof(element->animation_state));
|
||||
*out += sizeof(element->animation_state);
|
||||
*/
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->animation_count);
|
||||
*pos += sizeof(element->animation_count);
|
||||
*((uint16 *) *out) = SWAP_ENDIAN_LITTLE(element->animation_count);
|
||||
*out += sizeof(element->animation_count);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->animations);
|
||||
*pos += sizeof(element->animations);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(element->animations);
|
||||
*out += sizeof(element->animations);
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(element->vertex_count);
|
||||
*pos += sizeof(element->vertex_count);
|
||||
*((uint16 *) *out) = SWAP_ENDIAN_LITTLE(element->vertex_count);
|
||||
*out += sizeof(element->vertex_count);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(element->vertices_active);
|
||||
*pos += sizeof(element->vertices_active);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(element->vertices_active);
|
||||
*out += sizeof(element->vertices_active);
|
||||
|
||||
// Output dynamic length content directly after UIElement
|
||||
//
|
||||
|
|
@ -347,19 +354,19 @@ void ui_layout_serialize_element(
|
|||
// 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
|
||||
// Of course, updating the reference values (uint32) will be challenging since the file out 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);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(children[i]);
|
||||
*out += sizeof(*children);
|
||||
}
|
||||
|
||||
// State element data e.g. UIInputState
|
||||
ui_layout_serialize_element_state(element->type, data + element->state, pos);
|
||||
ui_layout_serialize_element_state(element->type, data + element->state, out);
|
||||
|
||||
// detailed element data/style_types e.g. UIInput
|
||||
// When you create a layout this is should only contain the default style type
|
||||
|
|
@ -370,32 +377,32 @@ void ui_layout_serialize_element(
|
|||
continue;
|
||||
}
|
||||
|
||||
ui_layout_serialize_element_detail(element->type, data + element->style_types[i], pos);
|
||||
ui_layout_serialize_element_detail(element->type, data + element->style_types[i], out);
|
||||
}
|
||||
|
||||
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);
|
||||
**out = animations[i].style_old;
|
||||
*out += sizeof(animations[i].style_old);
|
||||
|
||||
**pos = animations[i].style_new;
|
||||
*pos += sizeof(animations[i].style_new);
|
||||
**out = animations[i].style_new;
|
||||
*out += sizeof(animations[i].style_new);
|
||||
|
||||
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(animations[i].duration);
|
||||
*pos += sizeof(animations[i].duration);
|
||||
*((uint16 *) *out) = SWAP_ENDIAN_LITTLE(animations[i].duration);
|
||||
*out += sizeof(animations[i].duration);
|
||||
|
||||
**pos = animations[i].anim_type;
|
||||
*pos += sizeof(animations[i].anim_type);
|
||||
**out = animations[i].anim_type;
|
||||
*out += sizeof(animations[i].anim_type);
|
||||
|
||||
**pos = animations[i].keyframe_count;
|
||||
*pos += sizeof(animations[i].keyframe_count);
|
||||
**out = animations[i].keyframe_count;
|
||||
*out += 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);
|
||||
ui_layout_serialize_element_detail(element->type, keyframes + j * element_style_type_size, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -404,30 +411,23 @@ int32 layout_to_data(
|
|||
const UILayout* __restrict layout,
|
||||
byte* __restrict data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
byte* out = data;
|
||||
|
||||
// version
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_LAYOUT_VERSION);
|
||||
pos += sizeof(int32);
|
||||
*((int32 *) out) = SWAP_ENDIAN_LITTLE(UI_LAYOUT_VERSION);
|
||||
out += sizeof(int32);
|
||||
|
||||
// hashmap
|
||||
byte* start = pos;
|
||||
pos += hashmap_dump(&layout->hash_map, pos);
|
||||
out += hashmap_dump(&layout->hash_map, out);
|
||||
|
||||
// UIElement data
|
||||
uint32 chunk_id = 0;
|
||||
chunk_iterate_start(&layout->hash_map.buf, chunk_id)
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element((ChunkMemory *) &layout->hash_map.buf, chunk_id);
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_serialize_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
ui_layout_serialize_element(entry, layout->data, &out);
|
||||
chunk_iterate_end;
|
||||
|
||||
return (int32) (max_pos - data);
|
||||
return (int32) (out - data);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -449,54 +449,53 @@ void ui_layout_unserialize_element_detail(UIElementType type, void* __restrict d
|
|||
}
|
||||
|
||||
static
|
||||
void ui_layout_parse_element(HashEntryInt32* entry, byte* data, const byte** pos)
|
||||
void ui_layout_parse_element(HashEntryInt32* entry, byte* data, const byte** in)
|
||||
{
|
||||
// @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->state_flag = **in;
|
||||
*in += sizeof(element->state_flag);
|
||||
|
||||
element->type = (UIElementType) **pos;
|
||||
*pos += sizeof(element->type);
|
||||
element->type = (UIElementType) **in;
|
||||
*in += sizeof(element->type);
|
||||
|
||||
element->style_old = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_old);
|
||||
element->style_old = (UIStyleType) **in;
|
||||
*in += sizeof(element->style_old);
|
||||
|
||||
element->style_new = (UIStyleType) **pos;
|
||||
*pos += sizeof(element->style_new);
|
||||
element->style_new = (UIStyleType) **in;
|
||||
*in += sizeof(element->style_new);
|
||||
|
||||
element->parent = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->parent);
|
||||
element->parent = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += sizeof(element->parent);
|
||||
|
||||
element->state = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->state);
|
||||
element->state = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += 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->style_types[i] = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += sizeof(element->style_types[i]);
|
||||
}
|
||||
|
||||
element->children_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->children_count);
|
||||
element->children_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *in));
|
||||
*in += 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->animation_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *in));
|
||||
*in += sizeof(element->animation_count);
|
||||
|
||||
element->animations = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->animations);
|
||||
element->animations = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += sizeof(element->animations);
|
||||
|
||||
element->vertex_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(element->vertex_count);
|
||||
element->vertex_count = SWAP_ENDIAN_LITTLE(*((uint16 *) *in));
|
||||
*in += sizeof(element->vertex_count);
|
||||
|
||||
// @bug this needs to be changed?
|
||||
element->vertices_active = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(element->vertices_active);
|
||||
element->vertices_active = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += 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
|
||||
|
|
@ -512,12 +511,12 @@ void ui_layout_parse_element(HashEntryInt32* entry, byte* data, const byte** pos
|
|||
// 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);
|
||||
children[i] = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += sizeof(*children);
|
||||
}
|
||||
|
||||
// State element data e.g. UIInputState
|
||||
ui_layout_unserialize_element_state(element->type, data + element->state, pos);
|
||||
ui_layout_unserialize_element_state(element->type, data + element->state, in);
|
||||
|
||||
// detailed element data/style_types e.g. UIInput
|
||||
for (int32 i = 0; i < UI_STYLE_TYPE_SIZE; ++i) {
|
||||
|
|
@ -525,32 +524,32 @@ void ui_layout_parse_element(HashEntryInt32* entry, byte* data, const byte** pos
|
|||
continue;
|
||||
}
|
||||
|
||||
ui_layout_unserialize_element_detail(element->type, data + element->style_types[i], pos);
|
||||
ui_layout_unserialize_element_detail(element->type, data + element->style_types[i], in);
|
||||
}
|
||||
|
||||
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_old = (UIStyleType) **in;
|
||||
*in += sizeof(animations[i].style_old);
|
||||
|
||||
animations[i].style_new = (UIStyleType) **pos;
|
||||
*pos += sizeof(animations[i].style_new);
|
||||
animations[i].style_new = (UIStyleType) **in;
|
||||
*in += sizeof(animations[i].style_new);
|
||||
|
||||
animations[i].duration = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
||||
*pos += sizeof(animations[i].duration);
|
||||
animations[i].duration = SWAP_ENDIAN_LITTLE(*((uint16 *) *in));
|
||||
*in += sizeof(animations[i].duration);
|
||||
|
||||
animations[i].anim_type = (AnimationEaseType) **pos;
|
||||
*pos += sizeof(animations[i].anim_type);
|
||||
animations[i].anim_type = (AnimationEaseType) **in;
|
||||
*in += sizeof(animations[i].anim_type);
|
||||
|
||||
animations[i].keyframe_count = **pos;
|
||||
*pos += sizeof(animations[i].keyframe_count);
|
||||
animations[i].keyframe_count = **in;
|
||||
*in += 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);
|
||||
ui_layout_unserialize_element_detail(element->type, keyframes + j * element_style_type_size, in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -561,33 +560,26 @@ int32 layout_from_data(
|
|||
const byte* __restrict data,
|
||||
UILayout* __restrict layout
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = pos;
|
||||
const byte* in = data;
|
||||
|
||||
int32 version = *((int32 *) pos);
|
||||
pos += sizeof(version);
|
||||
int32 version = SWAP_ENDIAN_LITTLE(*((int32 *) in));
|
||||
in += 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);
|
||||
hashmap_create(&layout->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint32 *) in)), sizeof(HashEntryInt32), layout->data);
|
||||
|
||||
const byte* start = data;
|
||||
pos += hashmap_load(&layout->hash_map, pos);
|
||||
in += hashmap_load(&layout->hash_map, in);
|
||||
|
||||
// layout data
|
||||
// @performance We are iterating the hashmap twice (hashmap_load and here)
|
||||
uint32 chunk_id = 0;
|
||||
chunk_iterate_start(&layout->hash_map.buf, chunk_id)
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element((ChunkMemory *) &layout->hash_map.buf, chunk_id);
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_layout_parse_element(entry, layout->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
ui_layout_parse_element(entry, layout->data, &in);
|
||||
chunk_iterate_end;
|
||||
|
||||
layout->layout_size = (uint32) (max_pos - data);
|
||||
layout->layout_size = (uint32) (in - data);
|
||||
|
||||
return (int32) layout->layout_size;
|
||||
}
|
||||
|
|
@ -630,7 +622,8 @@ void layout_from_theme(
|
|||
continue;
|
||||
}
|
||||
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, style_entry->key);
|
||||
// +1 to skip '#' or '.'
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, style_entry->key + 1);
|
||||
if (!entry) {
|
||||
// Couldn't find the base element
|
||||
continue;
|
||||
|
|
@ -643,6 +636,16 @@ void layout_from_theme(
|
|||
|
||||
// @todo Continue implementation
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_LABEL: {
|
||||
ui_label_state_populate(group, (UILabelState *) (layout->data + element->state));
|
||||
ui_label_element_populate(
|
||||
layout,
|
||||
group,
|
||||
(UILabel *) (layout->data + element->style_types[UI_STYLE_TYPE_DEFAULT]),
|
||||
parent,
|
||||
variables
|
||||
);
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_state_populate(group, (UIInputState *) (layout->data + element->state));
|
||||
ui_input_element_populate(
|
||||
|
|
@ -653,6 +656,16 @@ void layout_from_theme(
|
|||
variables
|
||||
);
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_VIEW_WINDOW: {
|
||||
ui_window_state_populate(group, (UIWindowState *) (layout->data + element->state));
|
||||
ui_window_element_populate(
|
||||
layout,
|
||||
group,
|
||||
(UIWindow *) (layout->data + element->style_types[UI_STYLE_TYPE_DEFAULT]),
|
||||
parent,
|
||||
variables
|
||||
);
|
||||
} break;
|
||||
}
|
||||
chunk_iterate_end;
|
||||
|
||||
|
|
@ -674,7 +687,8 @@ void layout_from_theme(
|
|||
}
|
||||
|
||||
char pure_name[HASH_MAP_MAX_KEY_LENGTH];
|
||||
str_copy_until(style_entry->key, pure_name, ':');
|
||||
// +1 to skip '#' or '.'
|
||||
str_copy_until(style_entry->key + 1, pure_name, ':');
|
||||
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, pure_name);
|
||||
if (!entry) {
|
||||
|
|
@ -704,6 +718,15 @@ void layout_from_theme(
|
|||
|
||||
// @todo Continue implementation
|
||||
switch (element->type) {
|
||||
case UI_ELEMENT_TYPE_LABEL: {
|
||||
ui_label_element_populate(
|
||||
layout,
|
||||
group,
|
||||
(UILabel *) (layout->data + element->style_types[style_type]),
|
||||
parent,
|
||||
variables
|
||||
);
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_INPUT: {
|
||||
ui_input_element_populate(
|
||||
layout,
|
||||
|
|
@ -713,6 +736,15 @@ void layout_from_theme(
|
|||
variables
|
||||
);
|
||||
} break;
|
||||
case UI_ELEMENT_TYPE_VIEW_WINDOW: {
|
||||
ui_window_element_populate(
|
||||
layout,
|
||||
group,
|
||||
(UIWindow *) (layout->data + element->style_types[style_type]),
|
||||
parent,
|
||||
variables
|
||||
);
|
||||
} break;
|
||||
}
|
||||
chunk_iterate_end;
|
||||
}
|
||||
|
|
@ -769,7 +801,21 @@ void ui_layout_update(UILayout* layout, UIElement* element) {
|
|||
}
|
||||
}
|
||||
|
||||
// @question We might want to change the names of update/render
|
||||
// In some cases it's more like cache/render
|
||||
// @question We might want to allow rendering without caching (currently we always rely on the cache)
|
||||
// This increases our RAM requirements (every vertex is in cache AND in the asset AND in VRAM)
|
||||
// However, this also has the benefit of allowing us to ONLY re-render individual elements
|
||||
|
||||
// @performance In our immediate mode solution we decided the update/render based on a bitfield
|
||||
// That is very efficient, the code below isn't doing that maybe there is a way to implement that here as well?
|
||||
// I don't think so but it would be nice
|
||||
// This function caches the vertices
|
||||
void ui_layout_update_dfs(UILayout* layout, UIElement* element, byte category = 0) {
|
||||
if (element->type == UI_ELEMENT_TYPE_MANUAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (element->category == category) {
|
||||
ui_layout_update(layout, element);
|
||||
}
|
||||
|
|
@ -785,16 +831,21 @@ uint32 ui_layout_render_dfs(
|
|||
UIElement* element, Vertex3DTextureColor* __restrict vertices,
|
||||
byte category = 0
|
||||
) {
|
||||
if (element->type == UI_ELEMENT_TYPE_MANUAL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 vertex_count = 0;
|
||||
|
||||
if (element->category == category) {
|
||||
if (element->vertex_count && 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;
|
||||
}
|
||||
|
||||
uint32* children = (uint32 *) (element + 1);
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
uint32 child_vertex_count = ui_layout_render_dfs(layout, element, vertices, category);
|
||||
uint32 child_vertex_count = ui_layout_render_dfs(layout, (UIElement *) (layout->data + children[i]), vertices, category);
|
||||
vertices += child_vertex_count;
|
||||
vertex_count += child_vertex_count;
|
||||
}
|
||||
|
|
@ -807,6 +858,10 @@ uint32 ui_layout_update_render_dfs(
|
|||
UIElement* __restrict element, Vertex3DTextureColor* __restrict vertices,
|
||||
byte category = 0
|
||||
) {
|
||||
if (element->type == UI_ELEMENT_TYPE_MANUAL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 vertex_count = 0;
|
||||
|
||||
if (element->category == category) {
|
||||
|
|
@ -817,8 +872,9 @@ uint32 ui_layout_update_render_dfs(
|
|||
vertex_count += element->vertex_count;
|
||||
}
|
||||
|
||||
uint32* children = (uint32 *) (element + 1);
|
||||
for (int32 i = 0; i < element->children_count; ++i) {
|
||||
uint32 child_vertex_count = ui_layout_update_render_dfs(layout, element, vertices, category);
|
||||
uint32 child_vertex_count = ui_layout_update_render_dfs(layout, (UIElement *) (layout->data + children[i]), vertices, category);
|
||||
vertices += child_vertex_count;
|
||||
vertex_count += child_vertex_count;
|
||||
}
|
||||
|
|
|
|||
125
ui/UITheme.h
125
ui/UITheme.h
|
|
@ -204,46 +204,46 @@ void theme_from_file_txt(
|
|||
}
|
||||
|
||||
static inline
|
||||
void ui_theme_parse_group(HashEntryInt32* entry, byte* data, const byte** pos)
|
||||
void ui_theme_parse_group(HashEntryInt32* entry, byte* data, const byte** in)
|
||||
{
|
||||
// @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);
|
||||
|
||||
group->attribute_count = SWAP_ENDIAN_LITTLE(*((uint32 *) *pos));
|
||||
*pos += sizeof(group->attribute_count);
|
||||
group->attribute_count = SWAP_ENDIAN_LITTLE(*((uint32 *) *in));
|
||||
*in += sizeof(group->attribute_count);
|
||||
|
||||
UIAttribute* attribute_reference = (UIAttribute *) (group + 1);
|
||||
|
||||
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);
|
||||
attribute_reference[j].attribute_id = (UIAttributeType) SWAP_ENDIAN_LITTLE(*((uint16 *) *in));
|
||||
*in += sizeof(attribute_reference[j].attribute_id);
|
||||
|
||||
attribute_reference[j].datatype = *((UIAttributeDataType *) *pos);
|
||||
*pos += sizeof(attribute_reference[j].datatype);
|
||||
attribute_reference[j].datatype = *((UIAttributeDataType *) *in);
|
||||
*in += sizeof(attribute_reference[j].datatype);
|
||||
|
||||
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);
|
||||
attribute_reference[j].value_int = SWAP_ENDIAN_LITTLE(*((int32 *) *in));
|
||||
*in += 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);
|
||||
attribute_reference[j].value_float = SWAP_ENDIAN_LITTLE(*((f32 *) *in));
|
||||
*in += 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);
|
||||
memcpy(attribute_reference[j].value_str, *in, sizeof(attribute_reference[j].value_str));
|
||||
*in += 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);
|
||||
attribute_reference[j].value_v4_f32.x = SWAP_ENDIAN_LITTLE(*((f32 *) *in));
|
||||
*in += sizeof(attribute_reference[j].value_v4_f32.x);
|
||||
|
||||
attribute_reference[j].value_v4_f32.y = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.y);
|
||||
attribute_reference[j].value_v4_f32.y = SWAP_ENDIAN_LITTLE(*((f32 *) *in));
|
||||
*in += sizeof(attribute_reference[j].value_v4_f32.y);
|
||||
|
||||
attribute_reference[j].value_v4_f32.z = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.z);
|
||||
attribute_reference[j].value_v4_f32.z = SWAP_ENDIAN_LITTLE(*((f32 *) *in));
|
||||
*in += sizeof(attribute_reference[j].value_v4_f32.z);
|
||||
|
||||
attribute_reference[j].value_v4_f32.w = SWAP_ENDIAN_LITTLE(*((f32 *) *pos));
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.w);
|
||||
attribute_reference[j].value_v4_f32.w = SWAP_ENDIAN_LITTLE(*((f32 *) *in));
|
||||
*in += sizeof(attribute_reference[j].value_v4_f32.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,19 +267,17 @@ int32 theme_from_data(
|
|||
const byte* __restrict data,
|
||||
UIThemeStyle* __restrict theme
|
||||
) {
|
||||
const byte* pos = data;
|
||||
const byte* max_pos = data;
|
||||
const byte* in = data;
|
||||
|
||||
int32 version = *((int32 *) pos);
|
||||
pos += sizeof(version);
|
||||
int32 version = SWAP_ENDIAN_LITTLE(*((int32 *) in));
|
||||
in += 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(&theme->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint32 *) pos)), sizeof(HashEntryInt32), theme->data);
|
||||
hashmap_create(&theme->hash_map, (int32) SWAP_ENDIAN_LITTLE(*((uint32 *) in)), sizeof(HashEntryInt32), theme->data);
|
||||
|
||||
const byte* start = theme->hash_map.buf.memory;
|
||||
pos += hashmap_load(&theme->hash_map, pos);
|
||||
in += hashmap_load(&theme->hash_map, in);
|
||||
|
||||
// theme data
|
||||
// Layout: first load the size of the group, then load the individual attributes
|
||||
|
|
@ -287,16 +285,10 @@ int32 theme_from_data(
|
|||
uint32 chunk_id = 0;
|
||||
chunk_iterate_start(&theme->hash_map.buf, chunk_id)
|
||||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element((ChunkMemory *) &theme->hash_map.buf, chunk_id);
|
||||
|
||||
// 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);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
ui_theme_parse_group(entry, theme->data, &in);
|
||||
chunk_iterate_end;
|
||||
|
||||
return (int32) (max_pos - data);
|
||||
return (int32) (in - data);
|
||||
}
|
||||
|
||||
// Calculates the maximum theme size
|
||||
|
|
@ -311,45 +303,45 @@ int64 theme_data_size_max(const UIThemeStyle* theme)
|
|||
|
||||
// @todo Why do even need **pos, shouldn't it just be *pos
|
||||
static inline
|
||||
void ui_theme_serialize_group(const HashEntryInt32* entry, byte* data, byte** pos)
|
||||
void ui_theme_serialize_group(const HashEntryInt32* entry, byte* data, byte** out)
|
||||
{
|
||||
// @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);
|
||||
|
||||
*((uint32 *) *pos) = SWAP_ENDIAN_LITTLE(group->attribute_count);
|
||||
*pos += sizeof(group->attribute_count);
|
||||
*((uint32 *) *out) = SWAP_ENDIAN_LITTLE(group->attribute_count);
|
||||
*out += sizeof(group->attribute_count);
|
||||
|
||||
UIAttribute* attribute_reference = (UIAttribute *) (group + 1);
|
||||
|
||||
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);
|
||||
*((uint16 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].attribute_id);
|
||||
*out += sizeof(attribute_reference[j].attribute_id);
|
||||
|
||||
*((byte *) *pos) = attribute_reference[j].datatype;
|
||||
*pos += sizeof(attribute_reference[j].datatype);
|
||||
*((byte *) *out) = attribute_reference[j].datatype;
|
||||
*out += sizeof(attribute_reference[j].datatype);
|
||||
|
||||
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);
|
||||
*((int32 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_int);
|
||||
*out += 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);
|
||||
*((f32 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_float);
|
||||
*out += 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);
|
||||
memcpy(*out, attribute_reference[j].value_str, sizeof(attribute_reference[j].value_str));
|
||||
*out += 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 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.x);
|
||||
*out += sizeof(attribute_reference[j].value_v4_f32.x);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.y);
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.y);
|
||||
*((f32 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.y);
|
||||
*out += sizeof(attribute_reference[j].value_v4_f32.y);
|
||||
|
||||
*((f32 *) *pos) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.z);
|
||||
*pos += sizeof(attribute_reference[j].value_v4_f32.z);
|
||||
*((f32 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.z);
|
||||
*out += 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);
|
||||
*((f32 *) *out) = SWAP_ENDIAN_LITTLE(attribute_reference[j].value_v4_f32.w);
|
||||
*out += sizeof(attribute_reference[j].value_v4_f32.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -373,31 +365,24 @@ int32 theme_to_data(
|
|||
const UIThemeStyle* __restrict theme,
|
||||
byte* __restrict data
|
||||
) {
|
||||
byte* pos = data;
|
||||
byte* max_pos = data;
|
||||
byte* out = data;
|
||||
|
||||
// version
|
||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_THEME_VERSION);
|
||||
pos += sizeof(int32);
|
||||
*((int32 *) out) = SWAP_ENDIAN_LITTLE(UI_THEME_VERSION);
|
||||
out += sizeof(int32);
|
||||
|
||||
// hashmap
|
||||
byte* start = pos;
|
||||
pos += hashmap_dump(&theme->hash_map, pos);
|
||||
out += hashmap_dump(&theme->hash_map, out);
|
||||
|
||||
// theme data
|
||||
// Layout: first save the size of the group, then save the individual attributes
|
||||
uint32 chunk_id = 0;
|
||||
chunk_iterate_start(&theme->hash_map.buf, chunk_id)
|
||||
const HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element((ChunkMemory *) &theme->hash_map.buf, chunk_id);
|
||||
|
||||
pos = start + entry->value;
|
||||
ui_theme_serialize_group(entry, theme->data, &pos);
|
||||
if (pos > max_pos) {
|
||||
max_pos = pos;
|
||||
}
|
||||
ui_theme_serialize_group(entry, theme->data, &out);
|
||||
chunk_iterate_end;
|
||||
|
||||
return (int32) (max_pos - data);
|
||||
return (int32) (out - data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include "attribute/UIAttributeBackground.h"
|
||||
#include "attribute/UIAttributeDimension.h"
|
||||
#include "UIAnimation.h"
|
||||
#include "UIPanel.h"
|
||||
#include "UIStyleType.h"
|
||||
|
||||
struct UIWindowState {
|
||||
|
|
@ -29,4 +30,78 @@ struct UIWindow {
|
|||
UIWindow* styles[UI_STYLE_TYPE_SIZE];
|
||||
};
|
||||
|
||||
void ui_window_state_serialize(const UIWindowState* __restrict state, byte** __restrict pos)
|
||||
{
|
||||
(void *) state;
|
||||
(void **) pos;
|
||||
}
|
||||
|
||||
void ui_window_state_unserialize(UIWindowState* __restrict state, const byte** __restrict pos)
|
||||
{
|
||||
(void *) state;
|
||||
(void **) pos;
|
||||
}
|
||||
|
||||
void ui_window_state_populate(const UIAttributeGroup* __restrict group, UIWindowState* __restrict state)
|
||||
{
|
||||
(void *) group;
|
||||
(void *) state;
|
||||
}
|
||||
|
||||
void ui_window_element_serialize(const UIWindow* __restrict details, byte** __restrict pos)
|
||||
{
|
||||
(void *) details;
|
||||
(void **) pos;
|
||||
}
|
||||
|
||||
void ui_window_element_unserialize(UIWindow* __restrict details, const byte** __restrict pos)
|
||||
{
|
||||
(void *) details;
|
||||
(void **) pos;
|
||||
}
|
||||
|
||||
void ui_window_element_populate(
|
||||
UILayout* layout,
|
||||
const UIAttributeGroup* __restrict group,
|
||||
UIWindow* __restrict window,
|
||||
UIElement* parent,
|
||||
EvaluatorVariable* __restrict variables
|
||||
) {
|
||||
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
|
||||
|
||||
v4_f32* parent_dimension;
|
||||
switch (parent->type) {
|
||||
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();
|
||||
}
|
||||
|
||||
variables[2].value = parent_dimension->x;
|
||||
variables[3].value = parent_dimension->y;
|
||||
variables[4].value = parent_dimension->width;
|
||||
variables[5].value = parent_dimension->height;
|
||||
}
|
||||
|
||||
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
||||
|
||||
// First set all values, which we can set immediately
|
||||
for (uint32 i = 0; i < group->attribute_count; ++i) {
|
||||
switch (attributes[i].attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_POSITION_X:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
||||
case UI_ATTRIBUTE_TYPE_POSITION_Y:
|
||||
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
||||
ui_theme_assign_dimension(&window->dimension, &attributes[i], 6, variables);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include "../../compiler/CompilerUtils.h"
|
||||
#include "UIAttributeType.h"
|
||||
#include "UIAttributeDimension.h"
|
||||
#include "UIAttributeFont.h"
|
||||
|
||||
enum UIAttributeDataType : byte {
|
||||
UI_ATTRIBUTE_DATA_TYPE_INT,
|
||||
|
|
@ -24,6 +25,7 @@ struct UIAttribute {
|
|||
// @performance The string makes this struct really large when it is not needed in 95% of the cases
|
||||
char value_str[32];
|
||||
int32 value_int;
|
||||
uint32 value_uint;
|
||||
f32 value_float;
|
||||
v4_f32 value_v4_f32;
|
||||
};
|
||||
|
|
@ -218,19 +220,21 @@ void ui_attribute_parse_value(UIAttribute* attr, const char* attribute_name, con
|
|||
hexstr_to_rgba(&attr->value_v4_f32, pos);
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_V4_F32;
|
||||
} else {
|
||||
str_copy_until(attr->value_str, value, "\r\n");
|
||||
str_copy_until(value, attr->value_str, "\r\n");
|
||||
attr->datatype = UI_ATTRIBUTE_DATA_TYPE_STR;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_theme_assign_f32(f32* a, const UIAttribute* attr, int32 variable_count, const EvaluatorVariable* variables)
|
||||
void ui_theme_assign_f32(f32* a, const UIAttribute* attr, int32 variable_count = 0, const EvaluatorVariable* variables = NULL)
|
||||
{
|
||||
if (attr->datatype == UI_ATTRIBUTE_DATA_TYPE_INT) {
|
||||
*a = (f32) attr->value_int;
|
||||
} else if (attr->datatype == UI_ATTRIBUTE_DATA_TYPE_F32) {
|
||||
*a = (f32) attr->value_float;
|
||||
} else if (attr->datatype == UI_ATTRIBUTE_DATA_TYPE_STR) {
|
||||
ASSERT_SIMPLE(str_length(attr->value_str) > 0);
|
||||
|
||||
char value[32];
|
||||
memcpy(value, attr->value_str, ARRAY_COUNT(attr->value_str));
|
||||
*a = (f32) evaluator_evaluate(value, variable_count, variables);
|
||||
|
|
@ -259,4 +263,29 @@ void ui_theme_assign_dimension(UIAttributeDimension* dimension, const UIAttribut
|
|||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void ui_theme_assign_font(UIAttributeFont* font, const UIAttribute* attr)
|
||||
{
|
||||
switch (attr->attribute_id) {
|
||||
case UI_ATTRIBUTE_TYPE_FONT_NAME: {
|
||||
UNREACHABLE();
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_FONT_COLOR: {
|
||||
font->color = attr->value_uint;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_FONT_SIZE: {
|
||||
font->size = attr->value_float;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_FONT_WEIGHT: {
|
||||
font->weight = attr->value_float;
|
||||
} break;
|
||||
case UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT: {
|
||||
font->line_height = attr->value_float;
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#define TOS_UI_ATTRIBUTE_FONT_H
|
||||
|
||||
#include "../../stdlib/Types.h"
|
||||
#include "UIAttributeShadow.h"
|
||||
|
||||
enum UIFontDecoration : byte {
|
||||
UI_FONT_DECORATION_UNDERLINE = 1 << 0,
|
||||
|
|
@ -10,6 +11,7 @@ enum UIFontDecoration : byte {
|
|||
|
||||
struct UIAttributeFont {
|
||||
f32 size;
|
||||
f32 line_height;
|
||||
uint32 color;
|
||||
f32 weight;
|
||||
UIAttributeShadow shadow_outer;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ enum UIAttributeType : uint16 {
|
|||
UI_ATTRIBUTE_TYPE_CONTENT_ALIGN_V,
|
||||
|
||||
UI_ATTRIBUTE_TYPE_FONT_NAME,
|
||||
UI_ATTRIBUTE_TYPE_FONT_COLOR_INDEX,
|
||||
UI_ATTRIBUTE_TYPE_FONT_COLOR,
|
||||
UI_ATTRIBUTE_TYPE_FONT_SIZE,
|
||||
UI_ATTRIBUTE_TYPE_FONT_WEIGHT,
|
||||
|
|
|
|||
|
|
@ -1353,7 +1353,7 @@ void hexstr_to_rgba(v4_f32* rgba, const char* hex)
|
|||
}
|
||||
|
||||
inline constexpr
|
||||
void str_pad(const char* input, char* output, char pad, size_t len) {
|
||||
void str_pad_right(const char* input, char* output, char pad, size_t len) {
|
||||
size_t i = 0;
|
||||
for (; i < len && input[i] != '\0'; ++i) {
|
||||
output[i] = input[i];
|
||||
|
|
@ -1364,6 +1364,20 @@ void str_pad(const char* input, char* output, char pad, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
inline constexpr
|
||||
void str_pad_left(const char* input, char* output, char pad, size_t len) {
|
||||
size_t input_len = str_length(input);
|
||||
|
||||
size_t i = 0;
|
||||
for (; i < len - input_len; ++i) {
|
||||
*output++ = pad;
|
||||
}
|
||||
|
||||
for (; i < len && input[i] != '\0'; ++i) {
|
||||
output[i] = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
f32 str_to_float(const char* str, const char** pos = NULL)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user