mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-11 03:08:41 +00:00
huge re-write of ams. impl. of command buffer, ...
This commit is contained in:
parent
2521f5f2e4
commit
2ecb47117b
|
|
@ -24,6 +24,234 @@ f32 lerp(f32 a, f32 b, f32 t)
|
||||||
f32 smoothstep(f32 t) {
|
f32 smoothstep(f32 t) {
|
||||||
return t * t * (3 - 2 * t);
|
return t * t * (3 - 2 * t);
|
||||||
}
|
}
|
||||||
|
inline
|
||||||
|
f32 anim_discrete(f32 t) {
|
||||||
|
return t >= 1.0f ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_linear(f32 t) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_sine(f32 t) {
|
||||||
|
return 1.0f - cosf((t * OMS_PI) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_sine(f32 t) {
|
||||||
|
return sinf((t * OMS_PI) / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_sine(f32 t) {
|
||||||
|
return -(cosf(OMS_PI * t) - 1) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_quad(f32 t) {
|
||||||
|
return t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_quad(f32 t) {
|
||||||
|
return 1.0f - (1.0f - t) * (1.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_quad(f32 t) {
|
||||||
|
return t < 0.5f
|
||||||
|
? 2 * t * t
|
||||||
|
: 1.0f - powf(-2 * t + 2, 2) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_cubic(f32 t) {
|
||||||
|
return t * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_cubic(f32 t) {
|
||||||
|
return 1.0f - powf(1.0f - t, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_cubic(f32 t) {
|
||||||
|
return t < 0.5f
|
||||||
|
? 4 * t * t * t
|
||||||
|
: 1.0f - powf(-2 * t + 2, 3) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_quart(f32 t) {
|
||||||
|
return t * t * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_quart(f32 t) {
|
||||||
|
return 1.0f - powf(1.0f - t, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_perlin(f32 t) {
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_quart(f32 t) {
|
||||||
|
return t < 0.5f
|
||||||
|
? 8 * t * t * t * t
|
||||||
|
: 1.0f - powf(-2 * t + 2, 4) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_quint(f32 t) {
|
||||||
|
return t * t * t * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_quint(f32 t) {
|
||||||
|
return 1.0f - powf(1.0f - t, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_quint(f32 t) {
|
||||||
|
return t < 0.5f
|
||||||
|
? 16 * t * t * t * t * t
|
||||||
|
: 1.0f - powf(-2 * t + 2, 5) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_expo(f32 t) {
|
||||||
|
return t == 0.0f
|
||||||
|
? 0.0f
|
||||||
|
: powf(2, 10 * t - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_expo(f32 t) {
|
||||||
|
return t == 1.0f
|
||||||
|
? 1.0f
|
||||||
|
: 1.0f - powf(2, -10 * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_expo(f32 t) {
|
||||||
|
if (t == 0.0f || t == 1.0f) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t < 0.5f
|
||||||
|
? powf(2, 20 * t - 10) / 2.0f
|
||||||
|
: (2 - powf(2, -20 * t + 10)) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_circ(f32 t) {
|
||||||
|
return 1.0f - sqrtf(1.0f - powf(t, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_circ(f32 t) {
|
||||||
|
return sqrtf(1.0f - powf(t - 1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_circ(f32 t) {
|
||||||
|
return t < 0.5f
|
||||||
|
? (1.0f - sqrtf(1.0f - powf(2 * t, 2))) / 2.0f
|
||||||
|
: (sqrtf(1.0f - powf(-2 * t + 2, 2)) + 1) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_back(f32 t) {
|
||||||
|
const f32 c1 = 1.70158f;
|
||||||
|
const f32 c3 = c1 + 1.0f;
|
||||||
|
|
||||||
|
return c3 * t * t * t - c1 * t * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_back(f32 t) {
|
||||||
|
const f32 c1 = 1.70158f;
|
||||||
|
const f32 c3 = c1 + 1.0f;
|
||||||
|
|
||||||
|
return 1 + c3 * powf(t - 1, 3) + c1 * powf(t - 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_back(f32 t) {
|
||||||
|
const f32 c1 = 1.70158f;
|
||||||
|
const f32 c2 = c1 * 1.525f;
|
||||||
|
|
||||||
|
return t < 0.5f
|
||||||
|
? (powf(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2.0f
|
||||||
|
: (powf(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_elastic(f32 t) {
|
||||||
|
const f32 c4 = OMS_TWO_PI / 3;
|
||||||
|
|
||||||
|
if (t == 0.0f || t == 1.0f) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -powf(2, 10 * t - 10) * sinf((t * 10 - 10.75f) * c4);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_elastic(f32 t) {
|
||||||
|
const f32 c4 = OMS_TWO_PI / 3;
|
||||||
|
|
||||||
|
if (t == 0.0f || t == 1.0f) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
return powf(2, -10 * t) * sinf((t * 10 - 0.75f) * c4) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_elastic(f32 t) {
|
||||||
|
const f32 c5 = OMS_TWO_PI / 4.5f;
|
||||||
|
|
||||||
|
if (t == 0.0f || t == 1.0f) {
|
||||||
|
return t;
|
||||||
|
} else if (t < 0.5f) {
|
||||||
|
return -(powf(2, 20 * t - 10) * sinf((20 * t - 11.125f) * c5)) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (powf(2, -20 * t + 10) * sinf((20 * t - 11.125f) * c5)) / 2.0f + 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_out_bounce(f32 t) {
|
||||||
|
const f32 n1 = 7.5625f;
|
||||||
|
const f32 d1 = 2.75f;
|
||||||
|
|
||||||
|
if (t < 1.0f / d1) {
|
||||||
|
return n1 * t * t;
|
||||||
|
} else if (t < 2.0f / d1) {
|
||||||
|
return n1 * (t -= 1.5f / d1) * t + 0.75f;
|
||||||
|
} else if (t < 2.5f / d1) {
|
||||||
|
return n1 * (t -= 2.25f / d1) * t + 0.9375f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n1 * (t -= 2.625f / d1) * t + 0.984375f;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_bounce(f32 t) {
|
||||||
|
return 1.0f - anim_ease_out_bounce(1.0f - t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
f32 anim_ease_in_out_bounce(f32 t) {
|
||||||
|
return t < 0.5f
|
||||||
|
? (1.0f - anim_ease_out_bounce(1.0f - 2.0f * t)) / 2.0f
|
||||||
|
: (1.0f + anim_ease_out_bounce(2.0f * t - 1.0f)) / 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
f32 anim_ease(f32 t, AnimationEaseType type) {
|
f32 anim_ease(f32 t, AnimationEaseType type) {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
|
|
@ -125,233 +353,4 @@ f32 anim_ease(f32 t, AnimationEaseType type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_discrete(f32 t) {
|
|
||||||
return t >= 1.0f ? 1.0f : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_linear(f32 t) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_sine(f32 t) {
|
|
||||||
return 1 - cosf((t * OMS_PI) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_sine(f32 t) {
|
|
||||||
return sinf((t * OMS_PI) / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_sine(f32 t) {
|
|
||||||
return -(cosf(OMS_PI * t) - 1) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_quad(f32 t) {
|
|
||||||
return t * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_quad(f32 t) {
|
|
||||||
return 1 - (1 - t) * (1 - t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_quad(f32 t) {
|
|
||||||
return t < 0.5
|
|
||||||
? 2 * t * t
|
|
||||||
: 1 - pow(-2 * t + 2, 2) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_cubic(f32 t) {
|
|
||||||
return t * t * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_cubic(f32 t) {
|
|
||||||
return 1 - pow(1 - t, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_cubic(f32 t) {
|
|
||||||
return t < 0.5
|
|
||||||
? 4 * t * t * t
|
|
||||||
: 1 - pow(-2 * t + 2, 3) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_quart(f32 t) {
|
|
||||||
return t * t * t * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_quart(f32 t) {
|
|
||||||
return 1 - pow(1 - t, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_perlin(f32 t) {
|
|
||||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_quart(f32 t) {
|
|
||||||
return t < 0.5
|
|
||||||
? 8 * t * t * t * t
|
|
||||||
: 1 - pow(-2 * t + 2, 4) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_quint(f32 t) {
|
|
||||||
return t * t * t * t * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_quint(f32 t) {
|
|
||||||
return 1 - pow(1 - t, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_quint(f32 t) {
|
|
||||||
return t < 0.5
|
|
||||||
? 16 * t * t * t * t * t
|
|
||||||
: 1 - pow(-2 * t + 2, 5) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_expo(f32 t) {
|
|
||||||
return t == 0
|
|
||||||
? 0
|
|
||||||
: pow(2, 10 * t - 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_expo(f32 t) {
|
|
||||||
return t == 1
|
|
||||||
? 1
|
|
||||||
: 1 - pow(2, -10 * t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_expo(f32 t) {
|
|
||||||
if (t == 0 || t == 1) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return t < 0.5
|
|
||||||
? pow(2, 20 * t - 10) / 2
|
|
||||||
: (2 - pow(2, -20 * t + 10)) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_circ(f32 t) {
|
|
||||||
return 1 - sqrtf(1 - pow(t, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_circ(f32 t) {
|
|
||||||
return sqrtf(1 - pow(t - 1, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_circ(f32 t) {
|
|
||||||
return t < 0.5
|
|
||||||
? (1 - sqrtf(1 - pow(2 * t, 2))) / 2
|
|
||||||
: (sqrtf(1 - pow(-2 * t + 2, 2)) + 1) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_back(f32 t) {
|
|
||||||
const f32 c1 = 1.70158;
|
|
||||||
const f32 c3 = c1 + 1;
|
|
||||||
|
|
||||||
return c3 * t * t * t - c1 * t * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_back(f32 t) {
|
|
||||||
const f32 c1 = 1.70158;
|
|
||||||
const f32 c3 = c1 + 1;
|
|
||||||
|
|
||||||
return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_back(f32 t) {
|
|
||||||
const f32 c1 = 1.70158;
|
|
||||||
const f32 c2 = c1 * 1.525;
|
|
||||||
|
|
||||||
return t < 0.5
|
|
||||||
? (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
|
|
||||||
: (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_elastic(f32 t) {
|
|
||||||
const f32 c4 = (2 * OMS_PI) / 3;
|
|
||||||
|
|
||||||
if (t == 0 || t == 1) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -pow(2, 10 * t - 10) * sinf((t * 10 - 10.75) * c4);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_elastic(f32 t) {
|
|
||||||
const f32 c4 = (2 * OMS_PI) / 3;
|
|
||||||
|
|
||||||
if (t == 0.0 || t == 1.0) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pow(2, -10 * t) * sinf((t * 10 - 0.75) * c4) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_elastic(f32 t) {
|
|
||||||
const f32 c5 = (2 * OMS_PI) / 4.5;
|
|
||||||
|
|
||||||
if (t == 0.0 || t == 1.0) {
|
|
||||||
return t;
|
|
||||||
} else if (t < 0.5) {
|
|
||||||
return -(pow(2, 20 * t - 10) * sinf((20 * t - 11.125) * c5)) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pow(2, -20 * t + 10) * sinf((20 * t - 11.125) * c5)) / 2 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_bounce(f32 t) {
|
|
||||||
return 1 - anim_ease_out_bounce(1 - t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_out_bounce(f32 t) {
|
|
||||||
const f32 n1 = 7.5625;
|
|
||||||
const f32 d1 = 2.75;
|
|
||||||
|
|
||||||
if (t < 1 / d1) {
|
|
||||||
return n1 * t * t;
|
|
||||||
} else if (t < 2 / d1) {
|
|
||||||
return n1 * (t -= 1.5 / d1) * t + 0.75;
|
|
||||||
} else if (t < 2.5 / d1) {
|
|
||||||
return n1 * (t -= 2.25 / d1) * t + 0.9375;
|
|
||||||
}
|
|
||||||
|
|
||||||
return n1 * (t -= 2.625 / d1) * t + 0.984375;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
f32 anim_ease_in_out_bounce(f32 t) {
|
|
||||||
return t < 0.5
|
|
||||||
? (1 - anim_ease_out_bounce(1 - 2 * t)) / 2
|
|
||||||
: (1 + anim_ease_out_bounce(2 * t - 1)) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -12,61 +12,49 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "AssetType.h"
|
#include "AssetType.h"
|
||||||
|
|
||||||
#define MAX_ASSET_NAME_LENGTH 32
|
enum AssetState : byte {
|
||||||
|
ASSET_STATE_IN_RAM = 1 << 0,
|
||||||
|
ASSET_STATE_IN_VRAM = 1 << 1,
|
||||||
|
ASSET_STATE_RAM_GC = 1 << 2,
|
||||||
|
ASSET_STATE_VRAM_GC = 1 << 3,
|
||||||
|
};
|
||||||
|
|
||||||
struct Asset {
|
struct Asset {
|
||||||
// The id is the same as its location in memory/in the ams array
|
|
||||||
// This is is only an internal id and NOT the same as a db id (e.g. player id)
|
|
||||||
uint64 internal_id;
|
|
||||||
|
|
||||||
// Could be 0 if there is no official id
|
// Could be 0 if there is no official id
|
||||||
uint64 official_id;
|
uint32 official_id;
|
||||||
|
|
||||||
// @performance This is bad, this uses the same name as the hashmap
|
// @performance We would like to use a bool but windows only supports 32bit atomic values as smallest value
|
||||||
// We effectively store the asset name twice which shouldn't be the case
|
// Maybe if we would set the IS_LOADED_STATE in the enum as the highest bit we could use the state variable and check it with >=
|
||||||
char name[MAX_ASSET_NAME_LENGTH];
|
int32 is_loaded;
|
||||||
|
|
||||||
AssetType type;
|
|
||||||
|
|
||||||
// Counts the references to this asset
|
|
||||||
// e.g. textures
|
|
||||||
int32 reference_count;
|
|
||||||
|
|
||||||
// Describes how much ram/vram the asset uses
|
// Describes how much ram/vram the asset uses
|
||||||
// E.g. vram_size = 0 but ram_size > 0 means that it never uses any gpu memory
|
// E.g. vram_size = 0 but ram_size > 0 means that it never uses any gpu memory
|
||||||
uint32 ram_size;
|
uint32 ram_size;
|
||||||
uint32 vram_size;
|
uint32 vram_size;
|
||||||
uint64 last_access;
|
|
||||||
|
uint32 last_access;
|
||||||
|
|
||||||
// Usually 1 but in some cases an ams may hold entities of variable chunk length
|
// Usually 1 but in some cases an ams may hold entities of variable chunk length
|
||||||
// For textures for example a 128x128 is of size 1 but 256x256 is of size 4
|
// For textures for example a 128x128 is of size 1 but 256x256 is of size 4
|
||||||
uint32 size;
|
uint16 size;
|
||||||
|
|
||||||
// Variable used for thread safety
|
// Which asset component is used
|
||||||
bool is_loaded;
|
byte component_id;
|
||||||
|
|
||||||
// Describes if the memory is currently available in ram/vram
|
byte state;
|
||||||
// E.g. an asset might be uploaded to the gpu and no longer held in ram (or the other way around)
|
|
||||||
bool is_ram;
|
|
||||||
bool is_vram;
|
|
||||||
|
|
||||||
// Describes if the asset can be removed/garbage collected IF necessary
|
|
||||||
// This however only happens if space is needed
|
|
||||||
bool can_garbage_collect_ram;
|
|
||||||
bool can_garbage_collect_vram;
|
|
||||||
|
|
||||||
Asset* next;
|
|
||||||
Asset* prev;
|
|
||||||
|
|
||||||
// An asset can reference up to N other entities
|
|
||||||
// This allows us to quickly update the other entities
|
|
||||||
// Example: A player pulls N mobs
|
|
||||||
// @bug This means there are hard limits on how many mobs can be pulled by a player
|
|
||||||
Asset* references[50];
|
|
||||||
uint64 free_references; // bits show which is free
|
|
||||||
|
|
||||||
// Actual memory address and specific asset data
|
// Actual memory address and specific asset data
|
||||||
byte* self;
|
byte* self;
|
||||||
|
|
||||||
|
// Counts the references to this asset
|
||||||
|
// e.g. textures or entity schemas (NOT entities themselves)
|
||||||
|
uint16 reference_count;
|
||||||
|
|
||||||
|
// An asset can reference up to N other assets
|
||||||
|
// This allows us to quickly update the other assets
|
||||||
|
// Uses official_id
|
||||||
|
// @performance This could potentially be bad because many assets will have 0 or only 1-4 references
|
||||||
|
uint32 references[12];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -25,19 +25,8 @@
|
||||||
#include "../localization/Language.h"
|
#include "../localization/Language.h"
|
||||||
#include "../ui/UITheme.h"
|
#include "../ui/UITheme.h"
|
||||||
#include "AssetManagementSystem.h"
|
#include "AssetManagementSystem.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if __aarch64__
|
#include "../stdlib/Simd.h"
|
||||||
#include "../stdlib/sve/SVE_I32.h"
|
|
||||||
#else
|
|
||||||
#include "../stdlib/simd/SIMD_I32.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ASSET_ARCHIVE_VERSION 1
|
#define ASSET_ARCHIVE_VERSION 1
|
||||||
|
|
||||||
|
|
@ -78,7 +67,7 @@ struct AssetArchive {
|
||||||
|
|
||||||
// This is used to tell the asset archive in which AssetManagementSystem (AMS) which asset type is located.
|
// This is used to tell the asset archive in which AssetManagementSystem (AMS) which asset type is located.
|
||||||
// Remember, many AMS only contain one asset type (e.g. image, audio, ...)
|
// Remember, many AMS only contain one asset type (e.g. image, audio, ...)
|
||||||
int32 asset_type_map[ASSET_TYPE_SIZE];
|
byte asset_type_map[ASSET_TYPE_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculates how large the header memory has to be to hold all its information
|
// Calculates how large the header memory has to be to hold all its information
|
||||||
|
|
@ -183,37 +172,47 @@ void asset_archive_load(AssetArchive* archive, const char* path, BufferMemory* b
|
||||||
// Maybe we could just accept a int value which we set atomically as a flag that the asset is complete?
|
// Maybe we could just accept a int value which we set atomically as a flag that the asset is complete?
|
||||||
// this way we can check much faster if we can work with this data from the caller?!
|
// this way we can check much faster if we can work with this data from the caller?!
|
||||||
// The only problem is that we need to pass the pointer to this int in the thrd_queue since we queue the files to load there
|
// The only problem is that we need to pass the pointer to this int in the thrd_queue since we queue the files to load there
|
||||||
Asset* asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetManagementSystem* ams_array, RingMemory* ring)
|
Asset* asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetManagementSystem* ams, RingMemory* ring)
|
||||||
{
|
{
|
||||||
// @todo add calculation from element->type to ams index
|
// @todo add calculation from element->type to ams index. Probably requires an app specific conversion function
|
||||||
|
|
||||||
// We have to mask 0x00FFFFFF since the highest bits define the archive id, not the element id
|
// We have to mask 0x00FFFFFF since the highest bits define the archive id, not the element id
|
||||||
AssetArchiveElement* element = &archive->header.asset_element[id & 0x00FFFFFF];
|
AssetArchiveElement* element = &archive->header.asset_element[id & 0x00FFFFFF];
|
||||||
AssetManagementSystem* ams = &ams_array[archive->asset_type_map[element->type]];
|
|
||||||
|
|
||||||
// @todo This is a little bit stupid, reconsider
|
byte component_id = archive->asset_type_map[element->type];
|
||||||
char id_str[32];
|
AssetComponent* ac = &ams->asset_components[component_id];
|
||||||
_itoa(id, id_str, 16);
|
|
||||||
|
|
||||||
Asset* asset;
|
// Create a string representation from the asset id
|
||||||
|
// We can't just use the asset id, since an int can have a \0 between high byte and low byte
|
||||||
|
// @question We maybe can switch the AMS to work with ints as keys.
|
||||||
|
// We would then have to also create an application specific enum for general assets,
|
||||||
|
// that are not stored in the asset archive (e.g. color palette, which is generated at runtime).
|
||||||
|
char id_str[9];
|
||||||
|
int_to_hex(id, id_str);
|
||||||
|
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(ams, id_str);
|
||||||
|
|
||||||
// @performance I think we could optimize the ams_reserver_asset in a way so we don't have to lock it the entire time
|
|
||||||
pthread_mutex_lock(&ams->mutex);
|
|
||||||
asset = ams_get_asset(ams, id_str);
|
|
||||||
if (asset) {
|
if (asset) {
|
||||||
// Asset already loaded
|
// Prevent garbage collection
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
asset->state &= ~ASSET_STATE_RAM_GC;
|
||||||
|
asset->state &= ~ASSET_STATE_VRAM_GC;
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @bug Couldn't the asset become available from thrd_ams_get_asset_wait to here?
|
||||||
|
// This would mean we are overwriting it
|
||||||
|
// A solution could be a function called thrd_ams_get_reserve_wait() that reserves, if not available
|
||||||
|
// However, that function would have to lock the ams during that entire time
|
||||||
|
|
||||||
if (element->type == 0) {
|
if (element->type == 0) {
|
||||||
asset = ams_reserve_asset(ams, id_str, ams_calculate_chunks(ams, element->uncompressed));
|
asset = thrd_ams_reserve_asset(ams, (byte) component_id, id_str, element->uncompressed);
|
||||||
|
asset->official_id = id;
|
||||||
|
|
||||||
FileBody file = {};
|
FileBody file = {};
|
||||||
file.content = asset->self;
|
file.content = asset->self;
|
||||||
|
|
||||||
// @performance Consider to implement gzip here
|
// @performance Consider to implement general purpose fast compression algorithm
|
||||||
|
|
||||||
// We are directly reading into the correct destination
|
// We are directly reading into the correct destination
|
||||||
file_read(archive->fd, &file, element->start, element->length);
|
file_read(archive->fd, &file, element->start, element->length);
|
||||||
|
|
@ -230,8 +229,10 @@ Asset* asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetMana
|
||||||
|
|
||||||
// This happens while the file system loads the data
|
// This happens while the file system loads the data
|
||||||
// The important part is to reserve the uncompressed file size, not the compressed one
|
// The important part is to reserve the uncompressed file size, not the compressed one
|
||||||
asset = ams_reserve_asset(ams, id_str, ams_calculate_chunks(ams, element->uncompressed));
|
asset = thrd_ams_reserve_asset(ams, (byte) component_id, id_str, element->uncompressed);
|
||||||
asset->is_ram = true;
|
asset->official_id = id;
|
||||||
|
|
||||||
|
asset->state |= ASSET_STATE_IN_RAM;
|
||||||
|
|
||||||
file_async_wait(archive->fd_async, &file.ov, true);
|
file_async_wait(archive->fd_async, &file.ov, true);
|
||||||
switch (element->type) {
|
switch (element->type) {
|
||||||
|
|
@ -288,10 +289,13 @@ Asset* asset_archive_asset_load(const AssetArchive* archive, int32 id, AssetMana
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
|
||||||
|
// Even though dependencies are still being loaded
|
||||||
|
// the main program should still be able to do some work if possible
|
||||||
|
thrd_ams_set_loaded(asset);
|
||||||
|
|
||||||
// @performance maybe do in worker threads? This just feels very slow
|
// @performance maybe do in worker threads? This just feels very slow
|
||||||
// @question dependencies might be stored in different archives?
|
// @bug dependencies might be stored in different archives?
|
||||||
for (uint32 i = 0; i < element->dependency_count; ++i) {
|
for (uint32 i = 0; i < element->dependency_count; ++i) {
|
||||||
asset_archive_asset_load(archive, id, ams, ring);
|
asset_archive_asset_load(archive, id, ams, ring);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,191 +14,100 @@
|
||||||
#include "Asset.h"
|
#include "Asset.h"
|
||||||
#include "../memory/ChunkMemory.h"
|
#include "../memory/ChunkMemory.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
|
#include "../utils/BitUtils.h"
|
||||||
#include "../stdlib/HashMap.h"
|
#include "../stdlib/HashMap.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
|
#include "../thread/Atomic.h"
|
||||||
|
|
||||||
// The major asset types should have their own asset component system
|
// The major asset types should have their own asset component system
|
||||||
// All other entities are grouped together in one asset component system
|
// All other entities are grouped together in one asset component system
|
||||||
// @question Asset component systems could be created per region -> easy to simulate a specific region
|
struct AssetComponent {
|
||||||
// @bug This means players might not be able to transition from one area to another?!
|
ChunkMemory asset_memory;
|
||||||
|
|
||||||
// @performance There is a huge performance flaw. We CANNOT have an asset only in vram because it always also allocates the ram (asset_data_memory)
|
|
||||||
struct AssetManagementSystem {
|
|
||||||
// @question is this even necessary or could we integrate this directly into the system here?
|
|
||||||
HashMap hash_map;
|
|
||||||
|
|
||||||
uint64 ram_size;
|
uint64 ram_size;
|
||||||
uint64 vram_size;
|
uint64 vram_size;
|
||||||
uint64 asset_count;
|
uint64 asset_count;
|
||||||
int32 overhead;
|
|
||||||
bool has_changed;
|
|
||||||
|
|
||||||
// The indices of asset_memory and asset_data_memory are always linked
|
|
||||||
|
|
||||||
// @question Wouldn't it make much more sense to have a general AMS for this data
|
|
||||||
// In that case we would only need one AMS which holds the Asset information. All others would only need the data_memory
|
|
||||||
// We could probably dramatically simplify the AMS that holds the actual data. We might only need the ChunkMemory?
|
|
||||||
|
|
||||||
// @question Even further, why would we want to split stats and DATA at all? we are talking about assets which most likely don't fit into a single L1 cache line
|
|
||||||
// BUT they may fit in L2 or L3 and therefore require less pointer chasing
|
|
||||||
// Sure collecting data is faster with split memory (ram/vram usage)
|
|
||||||
|
|
||||||
// General asset memory
|
|
||||||
// Fixed chunk size of sizeof(Asset)
|
|
||||||
ChunkMemory asset_memory;
|
|
||||||
|
|
||||||
// Actual asset data
|
|
||||||
// Chunk size defined during initialization
|
|
||||||
ChunkMemory asset_data_memory;
|
|
||||||
|
|
||||||
// @performance Do we really need the linked list, the ChunkMemory should allow us to do some smart stuff
|
|
||||||
Asset* first;
|
|
||||||
Asset* last;
|
|
||||||
|
|
||||||
// @question do we want to create an extra threaded version? Or a combined one, like we have right now.
|
|
||||||
// @question Do we want to add a mutex to assets. This way we don't have to lock the entire ams.
|
// @question Do we want to add a mutex to assets. This way we don't have to lock the entire ams.
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
void ams_create(AssetManagementSystem* ams, BufferMemory* buf, int32 chunk_size, int32 count, int32 overhead = 0)
|
struct AssetManagementSystem {
|
||||||
|
HashMap hash_map;
|
||||||
|
|
||||||
|
int32 asset_component_count;
|
||||||
|
AssetComponent* asset_components;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ams_create(AssetManagementSystem* ams, BufferMemory* buf, int32 asset_component_count, int32 count)
|
||||||
{
|
{
|
||||||
// setup hash_map
|
hashmap_create(&ams->hash_map, count, sizeof(HashEntry) + sizeof(Asset), buf);
|
||||||
hashmap_create(&ams->hash_map, count, sizeof(HashEntryInt64), buf);
|
ams->asset_component_count = asset_component_count;
|
||||||
|
ams->asset_components = (AssetComponent *) buffer_get_memory(buf, asset_component_count * sizeof(AssetComponent), 64, true);
|
||||||
ams->overhead = overhead;
|
|
||||||
|
|
||||||
// setup asset_memory
|
|
||||||
chunk_init(&ams->asset_memory, buf, count, sizeof(Asset), 64);
|
|
||||||
|
|
||||||
// setup asset_data_memory
|
|
||||||
chunk_init(&ams->asset_data_memory, buf, count, chunk_size, 64);
|
|
||||||
|
|
||||||
ams->first = NULL;
|
|
||||||
ams->last = NULL;
|
|
||||||
|
|
||||||
pthread_mutex_init(&ams->mutex, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: buf size see ams_get_buffer_size
|
inline
|
||||||
void ams_create(AssetManagementSystem* ams, byte* buf, int32 chunk_size, int32 count, int32 overhead = 0)
|
void ams_component_create(AssetComponent* ac, BufferMemory* buf, int32 chunk_size, int32 count)
|
||||||
{
|
{
|
||||||
ASSERT_SIMPLE(chunk_size);
|
ASSERT_SIMPLE(chunk_size);
|
||||||
|
|
||||||
// setup hash_map
|
chunk_init(&ac->asset_memory, buf, count, chunk_size, 64);
|
||||||
hashmap_create(&ams->hash_map, count, sizeof(HashEntryInt64), buf);
|
pthread_mutex_init(&ac->mutex, NULL);
|
||||||
|
|
||||||
ams->overhead = overhead;
|
|
||||||
|
|
||||||
// setup asset_memory
|
|
||||||
ams->asset_memory.count = count;
|
|
||||||
ams->asset_memory.chunk_size = sizeof(Asset);
|
|
||||||
ams->asset_memory.last_pos = 0;
|
|
||||||
ams->asset_memory.alignment = 64;
|
|
||||||
ams->asset_memory.memory = buf;
|
|
||||||
ams->asset_memory.free = (uint64 *) (ams->asset_memory.memory + ams->asset_memory.chunk_size * count);
|
|
||||||
|
|
||||||
// setup asset_data_memory
|
|
||||||
ams->asset_data_memory.count = count;
|
|
||||||
ams->asset_data_memory.chunk_size = chunk_size;
|
|
||||||
ams->asset_data_memory.last_pos = 0;
|
|
||||||
ams->asset_data_memory.alignment = 64;
|
|
||||||
ams->asset_data_memory.memory = (byte *) (ams->asset_memory.free + CEIL_DIV(count, 64));
|
|
||||||
ams->asset_data_memory.free = (uint64 *) (ams->asset_data_memory.memory + ams->asset_data_memory.chunk_size * count);
|
|
||||||
|
|
||||||
ams->first = NULL;
|
|
||||||
ams->last = NULL;
|
|
||||||
|
|
||||||
pthread_mutex_init(&ams->mutex, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ams_component_create(AssetComponent* ac, byte* buf, int32 chunk_size, int32 count)
|
||||||
|
{
|
||||||
|
ASSERT_SIMPLE(chunk_size);
|
||||||
|
|
||||||
|
ac->asset_memory.count = count;
|
||||||
|
ac->asset_memory.chunk_size = chunk_size;
|
||||||
|
ac->asset_memory.last_pos = 0;
|
||||||
|
ac->asset_memory.alignment = 64;
|
||||||
|
ac->asset_memory.memory = buf;
|
||||||
|
ac->asset_memory.free = (uint64 *) (ac->asset_memory.memory + ac->asset_memory.chunk_size * count);
|
||||||
|
|
||||||
|
pthread_mutex_init(&ac->mutex, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ams_component_free(AssetComponent* ac)
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&ac->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
void ams_free(AssetManagementSystem* ams)
|
void ams_free(AssetManagementSystem* ams)
|
||||||
{
|
{
|
||||||
pthread_mutex_destroy(&ams->mutex);
|
for (int32 i = 0; i < ams->asset_component_count; ++i) {
|
||||||
}
|
ams_component_free(&ams->asset_components[i]);
|
||||||
|
|
||||||
inline
|
|
||||||
int32 ams_calculate_chunks(const AssetManagementSystem* ams, int32 byte_size)
|
|
||||||
{
|
|
||||||
return (int32) CEIL_DIV(byte_size + ams->overhead, ams->asset_data_memory.chunk_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
int64 ams_get_buffer_size(int32 count, int32 chunk_size)
|
|
||||||
{
|
|
||||||
return hashmap_size(count, sizeof(HashEntryInt64)) // hash map
|
|
||||||
+ sizeof(Asset) * count + CEIL_DIV(count, 64) * sizeof(uint64) // asset_memory
|
|
||||||
+ chunk_size * count + CEIL_DIV(count, 64) * sizeof(uint64); // asset_data_memory
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
void ams_update_stats(AssetManagementSystem* ams)
|
|
||||||
{
|
|
||||||
ams->vram_size = 0;
|
|
||||||
ams->ram_size = 0;
|
|
||||||
ams->asset_count = 0;
|
|
||||||
|
|
||||||
Asset* temp_asset = ams->first;
|
|
||||||
|
|
||||||
while (temp_asset) {
|
|
||||||
ams->vram_size += temp_asset->vram_size;
|
|
||||||
ams->ram_size += temp_asset->ram_size;
|
|
||||||
++ams->asset_count;
|
|
||||||
|
|
||||||
temp_asset = temp_asset->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ams->has_changed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
uint64 ams_get_asset_count(AssetManagementSystem* ams)
|
uint16 ams_calculate_chunks(const AssetComponent* ac, int32 byte_size, int32 overhead)
|
||||||
{
|
{
|
||||||
if (ams->has_changed) {
|
return (uint16) CEIL_DIV(byte_size + overhead, ac->asset_memory.chunk_size);
|
||||||
ams_update_stats(ams);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ams->asset_count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
uint64 ams_get_vram_usage(AssetManagementSystem* ams)
|
void thrd_ams_set_loaded(Asset* asset)
|
||||||
{
|
{
|
||||||
if (ams->has_changed) {
|
atomic_set_release(&asset->is_loaded, 1);
|
||||||
ams_update_stats(ams);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ams->vram_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
uint64 ams_get_ram_usage(AssetManagementSystem* ams)
|
bool thrd_ams_is_loaded(Asset* asset)
|
||||||
{
|
{
|
||||||
if (ams->has_changed) {
|
return asset && atomic_get_acquire(&asset->is_loaded) > 0;
|
||||||
ams_update_stats(ams);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ams->ram_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ams_free_asset(AssetManagementSystem* ams, Asset* asset)
|
|
||||||
{
|
|
||||||
asset->prev->next = asset->next;
|
|
||||||
asset->next->prev = asset->prev;
|
|
||||||
|
|
||||||
hashmap_delete_entry(&ams->hash_map, asset->name);
|
|
||||||
|
|
||||||
for (uint32 i = 0; i < asset->size; ++i) {
|
|
||||||
chunk_free_element(&ams->asset_memory, asset->internal_id + i);
|
|
||||||
chunk_free_element(&ams->asset_data_memory, asset->internal_id + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ams->has_changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
Asset* ams_get_asset(AssetManagementSystem* ams, uint64 element)
|
bool thrd_ams_is_in_vram(Asset* asset)
|
||||||
{
|
{
|
||||||
return (Asset *) chunk_get_element(&ams->asset_memory, element, false);
|
return asset && atomic_get_acquire(&asset->is_loaded)
|
||||||
|
&& (asset->state & ASSET_STATE_IN_VRAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|
@ -206,29 +115,6 @@ Asset* ams_get_asset(AssetManagementSystem* ams, const char* key)
|
||||||
{
|
{
|
||||||
HashEntry* entry = hashmap_get_entry(&ams->hash_map, key);
|
HashEntry* entry = hashmap_get_entry(&ams->hash_map, key);
|
||||||
|
|
||||||
DEBUG_MEMORY_READ(
|
|
||||||
(uint64) (entry ? (Asset *) entry->value : 0),
|
|
||||||
entry ? sizeof(Asset) : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
DEBUG_MEMORY_READ(
|
|
||||||
(uint64) (entry ? ((Asset *) entry->value)->self : 0),
|
|
||||||
entry ? ((Asset *) entry->value)->ram_size : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return entry ? (Asset *) entry->value : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
Asset* ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 hash)
|
|
||||||
{
|
|
||||||
HashEntry* entry = hashmap_get_entry(&ams->hash_map, key, hash);
|
|
||||||
|
|
||||||
DEBUG_MEMORY_READ(
|
|
||||||
(uint64) (entry ? (Asset *) entry->value : 0),
|
|
||||||
entry ? sizeof(Asset) : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
DEBUG_MEMORY_READ(
|
DEBUG_MEMORY_READ(
|
||||||
(uint64) (entry ? ((Asset *) entry->value)->self : 0),
|
(uint64) (entry ? ((Asset *) entry->value)->self : 0),
|
||||||
entry ? ((Asset *) entry->value)->ram_size : 0
|
entry ? ((Asset *) entry->value)->ram_size : 0
|
||||||
|
|
@ -238,159 +124,417 @@ Asset* ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @performance We could probably avoid locking by adding a atomic flag to indicate if the value is valid
|
// @performance We could probably avoid locking by adding a atomic flag to indicate if the value is valid
|
||||||
Asset* thrd_ams_get_asset(AssetManagementSystem* ams, uint64 element) {
|
inline
|
||||||
pthread_mutex_lock(&ams->mutex);
|
|
||||||
Asset* asset = ams_get_asset(ams, element);
|
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
|
||||||
|
|
||||||
return asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Asset* thrd_ams_get_asset(AssetManagementSystem* ams, const char* key) {
|
Asset* thrd_ams_get_asset(AssetManagementSystem* ams, const char* key) {
|
||||||
pthread_mutex_lock(&ams->mutex);
|
HashEntry* entry = hashmap_get_entry(&ams->hash_map, key);
|
||||||
Asset* asset = ams_get_asset(ams, key);
|
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
if (!entry || atomic_get_acquire(&((Asset *) entry->value)->is_loaded) <= 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_MEMORY_READ(
|
||||||
|
(uint64) (entry ? ((Asset *) entry->value)->self : 0),
|
||||||
|
entry ? ((Asset *) entry->value)->ram_size : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
return (Asset *) entry->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
Asset* thrd_ams_get_asset_wait(AssetManagementSystem* ams, const char* key) {
|
||||||
|
HashEntry* entry = hashmap_get_entry(&ams->hash_map, key);
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 state = 0;
|
||||||
|
while (!(state = atomic_get_acquire(&((Asset *) entry->value)->is_loaded))) {}
|
||||||
|
if (state < 0) {
|
||||||
|
// Marked for removal
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_MEMORY_READ(
|
||||||
|
(uint64) (entry ? ((Asset *) entry->value)->self : 0),
|
||||||
|
entry ? ((Asset *) entry->value)->ram_size : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
return (Asset *) entry->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
Asset* thrd_ams_get_asset_wait(AssetManagementSystem* ams, const char* key, uint64 hash) {
|
||||||
|
HashEntry* entry = hashmap_get_entry(&ams->hash_map, key, hash);
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 state = 0;
|
||||||
|
while (!(state = atomic_get_acquire(&((Asset *) entry->value)->is_loaded))) {}
|
||||||
|
if (state < 0) {
|
||||||
|
// Marked for removal
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_MEMORY_READ(
|
||||||
|
(uint64) (entry ? ((Asset *) entry->value)->self : 0),
|
||||||
|
entry ? ((Asset *) entry->value)->ram_size : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
return (Asset *) entry->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
Asset* thrd_ams_get_reserve_asset_wait(AssetManagementSystem* ams, byte type, const char* name, uint32 size, uint32 overhead = 0)
|
||||||
|
{
|
||||||
|
// @bug Isn't hashmap_get_reserve unsafe for threading?
|
||||||
|
HashEntry* entry = hashmap_get_reserve(&ams->hash_map, name);
|
||||||
|
Asset* asset = (Asset *) entry->value;
|
||||||
|
|
||||||
|
if (asset->self) {
|
||||||
|
int32 state = 0;
|
||||||
|
while (!(state = atomic_get_acquire(&((Asset *) entry->value)->is_loaded))) {}
|
||||||
|
if (state > 0) {
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetComponent* ac = &ams->asset_components[type];
|
||||||
|
uint16 elements = ams_calculate_chunks(ac, size, overhead);
|
||||||
|
int32 free_data = chunk_reserve(&ac->asset_memory, elements);
|
||||||
|
|
||||||
|
byte* data = chunk_get_element(&ac->asset_memory, free_data, true);
|
||||||
|
|
||||||
|
asset->component_id = type;
|
||||||
|
asset->self = data;
|
||||||
|
asset->size = elements; // Crucial for freeing
|
||||||
|
asset->ram_size = ac->asset_memory.chunk_size * elements;
|
||||||
|
|
||||||
|
ac->vram_size += asset->vram_size;
|
||||||
|
ac->ram_size += asset->ram_size;
|
||||||
|
++ac->asset_count;
|
||||||
|
|
||||||
|
DEBUG_MEMORY_RESERVE((uint64) asset, asset->ram_size, 180);
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset* thrd_ams_get_asset(AssetManagementSystem* ams, const char* key, uint64 hash) {
|
inline
|
||||||
pthread_mutex_lock(&ams->mutex);
|
void ams_remove_asset(AssetManagementSystem* ams, AssetComponent* ac, Asset* asset, const char* name)
|
||||||
Asset* asset = ams_get_asset(ams, key, hash);
|
{
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
// @todo remove from vram
|
||||||
|
|
||||||
return asset;
|
asset->is_loaded = 0;
|
||||||
|
ac->vram_size -= asset->vram_size;
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
--ac->asset_count;
|
||||||
|
|
||||||
|
hashmap_remove(&ams->hash_map, name);
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ams_remove_asset_ram(AssetManagementSystem* ams, AssetComponent* ac, Asset* asset)
|
||||||
|
{
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo It would be nice if we could remove the asset by passing it as a parameter instead of the name
|
||||||
|
// The problem is there is no correlation between asset data (e.g. internal_id) and global hashmap (e.g. element_id)
|
||||||
|
// This means we would have to iterate all hashmap entries and remove it this way, which is very slow
|
||||||
|
inline
|
||||||
|
void ams_remove_asset(AssetManagementSystem* ams, const char* name)
|
||||||
|
{
|
||||||
|
// @todo remove from vram
|
||||||
|
|
||||||
|
Asset* asset = ams_get_asset(ams, name);
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset->component_id];
|
||||||
|
|
||||||
|
asset->is_loaded = 0;
|
||||||
|
ac->vram_size -= asset->vram_size;
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
--ac->asset_count;
|
||||||
|
|
||||||
|
hashmap_remove(&ams->hash_map, name);
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ams_remove_asset(AssetManagementSystem* ams, Asset* asset, const char* name)
|
||||||
|
{
|
||||||
|
// @todo remove from vram
|
||||||
|
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset->component_id];
|
||||||
|
|
||||||
|
asset->is_loaded = 0;
|
||||||
|
ac->vram_size -= asset->vram_size;
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
--ac->asset_count;
|
||||||
|
|
||||||
|
hashmap_remove(&ams->hash_map, name);
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void ams_remove_asset_ram(AssetManagementSystem* ams, Asset* asset)
|
||||||
|
{
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset->component_id];
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void thrd_ams_remove_asset(AssetManagementSystem* ams, AssetComponent* ac, Asset* asset, const char* name)
|
||||||
|
{
|
||||||
|
// @todo remove from vram
|
||||||
|
|
||||||
|
asset->is_loaded = 0;
|
||||||
|
ac->vram_size -= asset->vram_size;
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
--ac->asset_count;
|
||||||
|
|
||||||
|
atomic_set_release(&asset->is_loaded, 0);
|
||||||
|
hashmap_remove(&ams->hash_map, name);
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void thrd_ams_remove_asset(AssetManagementSystem* ams, const char* name)
|
||||||
|
{
|
||||||
|
HashEntry* entry = hashmap_get_entry(&ams->hash_map, name);
|
||||||
|
Asset* asset = (Asset *) entry->value;
|
||||||
|
atomic_set_release(&asset->is_loaded, -1);
|
||||||
|
hashmap_remove(&ams->hash_map, name);
|
||||||
|
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset->component_id];
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
|
||||||
|
ac->vram_size -= asset->vram_size;
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
--ac->asset_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thrd_ams_remove_asset(AssetManagementSystem* ams, const char* name, Asset* asset)
|
||||||
|
{
|
||||||
|
atomic_set_release(&asset->is_loaded, -1);
|
||||||
|
hashmap_remove(&ams->hash_map, name);
|
||||||
|
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset->component_id];
|
||||||
|
chunk_free_elements(
|
||||||
|
&ac->asset_memory,
|
||||||
|
chunk_id_from_memory(&ac->asset_memory, asset->self),
|
||||||
|
asset->size
|
||||||
|
);
|
||||||
|
|
||||||
|
ac->vram_size -= asset->vram_size;
|
||||||
|
ac->ram_size -= asset->ram_size;
|
||||||
|
--ac->asset_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo implement defragment command to optimize memory layout since the memory layout will become fragmented over time
|
// @todo implement defragment command to optimize memory layout since the memory layout will become fragmented over time
|
||||||
|
|
||||||
// @performance This function is VERY important, check if we can optimize it
|
Asset* ams_reserve_asset(AssetManagementSystem* ams, byte type, const char* name, uint32 size, uint32 overhead = 0)
|
||||||
// We could probably optimize the threaded version by adding a atomic_set_release(asset->is_loaded, true);
|
|
||||||
Asset* ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 elements = 1)
|
|
||||||
{
|
{
|
||||||
int64 free_asset = chunk_reserve(&ams->asset_memory, elements, true);
|
ASSERT_SIMPLE(strlen(name) < HASH_MAP_MAX_KEY_LENGTH - 1);
|
||||||
if (free_asset < 0) {
|
|
||||||
ASSERT_SIMPLE(free_asset >= 0);
|
AssetComponent* ac = &ams->asset_components[type];
|
||||||
|
uint16 elements = ams_calculate_chunks(ac, size, overhead);
|
||||||
|
|
||||||
|
int32 free_data = chunk_reserve(&ac->asset_memory, elements);
|
||||||
|
if (free_data < 0) {
|
||||||
|
ASSERT_SIMPLE(free_data >= 0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t name_length = strlen(name);
|
byte* asset_data = chunk_get_element(&ac->asset_memory, free_data, true);
|
||||||
ASSERT_SIMPLE(name_length < MAX_ASSET_NAME_LENGTH - 1);
|
Asset* asset = (Asset *) hashmap_reserve(&ams->hash_map, name)->value;
|
||||||
|
|
||||||
Asset* asset = (Asset *) chunk_get_element(&ams->asset_memory, free_asset);
|
asset->component_id = type;
|
||||||
asset->internal_id = free_asset;
|
asset->self = asset_data;
|
||||||
|
|
||||||
strncpy(asset->name, name, name_length);
|
|
||||||
asset->name[name_length] = '\0';
|
|
||||||
|
|
||||||
hashmap_insert(&ams->hash_map, name, (uintptr_t) asset);
|
|
||||||
|
|
||||||
chunk_reserve_index(&ams->asset_data_memory, free_asset, elements, true);
|
|
||||||
asset->self = chunk_get_element(&ams->asset_data_memory, free_asset);
|
|
||||||
asset->size = elements; // Crucial for freeing
|
asset->size = elements; // Crucial for freeing
|
||||||
asset->ram_size = (ams->asset_memory.chunk_size + ams->asset_data_memory.chunk_size) * elements;
|
asset->ram_size = ac->asset_memory.chunk_size * elements;
|
||||||
|
|
||||||
DEBUG_MEMORY_RESERVE((uint64) asset->self, elements * ams->asset_data_memory.chunk_size, 180);
|
ac->vram_size += asset->vram_size;
|
||||||
|
ac->ram_size += asset->ram_size;
|
||||||
|
++ac->asset_count;
|
||||||
|
|
||||||
// @performance Do we really want a double linked list. Are we really using this feature or is the free_index enough?
|
DEBUG_MEMORY_RESERVE((uint64) asset, asset->ram_size, 180);
|
||||||
if (free_asset > 0 && free_asset < ams->asset_memory.count - 1) {
|
|
||||||
Asset* next = ams->first;
|
|
||||||
while (next->next != NULL
|
|
||||||
&& next->next->internal_id < asset->internal_id
|
|
||||||
&& next->internal_id < ams->asset_memory.count
|
|
||||||
) {
|
|
||||||
next = next->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
asset->prev = next;
|
|
||||||
asset->next = asset->prev->next;
|
|
||||||
|
|
||||||
if (asset->next) {
|
|
||||||
asset->next->prev = asset;
|
|
||||||
} else {
|
|
||||||
ams->last = asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
asset->prev->next = asset;
|
|
||||||
} else if (free_asset == 0) {
|
|
||||||
asset->next = ams->first;
|
|
||||||
|
|
||||||
if (ams->first) {
|
|
||||||
ams->first->prev = asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
ams->first = asset;
|
|
||||||
} else if (free_asset == ams->asset_memory.count - 1) {
|
|
||||||
asset->prev = ams->last;
|
|
||||||
|
|
||||||
// WARNING: no if here because we assume there is no ECS with just a size of 1
|
|
||||||
ams->last->next = asset;
|
|
||||||
ams->last = asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
ams->has_changed = true;
|
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ams_garbage_collect(AssetManagementSystem* ams, uint64 time, uint64 dt)
|
inline
|
||||||
{
|
Asset* thrd_ams_reserve_asset(AssetManagementSystem* ams, byte type, const char* name, uint32 size, uint32 overhead = 0) {
|
||||||
Asset* asset = ams->first;
|
AssetComponent* ac = &ams->asset_components[type];
|
||||||
|
uint16 elements = ams_calculate_chunks(ac, size, overhead);
|
||||||
|
|
||||||
while (asset) {
|
pthread_mutex_lock(&ams->asset_components[type].mutex);
|
||||||
// @performance We cannot just remove ram and keep vram. This is a huge flaw
|
int32 free_data = chunk_reserve(&ac->asset_memory, elements);
|
||||||
if (asset->can_garbage_collect_ram && asset->can_garbage_collect_vram && time - asset->last_access <= dt) {
|
if (free_data < 0) {
|
||||||
ams_free_asset(ams, asset);
|
pthread_mutex_unlock(&ams->asset_components[type].mutex);
|
||||||
|
ASSERT_SIMPLE(free_data >= 0);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&ams->asset_components[type].mutex);
|
||||||
|
|
||||||
|
byte* asset_data = chunk_get_element(&ac->asset_memory, free_data, true);
|
||||||
|
|
||||||
|
Asset asset = {};
|
||||||
|
|
||||||
|
asset.component_id = type;
|
||||||
|
asset.self = asset_data;
|
||||||
|
asset.size = elements; // Crucial for freeing
|
||||||
|
asset.ram_size = ac->asset_memory.chunk_size * elements;
|
||||||
|
|
||||||
|
ac->vram_size += asset.vram_size;
|
||||||
|
ac->ram_size += asset.ram_size;
|
||||||
|
++ac->asset_count;
|
||||||
|
|
||||||
|
DEBUG_MEMORY_RESERVE((uint64) asset_data, asset.ram_size, 180);
|
||||||
|
|
||||||
|
ASSERT_SIMPLE(strlen(name) < HASH_MAP_MAX_KEY_LENGTH - 1);
|
||||||
|
|
||||||
|
return (Asset *) hashmap_insert(&ams->hash_map, name, (byte *) &asset)->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Find a way to handle manual ram/vram changes
|
||||||
|
// Either implement a ams_update(AssetManagementSystem* ams, Asset* asset) function
|
||||||
|
// Or set .has_changed = true (even if garbage collection gets set) and call this func somewhere (maybe thread?)
|
||||||
|
// Perform general ams update (stats and garbage collection)
|
||||||
|
// We perform multiple things in one iteration to reduce the iteration costs
|
||||||
|
// @todo don't use uint64 for time, use uint32 and use relative time to start of program
|
||||||
|
void thrd_ams_update(AssetManagementSystem* ams, uint64 time, uint64 dt)
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < ams->asset_component_count; ++i) {
|
||||||
|
ams->asset_components[i].vram_size = 0;
|
||||||
|
ams->asset_components[i].ram_size = 0;
|
||||||
|
ams->asset_components[i].asset_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate the hash map to find all assets
|
||||||
|
int32 chunk_id = 0;
|
||||||
|
chunk_iterate_start(&ams->hash_map.buf, chunk_id)
|
||||||
|
HashEntry* entry = (HashEntry *) chunk_get_element(&ams->hash_map.buf, chunk_id);
|
||||||
|
Asset* asset = (Asset *) entry->value;
|
||||||
|
|
||||||
|
if (!thrd_ams_is_loaded(asset)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
asset = asset->next;
|
ams->asset_components[asset->component_id].vram_size += asset->vram_size;
|
||||||
}
|
ams->asset_components[asset->component_id].ram_size += asset->ram_size;
|
||||||
}
|
++ams->asset_components[asset->component_id].asset_count;
|
||||||
|
|
||||||
void ams_garbage_collect(AssetManagementSystem* ams)
|
if ((asset->state & ASSET_STATE_RAM_GC) || (asset->state & ASSET_STATE_VRAM_GC)) {
|
||||||
{
|
if ((asset->state & ASSET_STATE_RAM_GC)
|
||||||
Asset* asset = ams->first;
|
&& (asset->state & ASSET_STATE_VRAM_GC)
|
||||||
|
&& time - asset->last_access <= dt
|
||||||
while (asset) {
|
) {
|
||||||
// @performance We cannot just remove ram and keep vram. This is a huge flaw
|
// @performance Ideally we would like to pass the entry to delete
|
||||||
if (asset->can_garbage_collect_ram && asset->can_garbage_collect_vram) {
|
// The problem is the hashmap_delete function can't work with entries directly since it is not a doubly linked list
|
||||||
ams_free_asset(ams, asset);
|
thrd_ams_remove_asset(ams, &ams->asset_components[asset->component_id], asset, entry->key);
|
||||||
|
} else if ((asset->state & ASSET_STATE_RAM_GC)
|
||||||
|
&& time - asset->last_access <= dt
|
||||||
|
) {
|
||||||
|
ams_remove_asset_ram(ams, &ams->asset_components[asset->component_id], asset);
|
||||||
|
} else if ((asset->state & ASSET_STATE_VRAM_GC)
|
||||||
|
&& time - asset->last_access <= dt
|
||||||
|
) {
|
||||||
|
ams->asset_components[asset->component_id].vram_size -= asset->vram_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
chunk_iterate_end;
|
||||||
|
}
|
||||||
|
|
||||||
asset = asset->next;
|
Asset* ams_insert_asset(AssetManagementSystem* ams, Asset* asset_temp, const char* name)
|
||||||
|
{
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset_temp->component_id];
|
||||||
|
|
||||||
|
int32 free_data = chunk_reserve(&ac->asset_memory, asset_temp->size);
|
||||||
|
if (free_data < 0) {
|
||||||
|
ASSERT_SIMPLE(free_data >= 0);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void thrd_ams_garbage_collect(AssetManagementSystem* ams, uint64 time, uint64 dt)
|
byte* asset_data = chunk_get_element(&ac->asset_memory, free_data);
|
||||||
{
|
|
||||||
pthread_mutex_lock(&ams->mutex);
|
|
||||||
ams_garbage_collect(ams, time, dt);
|
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void thrd_ams_garbage_collect(AssetManagementSystem* ams)
|
asset_temp->self = asset_data;
|
||||||
{
|
asset_temp->size = asset_temp->size; // Crucial for freeing
|
||||||
pthread_mutex_lock(&ams->mutex);
|
asset_temp->ram_size = ac->asset_memory.chunk_size * asset_temp->size;
|
||||||
ams_garbage_collect(ams);
|
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Asset* thrd_ams_reserve_asset(AssetManagementSystem* ams, const char* name, uint32 elements = 1) {
|
ac->vram_size += asset_temp->vram_size;
|
||||||
pthread_mutex_lock(&ams->mutex);
|
ac->ram_size += asset_temp->ram_size;
|
||||||
Asset* asset = ams_reserve_asset(ams, name, elements);
|
++ac->asset_count;
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
|
||||||
|
Asset* asset = (Asset *) hashmap_insert(&ams->hash_map, name, (byte *) asset_temp)->value;
|
||||||
|
DEBUG_MEMORY_RESERVE((uint64) asset->self, asset->ram_size, 180);
|
||||||
|
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset* thrd_ams_reserve_asset_start(AssetManagementSystem* ams, const char* name, uint32 elements = 1) {
|
inline
|
||||||
pthread_mutex_lock(&ams->mutex);
|
Asset* thrd_ams_insert_asset(AssetManagementSystem* ams, Asset* asset_temp, const char* name)
|
||||||
|
{
|
||||||
|
AssetComponent* ac = &ams->asset_components[asset_temp->component_id];
|
||||||
|
|
||||||
return ams_reserve_asset(ams, name, elements);
|
pthread_mutex_lock(&ams->asset_components[asset_temp->component_id].mutex);
|
||||||
}
|
int32 free_data = chunk_reserve(&ac->asset_memory, asset_temp->size);
|
||||||
|
if (free_data < 0) {
|
||||||
|
pthread_mutex_unlock(&ams->asset_components[asset_temp->component_id].mutex);
|
||||||
|
ASSERT_SIMPLE(free_data >= 0);
|
||||||
|
|
||||||
void thrd_ams_reserve_asset_end(AssetManagementSystem* ams) {
|
return NULL;
|
||||||
pthread_mutex_unlock(&ams->mutex);
|
}
|
||||||
|
pthread_mutex_unlock(&ams->asset_components[asset_temp->component_id].mutex);
|
||||||
|
|
||||||
|
byte* asset_data = chunk_get_element(&ac->asset_memory, free_data);
|
||||||
|
memcpy(asset_data, asset_temp->self, sizeof(Asset));
|
||||||
|
|
||||||
|
asset_temp->self = asset_data;
|
||||||
|
asset_temp->ram_size = ac->asset_memory.chunk_size * asset_temp->size;
|
||||||
|
|
||||||
|
ac->vram_size += asset_temp->vram_size;
|
||||||
|
ac->ram_size += asset_temp->ram_size;
|
||||||
|
++ac->asset_count;
|
||||||
|
|
||||||
|
Asset* asset = (Asset *) hashmap_insert(&ams->hash_map, name, (byte *) asset_temp)->value;
|
||||||
|
DEBUG_MEMORY_RESERVE((uint64) asset->self, asset->ram_size, 180);
|
||||||
|
|
||||||
|
atomic_set_release(&asset->is_loaded, 1);
|
||||||
|
|
||||||
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#ifndef TOS_ASSET_TYPE_H
|
#ifndef TOS_ASSET_TYPE_H
|
||||||
#define TOS_ASSET_TYPE_H
|
#define TOS_ASSET_TYPE_H
|
||||||
|
|
||||||
enum AssetType {
|
enum AssetType : byte {
|
||||||
ASSET_TYPE_GENERAL,
|
ASSET_TYPE_GENERAL,
|
||||||
ASSET_TYPE_OBJ,
|
ASSET_TYPE_OBJ,
|
||||||
ASSET_TYPE_AUDIO,
|
ASSET_TYPE_AUDIO,
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@
|
||||||
|
|
||||||
#include "../utils/StringUtils.h"
|
#include "../utils/StringUtils.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Audio.h"
|
#include "Audio.h"
|
||||||
#include "AudioSetting.h"
|
#include "AudioSetting.h"
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,7 @@
|
||||||
#include "../utils/MathUtils.h"
|
#include "../utils/MathUtils.h"
|
||||||
#include "../memory/ChunkMemory.h"
|
#include "../memory/ChunkMemory.h"
|
||||||
#include "../math/matrix/MatrixFloat32.h"
|
#include "../math/matrix/MatrixFloat32.h"
|
||||||
|
#include "../thread/Atomic.h"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/threading/Atomic.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Atomic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if DIRECT_SOUND
|
#if DIRECT_SOUND
|
||||||
#include "../platform/win32/audio/DirectSound.h"
|
#include "../platform/win32/audio/DirectSound.h"
|
||||||
|
|
@ -50,10 +45,11 @@ enum AudioEffect {
|
||||||
AUDIO_EFFECT_EASE_IN = 1 << 14,
|
AUDIO_EFFECT_EASE_IN = 1 << 14,
|
||||||
AUDIO_EFFECT_EASE_OUT = 1 << 15,
|
AUDIO_EFFECT_EASE_OUT = 1 << 15,
|
||||||
AUDIO_EFFECT_SPEED = 1 << 16,
|
AUDIO_EFFECT_SPEED = 1 << 16,
|
||||||
|
AUDIO_EFFECT_REPEAT = 1 << 17,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioInstance {
|
struct AudioInstance {
|
||||||
int64 id;
|
int32 id;
|
||||||
AudioLocationSetting origin;
|
AudioLocationSetting origin;
|
||||||
|
|
||||||
uint32 audio_size;
|
uint32 audio_size;
|
||||||
|
|
@ -62,7 +58,6 @@ struct AudioInstance {
|
||||||
uint64 effect;
|
uint64 effect;
|
||||||
uint32 sample_index;
|
uint32 sample_index;
|
||||||
byte channels;
|
byte channels;
|
||||||
bool repeat;
|
|
||||||
|
|
||||||
// @todo How to implement audio that is only supposed to be played after a certain other sound file is finished
|
// @todo How to implement audio that is only supposed to be played after a certain other sound file is finished
|
||||||
// e.g. queueing soundtracks/ambient noise
|
// e.g. queueing soundtracks/ambient noise
|
||||||
|
|
@ -130,28 +125,37 @@ bool audio_mixer_is_active(AudioMixer* mixer) {
|
||||||
return (mixer->state_old = mixer_state) == AUDIO_MIXER_STATE_ACTIVE;
|
return (mixer->state_old = mixer_state) == AUDIO_MIXER_STATE_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo expand AudioLocationSetting so that it also includes audio effects, repeat etc.
|
void audio_mixer_play(AudioMixer* mixer, int32 id, Audio* audio, AudioInstance* settings = NULL)
|
||||||
void audio_mixer_add(AudioMixer* mixer, int64 id, Audio* audio, AudioLocationSetting* origin)
|
|
||||||
{
|
{
|
||||||
int64 index = chunk_reserve(&mixer->audio_instances, 1);
|
int32 index = chunk_reserve(&mixer->audio_instances, 1);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @question Do I really want to use audio instance? wouldn't Audio* be sufficient?
|
|
||||||
// Well AudioInstance is a little bit smaller but is this really worth it, probably yes?!
|
|
||||||
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, index);
|
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, index);
|
||||||
instance->id = id;
|
instance->id = id;
|
||||||
instance->audio_size = audio->size;
|
instance->audio_size = audio->size;
|
||||||
instance->audio_data = audio->data;
|
instance->audio_data = audio->data;
|
||||||
instance->channels = audio->channels;
|
instance->channels = audio->channels;
|
||||||
|
|
||||||
if (origin) {
|
if (settings) {
|
||||||
memcpy(&instance->origin, origin, sizeof(AudioLocationSetting));
|
memcpy(&instance->origin, &settings->origin, sizeof(AudioLocationSetting));
|
||||||
|
instance->effect = settings->effect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_mixer_add_unique(AudioMixer* mixer, int64 id, Audio* audio, AudioLocationSetting* origin)
|
void audio_mixer_play(AudioMixer* mixer, AudioInstance* settings)
|
||||||
|
{
|
||||||
|
int32 index = chunk_reserve(&mixer->audio_instances, 1);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, index);
|
||||||
|
memcpy(instance, settings, sizeof(AudioInstance));
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_mixer_play_unique(AudioMixer* mixer, int32 id, Audio* audio, AudioInstance* settings = NULL)
|
||||||
{
|
{
|
||||||
for (uint32 i = 0; i < mixer->audio_instances.count; ++i) {
|
for (uint32 i = 0; i < mixer->audio_instances.count; ++i) {
|
||||||
// @performance We are not really utilizing chunk memory.
|
// @performance We are not really utilizing chunk memory.
|
||||||
|
|
@ -163,16 +167,31 @@ void audio_mixer_add_unique(AudioMixer* mixer, int64 id, Audio* audio, AudioLoca
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_mixer_add(mixer, id, audio, origin);
|
audio_mixer_play(mixer, id, audio, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_mixer_remove(AudioMixer* mixer, int64 id)
|
void audio_mixer_play_unique(AudioMixer* mixer, AudioInstance* settings)
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < mixer->audio_instances.count; ++i) {
|
||||||
|
// @performance We are not really utilizing chunk memory.
|
||||||
|
// Maybe a simple array would be better
|
||||||
|
// Or we need to use more chunk functions / maybe even create a chunk_iterate() function?
|
||||||
|
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i);
|
||||||
|
if (instance->id == settings->id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_mixer_play(mixer, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_mixer_remove(AudioMixer* mixer, int32 id)
|
||||||
{
|
{
|
||||||
for (uint32 i = 0; i < mixer->audio_instances.count; ++i) {
|
for (uint32 i = 0; i < mixer->audio_instances.count; ++i) {
|
||||||
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i);
|
AudioInstance* instance = (AudioInstance *) chunk_get_element(&mixer->audio_instances, i);
|
||||||
if (instance->id == id) {
|
if (instance->id == id) {
|
||||||
instance->id = 0;
|
instance->id = 0;
|
||||||
chunk_free_element(&mixer->audio_instances, i);
|
chunk_free_elements(&mixer->audio_instances, i);
|
||||||
|
|
||||||
// No return, since we want to remove all instances
|
// No return, since we want to remove all instances
|
||||||
}
|
}
|
||||||
|
|
@ -475,7 +494,7 @@ void audio_mixer_mix(AudioMixer* mixer, uint32 size) {
|
||||||
// We make it stereo
|
// We make it stereo
|
||||||
for (int32 j = 0; j < limit; ++j) {
|
for (int32 j = 0; j < limit; ++j) {
|
||||||
if (sound_sample_index >= sound_sample_count) {
|
if (sound_sample_index >= sound_sample_count) {
|
||||||
if (!sound->repeat) {
|
if (!(sound->effect & AUDIO_EFFECT_REPEAT)) {
|
||||||
limit = j;
|
limit = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -494,7 +513,7 @@ void audio_mixer_mix(AudioMixer* mixer, uint32 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply effects based on sound's effect type
|
// Apply effects based on sound's effect type
|
||||||
if (sound->effect) {
|
if (sound->effect && sound->effect != AUDIO_EFFECT_REPEAT) {
|
||||||
int32 sample_adjustment = mixer_effects_mono(mixer, sound->effect, sound_sample_index);
|
int32 sample_adjustment = mixer_effects_mono(mixer, sound->effect, sound_sample_index);
|
||||||
sound_sample_index += sample_adjustment;
|
sound_sample_index += sample_adjustment;
|
||||||
limit += sample_adjustment;
|
limit += sample_adjustment;
|
||||||
|
|
@ -502,7 +521,7 @@ void audio_mixer_mix(AudioMixer* mixer, uint32 size) {
|
||||||
} else {
|
} else {
|
||||||
for (int32 j = 0; j < limit; ++j) {
|
for (int32 j = 0; j < limit; ++j) {
|
||||||
if (sound_sample_index >= sound_sample_count) {
|
if (sound_sample_index >= sound_sample_count) {
|
||||||
if (!sound->repeat) {
|
if (!(sound->effect & AUDIO_EFFECT_REPEAT)) {
|
||||||
limit = j;
|
limit = j;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -520,7 +539,7 @@ void audio_mixer_mix(AudioMixer* mixer, uint32 size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply effects based on sound's effect type
|
// Apply effects based on sound's effect type
|
||||||
if (sound->effect) {
|
if (sound->effect && sound->effect != AUDIO_EFFECT_REPEAT) {
|
||||||
int32 sample_adjustment = mixer_effects_stereo() / 2;;
|
int32 sample_adjustment = mixer_effects_stereo() / 2;;
|
||||||
sound_sample_index += sample_adjustment;
|
sound_sample_index += sample_adjustment;
|
||||||
limit += sample_adjustment;
|
limit += sample_adjustment;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../utils/EndianUtils.h"
|
#include "../utils/EndianUtils.h"
|
||||||
#include "../audio/Audio.cpp"
|
#include "../audio/Audio.cpp"
|
||||||
#include "../stdlib/simd/SIMD_I32.h"
|
#include "../stdlib/Simd.h"
|
||||||
|
|
||||||
#define QOA_SLICE_LEN 20
|
#define QOA_SLICE_LEN 20
|
||||||
#define QOA_SLICES_PER_FRAME 256
|
#define QOA_SLICES_PER_FRAME 256
|
||||||
|
|
|
||||||
491
command/AppCmdBuffer.cpp
Normal file
491
command/AppCmdBuffer.cpp
Normal file
|
|
@ -0,0 +1,491 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_APP_COMMAND_BUFFER_C
|
||||||
|
#define TOS_APP_COMMAND_BUFFER_C
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AppCmdBuffer by itself doesn't do much, it simply takes in commands and executes them.
|
||||||
|
* The actual execution depends on the implementation of the underlying systems like:
|
||||||
|
* ECS, AMS, AudioMixer, ...
|
||||||
|
* The AppCmdBuffer simplifies the interaction with those systems since the caller has to care less
|
||||||
|
* about the information flow, function structure etc.
|
||||||
|
* On the other hand the caller loses some control:
|
||||||
|
* No control over the execution order, unless additional overhead like priority gets introduced
|
||||||
|
* No control over what type of command are executed, unless additional overhead like command type checks get introduced
|
||||||
|
* ...
|
||||||
|
* In many cases you don't need this type of control, but when you need it you should probably look at how
|
||||||
|
* this AppCmdBuffer interacts with the individual systems and manually call those
|
||||||
|
*/
|
||||||
|
#include "AppCmdBuffer.h"
|
||||||
|
|
||||||
|
inline
|
||||||
|
void cmd_buffer_create(AppCmdBuffer* cb, BufferMemory* buf, int32 commands_count)
|
||||||
|
{
|
||||||
|
chunk_init(&cb->commands, buf, commands_count, sizeof(Command), 64);
|
||||||
|
pthread_mutex_init(&cb->mutex, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This doesn't load the asset directly but tells (most likely) a worker thread to load an asset
|
||||||
|
static inline
|
||||||
|
void cmd_asset_load_enqueue(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
queue_enqueue_wait_atomic(cb->assets_to_load, (byte *) cmd->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void* cmd_func_run(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
CommandFunc func = *((CommandFunc *) cmd->data);
|
||||||
|
return func(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_asset_load(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
int32 asset_id = (int32) str_to_int((char *) cmd->data);
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
return asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->thrd_mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_audio_play_enqueue(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, (char *) cmd->data);
|
||||||
|
if (!asset) {
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo How to handle settings = AudioInstance
|
||||||
|
audio_mixer_play(
|
||||||
|
&cb->mixer[(cmd->data + 32) ? *((int32 *) (cmd->data + 32)) : 0], // @bug how to handle multiple mixers
|
||||||
|
asset->official_id + 1, // @bug + 1 necessary since it starts at 0, I think. we are still in the design phase :)
|
||||||
|
(Audio *) asset->self
|
||||||
|
);
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_audio_play_async(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, (char *) cmd->data);
|
||||||
|
if (!asset) {
|
||||||
|
cmd_asset_load_enqueue(cb, cmd);
|
||||||
|
} else {
|
||||||
|
cmd_audio_play_enqueue(cb, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_texture_create(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, (char *) cmd->data);
|
||||||
|
if (!asset) {
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture* texture = (Texture *) asset->self;
|
||||||
|
if (cb->gpu_api == GPU_API_TYPE_OPENGL
|
||||||
|
&& !(texture->image.image_settings & IMAGE_SETTING_BOTTOM_TO_TOP)
|
||||||
|
) {
|
||||||
|
image_flip_vertical(cb->thrd_mem_vol, &texture->image);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_texture_load_async(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, (char *) cmd->data);
|
||||||
|
if (!asset) {
|
||||||
|
cmd_asset_load_enqueue(cb, cmd);
|
||||||
|
} else {
|
||||||
|
cmd_texture_create(cb, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_font_create(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, (char *) cmd->data);
|
||||||
|
if (!asset) {
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font* font = (Font *) asset->self;
|
||||||
|
if (cb->gpu_api == GPU_API_TYPE_OPENGL) {
|
||||||
|
font_invert_coordinates(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Asset* cmd_font_load_async(AppCmdBuffer* cb, Command* cmd)
|
||||||
|
{
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, (char *) cmd->data);
|
||||||
|
if (!asset) {
|
||||||
|
cmd_asset_load_enqueue(cb, cmd);
|
||||||
|
} else {
|
||||||
|
cmd_font_create(cb, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void thrd_cmd_insert(AppCmdBuffer* cb, Command* cmd_temp)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&cb->mutex);
|
||||||
|
int32 index = chunk_reserve(&cb->commands, 1);
|
||||||
|
if (index < 0) {
|
||||||
|
pthread_mutex_unlock(&cb->mutex);
|
||||||
|
ASSERT_SIMPLE(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > cb->last_element) {
|
||||||
|
cb->last_element = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command* cmd = (Command *) chunk_get_element(&cb->commands, index);
|
||||||
|
memcpy(cmd, cmd_temp, sizeof(Command));
|
||||||
|
pthread_mutex_unlock(&cb->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void thrd_cmd_insert(AppCmdBuffer* cb, CommandType type, int32 data)
|
||||||
|
{
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = type;
|
||||||
|
*((int32 *) cmd.data) = data;
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void thrd_cmd_insert(AppCmdBuffer* cb, CommandType type, const char* data)
|
||||||
|
{
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = type;
|
||||||
|
str_copy_short((char *) cmd.data, data);
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_func_insert(AppCmdBuffer* cb, CommandType type, CommandFunc* func) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_FUNC_RUN;
|
||||||
|
*((CommandFunc *) cmd.data) = *func;
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_audio_play(AppCmdBuffer* cb, int32 data) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_AUDIO_PLAY;
|
||||||
|
*((int32 *) cmd.data) = data;
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_audio_play(AppCmdBuffer* cb, const char* data) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_AUDIO_PLAY;
|
||||||
|
str_copy_short((char *) cmd.data, data);
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_func_run(AppCmdBuffer* cb, CommandFunc* func) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_FUNC_RUN;
|
||||||
|
*((CommandFunc *) cmd.data) = *func;
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_texture_load(AppCmdBuffer* cb, int32 data) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_TEXTURE_LOAD;
|
||||||
|
*((int32 *) cmd.data) = data;
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_texture_load(AppCmdBuffer* cb, const char* data) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_TEXTURE_LOAD;
|
||||||
|
str_copy_short((char *) cmd.data, data);
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_font_load(AppCmdBuffer* cb, int32 data) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_FONT_LOAD;
|
||||||
|
*((int32 *) cmd.data) = data;
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void thrd_cmd_font_load(AppCmdBuffer* cb, const char* data) {
|
||||||
|
Command cmd;
|
||||||
|
cmd.type = CMD_FONT_LOAD;
|
||||||
|
str_copy_short((char *) cmd.data, data);
|
||||||
|
|
||||||
|
thrd_cmd_insert(cb, &cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_asset_load(AppCmdBuffer* cb, int32 asset_id)
|
||||||
|
{
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
return asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_asset_load(AppCmdBuffer* cb, const char* asset_id_str)
|
||||||
|
{
|
||||||
|
int32 asset_id = (int32) str_to_int(asset_id_str);
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
return asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_audio_play(AppCmdBuffer* cb, int32 asset_id)
|
||||||
|
{
|
||||||
|
// Check if asset already loaded
|
||||||
|
char id_str[9];
|
||||||
|
int_to_hex(asset_id, id_str);
|
||||||
|
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, id_str);
|
||||||
|
|
||||||
|
// Load asset if not loaded
|
||||||
|
if (!asset) {
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
asset = asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo How to handle settings = AudioInstance
|
||||||
|
audio_mixer_play(
|
||||||
|
&cb->mixer[0], // @bug how to handle multiple mixers
|
||||||
|
asset->official_id + 1, // @bug + 1 necessary since it starts at 0, I think. we are still in the design phase :)
|
||||||
|
(Audio *) asset->self
|
||||||
|
);
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_audio_play(AppCmdBuffer* cb, const char* name) {
|
||||||
|
// Check if asset already loaded
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, name);
|
||||||
|
|
||||||
|
// Load asset if not loaded
|
||||||
|
if (!asset) {
|
||||||
|
int32 asset_id = (int32) hex_to_int(name);
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
asset = asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo How to handle settings = AudioInstance
|
||||||
|
audio_mixer_play(
|
||||||
|
&cb->mixer[0], // @bug how to handle multiple mixers
|
||||||
|
asset->official_id + 1, // @bug + 1 necessary since it starts at 0, I think. we are still in the design phase :)
|
||||||
|
(Audio *) asset->self
|
||||||
|
);
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void* cmd_func_run(AppCmdBuffer* cb, CommandFunc func) {
|
||||||
|
return func(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_texture_load(AppCmdBuffer* cb, int32 asset_id) {
|
||||||
|
// Check if asset already loaded
|
||||||
|
char id_str[9];
|
||||||
|
int_to_hex(asset_id, id_str);
|
||||||
|
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, id_str);
|
||||||
|
|
||||||
|
// Load asset if not loaded
|
||||||
|
if (!asset) {
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
asset = asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup basic texture
|
||||||
|
Texture* texture = (Texture *) asset->self;
|
||||||
|
if (cb->gpu_api == GPU_API_TYPE_OPENGL
|
||||||
|
&& !(texture->image.image_settings & IMAGE_SETTING_BOTTOM_TO_TOP)
|
||||||
|
) {
|
||||||
|
image_flip_vertical(cb->mem_vol, &texture->image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @question What about texture upload?
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_texture_load(AppCmdBuffer* cb, const char* name) {
|
||||||
|
// Check if asset already loaded
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, name);
|
||||||
|
|
||||||
|
// Load asset if not loaded
|
||||||
|
if (!asset) {
|
||||||
|
int32 asset_id = (int32) hex_to_int(name);
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
asset = asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup basic texture
|
||||||
|
Texture* texture = (Texture *) asset->self;
|
||||||
|
if (cb->gpu_api == GPU_API_TYPE_OPENGL
|
||||||
|
&& !(texture->image.image_settings & IMAGE_SETTING_BOTTOM_TO_TOP)
|
||||||
|
) {
|
||||||
|
image_flip_vertical(cb->mem_vol, &texture->image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @question What about texture upload?
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_font_load(AppCmdBuffer* cb, int32 asset_id) {
|
||||||
|
// Check if asset already loaded
|
||||||
|
char id_str[9];
|
||||||
|
int_to_hex(asset_id, id_str);
|
||||||
|
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, id_str);
|
||||||
|
|
||||||
|
// Load asset if not loaded
|
||||||
|
if (!asset) {
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
asset = asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup font
|
||||||
|
Font* font = (Font *) asset->self;
|
||||||
|
if (cb->gpu_api == GPU_API_TYPE_OPENGL) {
|
||||||
|
font_invert_coordinates(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @question What about also loading the font atlas
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Asset* cmd_font_load(AppCmdBuffer* cb, const char* name) {
|
||||||
|
// Check if asset already loaded
|
||||||
|
Asset* asset = thrd_ams_get_asset_wait(cb->ams, name);
|
||||||
|
|
||||||
|
// Load asset if not loaded
|
||||||
|
if (!asset) {
|
||||||
|
int32 asset_id = (int32) hex_to_int(name);
|
||||||
|
int32 archive_id = (asset_id >> 24) & 0xFF;
|
||||||
|
asset = asset_archive_asset_load(&cb->asset_archives[archive_id], asset_id, cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup font
|
||||||
|
Font* font = (Font *) asset->self;
|
||||||
|
if (cb->gpu_api == GPU_API_TYPE_OPENGL) {
|
||||||
|
font_invert_coordinates(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @question What about also loading the font atlas
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @question In some cases we don't remove an element if it couldn't get completed
|
||||||
|
// Would it make more sense to remove it and add a new follow up command automatically in such cases?
|
||||||
|
// e.g. couldn't play audio since it isn't loaded -> queue for asset load -> queue for internal play
|
||||||
|
// I gues this only makes sense if we would switch to a queue
|
||||||
|
void cmd_iterate(AppCmdBuffer* cb)
|
||||||
|
{
|
||||||
|
int32 last_element = 0;
|
||||||
|
int32 chunk_id = 0;
|
||||||
|
chunk_iterate_start(&cb->commands, chunk_id)
|
||||||
|
Command* cmd = (Command *) chunk_get_element(&cb->commands, chunk_id);
|
||||||
|
bool remove = true;
|
||||||
|
|
||||||
|
switch (cmd->type) {
|
||||||
|
case CMD_FUNC_RUN: {
|
||||||
|
cmd_func_run(cb, cmd);
|
||||||
|
} break;
|
||||||
|
case CMD_ASSET_ENQUEUE: {
|
||||||
|
cmd_asset_load_enqueue(cb, cmd);
|
||||||
|
} break;
|
||||||
|
case CMD_ASSET_LOAD: {
|
||||||
|
cmd_asset_load(cb, cmd);
|
||||||
|
} break;
|
||||||
|
case CMD_FILE_LOAD: {} break;
|
||||||
|
case CMD_TEXTURE_LOAD: {
|
||||||
|
remove = cmd_texture_load_async(cb, cmd) != NULL;
|
||||||
|
} break;
|
||||||
|
case CMD_TEXTURE_CREATE: {
|
||||||
|
// Internal only
|
||||||
|
cmd_texture_create(cb, cmd);
|
||||||
|
} break;
|
||||||
|
case CMD_FONT_LOAD: {
|
||||||
|
remove = cmd_font_load_async(cb, cmd) != NULL;
|
||||||
|
} break;
|
||||||
|
case CMD_FONT_CREATE: {
|
||||||
|
// Internal only
|
||||||
|
cmd_font_create(cb, cmd);
|
||||||
|
} break;
|
||||||
|
case CMD_AUDIO_PLAY: {
|
||||||
|
cmd_audio_play_async(cb, cmd);
|
||||||
|
} break;
|
||||||
|
case CMD_AUDIO_ENQUEUE: {
|
||||||
|
// Internal only
|
||||||
|
remove = cmd_audio_play_enqueue(cb, cmd) != NULL;
|
||||||
|
} break;
|
||||||
|
case CMD_SHADER_LOAD: {
|
||||||
|
remove = cmd_shader_load(cb, cmd) != NULL;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remove) {
|
||||||
|
last_element = chunk_id;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_free_element(&cb->commands, free_index, bit_index);
|
||||||
|
|
||||||
|
// @performance This adds some unnecessary overhead.
|
||||||
|
// It would be much better, if we could define cb->last_element as the limit in the for loop
|
||||||
|
if (chunk_id == cb->last_element) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chunk_iterate_end;
|
||||||
|
|
||||||
|
cb->last_element = last_element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @performance Locking the entire thing during the iteration is horribly slow, fix.
|
||||||
|
// Solution 1: Use Queue
|
||||||
|
// Solution 2: create a mask for the chunk->free which will be set (and only then locked) after everything is done
|
||||||
|
// This has the risk that if it takes a long time we may run out of free indices for insert
|
||||||
|
// This shouldn't happen since the command buffer shouldn't fill up in just 1-3 frames
|
||||||
|
void thrd_cmd_iterate(AppCmdBuffer* cb)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&cb->mutex);
|
||||||
|
cmd_iterate(cb);
|
||||||
|
pthread_mutex_unlock(&cb->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
60
command/AppCmdBuffer.h
Normal file
60
command/AppCmdBuffer.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_APP_COMMAND_BUFFER_H
|
||||||
|
#define TOS_APP_COMMAND_BUFFER_H
|
||||||
|
|
||||||
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../memory/ChunkMemory.h"
|
||||||
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../audio/AudioMixer.h"
|
||||||
|
#include "../audio/Audio.h"
|
||||||
|
#include "../asset/AssetArchive.h"
|
||||||
|
#include "../gpuapi/GpuApiType.h"
|
||||||
|
#include "../asset/Asset.h"
|
||||||
|
#include "../asset/AssetManagementSystem.h"
|
||||||
|
#include "../object/Texture.h"
|
||||||
|
#include "../memory/Queue.h"
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
struct AppCmdBuffer {
|
||||||
|
// @performance A queue would be much faster than ChunkMemory.
|
||||||
|
// We only use Chunk memory since we might want to run only certain commands instead of all of them
|
||||||
|
ChunkMemory commands;
|
||||||
|
int32 last_element;
|
||||||
|
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
// Application data for cmd access
|
||||||
|
// The list below depends on what kind of systems our command buffer needs access to
|
||||||
|
// Memory for when a buffer function (e.g. load_asset) is run in a thread context
|
||||||
|
RingMemory* thrd_mem_vol;
|
||||||
|
|
||||||
|
// Memory for when a buffer function (e.g. load_asset) is run in the main loop
|
||||||
|
RingMemory* mem_vol;
|
||||||
|
AssetManagementSystem* ams;
|
||||||
|
AssetArchive* asset_archives;
|
||||||
|
Queue* assets_to_load;
|
||||||
|
AudioMixer* mixer;
|
||||||
|
GpuApiType gpu_api;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if OPENGL
|
||||||
|
#include "../gpuapi/opengl/AppCmdBuffer.h"
|
||||||
|
#elif VULKAN
|
||||||
|
inline void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) { return NULL; }
|
||||||
|
inline void* cmd_shader_load(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||||
|
#elif DIRECTX
|
||||||
|
inline void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) { return NULL; }
|
||||||
|
inline void* cmd_shader_load(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||||
|
#else
|
||||||
|
inline void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) { return NULL; }
|
||||||
|
inline void* cmd_shader_load(AppCmdBuffer* cb, void* shader, int32* shader_ids) { return NULL; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
35
command/Command.h
Normal file
35
command/Command.h
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_COMMAND_H
|
||||||
|
#define TOS_COMMAND_H
|
||||||
|
|
||||||
|
#include "../stdlib/Types.h"
|
||||||
|
|
||||||
|
enum CommandType {
|
||||||
|
CMD_FUNC_RUN,
|
||||||
|
CMD_ASSET_ENQUEUE,
|
||||||
|
CMD_ASSET_LOAD,
|
||||||
|
CMD_FILE_LOAD,
|
||||||
|
CMD_FONT_LOAD,
|
||||||
|
CMD_FONT_CREATE,
|
||||||
|
CMD_TEXTURE_LOAD,
|
||||||
|
CMD_TEXTURE_CREATE, // Only for internal use
|
||||||
|
CMD_AUDIO_PLAY,
|
||||||
|
CMD_AUDIO_ENQUEUE, // Only for internal use
|
||||||
|
CMD_SHADER_LOAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command {
|
||||||
|
CommandType type;
|
||||||
|
byte data[28]; // @todo to be adjusted
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void* (*CommandFunc)(Command*);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* @copyright Jingga
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
#ifndef TOS_ANIMATION_ENTITY_H
|
|
||||||
#define TOS_ANIMATION_ENTITY_H
|
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
|
||||||
#include "../animation/AnimationEaseType.h"
|
|
||||||
#include "../animation/Animation.h"
|
|
||||||
#include "../utils/BitUtils.h"
|
|
||||||
#include "EntityComponentSystem.h"
|
|
||||||
|
|
||||||
struct AnimationEntity {
|
|
||||||
AnimationEaseType type;
|
|
||||||
uint32 start_time;
|
|
||||||
uint32 last_time;
|
|
||||||
f32 interval;
|
|
||||||
f32 progress;
|
|
||||||
byte state_last;
|
|
||||||
byte state;
|
|
||||||
|
|
||||||
// @question Do we want another flag that indicates if the entity got handled by the main loop?
|
|
||||||
// this way we could do the animation process in a thread and only overwrite the state_last whenever the flag is true
|
|
||||||
// However, we would have to implement locking or atomics which might be really bad depending on how we use this data
|
|
||||||
};
|
|
||||||
|
|
||||||
void update_animation_entity(AnimationEntity* anim, uint32 time, uint32 delay)
|
|
||||||
{
|
|
||||||
anim->state_last = anim->state;
|
|
||||||
|
|
||||||
switch (anim->type) {
|
|
||||||
case ANIMATION_EASE_DISCRETE: {
|
|
||||||
anim->progress = anim_discrete((f32) (time - anim->start_time + delay) / (f32) anim->interval);
|
|
||||||
anim->state = (int32) ((f32) anim->state - anim->progress);
|
|
||||||
} break;
|
|
||||||
default: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_animation_entities(EntityComponentSystem* ecs, uint32 time, uint32 delay)
|
|
||||||
{
|
|
||||||
int32 chunk_bytes = (ecs->entity_data_memory.size + 63) / 64;
|
|
||||||
|
|
||||||
// @performance It might make sense to iterate by int16 or even int32 instead of byte. Needs profiling
|
|
||||||
for (int32 i = 0; i < chunk_bytes; ++i) {
|
|
||||||
// @question Do we want this to be the first case. It probably depends on how often a byte is realistically empty
|
|
||||||
if (!ecs->entity_data_memory.free[i]) {
|
|
||||||
continue;
|
|
||||||
} else if (ecs->entity_data_memory.free[i] == 256) {
|
|
||||||
// @performance If we go larger than 8bit in the outer loop we also have to adjust it here
|
|
||||||
// AND maybe we would want to do sub checks then for 8bit again
|
|
||||||
for (int32 j = 0; j < 8; ++j) {
|
|
||||||
AnimationEntity* anim = (AnimationEntity *) chunk_get_element(&ecs->entity_data_memory, i * 8 + j);
|
|
||||||
update_animation_entity(anim, time, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @performance If we go larger than 8bit in the outer loop we also have to adjust it here
|
|
||||||
// AND maybe we would want to do sub checks then for 8bit again
|
|
||||||
for (int32 j = 0; j < 8; ++j) {
|
|
||||||
if (!IS_BIT_SET_L2R(ecs->entity_data_memory.free[i], j, 1)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimationEntity* anim = (AnimationEntity *) chunk_get_element(&ecs->entity_data_memory, i * 8 + j);
|
|
||||||
update_animation_entity(anim, time, delay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
69
entity/AnimationEntityComponent.h
Normal file
69
entity/AnimationEntityComponent.h
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_ANIMATION_ENTITY_H
|
||||||
|
#define TOS_ANIMATION_ENTITY_H
|
||||||
|
|
||||||
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../animation/AnimationEaseType.h"
|
||||||
|
#include "../animation/Animation.h"
|
||||||
|
#include "../utils/BitUtils.h"
|
||||||
|
#include "EntityComponentSystem.h"
|
||||||
|
#include "Entity.h"
|
||||||
|
|
||||||
|
enum AnimationSetting {
|
||||||
|
ANIMATION_SETTING_PAUSE = 1 << 0,
|
||||||
|
ANIMATION_SETTING_REPEAT = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationEntityComponent {
|
||||||
|
Entity* entity;
|
||||||
|
AnimationEaseType type;
|
||||||
|
uint32 start_time;
|
||||||
|
uint32 last_time;
|
||||||
|
f32 interval;
|
||||||
|
f32 progress;
|
||||||
|
byte state_last;
|
||||||
|
byte state;
|
||||||
|
|
||||||
|
// Contains repeat, pause etc
|
||||||
|
byte setting;
|
||||||
|
|
||||||
|
// @question Do we want another flag that indicates if the entity got handled by the main loop?
|
||||||
|
// this way we could do the animation process in a thread and only overwrite the state_last whenever the flag is true
|
||||||
|
// However, we would have to implement locking or atomics which might be really bad depending on how we use this data
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void update_animation_entity(AnimationEntityComponent* anim, uint32 time, uint32 delay)
|
||||||
|
{
|
||||||
|
anim->state_last = anim->state;
|
||||||
|
|
||||||
|
switch (anim->type) {
|
||||||
|
case ANIMATION_EASE_DISCRETE: {
|
||||||
|
anim->progress = anim_discrete((f32) (time - anim->start_time + delay) / (f32) anim->interval);
|
||||||
|
anim->state = (byte) ((f32) anim->state - anim->progress + FLOAT_CAST_EPS);
|
||||||
|
} break;
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_animation_entities(ChunkMemory* anim_ec, uint32 time, uint32 delay)
|
||||||
|
{
|
||||||
|
int32 chunk_id = 0;
|
||||||
|
chunk_iterate_start(anim_ec, chunk_id)
|
||||||
|
AnimationEntityComponent* anim = (AnimationEntityComponent *) chunk_get_element(anim_ec, chunk_id);
|
||||||
|
if (anim->setting & ANIMATION_SETTING_PAUSE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_animation_entity(anim, time, delay);
|
||||||
|
chunk_iterate_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
20
entity/CursorEntity.h
Normal file
20
entity/CursorEntity.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_ENTITY_CURSOR_H
|
||||||
|
#define TOS_ENTITY_CURSOR_H
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "AnimationEntityComponent.h"
|
||||||
|
|
||||||
|
struct EntityCursor {
|
||||||
|
Entity* general;
|
||||||
|
AnimationEntityComponent* anim;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -11,41 +11,23 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../stdlib/HashMap.h"
|
#include "../stdlib/HashMap.h"
|
||||||
#include "EntityType.h"
|
|
||||||
|
|
||||||
#define MAX_ENTITY_NAME_LENGTH 32
|
#define MAX_ENTITY_NAME_LENGTH 32
|
||||||
|
|
||||||
struct Entity {
|
struct Entity {
|
||||||
// The id is the same as its location in memory/in the ecs array
|
// The id is the same as its location in memory/in the ecs array
|
||||||
// This is is only an internal id and NOT the same as a db id (e.g. player id)
|
// This is is only an internal id and NOT the same as a db id (e.g. player id)
|
||||||
uint64 internal_id;
|
uint32 internal_id;
|
||||||
|
|
||||||
EntityType type;
|
uint32 last_access;
|
||||||
|
|
||||||
uint64 last_access;
|
// Which entity is used
|
||||||
|
byte type;
|
||||||
|
|
||||||
// Variable used for thread safety
|
byte state;
|
||||||
bool is_loaded;
|
|
||||||
|
|
||||||
// Describes if the asset can be removed/garbage collected IF necessary
|
// This entity represents the schema for this entity (most likely stored in a separate ecs)
|
||||||
// This however only happens if space is needed
|
uint32 schema;
|
||||||
bool can_garbage_collect_ram;
|
|
||||||
bool can_garbage_collect_vram;
|
|
||||||
|
|
||||||
// Counts the references to this entity
|
|
||||||
// e.g. textures
|
|
||||||
int16 reference_count;
|
|
||||||
|
|
||||||
// A entity can reference up to N other entities
|
|
||||||
// This allows us to quickly update the other entities
|
|
||||||
// Example: A player pulls N mobs
|
|
||||||
// @bug This means there are hard limits on how many mobs can be pulled by a player
|
|
||||||
// @question should this be an entity id?
|
|
||||||
Entity* references[50];
|
|
||||||
uint64 free_references; // bits show which is free
|
|
||||||
|
|
||||||
// @question should this be an entity id?
|
|
||||||
Entity* schema; // This entity represents the schema for this entity (most likely stored in a separate ecs)
|
|
||||||
|
|
||||||
// Actual memory address and specific entity data
|
// Actual memory address and specific entity data
|
||||||
byte* self;
|
byte* self;
|
||||||
|
|
@ -59,7 +41,7 @@ struct EntitySchema {
|
||||||
// Could be 0 if there is no official id
|
// Could be 0 if there is no official id
|
||||||
uint64 official_id;
|
uint64 official_id;
|
||||||
|
|
||||||
EntityType type;
|
byte type;
|
||||||
|
|
||||||
// Counts the references to this entity
|
// Counts the references to this entity
|
||||||
// e.g. textures
|
// e.g. textures
|
||||||
|
|
|
||||||
|
|
@ -13,55 +13,109 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/ChunkMemory.h"
|
#include "../memory/ChunkMemory.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
|
#include "../utils/BitUtils.h"
|
||||||
#include "../stdlib/HashMap.h"
|
#include "../stdlib/HashMap.h"
|
||||||
|
#include "../log/DebugMemory.h"
|
||||||
|
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
|
|
||||||
|
// Entities can be directly accessed by their id
|
||||||
|
// highest byte = entity type, lower bytes = id in respective ecs
|
||||||
struct EntityComponentSystem {
|
struct EntityComponentSystem {
|
||||||
// @question is this even necessary or could we integrate this directly into the system here?
|
int32 entity_type_count;
|
||||||
HashMap hash_map;
|
int32 component_type_count;
|
||||||
|
|
||||||
|
ChunkMemory* entities;
|
||||||
|
ChunkMemory* components;
|
||||||
|
|
||||||
uint64 ram_size;
|
uint64 ram_size;
|
||||||
uint64 vram_size;
|
uint64 vram_size;
|
||||||
uint64 entity_count;
|
uint64 entity_count;
|
||||||
int32 overhead;
|
uint64 component_count;
|
||||||
|
|
||||||
// @question Do we want this, I would assume this should be almost always true in the final game
|
// @question Do we want to add a mutex to assets. This way we don't have to lock the entire ams.
|
||||||
bool has_changed;
|
pthread_mutex_t* entity_mutex;
|
||||||
|
pthread_mutex_t* component_mutex;
|
||||||
// The indices of entity_memory and entity_data_memory are always linked
|
|
||||||
|
|
||||||
// @question Consider to reset entity_memory->last_pos to 0 before adding a new element
|
|
||||||
// This allows us to make the chunk memory more continuous which is better for iteration later on
|
|
||||||
// However, adding elements would now be slower. Needs profiling
|
|
||||||
|
|
||||||
// General entity memory
|
|
||||||
ChunkMemory entity_memory;
|
|
||||||
|
|
||||||
// Actual entity data
|
|
||||||
ChunkMemory entity_data_memory;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EntitySchemaSystem {
|
inline
|
||||||
// @question is this even necessary or could we integrate this directly into the system here?
|
void ecs_create(EntityComponentSystem* ecs, BufferMemory* buf, int32 entity_count, int32 component_count)
|
||||||
HashMap hash_map;
|
{
|
||||||
|
ecs->entity_type_count = entity_count;
|
||||||
|
ecs->entities = (ChunkMemory *) buffer_get_memory(buf, sizeof(ChunkMemory) * entity_count, 64);
|
||||||
|
|
||||||
uint64 ram_size;
|
ecs->component_type_count = component_count;
|
||||||
uint64 vram_size;
|
ecs->components = (ChunkMemory *) buffer_get_memory(buf, sizeof(ChunkMemory) * component_count, 64);
|
||||||
uint64 entity_count;
|
}
|
||||||
int32 overhead;
|
|
||||||
bool has_changed;
|
|
||||||
|
|
||||||
// The indices of entity_memory and entity_data_memory are always linked
|
inline
|
||||||
|
void ecs_entity_type_create(ChunkMemory* ec, BufferMemory* buf, int32 chunk_size, int32 count)
|
||||||
|
{
|
||||||
|
ASSERT_SIMPLE(chunk_size);
|
||||||
|
|
||||||
// General entity memory
|
chunk_init(ec, buf, count, chunk_size, 64);
|
||||||
ChunkMemory entity_memory;
|
//pthread_mutex_init(&ec->mutex, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Actual entity data
|
inline
|
||||||
ChunkMemory entity_data_memory;
|
void ecs_component_type_create(ChunkMemory* ec, BufferMemory* buf, int32 chunk_size, int32 count)
|
||||||
|
{
|
||||||
|
ASSERT_SIMPLE(chunk_size);
|
||||||
|
|
||||||
EntitySchema* first;
|
chunk_init(ec, buf, count, chunk_size, 64);
|
||||||
EntitySchema* last;
|
//pthread_mutex_init(&ec->mutex, NULL);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
Entity* ecs_get_entity(EntityComponentSystem* ecs, int32 entity_id)
|
||||||
|
{
|
||||||
|
int32 ecs_type = (entity_id >> 24) & 0xFF;
|
||||||
|
int32 raw_id = entity_id & 0x00FFFFFF;
|
||||||
|
|
||||||
|
int32 byte_index = raw_id / 64;
|
||||||
|
int32 bit_index = raw_id & 63;
|
||||||
|
|
||||||
|
return IS_BIT_SET_64_R2L(ecs->entities[ecs_type].free[byte_index], bit_index) ?
|
||||||
|
(Entity *) chunk_get_element(&ecs->entities[ecs_type], raw_id)
|
||||||
|
: NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* ecs_reserve_entity(EntityComponentSystem* ecs, uint32 entity_type)
|
||||||
|
{
|
||||||
|
ChunkMemory* mem = &ecs->entities[entity_type];
|
||||||
|
int32 free_entity = chunk_reserve(mem, 1);
|
||||||
|
if (free_entity < 0) {
|
||||||
|
ASSERT_SIMPLE(free_entity >= 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* entity = (Entity *) chunk_get_element(mem, free_entity);
|
||||||
|
|
||||||
|
// @todo log entity stats (count, ram, vram)
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* ecs_insert_entity(EntityComponentSystem* ecs, Entity* entity_temp, int32 entity_type)
|
||||||
|
{
|
||||||
|
ChunkMemory* mem = &ecs->entities[entity_type];
|
||||||
|
int32 free_entity = chunk_reserve(mem, 1);
|
||||||
|
if (free_entity < 0) {
|
||||||
|
ASSERT_SIMPLE(free_entity >= 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* entity = (Entity *) chunk_get_element(mem, free_entity);
|
||||||
|
memcpy(entity, entity_temp, mem->chunk_size);
|
||||||
|
|
||||||
|
// @todo log entity stats (count, ram, vram)
|
||||||
|
//DEBUG_MEMORY_RESERVE((uint64) entity, entity->ram_size, 180);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecs_insert_component()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
28
entity/EntitySize.h
Normal file
28
entity/EntitySize.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_ENTITY_SIZE_H
|
||||||
|
#define TOS_ENTITY_SIZE_H
|
||||||
|
|
||||||
|
enum EntitySize {
|
||||||
|
ENTITY_SIZE_32,
|
||||||
|
ENTITY_SIZE_64,
|
||||||
|
ENTITY_SIZE_128,
|
||||||
|
ENTITY_SIZE_256,
|
||||||
|
ENTITY_SIZE_512,
|
||||||
|
ENTITY_SIZE_1024,
|
||||||
|
ENTITY_SIZE_2048,
|
||||||
|
ENTITY_SIZE_4096,
|
||||||
|
ENTITY_SIZE_8192,
|
||||||
|
ENTITY_SIZE_16384,
|
||||||
|
ENTITY_SIZE_32768,
|
||||||
|
ENTITY_SIZE_65536,
|
||||||
|
ENTITY_SIZE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
/**
|
|
||||||
* Jingga
|
|
||||||
*
|
|
||||||
* @copyright Jingga
|
|
||||||
* @license OMS License 2.0
|
|
||||||
* @version 1.0.0
|
|
||||||
* @link https://jingga.app
|
|
||||||
*/
|
|
||||||
#ifndef TOS_ENTITY_TYPE_H
|
|
||||||
#define TOS_ENTITY_TYPE_H
|
|
||||||
|
|
||||||
enum EntityType {
|
|
||||||
ENTITY_TYPE_MONSTER,
|
|
||||||
ENTITY_TYPE_NPC,
|
|
||||||
ENTITY_TYPE_PLAYER,
|
|
||||||
ENTITY_TYPE_ITEM,
|
|
||||||
ENTITY_TYPE_OBJ,
|
|
||||||
ENTITY_TYPE_SIZE
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
23
font/Font.h
23
font/Font.h
|
|
@ -5,18 +5,8 @@
|
||||||
#include "../memory/BufferMemory.h"
|
#include "../memory/BufferMemory.h"
|
||||||
#include "../utils/EndianUtils.h"
|
#include "../utils/EndianUtils.h"
|
||||||
#include "../utils/Utils.h"
|
#include "../utils/Utils.h"
|
||||||
|
#include "../stdlib/Simd.h"
|
||||||
#if __aarch64__
|
#include "../system/FileUtils.cpp"
|
||||||
#include "../stdlib/sve/SVE_I32.h"
|
|
||||||
#else
|
|
||||||
#include "../stdlib/simd/SIMD_I32.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct GlyphMetrics {
|
struct GlyphMetrics {
|
||||||
f32 width; // Width of the glyph
|
f32 width; // Width of the glyph
|
||||||
|
|
@ -212,15 +202,6 @@ int32 font_from_data(
|
||||||
|
|
||||||
memcpy(font->glyphs, pos, font->glyph_count * sizeof(Glyph));
|
memcpy(font->glyphs, pos, font->glyph_count * sizeof(Glyph));
|
||||||
|
|
||||||
#if OPENGL
|
|
||||||
// @todo Implement y-offset correction
|
|
||||||
for (uint32 i = 0; i < font->glyph_count; ++i) {
|
|
||||||
float temp = font->glyphs[i].coords.y1;
|
|
||||||
font->glyphs[i].coords.y1 = 1.0f - font->glyphs[i].coords.y2;
|
|
||||||
font->glyphs[i].coords.y2 = 1.0f - temp;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SWAP_ENDIAN_LITTLE_SIMD(
|
SWAP_ENDIAN_LITTLE_SIMD(
|
||||||
(int32 *) font->glyphs,
|
(int32 *) font->glyphs,
|
||||||
(int32 *) font->glyphs,
|
(int32 *) font->glyphs,
|
||||||
|
|
|
||||||
19
gpuapi/GpuApiType.h
Normal file
19
gpuapi/GpuApiType.h
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_GPUAPI_TYPE_H
|
||||||
|
#define TOS_GPUAPI_TYPE_H
|
||||||
|
|
||||||
|
enum GpuApiType {
|
||||||
|
GPU_API_TYPE_NONE,
|
||||||
|
GPU_API_TYPE_OPENGL,
|
||||||
|
GPU_API_TYPE_VULKAN,
|
||||||
|
GPU_API_TYPE_DIRECTX
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
25
gpuapi/ShaderType.h
Normal file
25
gpuapi/ShaderType.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_GPUAPI_SHADER_TYPE_H
|
||||||
|
#define TOS_GPUAPI_SHADER_TYPE_H
|
||||||
|
|
||||||
|
enum ShaderType {
|
||||||
|
SHADER_TYPE_NONE,
|
||||||
|
SHADER_TYPE_VERTEX,
|
||||||
|
SHADER_TYPE_FRAGMENT,
|
||||||
|
SHADER_TYPE_GEOMETRY,
|
||||||
|
SHADER_TYPE_TESSELATION,
|
||||||
|
SHADER_TYPE_PIXEL,
|
||||||
|
SHADER_TYPE_MESH,
|
||||||
|
SHADER_TYPE_RAYTRACING,
|
||||||
|
SHADER_TYPE_TENSOR,
|
||||||
|
SHADER_TYPE_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
*/
|
*/
|
||||||
#ifndef TOS_GPUAPI_DIRECTX_GPU_API_CONTAINER
|
#ifndef TOS_GPUAPI_DIRECTX_GPU_API_CONTAINER_H
|
||||||
#define TOS_GPUAPI_DIRECTX_GPU_API_CONTAINER
|
#define TOS_GPUAPI_DIRECTX_GPU_API_CONTAINER_H
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
|
|
|
||||||
20
gpuapi/direct3d/Shader.h
Normal file
20
gpuapi/direct3d/Shader.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_GPUAPI_DIRECT3D_SHADER_H
|
||||||
|
#define TOS_GPUAPI_DIRECT3D_SHADER_H
|
||||||
|
|
||||||
|
#include "../../stdlib/Types.h"
|
||||||
|
|
||||||
|
struct Shader {
|
||||||
|
uint32 id;
|
||||||
|
uint32 locations[7];
|
||||||
|
byte data[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
66
gpuapi/opengl/AppCmdBuffer.h
Normal file
66
gpuapi/opengl/AppCmdBuffer.h
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_GPUAPI_OPENGL_APP_CMD_BUFFER_H
|
||||||
|
#define TOS_GPUAPI_OPENGL_APP_CMD_BUFFER_H
|
||||||
|
|
||||||
|
#include "../../stdlib/Types.h"
|
||||||
|
#include "OpenglUtils.h"
|
||||||
|
#include "Shader.h"
|
||||||
|
#include "ShaderUtils.h"
|
||||||
|
#include "../ShaderType.h"
|
||||||
|
#include "../../asset/Asset.h"
|
||||||
|
|
||||||
|
void* cmd_shader_load(AppCmdBuffer* cb, Command* cmd) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* cmd_shader_load(AppCmdBuffer* cb, Shader* shader, int32* shader_ids) {
|
||||||
|
char asset_id[9];
|
||||||
|
|
||||||
|
int32 shader_assets[SHADER_TYPE_SIZE];
|
||||||
|
for (int32 i = 0; i < SHADER_TYPE_SIZE; ++i) {
|
||||||
|
shader_assets[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 i = 0; i < SHADER_TYPE_SIZE; ++i) {
|
||||||
|
if (!shader_ids[i]) {
|
||||||
|
continue;
|
||||||
|
} else if (shader_ids[i] < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sub asset
|
||||||
|
int_to_hex(shader_ids[i], asset_id);
|
||||||
|
Asset* shader_asset = thrd_ams_get_asset_wait(cb->ams, asset_id);
|
||||||
|
if (!shader_asset) {
|
||||||
|
int32 archive_id = (shader_ids[i] >> 24) & 0xFF;
|
||||||
|
shader_asset = asset_archive_asset_load(&cb->asset_archives[archive_id], shader_ids[i], cb->ams, cb->mem_vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sub shader
|
||||||
|
shader_assets[i] = shader_make(
|
||||||
|
shader_type_index((ShaderType) (i + 1)),
|
||||||
|
(char *) shader_asset->self,
|
||||||
|
cb->mem_vol
|
||||||
|
);
|
||||||
|
|
||||||
|
shader_asset->state |= ASSET_STATE_RAM_GC;
|
||||||
|
shader_asset->state |= ASSET_STATE_VRAM_GC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make shader/program
|
||||||
|
shader->id = program_make(
|
||||||
|
shader_assets[0], shader_assets[1], shader_assets[2],
|
||||||
|
cb->mem_vol
|
||||||
|
);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
*/
|
*/
|
||||||
#ifndef TOS_GPUAPI_OPENGL_GPU_API_CONTAINER
|
#ifndef TOS_GPUAPI_OPENGL_GPU_API_CONTAINER_H
|
||||||
#define TOS_GPUAPI_OPENGL_GPU_API_CONTAINER
|
#define TOS_GPUAPI_OPENGL_GPU_API_CONTAINER_H
|
||||||
|
|
||||||
#include "../../stdlib/Types.h"
|
#include "../../stdlib/Types.h"
|
||||||
#include "OpenglUtils.h"
|
#include "OpenglUtils.h"
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,13 @@
|
||||||
#include "../../image/Image.cpp"
|
#include "../../image/Image.cpp"
|
||||||
#include "../../utils/StringUtils.h"
|
#include "../../utils/StringUtils.h"
|
||||||
#include "../../log/Log.h"
|
#include "../../log/Log.h"
|
||||||
|
#include "../../system/FileUtils.cpp"
|
||||||
#include "../RenderUtils.h"
|
#include "../RenderUtils.h"
|
||||||
#include "Opengl.h"
|
#include "Opengl.h"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <windows.h>
|
|
||||||
#include "../../platform/win32/FileUtils.cpp"
|
|
||||||
#include "../../platform/win32/Window.h"
|
#include "../../platform/win32/Window.h"
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#include "../../platform/linux/FileUtils.cpp"
|
|
||||||
#include "../../platform/linux/Window.h"
|
#include "../../platform/linux/Window.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -85,10 +82,10 @@ void opengl_info(OpenglInfo* info)
|
||||||
|
|
||||||
for (char *at = version; *at; ++at) {
|
for (char *at = version; *at; ++at) {
|
||||||
if (*at == '.') {
|
if (*at == '.') {
|
||||||
info->major = str_to_int(version);
|
info->major = (int32) str_to_int(version);
|
||||||
|
|
||||||
++at;
|
++at;
|
||||||
info->minor = str_to_int(at);
|
info->minor = (int32) str_to_int(at);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@
|
||||||
#include "../../stdlib/Types.h"
|
#include "../../stdlib/Types.h"
|
||||||
|
|
||||||
struct Shader {
|
struct Shader {
|
||||||
uint32 shader_id;
|
uint32 id;
|
||||||
uint32 shader_locations[7];
|
uint32 locations[7];
|
||||||
byte shader_data[16];
|
byte data[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -13,6 +13,19 @@
|
||||||
#include "../../memory/RingMemory.h"
|
#include "../../memory/RingMemory.h"
|
||||||
#include "../../log/Log.h"
|
#include "../../log/Log.h"
|
||||||
#include "Opengl.h"
|
#include "Opengl.h"
|
||||||
|
#include "../ShaderType.h"
|
||||||
|
|
||||||
|
int32 shader_type_index(ShaderType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case SHADER_TYPE_VERTEX:
|
||||||
|
return GL_VERTEX_SHADER;
|
||||||
|
case SHADER_TYPE_FRAGMENT:
|
||||||
|
return GL_FRAGMENT_SHADER;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set value based on shader uniform name
|
// Set value based on shader uniform name
|
||||||
inline
|
inline
|
||||||
|
|
@ -333,6 +346,7 @@ GLuint program_make(
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @question Depending on how the different gpu apis work we may want to pass Shader* to have a uniform structure
|
||||||
inline
|
inline
|
||||||
void pipeline_use(uint32 id)
|
void pipeline_use(uint32 id)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
*/
|
*/
|
||||||
#ifndef TOS_GPUAPI_VULKAN_GPU_API_CONTAINER
|
#ifndef TOS_GPUAPI_VULKAN_GPU_API_CONTAINER_H
|
||||||
#define TOS_GPUAPI_VULKAN_GPU_API_CONTAINER
|
#define TOS_GPUAPI_VULKAN_GPU_API_CONTAINER_H
|
||||||
|
|
||||||
#include "../../stdlib/Types.h"
|
#include "../../stdlib/Types.h"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
|
||||||
20
gpuapi/vulkan/Shader.h
Normal file
20
gpuapi/vulkan/Shader.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_GPUAPI_VULKAN_SHADER_H
|
||||||
|
#define TOS_GPUAPI_VULKAN_SHADER_H
|
||||||
|
|
||||||
|
#include "../../stdlib/Types.h"
|
||||||
|
|
||||||
|
struct Shader {
|
||||||
|
uint32 id;
|
||||||
|
uint32 locations[7];
|
||||||
|
byte data[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -42,7 +42,7 @@ inline void shader_set_value(VkDevice device, VkCommandBuffer commandBuffer, VkD
|
||||||
descriptorWrite.descriptorCount = 1;
|
descriptorWrite.descriptorCount = 1;
|
||||||
descriptorWrite.pBufferInfo = &bufferInfo;
|
descriptorWrite.pBufferInfo = &bufferInfo;
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, nullptr);
|
vkUpdateDescriptorSets(device, 1, &descriptorWrite, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkShaderModule shader_make(VkDevice device, const char* source, int32 source_size)
|
VkShaderModule shader_make(VkDevice device, const char* source, int32 source_size)
|
||||||
|
|
|
||||||
|
|
@ -743,7 +743,7 @@ void vulkan_command_pool_create(
|
||||||
|
|
||||||
void vulkan_command_buffer_create(VkDevice device, VkCommandBuffer* command_buffer, VkCommandPool command_pool)
|
void vulkan_command_buffer_create(VkDevice device, VkCommandBuffer* command_buffer, VkCommandPool command_pool)
|
||||||
{
|
{
|
||||||
VkCommandBufferAllocateInfo allocInfo{};
|
VkCommandBufferAllocateInfo allocInfo = {};
|
||||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
allocInfo.commandPool = command_pool;
|
allocInfo.commandPool = command_pool;
|
||||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@
|
||||||
|
|
||||||
#include "../utils/StringUtils.h"
|
#include "../utils/StringUtils.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
#include "Tga.h"
|
#include "Tga.h"
|
||||||
|
|
@ -50,17 +45,6 @@ void image_flip_vertical(RingMemory* ring, Image* image)
|
||||||
memcpy(image->pixels + y * stride, end - y * stride, stride);
|
memcpy(image->pixels + y * stride, end - y * stride, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flipping with small temp row
|
|
||||||
byte* temp_row = ring_get_memory(ring, stride);
|
|
||||||
|
|
||||||
for (int y = 0; y < image->height / 2; ++y) {
|
|
||||||
memcpy(temp_row, image->pixels + y * stride, stride);
|
|
||||||
|
|
||||||
memcpy(image->pixels + y * stride, image->pixels - y * stride, stride);
|
|
||||||
memcpy(image->pixels - y * stride, temp_row, stride);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
image->image_settings ^= IMAGE_SETTING_BOTTOM_TO_TOP;
|
image->image_settings ^= IMAGE_SETTING_BOTTOM_TO_TOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LANGUAGE_VERSION 1
|
#define LANGUAGE_VERSION 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,23 @@
|
||||||
#include "../utils/StringUtils.h"
|
#include "../utils/StringUtils.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
#include "../utils/MathUtils.h"
|
#include "../utils/MathUtils.h"
|
||||||
|
#include "../thread/Atomic.h"
|
||||||
|
|
||||||
|
// Required for rdtsc();
|
||||||
|
#if _WIN32
|
||||||
|
#include <intrin.h>
|
||||||
|
#else
|
||||||
|
#include <x86intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
global_persist DebugContainer* debug_container = NULL;
|
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
|
#if _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "../platform/win32/threading/Atomic.h"
|
|
||||||
#include "../platform/win32/threading/Spinlock.cpp"
|
|
||||||
void setup_performance_count() {
|
void setup_performance_count() {
|
||||||
if (!debug_container) {
|
if (!debug_container) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -26,8 +36,6 @@ global_persist DebugContainer* debug_container = NULL;
|
||||||
debug_container->performance_count_frequency = perf_counter.QuadPart;
|
debug_container->performance_count_frequency = perf_counter.QuadPart;
|
||||||
}
|
}
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#include "../platform/linux/threading/Atomic.h"
|
|
||||||
#include "../platform/linux/threading/Spinlock.cpp"
|
|
||||||
void setup_performance_count() {
|
void setup_performance_count() {
|
||||||
if (!debug_container) {
|
if (!debug_container) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -102,8 +110,8 @@ void update_timing_stat(uint32 stat, const char* function)
|
||||||
|
|
||||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||||
timing_stat->function = function;
|
timing_stat->function = function;
|
||||||
timing_stat->delta_tick = new_tick_count - timing_stat->old_tick_count;
|
timing_stat->delta_tick = (uint32) (new_tick_count - timing_stat->old_tick_count);
|
||||||
timing_stat->delta_time = (double) timing_stat->delta_tick / (double) debug_container->performance_count_frequency;
|
timing_stat->delta_time = (f64) timing_stat->delta_tick / (f64) debug_container->performance_count_frequency;
|
||||||
timing_stat->old_tick_count = new_tick_count;
|
timing_stat->old_tick_count = new_tick_count;
|
||||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||||
}
|
}
|
||||||
|
|
@ -125,8 +133,8 @@ void update_timing_stat_end(uint32 stat, const char* function)
|
||||||
|
|
||||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||||
timing_stat->function = function;
|
timing_stat->function = function;
|
||||||
timing_stat->delta_tick = new_tick_count - timing_stat->old_tick_count;
|
timing_stat->delta_tick = (uint32) (new_tick_count - timing_stat->old_tick_count);
|
||||||
timing_stat->delta_time = (double) timing_stat->delta_tick / (double) debug_container->performance_count_frequency;
|
timing_stat->delta_time = (f64) timing_stat->delta_tick / (f64) debug_container->performance_count_frequency;
|
||||||
timing_stat->old_tick_count = new_tick_count;
|
timing_stat->old_tick_count = new_tick_count;
|
||||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||||
}
|
}
|
||||||
|
|
@ -140,8 +148,8 @@ void update_timing_stat_end_continued(uint32 stat, const char* function)
|
||||||
|
|
||||||
spinlock_start(&debug_container->perf_stats_spinlock);
|
spinlock_start(&debug_container->perf_stats_spinlock);
|
||||||
timing_stat->function = function;
|
timing_stat->function = function;
|
||||||
timing_stat->delta_tick = timing_stat->delta_tick + new_tick_count - timing_stat->old_tick_count;
|
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 + (double) timing_stat->delta_tick / (double) debug_container->performance_count_frequency;
|
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;
|
timing_stat->old_tick_count = new_tick_count;
|
||||||
spinlock_end(&debug_container->perf_stats_spinlock);
|
spinlock_end(&debug_container->perf_stats_spinlock);
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +277,7 @@ void debug_memory_reserve(uint64 start, uint64 size, int32 type, const char* fun
|
||||||
uint64 idx = atomic_fetch_add_relaxed(&mem->reserve_action_idx, 1);
|
uint64 idx = atomic_fetch_add_relaxed(&mem->reserve_action_idx, 1);
|
||||||
if (idx >= ARRAY_COUNT(mem->reserve_action)) {
|
if (idx >= ARRAY_COUNT(mem->reserve_action)) {
|
||||||
atomic_set_acquire(&mem->reserve_action_idx, 1);
|
atomic_set_acquire(&mem->reserve_action_idx, 1);
|
||||||
idx %= ARRAY_COUNT(mem->last_action);
|
idx %= ARRAY_COUNT(mem->reserve_action);
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugMemoryRange* dmr = &mem->reserve_action[idx];
|
DebugMemoryRange* dmr = &mem->reserve_action[idx];
|
||||||
|
|
@ -281,6 +289,27 @@ void debug_memory_reserve(uint64 start, uint64 size, int32 type, const char* fun
|
||||||
dmr->function_name = function;
|
dmr->function_name = function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// undo reserve
|
||||||
|
void debug_memory_free(uint64 start, uint64 size)
|
||||||
|
{
|
||||||
|
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
|
// @bug This probably requires thread safety
|
||||||
inline
|
inline
|
||||||
void debug_memory_reset()
|
void debug_memory_reset()
|
||||||
|
|
@ -302,7 +331,7 @@ void debug_memory_reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @bug This probably requires thread safety
|
// @bug This probably requires thread safety
|
||||||
byte* log_get_memory(uint64 size, byte aligned = 1, bool zeroed = false)
|
byte* log_get_memory(uint64 size, byte aligned = 4, bool zeroed = false)
|
||||||
{
|
{
|
||||||
if (!debug_container) {
|
if (!debug_container) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -347,8 +376,8 @@ void log(const char* str, bool should_log, bool save, const char* file, const ch
|
||||||
size_t file_len = strlen(file);
|
size_t file_len = strlen(file);
|
||||||
size_t function_len = strlen(function);
|
size_t function_len = strlen(function);
|
||||||
|
|
||||||
char line_str[10];
|
char line_str[14];
|
||||||
int_to_str(line, line_str, '\0');
|
uint_to_str(line, line_str);
|
||||||
|
|
||||||
size_t line_len = strlen(line_str);
|
size_t line_len = strlen(line_str);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,10 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "DebugMemory.h"
|
#include "DebugMemory.h"
|
||||||
#include "TimingStat.h"
|
#include "TimingStat.h"
|
||||||
|
#include "../thread/Spinlock.h"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "../platform/win32/threading/Spinlock.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Spinlock.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct LogMemory {
|
struct LogMemory {
|
||||||
|
|
@ -26,7 +24,7 @@ struct LogMemory {
|
||||||
uint32 id;
|
uint32 id;
|
||||||
uint64 size;
|
uint64 size;
|
||||||
uint64 pos;
|
uint64 pos;
|
||||||
int32 alignment;
|
uint32 alignment;
|
||||||
uint64 start;
|
uint64 start;
|
||||||
uint64 end;
|
uint64 end;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,8 @@
|
||||||
#ifndef TOS_LOG_DEBUG_MEMORY_H
|
#ifndef TOS_LOG_DEBUG_MEMORY_H
|
||||||
#define TOS_LOG_DEBUG_MEMORY_H
|
#define TOS_LOG_DEBUG_MEMORY_H
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
|
||||||
// required for __rdtsc
|
|
||||||
#if _WIN32
|
|
||||||
#include <intrin.h>
|
|
||||||
#else
|
|
||||||
#include <x86intrin.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEBUG_MEMORY_RANGE_MAX 500
|
#define DEBUG_MEMORY_RANGE_MAX 500
|
||||||
#define DEBUG_MEMORY_RANGE_RES_MAX 100
|
#define DEBUG_MEMORY_RANGE_RES_MAX 100
|
||||||
|
|
||||||
|
|
@ -55,6 +45,7 @@ struct DebugMemoryContainer {
|
||||||
void debug_memory_init(uint64, uint64);
|
void debug_memory_init(uint64, uint64);
|
||||||
void debug_memory_log(uint64, uint64, int32, const char*);
|
void debug_memory_log(uint64, uint64, int32, const char*);
|
||||||
void debug_memory_reserve(uint64, uint64, int32, const char*);
|
void debug_memory_reserve(uint64, uint64, int32, const char*);
|
||||||
|
void debug_memory_free(uint64, uint64);
|
||||||
void debug_memory_reset();
|
void debug_memory_reset();
|
||||||
|
|
||||||
#define DEBUG_MEMORY_INIT(start, size) debug_memory_init((start), (size))
|
#define DEBUG_MEMORY_INIT(start, size) debug_memory_init((start), (size))
|
||||||
|
|
@ -62,6 +53,7 @@ struct DebugMemoryContainer {
|
||||||
#define DEBUG_MEMORY_WRITE(start, size) debug_memory_log((start), (size), 1, __func__)
|
#define DEBUG_MEMORY_WRITE(start, size) debug_memory_log((start), (size), 1, __func__)
|
||||||
#define DEBUG_MEMORY_DELETE(start, size) debug_memory_log((start), (size), -1, __func__)
|
#define DEBUG_MEMORY_DELETE(start, size) debug_memory_log((start), (size), -1, __func__)
|
||||||
#define DEBUG_MEMORY_RESERVE(start, size, type) debug_memory_reserve((start), (size), (type), __func__)
|
#define DEBUG_MEMORY_RESERVE(start, size, type) debug_memory_reserve((start), (size), (type), __func__)
|
||||||
|
#define DEBUG_MEMORY_FREE(start, size) debug_memory_free((start), (size))
|
||||||
#define DEBUG_MEMORY_RESET() debug_memory_reset()
|
#define DEBUG_MEMORY_RESET() debug_memory_reset()
|
||||||
#else
|
#else
|
||||||
#define DEBUG_MEMORY_INIT(start, size) ((void) 0)
|
#define DEBUG_MEMORY_INIT(start, size) ((void) 0)
|
||||||
|
|
@ -69,6 +61,7 @@ struct DebugMemoryContainer {
|
||||||
#define DEBUG_MEMORY_WRITE(start, size) ((void) 0)
|
#define DEBUG_MEMORY_WRITE(start, size) ((void) 0)
|
||||||
#define DEBUG_MEMORY_DELETE(start, size) ((void) 0)
|
#define DEBUG_MEMORY_DELETE(start, size) ((void) 0)
|
||||||
#define DEBUG_MEMORY_RESERVE(start, size, type) ((void) 0)
|
#define DEBUG_MEMORY_RESERVE(start, size, type) ((void) 0)
|
||||||
|
#define DEBUG_MEMORY_FREE(start, size) ((void) 0)
|
||||||
#define DEBUG_MEMORY_RESET() ((void) 0)
|
#define DEBUG_MEMORY_RESET() ((void) 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ void log_counter(int32, int64);
|
||||||
printf("%ld\n", __rdtsc() - (time_start)); \
|
printf("%ld\n", __rdtsc() - (time_start)); \
|
||||||
})
|
})
|
||||||
|
|
||||||
#if (!DEBUG && !INTERNAL)
|
#if (!DEBUG && !INTERNAL) || RELEASE
|
||||||
// Don't perform any logging at log level 0
|
// Don't perform any logging at log level 0
|
||||||
#define LOG(str, should_log, save) log((str), (should_log), (save), __FILE__, __func__, __LINE__)
|
#define LOG(str, should_log, save) log((str), (should_log), (save), __FILE__, __func__, __LINE__)
|
||||||
#define LOG_FORMAT(format, data_type, data, should_log, save) log((format), (data_type), (data), (should_log), (save), __FILE__, __func__, __LINE__)
|
#define LOG_FORMAT(format, data_type, data, should_log, save) log((format), (data_type), (data), (should_log), (save), __FILE__, __func__, __LINE__)
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@
|
||||||
struct TimingStat {
|
struct TimingStat {
|
||||||
const char* function;
|
const char* function;
|
||||||
uint64 old_tick_count;
|
uint64 old_tick_count;
|
||||||
uint64 delta_tick;
|
f64 delta_time;
|
||||||
double delta_time;
|
uint32 delta_tick;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sometimes we want to only do logging in debug mode.
|
// Sometimes we want to only do logging in debug mode.
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,7 @@
|
||||||
#define TOS_MATH_MATRIX_VECTOR_FLOAT32_H
|
#define TOS_MATH_MATRIX_VECTOR_FLOAT32_H
|
||||||
|
|
||||||
#include "../../utils/MathUtils.h"
|
#include "../../utils/MathUtils.h"
|
||||||
|
#include "../../stdlib/Simd.h"
|
||||||
#if __aarch64__
|
|
||||||
#include "../../../GameEngine/stdlib/sve/SVE_F32.h"
|
|
||||||
#else
|
|
||||||
#include "../../../GameEngine/stdlib/simd/SIMD_F32.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct v3_f32_4 {
|
struct v3_f32_4 {
|
||||||
union {
|
union {
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,6 @@
|
||||||
#define TOS_MATH_MATRIX_VECTOR_FLOAT64_H
|
#define TOS_MATH_MATRIX_VECTOR_FLOAT64_H
|
||||||
|
|
||||||
#include "../../utils/MathUtils.h"
|
#include "../../utils/MathUtils.h"
|
||||||
|
#include "../../stdlib/Simd.h"
|
||||||
#if __aarch64__
|
|
||||||
#include "../../../GameEngine/stdlib/sve/SVE_F64.h"
|
|
||||||
#else
|
|
||||||
#include "../../../GameEngine/stdlib/simd/SIMD_F64.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -13,12 +13,7 @@
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
|
|
||||||
#include "../../utils/MathUtils.h"
|
#include "../../utils/MathUtils.h"
|
||||||
|
#include "../../stdlib/Simd.h"
|
||||||
#if __aarch64__
|
|
||||||
#include "../../../GameEngine/stdlib/sve/SVE_I32.h"
|
|
||||||
#else
|
|
||||||
#include "../../../GameEngine/stdlib/simd/SIMD_I32.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct v3_int32_4 {
|
struct v3_int32_4 {
|
||||||
union {
|
union {
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,7 @@
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
|
|
||||||
#include "../../utils/MathUtils.h"
|
#include "../../utils/MathUtils.h"
|
||||||
|
#include "../../stdlib/Simd.h"
|
||||||
#if __aarch64__
|
|
||||||
#include "../../../GameEngine/stdlib/sve/SVE_I64.h"
|
|
||||||
#else
|
|
||||||
#include "../../../GameEngine/stdlib/simd/SIMD_I64.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct v3_int64_2 {
|
struct v3_int64_2 {
|
||||||
union {
|
union {
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,7 @@
|
||||||
#include "../utils/EndianUtils.h"
|
#include "../utils/EndianUtils.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
|
#include "../system/Allocator.h"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/Allocator.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/Allocator.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// @question Consider to use element_alignment to automatically align/pad elements
|
// @question Consider to use element_alignment to automatically align/pad elements
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,27 +14,19 @@
|
||||||
#include "../utils/MathUtils.h"
|
#include "../utils/MathUtils.h"
|
||||||
#include "../utils/TestUtils.h"
|
#include "../utils/TestUtils.h"
|
||||||
#include "../utils/EndianUtils.h"
|
#include "../utils/EndianUtils.h"
|
||||||
|
#include "../utils/BitUtils.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
#include "BufferMemory.h"
|
#include "BufferMemory.h"
|
||||||
|
#include "../system/Allocator.h"
|
||||||
#if _WIN32
|
#include "../thread/Thread.h"
|
||||||
#include "../platform/win32/Allocator.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/Allocator.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/threading/Thread.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Thread.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ChunkMemory {
|
struct ChunkMemory {
|
||||||
byte* memory;
|
byte* memory;
|
||||||
|
|
||||||
uint64 count;
|
// @question Why are we making the count 64 bit? is this really realistically possible?
|
||||||
uint64 size;
|
uint64 size;
|
||||||
uint64 last_pos;
|
int32 last_pos;
|
||||||
|
uint32 count;
|
||||||
uint32 chunk_size;
|
uint32 chunk_size;
|
||||||
uint32 alignment;
|
uint32 alignment;
|
||||||
|
|
||||||
|
|
@ -44,7 +36,7 @@ struct ChunkMemory {
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void chunk_alloc(ChunkMemory* buf, uint64 count, uint32 chunk_size, int32 alignment = 64)
|
void chunk_alloc(ChunkMemory* buf, uint32 count, uint32 chunk_size, int32 alignment = 64)
|
||||||
{
|
{
|
||||||
ASSERT_SIMPLE(chunk_size);
|
ASSERT_SIMPLE(chunk_size);
|
||||||
ASSERT_SIMPLE(count);
|
ASSERT_SIMPLE(count);
|
||||||
|
|
@ -58,7 +50,7 @@ void chunk_alloc(ChunkMemory* buf, uint64 count, uint32 chunk_size, int32 alignm
|
||||||
buf->count = count;
|
buf->count = count;
|
||||||
buf->size = count * chunk_size + sizeof(uint64) * CEIL_DIV(count, 64);
|
buf->size = count * chunk_size + sizeof(uint64) * CEIL_DIV(count, 64);
|
||||||
buf->chunk_size = chunk_size;
|
buf->chunk_size = chunk_size;
|
||||||
buf->last_pos = 0;
|
buf->last_pos = -1;
|
||||||
buf->alignment = alignment;
|
buf->alignment = alignment;
|
||||||
|
|
||||||
// @question Could it be beneficial to have this before the element data?
|
// @question Could it be beneficial to have this before the element data?
|
||||||
|
|
@ -70,7 +62,7 @@ void chunk_alloc(ChunkMemory* buf, uint64 count, uint32 chunk_size, int32 alignm
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void chunk_init(ChunkMemory* buf, BufferMemory* data, uint64 count, uint32 chunk_size, int32 alignment = 64)
|
void chunk_init(ChunkMemory* buf, BufferMemory* data, uint32 count, uint32 chunk_size, int32 alignment = 64)
|
||||||
{
|
{
|
||||||
ASSERT_SIMPLE(chunk_size);
|
ASSERT_SIMPLE(chunk_size);
|
||||||
ASSERT_SIMPLE(count);
|
ASSERT_SIMPLE(count);
|
||||||
|
|
@ -82,7 +74,7 @@ void chunk_init(ChunkMemory* buf, BufferMemory* data, uint64 count, uint32 chunk
|
||||||
buf->count = count;
|
buf->count = count;
|
||||||
buf->size = count * chunk_size + sizeof(uint64) * CEIL_DIV(count, 64);
|
buf->size = count * chunk_size + sizeof(uint64) * CEIL_DIV(count, 64);
|
||||||
buf->chunk_size = chunk_size;
|
buf->chunk_size = chunk_size;
|
||||||
buf->last_pos = 0;
|
buf->last_pos = -1;
|
||||||
buf->alignment = alignment;
|
buf->alignment = alignment;
|
||||||
|
|
||||||
// @question Could it be beneficial to have this before the element data?
|
// @question Could it be beneficial to have this before the element data?
|
||||||
|
|
@ -95,7 +87,7 @@ void chunk_init(ChunkMemory* buf, BufferMemory* data, uint64 count, uint32 chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void chunk_init(ChunkMemory* buf, byte* data, uint64 count, uint32 chunk_size, int32 alignment = 64)
|
void chunk_init(ChunkMemory* buf, byte* data, uint32 count, uint32 chunk_size, int32 alignment = 64)
|
||||||
{
|
{
|
||||||
ASSERT_SIMPLE(chunk_size);
|
ASSERT_SIMPLE(chunk_size);
|
||||||
ASSERT_SIMPLE(count);
|
ASSERT_SIMPLE(count);
|
||||||
|
|
@ -108,7 +100,7 @@ void chunk_init(ChunkMemory* buf, byte* data, uint64 count, uint32 chunk_size, i
|
||||||
buf->count = count;
|
buf->count = count;
|
||||||
buf->size = count * chunk_size + sizeof(uint64) * CEIL_DIV(count, 64);
|
buf->size = count * chunk_size + sizeof(uint64) * CEIL_DIV(count, 64);
|
||||||
buf->chunk_size = chunk_size;
|
buf->chunk_size = chunk_size;
|
||||||
buf->last_pos = 0;
|
buf->last_pos = -1;
|
||||||
buf->alignment = alignment;
|
buf->alignment = alignment;
|
||||||
|
|
||||||
// @question Could it be beneficial to have this before the element data?
|
// @question Could it be beneficial to have this before the element data?
|
||||||
|
|
@ -131,6 +123,11 @@ void chunk_free(ChunkMemory* buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
uint32 chunk_id_from_memory(ChunkMemory* buf, byte* pos) {
|
||||||
|
return (uint32) ((uintptr_t) pos - (uintptr_t) buf->memory) / buf->chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
byte* chunk_get_element(ChunkMemory* buf, uint64 element, bool zeroed = false)
|
byte* chunk_get_element(ChunkMemory* buf, uint64 element, bool zeroed = false)
|
||||||
{
|
{
|
||||||
|
|
@ -146,93 +143,102 @@ byte* chunk_get_element(ChunkMemory* buf, uint64 element, bool zeroed = false)
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// @performance This is a very important function, revisit in the future for optimization (e.g. ABM)
|
||||||
* In some cases we know exactly which index is free
|
int32 chunk_reserve(ChunkMemory* buf, uint32 elements = 1)
|
||||||
*/
|
|
||||||
void chunk_reserve_index(ChunkMemory* buf, int64 index, int64 elements = 1, bool zeroed = false)
|
|
||||||
{
|
{
|
||||||
int64 byte_index = index / 64;
|
int32 free_index = (buf->last_pos + 1) / 64;
|
||||||
int32 bit_index = index % 64;
|
int32 bit_index = (buf->last_pos + 1) & 63;
|
||||||
|
int32 free_element = -1;
|
||||||
|
|
||||||
// Mark the bits as reserved
|
int32 i = -1;
|
||||||
for (int32 j = 0; j < elements; ++j) {
|
int32 consecutive_free_bits = 0;
|
||||||
int64 current_byte_index = byte_index + (bit_index + j) / 64;
|
|
||||||
int32 current_bit_index = (bit_index + j) % 64;
|
|
||||||
buf->free[current_byte_index] |= (1LL << current_bit_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zeroed) {
|
while (free_element < 0 && ++i < buf->count) {
|
||||||
memset(buf->memory + index * buf->chunk_size, 0, elements * buf->chunk_size);
|
// Skip fully filled ranges
|
||||||
}
|
if (free_index * 64 + bit_index + elements - consecutive_free_bits >= buf->count) {
|
||||||
|
|
||||||
DEBUG_MEMORY_WRITE((uint64) (buf->memory + index * buf->chunk_size), elements * buf->chunk_size);
|
|
||||||
|
|
||||||
buf->last_pos = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64 chunk_reserve(ChunkMemory* buf, uint64 elements = 1, bool zeroed = false)
|
|
||||||
{
|
|
||||||
int64 free_index = (buf->last_pos + 1) / 64;
|
|
||||||
int32 bit_index = buf->last_pos - free_index * 64;
|
|
||||||
int64 free_element = -1;
|
|
||||||
|
|
||||||
int32 i = 0;
|
|
||||||
int64 max_bytes = (buf->count + 7) / 64;
|
|
||||||
|
|
||||||
while (free_element < 0 && i < buf->count) {
|
|
||||||
++i;
|
|
||||||
|
|
||||||
if (free_index >= max_bytes) {
|
|
||||||
free_index = 0;
|
free_index = 0;
|
||||||
}
|
bit_index = 0;
|
||||||
|
i += buf->count - (free_index * 64 + bit_index);
|
||||||
if (buf->free[free_index] == 0xFF) {
|
consecutive_free_bits = 0;
|
||||||
|
} else if (buf->free[free_index] == 0xFFFFFFFFFFFFFFFF) {
|
||||||
++free_index;
|
++free_index;
|
||||||
|
bit_index = 0;
|
||||||
|
i += 63;
|
||||||
|
consecutive_free_bits = 0;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @performance There is some redundancy happening down below, we should ++free_index in certain conditions?
|
// Find first free element
|
||||||
for (; bit_index < 64; ++bit_index) {
|
while (IS_BIT_SET_64_R2L(buf->free[free_index], bit_index)) {
|
||||||
int32 consecutive_free_bits = 0;
|
consecutive_free_bits = 0;
|
||||||
|
++bit_index;
|
||||||
|
++i;
|
||||||
|
|
||||||
// Check if there are 'elements' consecutive free bits
|
// We still need to check for overflow since our initial bit_index is based on buf->last_pos
|
||||||
for (int32 j = 0; j < elements; ++j) {
|
if (bit_index > 63) {
|
||||||
// Check if there is enough space until the end of the buffer.
|
bit_index = 0;
|
||||||
// Remember, the last free index may only allow only 1 bit if the size is 65
|
++free_index;
|
||||||
if (free_index * 64 + (bit_index + j) >= buf->count) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64 current_free_index = free_index + (bit_index + j) / 64;
|
|
||||||
int32 current_bit_index = (bit_index + j) % 64;
|
|
||||||
|
|
||||||
int64 mask = 1LL << current_bit_index;
|
|
||||||
if ((buf->free[current_free_index] & mask) == 0) {
|
|
||||||
++consecutive_free_bits;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (consecutive_free_bits == elements) {
|
|
||||||
free_element = free_index * 64 + bit_index;
|
|
||||||
|
|
||||||
// Mark the bits as reserved
|
|
||||||
for (int32 j = 0; j < elements; ++j) {
|
|
||||||
int64 current_free_index = free_index + (bit_index + j) / 64;
|
|
||||||
int32 current_bit_index = (bit_index + j) % 64;
|
|
||||||
buf->free[current_free_index] |= (1LL << current_bit_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bit_index = 0;
|
// 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)) {
|
||||||
|
consecutive_free_bits = 0;
|
||||||
|
|
||||||
++i;
|
continue;
|
||||||
++free_index;
|
}
|
||||||
|
|
||||||
|
// We found our first free element, let's check if we have enough free space
|
||||||
|
while (!IS_BIT_SET_64_R2L(buf->free[free_index], bit_index)
|
||||||
|
&& consecutive_free_bits != elements
|
||||||
|
&& free_index * 64 + bit_index + elements - consecutive_free_bits < buf->count
|
||||||
|
) {
|
||||||
|
++i;
|
||||||
|
++consecutive_free_bits;
|
||||||
|
++bit_index;
|
||||||
|
|
||||||
|
if (bit_index > 63) {
|
||||||
|
bit_index = 0;
|
||||||
|
++free_index;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have enough free bits?
|
||||||
|
if (consecutive_free_bits == elements) {
|
||||||
|
free_element = free_index * 64 + bit_index - elements;
|
||||||
|
int32 possible_free_index = free_element / 64;
|
||||||
|
int32 possible_bit_index = free_element & 63;
|
||||||
|
|
||||||
|
// Mark as used
|
||||||
|
if (elements == 1) {
|
||||||
|
buf->free[possible_free_index] |= (1LL << possible_bit_index);
|
||||||
|
} else {
|
||||||
|
uint32 elements_temp = elements;
|
||||||
|
int64 current_free_index = possible_free_index;
|
||||||
|
int32 current_bit_index = possible_bit_index;
|
||||||
|
|
||||||
|
while (elements > 0) {
|
||||||
|
// Calculate the number of bits we can set in the current 64-bit block
|
||||||
|
int32 bits_in_current_block = OMS_MIN(64 - current_bit_index, elements);
|
||||||
|
|
||||||
|
// Create a mask to set the bits
|
||||||
|
uint64 mask = ((1ULL << bits_in_current_block) - 1) << current_bit_index;
|
||||||
|
buf->free[current_free_index] |= mask;
|
||||||
|
|
||||||
|
// Update the counters and indices
|
||||||
|
elements -= bits_in_current_block;
|
||||||
|
++current_free_index;
|
||||||
|
current_bit_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (free_element < 0) {
|
if (free_element < 0) {
|
||||||
|
|
@ -240,70 +246,46 @@ int64 chunk_reserve(ChunkMemory* buf, uint64 elements = 1, bool zeroed = false)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zeroed) {
|
|
||||||
memset(buf->memory + free_element * buf->chunk_size, 0, elements * buf->chunk_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_MEMORY_WRITE((uint64) (buf->memory + free_element * buf->chunk_size), elements * buf->chunk_size);
|
DEBUG_MEMORY_WRITE((uint64) (buf->memory + free_element * buf->chunk_size), elements * buf->chunk_size);
|
||||||
|
|
||||||
buf->last_pos = free_element;
|
buf->last_pos = free_element;
|
||||||
|
|
||||||
return free_element;
|
return (int32) free_element;
|
||||||
}
|
|
||||||
|
|
||||||
byte* chunk_find_free(ChunkMemory* buf)
|
|
||||||
{
|
|
||||||
int64 free_index = (buf->last_pos + 1) / 64;
|
|
||||||
int32 bit_index;
|
|
||||||
|
|
||||||
int64 free_element = -1;
|
|
||||||
int64 mask;
|
|
||||||
|
|
||||||
int32 i = 0;
|
|
||||||
int64 max_bytes = (buf->count + 7) / 64;
|
|
||||||
|
|
||||||
while (free_element < 0 && i < buf->count) {
|
|
||||||
if (free_index >= max_bytes) {
|
|
||||||
free_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf->free[free_index] == 0xFF) {
|
|
||||||
++i;
|
|
||||||
++free_index;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This always breaks!
|
|
||||||
// @performance on the first iteration through the buffer we could optimize this by starting at a different bit_index
|
|
||||||
// because we know that the bit_index is based on last_pos
|
|
||||||
for (bit_index = 0; bit_index < 64; ++bit_index) {
|
|
||||||
mask = 1LL << bit_index;
|
|
||||||
if ((buf->free[free_index] & mask) == 0) {
|
|
||||||
free_element = free_index * 64 + bit_index;
|
|
||||||
buf->free[free_index] |= (1LL << bit_index);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (free_element < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf->memory + free_element * buf->chunk_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void chunk_free_element(ChunkMemory* buf, uint64 element)
|
void chunk_free_element(ChunkMemory* buf, uint64 free_index, int32 bit_index)
|
||||||
|
{
|
||||||
|
DEBUG_MEMORY_DELETE((uint64) (buf->memory + (free_index * 64 + bit_index) * buf->chunk_size), buf->chunk_size);
|
||||||
|
buf->free[free_index] &= ~(1LL << bit_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void chunk_free_elements(ChunkMemory* buf, uint64 element, uint32 element_count = 1)
|
||||||
{
|
{
|
||||||
DEBUG_MEMORY_DELETE((uint64) (buf->memory + element * buf->chunk_size), buf->chunk_size);
|
DEBUG_MEMORY_DELETE((uint64) (buf->memory + element * buf->chunk_size), buf->chunk_size);
|
||||||
|
|
||||||
int64 free_index = element / 64;
|
int64 free_index = element / 64;
|
||||||
int32 bit_index = element % 64;
|
int32 bit_index = element & 63;
|
||||||
|
|
||||||
buf->free[free_index] &= ~(1LL << bit_index);
|
if (element == 1) {
|
||||||
|
chunk_free_element(buf, free_index, bit_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (element_count > 0) {
|
||||||
|
// Calculate the number of bits we can clear in the current 64-bit block
|
||||||
|
uint32 bits_in_current_block = OMS_MIN(64 - bit_index, element_count);
|
||||||
|
|
||||||
|
// Create a mask to clear the bits
|
||||||
|
uint64 mask = ((1ULL << bits_in_current_block) - 1) << bit_index;
|
||||||
|
buf->free[free_index] &= ~mask;
|
||||||
|
|
||||||
|
// Update the counters and indices
|
||||||
|
element_count -= bits_in_current_block;
|
||||||
|
++free_index;
|
||||||
|
bit_index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|
@ -312,7 +294,7 @@ int64 chunk_dump(const ChunkMemory* buf, byte* data)
|
||||||
byte* start = data;
|
byte* start = data;
|
||||||
|
|
||||||
// Count
|
// Count
|
||||||
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->count);
|
*((uint32 *) data) = SWAP_ENDIAN_LITTLE(buf->count);
|
||||||
data += sizeof(buf->count);
|
data += sizeof(buf->count);
|
||||||
|
|
||||||
// Size
|
// Size
|
||||||
|
|
@ -324,7 +306,7 @@ int64 chunk_dump(const ChunkMemory* buf, byte* data)
|
||||||
data += sizeof(buf->chunk_size);
|
data += sizeof(buf->chunk_size);
|
||||||
|
|
||||||
// Last pos
|
// Last pos
|
||||||
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(buf->last_pos);
|
*((int32 *) data) = SWAP_ENDIAN_LITTLE(buf->last_pos);
|
||||||
data += sizeof(buf->last_pos);
|
data += sizeof(buf->last_pos);
|
||||||
|
|
||||||
// Alignment
|
// Alignment
|
||||||
|
|
@ -343,7 +325,7 @@ inline
|
||||||
int64 chunk_load(ChunkMemory* buf, const byte* data)
|
int64 chunk_load(ChunkMemory* buf, const byte* data)
|
||||||
{
|
{
|
||||||
// Count
|
// Count
|
||||||
buf->count = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
buf->count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
|
||||||
data += sizeof(buf->count);
|
data += sizeof(buf->count);
|
||||||
|
|
||||||
// Size
|
// Size
|
||||||
|
|
@ -355,7 +337,7 @@ int64 chunk_load(ChunkMemory* buf, const byte* data)
|
||||||
data += sizeof(buf->chunk_size);
|
data += sizeof(buf->chunk_size);
|
||||||
|
|
||||||
// Last pos
|
// Last pos
|
||||||
buf->last_pos = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
buf->last_pos = SWAP_ENDIAN_LITTLE(*((int32 *) data));
|
||||||
data += sizeof(buf->last_pos);
|
data += sizeof(buf->last_pos);
|
||||||
|
|
||||||
// Alignment
|
// Alignment
|
||||||
|
|
@ -370,4 +352,28 @@ int64 chunk_load(ChunkMemory* buf, const byte* data)
|
||||||
return buf->size;
|
return buf->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define chunk_iterate_start(buf, chunk_id) \
|
||||||
|
int32 free_index = 0; \
|
||||||
|
int32 bit_index = 0; \
|
||||||
|
\
|
||||||
|
/* Iterate the chunk memory */ \
|
||||||
|
for (; chunk_id < (buf)->count; ++chunk_id) { \
|
||||||
|
/* Check if asset is defined */ \
|
||||||
|
if (!(buf)->free[free_index]) { \
|
||||||
|
/* Skip various elements */ \
|
||||||
|
/* @performance Consider to only check 1 byte instead of 8 */ \
|
||||||
|
/* There are probably even better ways by using compiler intrinsics if available */ \
|
||||||
|
bit_index += 63; /* +64 - 1 since the loop also increases by 1 */ \
|
||||||
|
} else if ((buf)->free[free_index] & (1ULL << bit_index)) {
|
||||||
|
|
||||||
|
#define chunk_iterate_end \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
++bit_index; \
|
||||||
|
if (bit_index > 63) { \
|
||||||
|
bit_index = 0; \
|
||||||
|
++free_index; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -14,12 +14,7 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
#include "BufferMemory.h"
|
#include "BufferMemory.h"
|
||||||
|
#include "../system/Allocator.h"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/Allocator.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/Allocator.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Heap {
|
struct Heap {
|
||||||
byte* elements;
|
byte* elements;
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,10 @@
|
||||||
|
|
||||||
#include "BufferMemory.h"
|
#include "BufferMemory.h"
|
||||||
#include "../log/DebugMemory.h"
|
#include "../log/DebugMemory.h"
|
||||||
|
#include "../thread/Atomic.h"
|
||||||
#if _WIN32
|
#include "../thread/Semaphore.h"
|
||||||
#include "../platform/win32/Allocator.h"
|
#include "../thread/ThreadDefines.h"
|
||||||
#include "../platform/win32/threading/ThreadDefines.h"
|
#include "../system/Allocator.h"
|
||||||
#include "../platform/win32/threading/Semaphore.h"
|
|
||||||
#include "../platform/win32/threading/Atomic.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/Allocator.h"
|
|
||||||
#include "../platform/linux/threading/ThreadDefines.h"
|
|
||||||
#include "../platform/linux/threading/Semaphore.h"
|
|
||||||
#include "../platform/linux/threading/Atomic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// WARNING: Changing this structure has effects on other data structures (e.g. Queue)
|
// WARNING: Changing this structure has effects on other data structures (e.g. Queue)
|
||||||
// When chaning make sure you understand what you are doing
|
// When chaning make sure you understand what you are doing
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,14 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../thread/Thread.h"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/threading/Thread.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Thread.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ThreadedChunkMemory {
|
struct ThreadedChunkMemory {
|
||||||
byte* memory;
|
byte* memory;
|
||||||
|
|
||||||
uint64 count;
|
|
||||||
uint64 size;
|
uint64 size;
|
||||||
int64 last_pos;
|
uint32 last_pos;
|
||||||
|
uint32 count;
|
||||||
uint32 chunk_size;
|
uint32 chunk_size;
|
||||||
int32 alignment;
|
int32 alignment;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,8 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../utils/Utils.h"
|
#include "../utils/Utils.h"
|
||||||
#include "RingMemory.h"
|
#include "RingMemory.h"
|
||||||
|
#include "../thread/Thread.h"
|
||||||
#if _WIN32
|
#include "../thread/Semaphore.h"
|
||||||
#include "../platform/win32/threading/Thread.h"
|
|
||||||
#include "../platform/win32/threading/Semaphore.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Thread.h"
|
|
||||||
#include "../platform/linux/threading/Semaphore.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ThreadedQueue {
|
struct ThreadedQueue {
|
||||||
byte* memory;
|
byte* memory;
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,7 @@
|
||||||
#define TOS_MEMORY_THREADED_RING_MEMORY_H
|
#define TOS_MEMORY_THREADED_RING_MEMORY_H
|
||||||
|
|
||||||
#include "RingMemory.h"
|
#include "RingMemory.h"
|
||||||
|
#include "../thread/Thread.h"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/threading/Thread.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Thread.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// @todo This is a horrible implementation. Please implement a lock free solution
|
// @todo This is a horrible implementation. Please implement a lock free solution
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#define TOS_MODELS_MOB_STATS_C
|
#define TOS_MODELS_MOB_STATS_C
|
||||||
|
|
||||||
#include "MobStats.h"
|
#include "MobStats.h"
|
||||||
#include "../../stdlib/simd/SIMD_I32.h"
|
#include "../../stdlib/Simd.h"
|
||||||
|
|
||||||
// Calculate whenever character points or items change
|
// Calculate whenever character points or items change
|
||||||
// 1. combine primary Item points with character points
|
// 1. combine primary Item points with character points
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#ifndef TOS_MODELS_MOB_PRIMARY_STATS_POINTS_C
|
#ifndef TOS_MODELS_MOB_PRIMARY_STATS_POINTS_C
|
||||||
#define TOS_MODELS_MOB_PRIMARY_STATS_POINTS_C
|
#define TOS_MODELS_MOB_PRIMARY_STATS_POINTS_C
|
||||||
|
|
||||||
#include "../../stdlib/simd/SIMD_I8.h"
|
#include "../../stdlib/Simd.h"
|
||||||
#include "PrimaryStatsPoints.h"
|
#include "PrimaryStatsPoints.h"
|
||||||
|
|
||||||
void calculate_primary_values(const PrimaryStatsPoints* points, PrimaryStatsValues* values, int step = 8)
|
void calculate_primary_values(const PrimaryStatsPoints* points, PrimaryStatsValues* values, int step = 8)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#ifndef TOS_MODELS_MOB_SECONDARY_STATS_POINTS_C
|
#ifndef TOS_MODELS_MOB_SECONDARY_STATS_POINTS_C
|
||||||
#define TOS_MODELS_MOB_SECONDARY_STATS_POINTS_C
|
#define TOS_MODELS_MOB_SECONDARY_STATS_POINTS_C
|
||||||
|
|
||||||
#include "../../stdlib/simd/SIMD_I8.h"
|
#include "../../stdlib/Simd.h"
|
||||||
#include "SecondaryStatsPoints.h"
|
#include "SecondaryStatsPoints.h"
|
||||||
|
|
||||||
void calculate_primary_values(const SecondaryStatsPoints* points, SecondaryStatsValues* values, int step = 8)
|
void calculate_primary_values(const SecondaryStatsPoints* points, SecondaryStatsValues* values, int step = 8)
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,7 @@
|
||||||
#define TOS_MODULE_H
|
#define TOS_MODULE_H
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../../GameEngine/system/Library.h"
|
||||||
#ifdef _WIN32
|
|
||||||
#include "../../GameEngine/platform/win32/Library.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum ModuleType {
|
enum ModuleType {
|
||||||
MODULE_TYPE_HUD,
|
MODULE_TYPE_HUD,
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,11 @@
|
||||||
|
|
||||||
#include "Module.h"
|
#include "Module.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#include "../platform/win32/UtilsWin32.h"
|
#include "../platform/win32/UtilsWin32.h"
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct ModuleManager {
|
struct ModuleManager {
|
||||||
|
|
|
||||||
18
network/Socket.h
Normal file
18
network/Socket.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_NETWORK_SOCKET_H
|
||||||
|
#define TOS_NETWORK_SOCKET_H
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/network/Socket.h"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/network/Socket.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -11,12 +11,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Skeleton {
|
struct Skeleton {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Hitbox {
|
struct Hitbox {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Material {
|
struct Material {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,22 +11,11 @@
|
||||||
|
|
||||||
#include "Vertex.h"
|
#include "Vertex.h"
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../memory/RingMemory.h"
|
#include "../memory/RingMemory.h"
|
||||||
#include "../utils/EndianUtils.h"
|
#include "../utils/EndianUtils.h"
|
||||||
#include "../utils/StringUtils.h"
|
#include "../utils/StringUtils.h"
|
||||||
|
#include "../stdlib/Simd.h"
|
||||||
#if __aarch64__
|
|
||||||
#include "../stdlib/sve/SVE_I32.h"
|
|
||||||
#else
|
|
||||||
#include "../stdlib/simd/SIMD_I32.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MESH_VERSION 1
|
#define MESH_VERSION 1
|
||||||
|
|
||||||
|
|
@ -36,8 +25,6 @@
|
||||||
struct Mesh {
|
struct Mesh {
|
||||||
byte* data; // memory owner that subdivides into the pointers below
|
byte* data; // memory owner that subdivides into the pointers below
|
||||||
|
|
||||||
// @todo Implement the version into the file, currently not implemented
|
|
||||||
int32 version;
|
|
||||||
uint32 object;
|
uint32 object;
|
||||||
|
|
||||||
uint32 group_count;
|
uint32 group_count;
|
||||||
|
|
@ -90,7 +77,8 @@ void mesh_from_file_txt(
|
||||||
// move past the version string
|
// move past the version string
|
||||||
pos += 8;
|
pos += 8;
|
||||||
|
|
||||||
mesh->version = strtol(pos, &pos, 10); ++pos;
|
// @todo us version for different handling
|
||||||
|
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||||
|
|
||||||
int32 object_index = 0;
|
int32 object_index = 0;
|
||||||
int32 group_index = 0;
|
int32 group_index = 0;
|
||||||
|
|
@ -480,9 +468,9 @@ int32 mesh_from_data(
|
||||||
{
|
{
|
||||||
const byte* pos = data;
|
const byte* pos = data;
|
||||||
|
|
||||||
// Read version
|
// Read version, use to handle different versions differently
|
||||||
mesh->version = *((int32 *) pos);
|
int32 version = *((int32 *) pos);
|
||||||
pos += sizeof(mesh->version);
|
pos += sizeof(version);
|
||||||
|
|
||||||
// Read base data
|
// Read base data
|
||||||
mesh->vertex_type = *((int32 *) pos);
|
mesh->vertex_type = *((int32 *) pos);
|
||||||
|
|
@ -549,7 +537,7 @@ int32 mesh_from_data(
|
||||||
// We would have to check the vertex format to calculate the actual size
|
// We would have to check the vertex format to calculate the actual size
|
||||||
int32 mesh_data_size(const Mesh* mesh)
|
int32 mesh_data_size(const Mesh* mesh)
|
||||||
{
|
{
|
||||||
return sizeof(mesh->version)
|
return sizeof(int32)
|
||||||
+ sizeof(mesh->vertex_type)
|
+ sizeof(mesh->vertex_type)
|
||||||
+ sizeof(mesh->vertex_count)
|
+ sizeof(mesh->vertex_count)
|
||||||
+ 12 * sizeof(f32) * mesh->vertex_count; // 12 is the maximum value
|
+ 12 * sizeof(f32) * mesh->vertex_count; // 12 is the maximum value
|
||||||
|
|
@ -565,8 +553,8 @@ int32 mesh_to_data(
|
||||||
byte* pos = data;
|
byte* pos = data;
|
||||||
|
|
||||||
// version
|
// version
|
||||||
memcpy(pos, &mesh->version, sizeof(mesh->version));
|
*((int32 *) pos) = MESH_VERSION;
|
||||||
pos += sizeof(mesh->version);
|
pos += sizeof(int32);
|
||||||
|
|
||||||
// vertices
|
// vertices
|
||||||
if (vertex_save_format == VERTEX_TYPE_ALL) {
|
if (vertex_save_format == VERTEX_TYPE_ALL) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
#include "../../utils/TestUtils.h"
|
#include "../../utils/TestUtils.h"
|
||||||
|
|
||||||
// @todo Currently alignment only effects the starting position, but it should also effect the ending/size
|
// @todo Currently alignment only effects the starting position, but it should also effect the ending/size
|
||||||
// @todo Consider to rename file to Allocator.h
|
|
||||||
|
|
||||||
// @question Since we store at least the size of the memory in the beginning,
|
// @question Since we store at least the size of the memory in the beginning,
|
||||||
// does this have a negative impact on caching?
|
// does this have a negative impact on caching?
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ void relative_to_absolute(const char* rel, char* path)
|
||||||
++self_path_length;
|
++self_path_length;
|
||||||
|
|
||||||
memcpy(path, self_path, self_path_length);
|
memcpy(path, self_path, self_path_length);
|
||||||
strcpy(path + self_path_length, temp);
|
str_copy_short(path + self_path_length, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo implement relative path support, similar to UtilsWin32
|
// @todo implement relative path support, similar to UtilsWin32
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
*/
|
*/
|
||||||
#ifndef TOS_PLATFORM_LINUX_LIBRARY_H
|
#ifndef TOS_PLATFORM_LINUX_LIBRARY_C
|
||||||
#define TOS_PLATFORM_LINUX_LIBRARY_H
|
#define TOS_PLATFORM_LINUX_LIBRARY_C
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
@ -18,30 +18,22 @@
|
||||||
#include "../../stdlib/Types.h"
|
#include "../../stdlib/Types.h"
|
||||||
#include "../../utils/StringUtils.h"
|
#include "../../utils/StringUtils.h"
|
||||||
#include "UtilsLinux.h"
|
#include "UtilsLinux.h"
|
||||||
#include "../Library.h"
|
#include "../../system/Library.h"
|
||||||
|
|
||||||
// @todo Rename file to Library.cpp
|
// @todo Rename file to Library.cpp
|
||||||
|
|
||||||
inline
|
inline
|
||||||
bool library_load(Library* lib)
|
bool library_load(Library* lib)
|
||||||
{
|
{
|
||||||
size_t path_length = strlen(lib->dir);
|
|
||||||
|
|
||||||
char dst[PATH_MAX];
|
char dst[PATH_MAX];
|
||||||
str_concat(
|
str_concat_new(dst, lib->dir, lib->dst);
|
||||||
lib->dir, path_length,
|
|
||||||
lib->dst, strlen(lib->dst),
|
|
||||||
dst
|
|
||||||
);
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
char src[PATH_MAX];
|
char src[PATH_MAX];
|
||||||
size_t dst_len = strlen(dst);
|
size_t dst_len = strlen(dst);
|
||||||
|
|
||||||
memcpy(src, dst, dst_len + 1);
|
memcpy(src, dst, dst_len + 1);
|
||||||
|
str_insert(dst, dst_len - (sizeof(".so") - 1), "_temp");
|
||||||
memcpy(dst + dst_len - (sizeof(".so") - 1), "_temp", sizeof("_temp") - 1);
|
|
||||||
memcpy(dst + dst_len - (sizeof(".so") - 1) + (sizeof("_temp") - 1), ".so", sizeof(".so"));
|
|
||||||
|
|
||||||
lib->last_load = file_last_modified(src);
|
lib->last_load = file_last_modified(src);
|
||||||
file_copy(src, dst);
|
file_copy(src, dst);
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
|
|
||||||
#if __aarch64__
|
#if __aarch64__
|
||||||
#include "../../stdlib/simd/SIMD_Helper.h"
|
#include "../../stdlib/SIMD_Helper.h"
|
||||||
#else
|
#else
|
||||||
#include "../../stdlib/sve/SVE_Helper.h"
|
#include "../../stdlib/sve/SVE_Helper.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -358,7 +358,7 @@ uint32 display_info_get(DisplayInfo* info) {
|
||||||
mode.dmSize = sizeof(mode);
|
mode.dmSize = sizeof(mode);
|
||||||
|
|
||||||
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||||
strcpy(info[i].name, device.DeviceName);
|
str_copy_short(info[i].name, device.DeviceName);
|
||||||
info[i].width = mode.dmPelsWidth;
|
info[i].width = mode.dmPelsWidth;
|
||||||
info[i].height = mode.dmPelsHeight;
|
info[i].height = mode.dmPelsHeight;
|
||||||
info[i].hz = mode.dmDisplayFrequency;
|
info[i].hz = mode.dmDisplayFrequency;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
#include "../../utils/TestUtils.h"
|
#include "../../utils/TestUtils.h"
|
||||||
|
|
||||||
// @todo Currently alignment only effects the starting position, but it should also effect the ending/size
|
// @todo Currently alignment only effects the starting position, but it should also effect the ending/size
|
||||||
// @todo Consider to rename file to Allocator.h
|
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void* platform_alloc(size_t size)
|
void* platform_alloc(size_t size)
|
||||||
|
|
@ -23,6 +22,10 @@ void* platform_alloc(size_t size)
|
||||||
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @question Since we store at least the size of the memory in the beginning,
|
||||||
|
// does this have a negative impact on caching?
|
||||||
|
// Our Memory doesn't start at the cache line beginning but at least offset by sizeof(size_t)
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void* platform_alloc_aligned(size_t size, int32 alignment)
|
void* platform_alloc_aligned(size_t size, int32 alignment)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ void relative_to_absolute(const char* rel, char* path)
|
||||||
++self_path_length;
|
++self_path_length;
|
||||||
|
|
||||||
memcpy(path, self_path, self_path_length);
|
memcpy(path, self_path, self_path_length);
|
||||||
strcpy(path + self_path_length, temp);
|
str_copy_short(path + self_path_length, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64
|
inline uint64
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
* @link https://jingga.app
|
* @link https://jingga.app
|
||||||
*/
|
*/
|
||||||
#ifndef TOS_PLATFORM_WIN32_LIBRARY_H
|
#ifndef TOS_PLATFORM_WIN32_LIBRARY_C
|
||||||
#define TOS_PLATFORM_WIN32_LIBRARY_H
|
#define TOS_PLATFORM_WIN32_LIBRARY_C
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -23,23 +23,15 @@
|
||||||
inline
|
inline
|
||||||
bool library_load(Library* lib)
|
bool library_load(Library* lib)
|
||||||
{
|
{
|
||||||
size_t path_length = strlen(lib->dir);
|
|
||||||
|
|
||||||
char dst[MAX_PATH];
|
char dst[MAX_PATH];
|
||||||
str_concat(
|
str_concat_new(dst, lib->dir, lib->dst);
|
||||||
lib->dir, path_length,
|
|
||||||
lib->dst, strlen(lib->dst),
|
|
||||||
dst
|
|
||||||
);
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
char src[MAX_PATH];
|
char src[MAX_PATH];
|
||||||
size_t dst_len = strlen(dst);
|
size_t dst_len = strlen(dst);
|
||||||
|
|
||||||
memcpy(src, dst, dst_len + 1);
|
memcpy(src, dst, dst_len + 1);
|
||||||
|
str_insert(dst, dst_len - (sizeof(".dll") - 1), "_temp");
|
||||||
memcpy(dst + dst_len - (sizeof(".dll") - 1), "_temp", sizeof(".temp") - 1);
|
|
||||||
memcpy(dst + dst_len - (sizeof(".dll") - 1) + (sizeof(".temp") - 1), ".dll", sizeof(".dll"));
|
|
||||||
|
|
||||||
lib->last_load = file_last_modified(src);
|
lib->last_load = file_last_modified(src);
|
||||||
file_copy(src, dst);
|
file_copy(src, dst);
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
#if __aarch64__
|
#if __aarch64__
|
||||||
#include "../../stdlib/sve/SVE_Helper.h"
|
#include "../../stdlib/sve/SVE_Helper.h"
|
||||||
#else
|
#else
|
||||||
#include "../../stdlib/simd/SIMD_Helper.h"
|
#include "../../stdlib/SIMD_Helper.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// @performance Do we really need all these libs, can't we simplify that?!
|
// @performance Do we really need all these libs, can't we simplify that?!
|
||||||
|
|
@ -451,7 +451,7 @@ void display_info_get_primary(DisplayInfo* info) {
|
||||||
mode.dmSize = sizeof(mode);
|
mode.dmSize = sizeof(mode);
|
||||||
|
|
||||||
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||||
strcpy(info->name, device.DeviceName);
|
str_copy_short(info->name, device.DeviceName);
|
||||||
info->width = mode.dmPelsWidth;
|
info->width = mode.dmPelsWidth;
|
||||||
info->height = mode.dmPelsHeight;
|
info->height = mode.dmPelsHeight;
|
||||||
info->hz = mode.dmDisplayFrequency;
|
info->hz = mode.dmDisplayFrequency;
|
||||||
|
|
@ -473,7 +473,7 @@ uint32 display_info_get(DisplayInfo* info) {
|
||||||
mode.dmSize = sizeof(mode);
|
mode.dmSize = sizeof(mode);
|
||||||
|
|
||||||
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
if (EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
|
||||||
strcpy(info[i].name, device.DeviceName);
|
str_copy_short(info[i].name, device.DeviceName);
|
||||||
info[i].width = mode.dmPelsWidth;
|
info[i].width = mode.dmPelsWidth;
|
||||||
info[i].height = mode.dmPelsHeight;
|
info[i].height = mode.dmPelsHeight;
|
||||||
info[i].hz = mode.dmDisplayFrequency;
|
info[i].hz = mode.dmDisplayFrequency;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#define TOS_PLATFORM_WIN32_THREADING_SPINLOCK_C
|
#define TOS_PLATFORM_WIN32_THREADING_SPINLOCK_C
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include "../../../stdlib/Types.h"
|
||||||
#include "../TimeUtils.h"
|
#include "../TimeUtils.h"
|
||||||
#include "Spinlock.h"
|
#include "Spinlock.h"
|
||||||
|
|
||||||
|
|
|
||||||
475
stdlib/HashMap.h
475
stdlib/HashMap.h
|
|
@ -16,57 +16,113 @@
|
||||||
#include "../memory/ChunkMemory.h"
|
#include "../memory/ChunkMemory.h"
|
||||||
#include "../utils/StringUtils.h"
|
#include "../utils/StringUtils.h"
|
||||||
|
|
||||||
#define HASH_MAP_MAX_KEY_LENGTH 32
|
// WARNING Length of 28 used to ensure perfect padding with element_id and key
|
||||||
|
#define HASH_MAP_MAX_KEY_LENGTH 28
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// string key
|
||||||
|
/////////////////////////////
|
||||||
struct HashEntryInt32 {
|
struct HashEntryInt32 {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntryInt32* next;
|
HashEntryInt32* next;
|
||||||
int32 value;
|
int32 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashEntryInt64 {
|
struct HashEntryInt64 {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntryInt64* next;
|
HashEntryInt64* next;
|
||||||
int64 value;
|
int64 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashEntryUIntPtr {
|
struct HashEntryUIntPtr {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntryUIntPtr* next;
|
HashEntryUIntPtr* next;
|
||||||
uintptr_t value;
|
uintptr_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashEntryVoidP {
|
struct HashEntryVoidP {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntryVoidP* next;
|
HashEntryVoidP* next;
|
||||||
void* value;
|
void* value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashEntryFloat {
|
struct HashEntryFloat {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntryFloat* next;
|
HashEntryFloat* next;
|
||||||
f32 value;
|
f32 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashEntryStr {
|
struct HashEntryStr {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntryStr* next;
|
HashEntryStr* next;
|
||||||
char value[HASH_MAP_MAX_KEY_LENGTH];
|
char value[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashEntry {
|
struct HashEntry {
|
||||||
int64 element_id;
|
uint32 element_id;
|
||||||
char key[HASH_MAP_MAX_KEY_LENGTH];
|
char key[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
HashEntry* next;
|
HashEntry* next;
|
||||||
byte* value;
|
byte* value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// int key
|
||||||
|
/////////////////////////////
|
||||||
|
struct HashEntryInt32KeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryInt32KeyInt32* next;
|
||||||
|
int32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashEntryInt64KeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryInt64KeyInt32* next;
|
||||||
|
int64 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashEntryUIntPtrKeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryUIntPtrKeyInt32* next;
|
||||||
|
uintptr_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashEntryVoidPKeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryVoidPKeyInt32* next;
|
||||||
|
void* value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashEntryFloatKeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryFloatKeyInt32* next;
|
||||||
|
f32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashEntryStrKeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryStrKeyInt32* next;
|
||||||
|
char value[HASH_MAP_MAX_KEY_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashEntryKeyInt32 {
|
||||||
|
uint32 element_id;
|
||||||
|
int32 key;
|
||||||
|
HashEntryKeyInt32* next;
|
||||||
|
byte* value;
|
||||||
|
};
|
||||||
|
|
||||||
struct HashMap {
|
struct HashMap {
|
||||||
void** table;
|
void** table;
|
||||||
ChunkMemory buf;
|
ChunkMemory buf;
|
||||||
|
|
@ -83,7 +139,7 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, RingMemory* ri
|
||||||
);
|
);
|
||||||
|
|
||||||
hm->table = (void **) data;
|
hm->table = (void **) data;
|
||||||
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 1);
|
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: element_size = element size + remaining HashEntry data size
|
// WARNING: element_size = element size + remaining HashEntry data size
|
||||||
|
|
@ -96,14 +152,14 @@ void hashmap_create(HashMap* hm, int32 count, int32 element_size, BufferMemory*
|
||||||
);
|
);
|
||||||
|
|
||||||
hm->table = (void **) data;
|
hm->table = (void **) data;
|
||||||
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 1);
|
chunk_init(&hm->buf, data + sizeof(void *) * count, count, element_size, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: element_size = element size + remaining HashEntry data size
|
// WARNING: element_size = element size + remaining HashEntry data size
|
||||||
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf)
|
void hashmap_create(HashMap* hm, int32 count, int32 element_size, byte* buf)
|
||||||
{
|
{
|
||||||
hm->table = (void **) buf;
|
hm->table = (void **) buf;
|
||||||
chunk_init(&hm->buf, buf + sizeof(void *) * count, count, element_size, 1);
|
chunk_init(&hm->buf, buf + sizeof(void *) * count, count, element_size, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates how large a hashmap will be
|
// Calculates how large a hashmap will be
|
||||||
|
|
@ -121,10 +177,13 @@ int64 hashmap_size(const HashMap* hm)
|
||||||
return hm->buf.count * sizeof(hm->table) + hm->buf.size;
|
return hm->buf.count * sizeof(hm->table) + hm->buf.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// string key
|
||||||
|
/////////////////////////////
|
||||||
void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true);
|
HashEntryInt32* entry = (HashEntryInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -150,7 +209,7 @@ void hashmap_insert(HashMap* hm, const char* key, int32 value) {
|
||||||
void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntryInt64* entry = (HashEntryInt64 *) chunk_get_element(&hm->buf, element, true);
|
HashEntryInt64* entry = (HashEntryInt64 *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -175,7 +234,7 @@ void hashmap_insert(HashMap* hm, const char* key, int64 value) {
|
||||||
void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntryUIntPtr* entry = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, element, true);
|
HashEntryUIntPtr* entry = (HashEntryUIntPtr *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -200,7 +259,7 @@ void hashmap_insert(HashMap* hm, const char* key, uintptr_t value) {
|
||||||
void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntryVoidP* entry = (HashEntryVoidP *) chunk_get_element(&hm->buf, element, true);
|
HashEntryVoidP* entry = (HashEntryVoidP *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -225,7 +284,7 @@ void hashmap_insert(HashMap* hm, const char* key, void* value) {
|
||||||
void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntryFloat* entry = (HashEntryFloat *) chunk_get_element(&hm->buf, element, true);
|
HashEntryFloat* entry = (HashEntryFloat *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -250,7 +309,7 @@ void hashmap_insert(HashMap* hm, const char* key, f32 value) {
|
||||||
void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntryStr* entry = (HashEntryStr *) chunk_get_element(&hm->buf, element, true);
|
HashEntryStr* entry = (HashEntryStr *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -274,10 +333,10 @@ void hashmap_insert(HashMap* hm, const char* key, const char* value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
HashEntry* hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
int64 element = chunk_reserve(&hm->buf, 1);
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
||||||
entry->element_id = element;
|
entry->element_id = element;
|
||||||
|
|
||||||
|
|
@ -300,6 +359,73 @@ void hashmap_insert(HashMap* hm, const char* key, byte* value) {
|
||||||
} else {
|
} else {
|
||||||
hm->table[index] = entry;
|
hm->table[index] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashEntry* hashmap_reserve(HashMap* hm, const char* key) {
|
||||||
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntry* entry = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->value = (byte *) entry + sizeof(HashEntry);
|
||||||
|
|
||||||
|
strncpy(entry->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||||
|
entry->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||||
|
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntry* tmp = (HashEntry *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns existing element or element to be filled
|
||||||
|
HashEntry* hashmap_get_reserve(HashMap* hm, const char* key)
|
||||||
|
{
|
||||||
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
|
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||||
|
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||||
|
DEBUG_MEMORY_READ((uint64) entry, sizeof(HashEntry));
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((HashEntry *) entry->next) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (HashEntry *) entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntry* entry_new = (HashEntry *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry_new->element_id = element;
|
||||||
|
|
||||||
|
entry_new->value = (byte *) entry_new + sizeof(HashEntry);
|
||||||
|
|
||||||
|
strncpy(entry_new->key, key, HASH_MAP_MAX_KEY_LENGTH);
|
||||||
|
entry_new->key[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
entry->next = entry_new;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
|
HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
|
||||||
|
|
@ -307,7 +433,8 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key) {
|
||||||
HashEntry* entry = (HashEntry *) hm->table[index];
|
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||||
|
|
||||||
while (entry != NULL) {
|
while (entry != NULL) {
|
||||||
if (strncmp(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||||
|
DEBUG_MEMORY_READ((uint64) entry, sizeof(HashEntry));
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,7 +451,8 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 hash) {
|
||||||
HashEntry* entry = (HashEntry *) hm->table[hash];
|
HashEntry* entry = (HashEntry *) hm->table[hash];
|
||||||
|
|
||||||
while (entry != NULL) {
|
while (entry != NULL) {
|
||||||
if (strncmp(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||||
|
DEBUG_MEMORY_READ((uint64) entry, sizeof(HashEntry));
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,20 +462,253 @@ HashEntry* hashmap_get_entry(const HashMap* hm, const char* key, uint64 hash) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hashmap_delete_entry(HashMap* hm, const char* key) {
|
// @performance If we had a doubly linked list we could delete keys much easier
|
||||||
|
// However that would make insertion slower
|
||||||
|
// Maybe we create a nother hashmap that is doubly linked
|
||||||
|
void hashmap_remove(HashMap* hm, const char* key) {
|
||||||
uint64 index = hash_djb2(key) % hm->buf.count;
|
uint64 index = hash_djb2(key) % hm->buf.count;
|
||||||
HashEntry* entry = (HashEntry *) hm->table[index];
|
HashEntry* entry = (HashEntry *) hm->table[index];
|
||||||
HashEntry* prev = NULL;
|
HashEntry* prev = NULL;
|
||||||
|
|
||||||
while (entry != NULL) {
|
while (entry != NULL) {
|
||||||
if (strncmp(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
if (str_compare(entry->key, key, HASH_MAP_MAX_KEY_LENGTH) == 0) {
|
||||||
if (prev == NULL) {
|
if (prev == NULL) {
|
||||||
hm->table[index] = entry->next;
|
hm->table[index] = entry->next;
|
||||||
} else {
|
} else {
|
||||||
prev->next = entry->next;
|
prev->next = entry->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_free_element(&hm->buf, entry->element_id);
|
chunk_free_elements(&hm->buf, entry->element_id);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = entry;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// int key
|
||||||
|
/////////////////////////////
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, int32 value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryInt32KeyInt32* entry = (HashEntryInt32KeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryInt32KeyInt32* tmp = (HashEntryInt32KeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, int64 value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryInt64KeyInt32* entry = (HashEntryInt64KeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryInt64KeyInt32* tmp = (HashEntryInt64KeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, uintptr_t value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryUIntPtrKeyInt32* entry = (HashEntryUIntPtrKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryUIntPtrKeyInt32* tmp = (HashEntryUIntPtrKeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, void* value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryVoidPKeyInt32* entry = (HashEntryVoidPKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryVoidPKeyInt32* tmp = (HashEntryVoidPKeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, f32 value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryFloatKeyInt32* entry = (HashEntryFloatKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryFloatKeyInt32* tmp = (HashEntryFloatKeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, const char* value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryStrKeyInt32* entry = (HashEntryStrKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
|
||||||
|
strncpy(entry->value, value, HASH_MAP_MAX_KEY_LENGTH);
|
||||||
|
entry->value[HASH_MAP_MAX_KEY_LENGTH - 1] = '\0';
|
||||||
|
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryStrKeyInt32* tmp = (HashEntryStrKeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashmap_insert(HashMap* hm, int32 key, byte* value) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
|
||||||
|
int32 element = chunk_reserve(&hm->buf, 1);
|
||||||
|
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) chunk_get_element(&hm->buf, element, true);
|
||||||
|
entry->element_id = element;
|
||||||
|
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = (byte *) entry + sizeof(HashEntryKeyInt32);
|
||||||
|
|
||||||
|
memcpy(entry->value, value, hm->buf.chunk_size - sizeof(HashEntryKeyInt32));
|
||||||
|
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
if (hm->table[index]) {
|
||||||
|
HashEntryKeyInt32* tmp = (HashEntryKeyInt32 *) hm->table[index];
|
||||||
|
while(tmp->next) {
|
||||||
|
tmp = tmp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp->next = entry;
|
||||||
|
} else {
|
||||||
|
hm->table[index] = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashEntryKeyInt32* hashmap_get_entry(const HashMap* hm, int32 key) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) hm->table[index];
|
||||||
|
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (entry->key == key) {
|
||||||
|
DEBUG_MEMORY_READ((uint64) entry, sizeof(HashEntryKeyInt32));
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (HashEntryKeyInt32 *) entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function only saves one step (omission of the hash function)
|
||||||
|
// The reason for this is in some cases we can use compile time hashing
|
||||||
|
HashEntryKeyInt32* hashmap_get_entry(const HashMap* hm, int32 key, uint64 hash) {
|
||||||
|
hash %= hm->buf.count;
|
||||||
|
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) hm->table[hash];
|
||||||
|
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (entry->key == key) {
|
||||||
|
DEBUG_MEMORY_READ((uint64) entry, sizeof(HashEntryKeyInt32));
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (HashEntryKeyInt32 *) entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @performance If we had a doubly linked list we could delete keys much easier
|
||||||
|
// However that would make insertion slower
|
||||||
|
// Maybe we create a nother hashmap that is doubly linked
|
||||||
|
void hashmap_remove(HashMap* hm, int32 key) {
|
||||||
|
uint64 index = key % hm->buf.count;
|
||||||
|
HashEntryKeyInt32* entry = (HashEntryKeyInt32 *) hm->table[index];
|
||||||
|
HashEntryKeyInt32* prev = NULL;
|
||||||
|
|
||||||
|
while (entry != NULL) {
|
||||||
|
if (entry->key == key) {
|
||||||
|
if (prev == NULL) {
|
||||||
|
hm->table[index] = entry->next;
|
||||||
|
} else {
|
||||||
|
prev->next = entry->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_free_elements(&hm->buf, entry->element_id);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -357,10 +718,22 @@ void hashmap_delete_entry(HashMap* hm, const char* key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int32 hashmap_value_size(const HashMap* hm)
|
||||||
|
{
|
||||||
|
return (uint32) (
|
||||||
|
hm->buf.chunk_size
|
||||||
|
- sizeof(uint32) // element id
|
||||||
|
- sizeof(char) * HASH_MAP_MAX_KEY_LENGTH // key
|
||||||
|
- sizeof(uintptr_t) // next pointer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @question Shouldn't we also store the hashmap count, chunk size etc? Currently not done and expected to be correctly initialized.
|
||||||
inline
|
inline
|
||||||
int64 hashmap_dump(const HashMap* hm, byte* data)
|
int64 hashmap_dump(const HashMap* hm, byte* data)
|
||||||
{
|
{
|
||||||
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(hm->buf.count);
|
*((uint32 *) data) = SWAP_ENDIAN_LITTLE(hm->buf.count);
|
||||||
data += sizeof(hm->buf.count);
|
data += sizeof(hm->buf.count);
|
||||||
|
|
||||||
// Dump the table content where the elements are relative indices/pointers
|
// Dump the table content where the elements are relative indices/pointers
|
||||||
|
|
@ -371,17 +744,19 @@ int64 hashmap_dump(const HashMap* hm, byte* data)
|
||||||
}
|
}
|
||||||
data += sizeof(uint64) * hm->buf.count;
|
data += sizeof(uint64) * hm->buf.count;
|
||||||
|
|
||||||
int64 value_size = hm->buf.chunk_size - sizeof(uint64) - sizeof(char) * HASH_MAP_MAX_KEY_LENGTH - sizeof(uint64);
|
// @bug what if Int32 key?
|
||||||
|
int32 value_size = hashmap_value_size(hm);
|
||||||
|
|
||||||
// Dumb hash map content = buffer memory
|
// Dumb hash map content = buffer memory
|
||||||
|
// Since we are using ChunkMemory we can be smart about it and iterate the chunk memory instead of performing pointer chasing
|
||||||
int32 free_index = 0;
|
int32 free_index = 0;
|
||||||
int32 bit_index = 0;
|
int32 bit_index = 0;
|
||||||
for (uint32 i = 0; i < hm->buf.count; ++i) {
|
for (uint32 i = 0; i < hm->buf.count; ++i) {
|
||||||
if ((hm->buf.free[free_index] & (1ULL << bit_index)) > 0) {
|
if (hm->buf.free[free_index] & (1ULL << bit_index)) {
|
||||||
HashEntry* entry = (HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, i);
|
HashEntry* entry = (HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, i);
|
||||||
|
|
||||||
// element_id
|
// element_id
|
||||||
*((uint64 *) data) = SWAP_ENDIAN_LITTLE(entry->element_id);
|
*((uint32 *) data) = SWAP_ENDIAN_LITTLE(entry->element_id);
|
||||||
data += sizeof(entry->element_id);
|
data += sizeof(entry->element_id);
|
||||||
|
|
||||||
// key
|
// key
|
||||||
|
|
@ -430,8 +805,8 @@ int64 hashmap_dump(const HashMap* hm, byte* data)
|
||||||
inline
|
inline
|
||||||
int64 hashmap_load(HashMap* hm, const byte* data)
|
int64 hashmap_load(HashMap* hm, const byte* data)
|
||||||
{
|
{
|
||||||
uint64 count = SWAP_ENDIAN_LITTLE(*((uint64 *) data));
|
uint64 count = SWAP_ENDIAN_LITTLE(*((uint32 *) data));
|
||||||
data += sizeof(uint64);
|
data += sizeof(uint32);
|
||||||
|
|
||||||
// Load the table content
|
// Load the table content
|
||||||
for (uint32 i = 0; i < count; ++i) {
|
for (uint32 i = 0; i < count; ++i) {
|
||||||
|
|
@ -450,33 +825,31 @@ int64 hashmap_load(HashMap* hm, const byte* data)
|
||||||
// @question don't we have to possibly endian swap check the free array as well?
|
// @question don't we have to possibly endian swap check the free array as well?
|
||||||
memcpy(hm->buf.free, data, sizeof(uint64) * CEIL_DIV(hm->buf.count, 64));
|
memcpy(hm->buf.free, data, sizeof(uint64) * CEIL_DIV(hm->buf.count, 64));
|
||||||
|
|
||||||
int64 value_size = hm->buf.chunk_size - sizeof(uint64) - sizeof(char) * HASH_MAP_MAX_KEY_LENGTH - sizeof(uint64);
|
// @bug what if Int32 key?
|
||||||
|
int32 value_size = hashmap_value_size(hm);
|
||||||
|
|
||||||
// Switch endian AND turn offsets to pointers
|
// Switch endian AND turn offsets to pointers
|
||||||
int32 free_index = 0;
|
int32 chunk_id = 0;
|
||||||
int32 bit_index = 0;
|
chunk_iterate_start(&hm->buf, chunk_id)
|
||||||
for (uint32 i = 0; i < hm->buf.count; ++i) {
|
HashEntry* entry = (HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, chunk_id);
|
||||||
if ((hm->buf.free[free_index] & (1ULL << bit_index)) > 0) {
|
|
||||||
HashEntry* entry = (HashEntry *) chunk_get_element((ChunkMemory *) &hm->buf, i);
|
|
||||||
|
|
||||||
// element id
|
// element id
|
||||||
entry->element_id = SWAP_ENDIAN_LITTLE(entry->element_id);
|
entry->element_id = SWAP_ENDIAN_LITTLE(entry->element_id);
|
||||||
|
|
||||||
// key is already loaded with the memcpy
|
// key is already loaded with the memcpy
|
||||||
// @question Do we even want to use memcpy? We are re-checking all the values here anyways
|
// @question Do we even want to use memcpy? We are re-checking all the values here anyways
|
||||||
|
|
||||||
// next pointer
|
// next pointer
|
||||||
if (entry->next) {
|
if (entry->next) {
|
||||||
entry->next = (HashEntry *) (hm->buf.memory + SWAP_ENDIAN_LITTLE((uint64) entry->next));
|
entry->next = (HashEntry *) (hm->buf.memory + SWAP_ENDIAN_LITTLE((uint64) entry->next));
|
||||||
}
|
|
||||||
|
|
||||||
if (value_size == 4) {
|
|
||||||
((HashEntryInt32 *) entry)->value = SWAP_ENDIAN_LITTLE(((HashEntryInt32 *) entry)->value);
|
|
||||||
} else if (value_size == 8) {
|
|
||||||
((HashEntryInt64 *) entry)->value = SWAP_ENDIAN_LITTLE(((HashEntryInt64 *) entry)->value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (value_size == 4) {
|
||||||
|
((HashEntryInt32 *) entry)->value = SWAP_ENDIAN_LITTLE(((HashEntryInt32 *) entry)->value);
|
||||||
|
} else if (value_size == 8) {
|
||||||
|
((HashEntryInt64 *) entry)->value = SWAP_ENDIAN_LITTLE(((HashEntryInt64 *) entry)->value);
|
||||||
|
}
|
||||||
|
chunk_iterate_end;
|
||||||
|
|
||||||
// How many bytes was read from data
|
// How many bytes was read from data
|
||||||
return sizeof(hm->buf.count) // hash map count = buffer count
|
return sizeof(hm->buf.count) // hash map count = buffer count
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, int32 value) {
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryInt32* entry = (PerfectHashEntryInt32 *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryInt32* entry = (PerfectHashEntryInt32 *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +207,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, int64 value) {
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryInt64* entry = (PerfectHashEntryInt64 *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryInt64* entry = (PerfectHashEntryInt64 *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, uintptr_t value
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryUIntPtr* entry = (PerfectHashEntryUIntPtr *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryUIntPtr* entry = (PerfectHashEntryUIntPtr *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,7 +225,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, void* value) {
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryVoidP* entry = (PerfectHashEntryVoidP *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryVoidP* entry = (PerfectHashEntryVoidP *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,7 +234,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, f32 value) {
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryFloat* entry = (PerfectHashEntryFloat *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryFloat* entry = (PerfectHashEntryFloat *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
entry->value = value;
|
entry->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,7 +243,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, const char* val
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
memcpy(entry->value, value, PERFECT_HASH_MAP_MAX_KEY_LENGTH);
|
memcpy(entry->value, value, PERFECT_HASH_MAP_MAX_KEY_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,7 +252,7 @@ void perfect_hashmap_insert(PerfectHashMap* hm, const char* key, byte* value) {
|
||||||
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
int32 index = hm->hash_function(key, hm->hash_seed) % hm->map_size;
|
||||||
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
PerfectHashEntryStr* entry = (PerfectHashEntryStr *) (hm->hash_entries + hm->entry_size * index);
|
||||||
entry->element_id = index;
|
entry->element_id = index;
|
||||||
strcpy(entry->key, key);
|
str_copy_short(entry->key, key);
|
||||||
memcpy(entry->value, value, hm->entry_size - sizeof(PerfectHashEntry));
|
memcpy(entry->value, value, hm->entry_size - sizeof(PerfectHashEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,12 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <immintrin.h>
|
#include <immintrin.h>
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
#include "../Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
// @todo split into platform code for windows and linux
|
// @todo split into platform code for windows and linux
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#endif
|
#endif
|
||||||
24
stdlib/Simd.h
Normal file
24
stdlib/Simd.h
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_STDLIB_SIMD_H
|
||||||
|
#define TOS_STDLIB_SIMD_H
|
||||||
|
|
||||||
|
#if __aarch64__
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include "simd/SIMD_F32.h"
|
||||||
|
#include "simd/SIMD_F64.h"
|
||||||
|
#include "simd/SIMD_I8.h"
|
||||||
|
#include "simd/SIMD_I16.h"
|
||||||
|
#include "simd/SIMD_I32.h"
|
||||||
|
#include "simd/SIMD_I64.h"
|
||||||
|
#include "simd/SIMD_SVML.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -12,15 +12,9 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "HashMap.h"
|
#include "HashMap.h"
|
||||||
|
|
||||||
#if _WIN32
|
#include "../thread/Atomic.h"
|
||||||
#include "../platform/win32/threading/Thread.h"
|
#include "../thread/Semaphore.h"
|
||||||
#include "../platform/win32/threading/Semaphore.h"
|
#include "../thread/Thread.h"
|
||||||
#include "../platform/win32/threading/Atomic.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Thread.h"
|
|
||||||
#include "../platform/linux/threading/Semaphore.h"
|
|
||||||
#include "../platform/linux/threading/Atomic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ThreadedHashMap {
|
struct ThreadedHashMap {
|
||||||
void** table;
|
void** table;
|
||||||
|
|
@ -125,9 +119,9 @@ void thrd_hashmap_get_entry(ThreadedHashMap* hm, HashEntry* entry, const char* k
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void thrd_hashmap_delete_entry(ThreadedHashMap* hm, const char* key) {
|
void thrd_hashmap_remove(ThreadedHashMap* hm, const char* key) {
|
||||||
pthread_mutex_lock(&hm->mutex);
|
pthread_mutex_lock(&hm->mutex);
|
||||||
hashmap_delete_entry((HashMap *) hm, key);
|
hashmap_remove((HashMap *) hm, key);
|
||||||
pthread_mutex_unlock(&hm->mutex);
|
pthread_mutex_unlock(&hm->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,11 @@ typedef intptr_t smm;
|
||||||
#define MIN_INT32 0x80000000
|
#define MIN_INT32 0x80000000
|
||||||
#define MIN_INT64 0x8000000000000000
|
#define MIN_INT64 0x8000000000000000
|
||||||
|
|
||||||
|
#define MIN_MILLI 60000
|
||||||
#define SEC_MILLI 1000
|
#define SEC_MILLI 1000
|
||||||
#define MILLI_MICRO 1000
|
#define MIN_MICRO 60000000
|
||||||
#define SEC_MICRO 1000000
|
#define SEC_MICRO 1000000
|
||||||
|
#define MILLI_MICRO 1000
|
||||||
|
|
||||||
#define MHZ 1000000
|
#define MHZ 1000000
|
||||||
#define GHZ 1000000000
|
#define GHZ 1000000000
|
||||||
|
|
|
||||||
18
system/Allocator.h
Normal file
18
system/Allocator.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_SYSTEM_ALLOCATOR_H
|
||||||
|
#define TOS_SYSTEM_ALLOCATOR_H
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/Allocator.h"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/Allocator.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
system/FileUtils.cpp
Normal file
18
system/FileUtils.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_SYSTEM_FILE_UTILS_C
|
||||||
|
#define TOS_SYSTEM_FILE_UTILS_C
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/FileUtils.cpp"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/FileUtils.cpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
system/Library.cpp
Normal file
18
system/Library.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_SYSTEM_LIBRARY_C
|
||||||
|
#define TOS_SYSTEM_LIBRARY_C
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/Library.cpp"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/Library.cpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
system/SystemInfo.cpp
Normal file
18
system/SystemInfo.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_SYSTEM_INFO_C
|
||||||
|
#define TOS_SYSTEM_INFO_C
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/SystemInfo.cpp"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/SystemInfo.cpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
thread/Atomic.h
Normal file
18
thread/Atomic.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_THREADS_ATOMIC_H
|
||||||
|
#define TOS_THREADS_ATOMIC_H
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/threading/Atomic.h"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/threading/Atomic.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
thread/Semaphore.h
Normal file
18
thread/Semaphore.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_THREADS_SEMAPHORE_H
|
||||||
|
#define TOS_THREADS_SEMAPHORE_H
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/threading/Semaphore.h"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/threading/Semaphore.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
thread/Spinlock.cpp
Normal file
18
thread/Spinlock.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_THREADS_SPINLOCK_C
|
||||||
|
#define TOS_THREADS_SPINLOCK_C
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/threading/Spinlock.cpp"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/threading/Spinlock.cpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
thread/Spinlock.h
Normal file
18
thread/Spinlock.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_THREADS_SPINLOCK_H
|
||||||
|
#define TOS_THREADS_SPINLOCK_H
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/threading/Spinlock.h"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/threading/Spinlock.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -13,13 +13,12 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
|
#include "Atomic.h"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
#include "../platform/win32/threading/Thread.h"
|
#include "../platform/win32/threading/Thread.h"
|
||||||
#include "../platform/win32/threading/Atomic.h"
|
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#include "../platform/linux/threading/Thread.h"
|
#include "../platform/linux/threading/Thread.h"
|
||||||
#include "../platform/linux/threading/Atomic.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ThreadJob.h"
|
#include "ThreadJob.h"
|
||||||
|
|
|
||||||
18
thread/ThreadDefines.h
Normal file
18
thread/ThreadDefines.h
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Jingga
|
||||||
|
*
|
||||||
|
* @copyright Jingga
|
||||||
|
* @license OMS License 2.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @link https://jingga.app
|
||||||
|
*/
|
||||||
|
#ifndef TOS_THREADS_THREAD_DEFINES_H
|
||||||
|
#define TOS_THREADS_THREAD_DEFINES_H
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#include "../platform/win32/threading/ThreadDefines.h"
|
||||||
|
#elif __linux__
|
||||||
|
#include "../platform/linux/threading/ThreadDefines.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -14,12 +14,7 @@
|
||||||
|
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/ThreadedRingMemory.h"
|
#include "../memory/ThreadedRingMemory.h"
|
||||||
|
#include "../thread/ThreadDefines.h"
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/threading/ThreadDefines.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/ThreadDefines.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void (*ThreadPoolJobFunc)(void*);
|
typedef void (*ThreadPoolJobFunc)(void*);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,9 @@
|
||||||
#include "../stdlib/Types.h"
|
#include "../stdlib/Types.h"
|
||||||
#include "../memory/Queue.h"
|
#include "../memory/Queue.h"
|
||||||
#include "../memory/BufferMemory.h"
|
#include "../memory/BufferMemory.h"
|
||||||
|
#include "../log/DebugMemory.h"
|
||||||
#ifdef _WIN32
|
#include "Thread.h"
|
||||||
#include "../platform/win32/threading/Thread.h"
|
#include "Atomic.h"
|
||||||
#include "../platform/win32/threading/Atomic.h"
|
|
||||||
#elif __linux__
|
|
||||||
#include "../platform/linux/threading/Thread.h"
|
|
||||||
#include "../platform/linux/threading/Atomic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "ThreadJob.h"
|
#include "ThreadJob.h"
|
||||||
|
|
||||||
struct ThreadPool {
|
struct ThreadPool {
|
||||||
|
|
@ -70,6 +64,8 @@ static THREAD_RETURN thread_pool_worker(void* arg)
|
||||||
atomic_increment_relaxed(&pool->working_cnt);
|
atomic_increment_relaxed(&pool->working_cnt);
|
||||||
atomic_set_release(&work->state, 2);
|
atomic_set_release(&work->state, 2);
|
||||||
work->func(work);
|
work->func(work);
|
||||||
|
// At the end of a thread the ring memory automatically is considered freed
|
||||||
|
DEBUG_MEMORY_FREE((uint64) work->ring.memory, work->ring.size);
|
||||||
atomic_set_release(&work->state, 1);
|
atomic_set_release(&work->state, 1);
|
||||||
|
|
||||||
// Job gets marked after completion -> can be overwritten now
|
// Job gets marked after completion -> can be overwritten now
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ UIAttribute* ui_attribute_from_group(UIAttributeGroup* group, UIAttributeType ty
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const char* ui_attribute_type_to_string_const(UIAttributeType e)
|
constexpr const char* ui_attribute_type_to_string(UIAttributeType e)
|
||||||
{
|
{
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case UI_ATTRIBUTE_TYPE_TYPE:
|
case UI_ATTRIBUTE_TYPE_TYPE:
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ enum UIElementType {
|
||||||
UI_ELEMENT_TYPE_SIZE,
|
UI_ELEMENT_TYPE_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr const char* ui_element_type_to_string_const(UIElementType e)
|
constexpr const char* ui_element_type_to_string(UIElementType e)
|
||||||
{
|
{
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case UI_ELEMENT_TYPE_BUTTON:
|
case UI_ELEMENT_TYPE_BUTTON:
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ struct UILayout {
|
||||||
int32 vertex_size;
|
int32 vertex_size;
|
||||||
Asset* ui_asset;
|
Asset* ui_asset;
|
||||||
|
|
||||||
|
// @question Should we maybe also hold the font atlas asset here AND the color palette?
|
||||||
|
|
||||||
// Defines the length of the static vertex array
|
// Defines the length of the static vertex array
|
||||||
int32 vertex_size_static;
|
int32 vertex_size_static;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
102
ui/UITheme.h
102
ui/UITheme.h
|
|
@ -7,16 +7,11 @@
|
||||||
#include "../utils/StringUtils.h"
|
#include "../utils/StringUtils.h"
|
||||||
#include "../stdlib/HashMap.h"
|
#include "../stdlib/HashMap.h"
|
||||||
#include "../font/Font.h"
|
#include "../font/Font.h"
|
||||||
|
#include "../system/FileUtils.cpp"
|
||||||
|
|
||||||
#include "UIAttribute.h"
|
#include "UIAttribute.h"
|
||||||
#include "UIElementType.h"
|
#include "UIElementType.h"
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
#include "../platform/win32/FileUtils.cpp"
|
|
||||||
#else
|
|
||||||
#include "../platform/linux/FileUtils.cpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define UI_THEME_VERSION 1
|
#define UI_THEME_VERSION 1
|
||||||
|
|
||||||
// @question Currently there is some data duplication in here and in the UIElement.
|
// @question Currently there is some data duplication in here and in the UIElement.
|
||||||
|
|
@ -27,8 +22,6 @@
|
||||||
struct UIThemeStyle {
|
struct UIThemeStyle {
|
||||||
byte* data;
|
byte* data;
|
||||||
|
|
||||||
int32 version;
|
|
||||||
|
|
||||||
// A theme may have N named styles
|
// A theme may have N named styles
|
||||||
// The hashmap contains the offset where the respective style can be found
|
// The hashmap contains the offset where the respective style can be found
|
||||||
// @performance Switch to perfect hash map
|
// @performance Switch to perfect hash map
|
||||||
|
|
@ -121,7 +114,8 @@ void theme_from_file_txt(
|
||||||
// move past the version string
|
// move past the version string
|
||||||
pos += 8;
|
pos += 8;
|
||||||
|
|
||||||
theme->version = strtol(pos, &pos, 10); ++pos;
|
// Use version for different handling
|
||||||
|
int32 version = strtol(pos, &pos, 10); ++pos;
|
||||||
|
|
||||||
bool block_open = false;
|
bool block_open = false;
|
||||||
char block_name[32];
|
char block_name[32];
|
||||||
|
|
@ -157,7 +151,9 @@ void theme_from_file_txt(
|
||||||
UIAttributeGroup* temp_group = NULL;
|
UIAttributeGroup* temp_group = NULL;
|
||||||
|
|
||||||
pos = (char *) file.content;
|
pos = (char *) file.content;
|
||||||
pos += 8; // move past version
|
|
||||||
|
// move past version string
|
||||||
|
str_move_past(&pos, '\n');
|
||||||
|
|
||||||
while (*pos != '\0') {
|
while (*pos != '\0') {
|
||||||
str_skip_whitespace(&pos);
|
str_skip_whitespace(&pos);
|
||||||
|
|
@ -213,14 +209,14 @@ void theme_from_file_txt(
|
||||||
|
|
||||||
// Handle different attribute types
|
// Handle different attribute types
|
||||||
UIAttribute attribute = {};
|
UIAttribute attribute = {};
|
||||||
if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_TYPE), attribute_name) == 0) {
|
if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TYPE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TYPE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TYPE;
|
||||||
|
|
||||||
char str[32];
|
char str[32];
|
||||||
str_copy_move_until(&pos, str, '\n');
|
str_copy_move_until(&pos, str, '\n');
|
||||||
|
|
||||||
for (int32 j = 0; j < UI_ELEMENT_TYPE_SIZE; ++j) {
|
for (int32 j = 0; j < UI_ELEMENT_TYPE_SIZE; ++j) {
|
||||||
if (strcmp(str, ui_element_type_to_string_const((UIElementType) j)) == 0) {
|
if (strcmp(str, ui_element_type_to_string((UIElementType) j)) == 0) {
|
||||||
|
|
||||||
attribute.value_int = j;
|
attribute.value_int = j;
|
||||||
break;
|
break;
|
||||||
|
|
@ -228,135 +224,135 @@ void theme_from_file_txt(
|
||||||
}
|
}
|
||||||
|
|
||||||
++pos;
|
++pos;
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_STYLE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_STYLE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_STYLE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_STYLE;
|
||||||
|
|
||||||
str_copy_move_until(&pos, attribute.value_str, '\n');
|
str_copy_move_until(&pos, attribute.value_str, '\n');
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_FONT_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_FONT_SIZE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_SIZE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_SIZE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_SIZE;
|
||||||
attribute.value_float = strtof(pos, &pos);
|
attribute.value_float = strtof(pos, &pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_FONT_WEIGHT), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_WEIGHT), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_WEIGHT;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_WEIGHT;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_FONT_LINE_HEIGHT;
|
||||||
attribute.value_float = strtof(pos, &pos);
|
attribute.value_float = strtof(pos, &pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_ALIGN_H), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_ALIGN_H), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ALIGN_H;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ALIGN_H;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_ALIGN_V), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_ALIGN_V), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ALIGN_V;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ALIGN_V;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_ZINDEX), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_ZINDEX), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ZINDEX;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_ZINDEX;
|
||||||
attribute.value_float = SWAP_ENDIAN_LITTLE(strtof(pos, &pos));
|
attribute.value_float = SWAP_ENDIAN_LITTLE(strtof(pos, &pos));
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG;
|
||||||
|
|
||||||
str_copy_move_until(&pos, attribute.value_str, '\n');
|
str_copy_move_until(&pos, attribute.value_str, '\n');
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_OPACITY;
|
||||||
attribute.value_float = SWAP_ENDIAN_LITTLE(strtof(pos, &pos));
|
attribute.value_float = SWAP_ENDIAN_LITTLE(strtof(pos, &pos));
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_V;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_POSITION_H;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BACKGROUND_IMG_STYLE;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_WIDTH), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_WIDTH), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_WIDTH;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_WIDTH;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_TOP_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_TOP_WIDTH;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_RIGHT_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_RIGHT_WIDTH;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_BOTTOM_WIDTH;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_LEFT_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_BORDER_LEFT_WIDTH;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_PADDING), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_PADDING_TOP), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_TOP), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_TOP;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_TOP;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_PADDING_RIGHT), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_RIGHT), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_RIGHT;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_RIGHT;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_PADDING_BOTTOM), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_BOTTOM), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_BOTTOM;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_BOTTOM;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_PADDING_LEFT), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_PADDING_LEFT), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_LEFT;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_PADDING_LEFT;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_ANGLE;
|
||||||
attribute.value_float = strtof(pos, &pos);
|
attribute.value_float = strtof(pos, &pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_INNER_DISTANCE;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR), attribute_name) == 0) {
|
||||||
++pos; // Skip '#'
|
++pos; // Skip '#'
|
||||||
|
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_COLOR;
|
||||||
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
hexstr_to_rgba(&attribute.value_v4_f32, pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_ANGLE;
|
||||||
attribute.value_float = strtof(pos, &pos);
|
attribute.value_float = strtof(pos, &pos);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_SHADOW_OUTER_DISTANCE;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TRANSITION_ANIMATION;
|
||||||
attribute.value_int = strtoul(pos, &pos, 10);
|
attribute.value_int = strtoul(pos, &pos, 10);
|
||||||
} else if (strcmp(ui_attribute_type_to_string_const(UI_ATTRIBUTE_TYPE_TRANSITION_DURATION), attribute_name) == 0) {
|
} else if (strcmp(ui_attribute_type_to_string(UI_ATTRIBUTE_TYPE_TRANSITION_DURATION), attribute_name) == 0) {
|
||||||
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TRANSITION_DURATION;
|
attribute.attribute_id = UI_ATTRIBUTE_TYPE_TRANSITION_DURATION;
|
||||||
attribute.value_float = strtof(pos, &pos);
|
attribute.value_float = strtof(pos, &pos);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -406,8 +402,8 @@ int32 theme_from_data(
|
||||||
) {
|
) {
|
||||||
const byte* pos = data;
|
const byte* pos = data;
|
||||||
|
|
||||||
theme->version = *((int32 *) pos);
|
int32 version = *((int32 *) pos);
|
||||||
pos += sizeof(theme->version);
|
pos += sizeof(version);
|
||||||
|
|
||||||
// Prepare hashmap (incl. reserve memory) by initializing it the same way we originally did
|
// 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()
|
// Of course we still need to populate the data using hashmap_load()
|
||||||
|
|
@ -487,8 +483,8 @@ int32 theme_to_data(
|
||||||
byte* pos = data;
|
byte* pos = data;
|
||||||
|
|
||||||
// version
|
// version
|
||||||
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(theme->version);
|
*((int32 *) pos) = SWAP_ENDIAN_LITTLE(UI_THEME_VERSION);
|
||||||
pos += sizeof(theme->version);
|
pos += sizeof(int32);
|
||||||
|
|
||||||
// hashmap
|
// hashmap
|
||||||
byte* start = pos;
|
byte* start = pos;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
// Right to left (little endian)
|
// Right to left (little endian)
|
||||||
#define IS_BIT_SET_R2L(num, pos) ((bool) ((num) & (1 << (pos))))
|
#define IS_BIT_SET_R2L(num, pos) ((bool) ((num) & (1 << (pos))))
|
||||||
|
#define IS_BIT_SET_64_R2L(num, pos) ((bool) ((num) & (1LL << (pos))))
|
||||||
#define BIT_SET_R2L(num, pos) ((num) | ((uint32) 1 << (pos)))
|
#define BIT_SET_R2L(num, pos) ((num) | ((uint32) 1 << (pos)))
|
||||||
#define BIT_UNSET_R2L(num, pos) ((num) & ~((uint32) 1 << (pos)))
|
#define BIT_UNSET_R2L(num, pos) ((num) & ~((uint32) 1 << (pos)))
|
||||||
#define BIT_FLIP_R2L(num, pos) ((num) ^ ((uint32) 1 << (pos)))
|
#define BIT_FLIP_R2L(num, pos) ((num) ^ ((uint32) 1 << (pos)))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#define OMS_CEIL(x) ((x) == (int)(x) ? (int)(x) : ((x) > 0 ? (int)(x) + 1 : (int)(x)))
|
#define OMS_CEIL(x) ((x) == (int)(x) ? (int)(x) : ((x) > 0 ? (int)(x) + 1 : (int)(x)))
|
||||||
#define OMS_ROUND(x) (((x) >= 0) ? ((int)((x) + 0.5f)) : ((int)((x) - 0.5f)))
|
#define OMS_ROUND(x) (((x) >= 0) ? ((int)((x) + 0.5f)) : ((int)((x) - 0.5f)))
|
||||||
#define OMS_ROUND_POSITIVE(x) ((int)((x) + 0.5f))
|
#define OMS_ROUND_POSITIVE(x) ((int)((x) + 0.5f))
|
||||||
|
#define FLOAT_CAST_EPS 0.001953125
|
||||||
|
|
||||||
// Modulo function when b is a power of 2
|
// Modulo function when b is a power of 2
|
||||||
#define MODULO_2(a, b) ((a) & (b - 1))
|
#define MODULO_2(a, b) ((a) & (b - 1))
|
||||||
|
|
|
||||||
|
|
@ -167,13 +167,14 @@ void wchar_to_char(const char* __restrict str, char* __restrict dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr
|
inline constexpr
|
||||||
int32 str_to_int(const char* str)
|
int64 str_to_int(const char* str)
|
||||||
{
|
{
|
||||||
int32 result = 0;
|
int64 result = 0;
|
||||||
|
|
||||||
int32 sign = 1;
|
int64 sign = 1;
|
||||||
if (*str++ == '-') {
|
if (*str == '-') {
|
||||||
sign = -1;
|
sign = -1;
|
||||||
|
++str;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (*str >= '0' && *str <= '9') {
|
while (*str >= '0' && *str <= '9') {
|
||||||
|
|
@ -186,15 +187,21 @@ int32 str_to_int(const char* str)
|
||||||
return result * sign;
|
return result * sign;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr
|
inline
|
||||||
int32 int_to_str(int64 number, char *str, const char thousands = ',') {
|
int32 int_to_str(int64 number, char str[15], const char thousands)
|
||||||
|
{
|
||||||
|
if (number == 0) {
|
||||||
|
*str++ = '0';
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int32 i = 0;
|
int32 i = 0;
|
||||||
int32 digit_count = 0;
|
int32 digit_count = 0;
|
||||||
int64 sign = number;
|
int64 sign = number;
|
||||||
|
|
||||||
if (number == 0) {
|
if (number < 0) {
|
||||||
str[i++] = '0';
|
|
||||||
} else if (number < 0) {
|
|
||||||
number = -number;
|
number = -number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,8 +219,84 @@ int32 int_to_str(int64 number, char *str, const char thousands = ',') {
|
||||||
str[i++] = '-';
|
str[i++] = '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int32 j = 0, k = i - 1; j < k; ++j, --k) {
|
||||||
|
char temp = str[j];
|
||||||
|
str[j] = str[k];
|
||||||
|
str[k] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
str[i] = '\0';
|
str[i] = '\0';
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr
|
||||||
|
int32 int_to_str(int64 number, char str[12]) {
|
||||||
|
int32 i = -1;
|
||||||
|
int64 sign = number;
|
||||||
|
|
||||||
|
if (number < 0) {
|
||||||
|
number = -number;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
str[++i] = number % 10 + '0';
|
||||||
|
number /= 10;
|
||||||
|
} while (number > 0);
|
||||||
|
|
||||||
|
if (sign < 0) {
|
||||||
|
str[++i] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 j = 0, k = i; j < k; ++j, --k) {
|
||||||
|
char temp = str[j];
|
||||||
|
str[j] = str[k];
|
||||||
|
str[k] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[++i] = '\0';
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr
|
||||||
|
int32 uint_to_str(uint64 number, char str[12]) {
|
||||||
|
int32 i = -1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
str[++i] = number % 10 + '0';
|
||||||
|
number /= 10;
|
||||||
|
} while (number > 0);
|
||||||
|
|
||||||
|
for (int32 j = 0, k = i; j < k; ++j, --k) {
|
||||||
|
char temp = str[j];
|
||||||
|
str[j] = str[k];
|
||||||
|
str[k] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[++i] = '\0';
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char HEX_TABLE[] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F'
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr
|
||||||
|
int32 int_to_hex(int64 number, char str[9]) {
|
||||||
|
int32 i = -1;
|
||||||
|
uint64 n = (uint64) number;
|
||||||
|
|
||||||
|
do {
|
||||||
|
byte digit = n % 16;
|
||||||
|
str[++i] = HEX_TABLE[digit];
|
||||||
|
n /= 16;
|
||||||
|
} while (n > 0);
|
||||||
|
|
||||||
|
str[++i] = '\0';
|
||||||
|
|
||||||
for (int32 j = 0, k = i - 1; j < k; ++j, --k) {
|
for (int32 j = 0, k = i - 1; j < k; ++j, --k) {
|
||||||
char temp = str[j];
|
char temp = str[j];
|
||||||
str[j] = str[k];
|
str[j] = str[k];
|
||||||
|
|
@ -223,6 +306,29 @@ int32 int_to_str(int64 number, char *str, const char thousands = ',') {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline constexpr
|
||||||
|
int64 hex_to_int(const char* hex)
|
||||||
|
{
|
||||||
|
int64 result = 0;
|
||||||
|
while ((*hex >= '0' && *hex <= '9')
|
||||||
|
|| (*hex >= 'A' && *hex <= 'F')
|
||||||
|
|| (*hex >= 'a' && *hex <= 'f')
|
||||||
|
) {
|
||||||
|
byte value = *hex++;
|
||||||
|
if (value >= '0' && value <= '9') {
|
||||||
|
value = value - '0';
|
||||||
|
} else if (value >= 'A' && value <='F') {
|
||||||
|
value = value - 'A' + 10;
|
||||||
|
} else if (value >= 'a' && value <='f') {
|
||||||
|
value = value - 'a' + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (result << 4) | (value & 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
size_t str_count(const char* __restrict str, const char* __restrict substr)
|
size_t str_count(const char* __restrict str, const char* __restrict substr)
|
||||||
{
|
{
|
||||||
|
|
@ -241,6 +347,142 @@ size_t str_count(const char* __restrict str, const char* __restrict substr)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline constexpr
|
||||||
|
int32 is_eol(const char* str)
|
||||||
|
{
|
||||||
|
if (*str == '\n') {
|
||||||
|
return 1;
|
||||||
|
} else if (*str == '\r' && str[1] == '\n') {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_copy_until(const char* __restrict src, char* __restrict dest, char delim)
|
||||||
|
{
|
||||||
|
while (*src != delim && *src != '\0') {
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_copy_until(const char* __restrict src, char* __restrict dest, const char* __restrict delim, int32 len)
|
||||||
|
{
|
||||||
|
while (*src != '\0') {
|
||||||
|
for (int32 i = 0; i < len; ++i) {
|
||||||
|
if (*src == delim[i]) {
|
||||||
|
*dest = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int32 str_copy_until(char* __restrict dest, const char* __restrict src, char delim)
|
||||||
|
{
|
||||||
|
int32 len = 0;
|
||||||
|
while (*src != delim && *src != '\0') {
|
||||||
|
*dest++ = *src++;
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = '\0';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_copy_short(char* __restrict dest, const char* __restrict src, char delim = '\0')
|
||||||
|
{
|
||||||
|
while (*src != delim) {
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_copy_long(char* __restrict dest, const char* __restrict src, char delim = '\0')
|
||||||
|
{
|
||||||
|
char* d = dest;
|
||||||
|
const char *s = src;
|
||||||
|
|
||||||
|
// Align destination to its natural alignment
|
||||||
|
while (((uintptr_t) d & (sizeof(uintptr_t) - 1)) != 0 && *s != '\0') {
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy using larger chunks (size of uintptr_t)
|
||||||
|
uintptr_t* aligned_dest = (uintptr_t *) d;
|
||||||
|
const uintptr_t* aligned_src = (const uintptr_t *) s;
|
||||||
|
|
||||||
|
while (*aligned_src != 0) {
|
||||||
|
*aligned_dest++ = *aligned_src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = (char *) aligned_dest;
|
||||||
|
s = (const char *) aligned_src;
|
||||||
|
|
||||||
|
// Copy remaining bytes
|
||||||
|
while (*s != '\0') {
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*d = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_copy_move_until(char** __restrict src, char* __restrict dest, char delim)
|
||||||
|
{
|
||||||
|
while (**src != delim && **src != '\0') {
|
||||||
|
*dest++ = **src;
|
||||||
|
++(*src);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_copy_move_until(char** __restrict src, char* __restrict dest, const char* __restrict delim, int32 len)
|
||||||
|
{
|
||||||
|
while (**src != '\0') {
|
||||||
|
for (int32 i = 0; i < len; ++i) {
|
||||||
|
if (**src == delim[i]) {
|
||||||
|
*dest = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest++ = **src;
|
||||||
|
++(*src);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
int32 strcpy_to_eol(const char* src, char* dst)
|
||||||
|
{
|
||||||
|
int32 offset = 0;
|
||||||
|
while (!is_eol(src) && *src != '\0') {
|
||||||
|
*dst++ = *src++;
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = '\0';
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
char* strsep(const char** sp, const char* sep)
|
char* strsep(const char** sp, const char* sep)
|
||||||
{
|
{
|
||||||
|
|
@ -262,69 +504,58 @@ char* strsep(const char** sp, const char* sep)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int64
|
inline void
|
||||||
str_concat(
|
str_concat_new(
|
||||||
|
char* dst,
|
||||||
const char* src1,
|
const char* src1,
|
||||||
const char* src2,
|
const char* src2
|
||||||
char* dst
|
|
||||||
) {
|
) {
|
||||||
int64 len = strlen(src1);
|
while (*src1) { *dst++ = *src1++; }
|
||||||
int64 len_total = len;
|
while (*src2) { *dst++ = *src2++; }
|
||||||
|
|
||||||
memcpy(dst, src1, len);
|
|
||||||
dst += len;
|
|
||||||
|
|
||||||
len = strlen(src2);
|
|
||||||
memcpy(dst, src2, len);
|
|
||||||
dst += len;
|
|
||||||
|
|
||||||
*dst = '\0';
|
*dst = '\0';
|
||||||
|
|
||||||
return len_total + len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @question Why is this called str_add instead of str_concat like the other functions?
|
|
||||||
inline void
|
|
||||||
str_add(char* base, const char* src)
|
|
||||||
{
|
|
||||||
while (*base) {
|
|
||||||
++base;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(base, src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
str_add(char* base, const char* src, size_t src_length)
|
str_concat_append(char* dst, const char* src)
|
||||||
{
|
{
|
||||||
while (*base) {
|
while (*dst) {
|
||||||
++base;
|
++dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(base, src, src_length);
|
str_copy_short(dst, src);
|
||||||
base[src_length] = '\0';
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
str_concat_new(char* dst, const char* src1, const char* src2, const char* src3)
|
||||||
|
{
|
||||||
|
while (*src1) { *dst++ = *src1++; }
|
||||||
|
while (*src2) { *dst++ = *src2++; }
|
||||||
|
while (*src3) { *dst++ = *src3++; }
|
||||||
|
|
||||||
|
*dst = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int64
|
inline int64
|
||||||
str_add(char* base, size_t base_length, const char* src, size_t src_length)
|
str_concat_append(char* dst, size_t dst_length, const char* src, size_t src_length)
|
||||||
{
|
{
|
||||||
memcpy(&base[base_length], src, src_length);
|
memcpy(&dst[dst_length], src, src_length);
|
||||||
base[base_length + src_length] = '\0';
|
dst[dst_length + src_length] = '\0';
|
||||||
|
|
||||||
return base_length + src_length;
|
return dst_length + src_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
str_add(char* base, size_t base_length, const char* src)
|
str_concat_append(char* dst, size_t dst_length, const char* src)
|
||||||
{
|
{
|
||||||
strcpy(&base[base_length], src);
|
str_copy_short(&dst[dst_length], src);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int64
|
inline int64
|
||||||
str_concat(
|
str_concat_new(
|
||||||
|
char* dst,
|
||||||
const char* src1, size_t src1_length,
|
const char* src1, size_t src1_length,
|
||||||
const char* src2, size_t src2_length,
|
const char* src2, size_t src2_length
|
||||||
char* dst
|
|
||||||
) {
|
) {
|
||||||
memcpy(dst, src1, src1_length);
|
memcpy(dst, src1, src1_length);
|
||||||
dst += src1_length;
|
dst += src1_length;
|
||||||
|
|
@ -338,10 +569,10 @@ str_concat(
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void str_concat(
|
void str_concat_new(
|
||||||
|
char* dst,
|
||||||
const char* src, size_t src_length,
|
const char* src, size_t src_length,
|
||||||
int64 data,
|
int64 data
|
||||||
char* dst
|
|
||||||
) {
|
) {
|
||||||
memcpy(dst, src, src_length);
|
memcpy(dst, src, src_length);
|
||||||
int32 len = int_to_str(data, dst + src_length);
|
int32 len = int_to_str(data, dst + src_length);
|
||||||
|
|
@ -349,6 +580,32 @@ void str_concat(
|
||||||
dst[src_length + len] = '\0';
|
dst[src_length + len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_concat_append(
|
||||||
|
char* dst,
|
||||||
|
int64 data
|
||||||
|
) {
|
||||||
|
size_t dst_len = strlen(dst);
|
||||||
|
int_to_str(data, dst + dst_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
str_concat_new(char* dst, const char* src, int64 data)
|
||||||
|
{
|
||||||
|
size_t src_len = strlen(src);
|
||||||
|
memcpy(dst, src, src_len);
|
||||||
|
|
||||||
|
int_to_str(data, dst + src_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void str_insert(char* __restrict dst, size_t insert_pos, const char* __restrict src) {
|
||||||
|
size_t src_length = strlen(src);
|
||||||
|
size_t dst_length = strlen(dst);
|
||||||
|
memcpy(dst + insert_pos + src_length, dst + insert_pos, dst_length - insert_pos + 1);
|
||||||
|
memcpy(dst + insert_pos, src, src_length);
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
char* strtok(char* str, const char* __restrict delim, char* *key) {
|
char* strtok(char* str, const char* __restrict delim, char* *key) {
|
||||||
char* result;
|
char* result;
|
||||||
|
|
@ -426,6 +683,77 @@ void create_const_name(unsigned char* name)
|
||||||
*name = '\0';
|
*name = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 str_compare(const char* str1, const char* str2)
|
||||||
|
{
|
||||||
|
byte c1, c2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
c1 = (byte) *str1++;
|
||||||
|
c2 = (byte) *str2++;
|
||||||
|
|
||||||
|
if (c1 == '\0') {
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
} while (c1 == c2);
|
||||||
|
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 str_compare(const char* str1, const char* str2, size_t n)
|
||||||
|
{
|
||||||
|
byte c1 = '\0';
|
||||||
|
byte c2 = '\0';
|
||||||
|
|
||||||
|
if (n >= 4) {
|
||||||
|
size_t n4 = n >> 2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
c1 = (byte) *str1++;
|
||||||
|
c2 = (byte) *str2++;
|
||||||
|
|
||||||
|
if (c1 == '\0' || c1 != c2) {
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
c1 = (byte) *str1++;
|
||||||
|
c2 = (byte) *str2++;
|
||||||
|
|
||||||
|
if (c1 == '\0' || c1 != c2) {
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
c1 = (byte) *str1++;
|
||||||
|
c2 = (byte) *str2++;
|
||||||
|
|
||||||
|
if (c1 == '\0' || c1 != c2) {
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
c1 = (byte) *str1++;
|
||||||
|
c2 = (byte) *str2++;
|
||||||
|
|
||||||
|
if (c1 == '\0' || c1 != c2) {
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
} while (--n4 > 0);
|
||||||
|
|
||||||
|
n &= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (n > 0) {
|
||||||
|
c1 = (byte) *str1++;
|
||||||
|
c2 = (byte) *str2++;
|
||||||
|
|
||||||
|
if (c1 == '\0' || c1 != c2) {
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
--n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c1 - c2;
|
||||||
|
}
|
||||||
|
|
||||||
inline constexpr
|
inline constexpr
|
||||||
bool str_ends_with(const char* str, const char* suffix) {
|
bool str_ends_with(const char* str, const char* suffix) {
|
||||||
if (!str || !suffix) {
|
if (!str || !suffix) {
|
||||||
|
|
@ -439,7 +767,7 @@ bool str_ends_with(const char* str, const char* suffix) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return strncmp(str + str_len - suffix_len, suffix, suffix_len) == 0;
|
return str_compare(str + str_len - suffix_len, suffix, suffix_len) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: result needs to have the correct length
|
// WARNING: result needs to have the correct length
|
||||||
|
|
@ -452,7 +780,7 @@ void str_replace(const char* str, const char* __restrict search, const char* __r
|
||||||
size_t replace_len = strlen(replace);
|
size_t replace_len = strlen(replace);
|
||||||
|
|
||||||
if (search_len == 0) {
|
if (search_len == 0) {
|
||||||
strcpy(result, str);
|
str_copy_short(result, str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,7 +799,7 @@ void str_replace(const char* str, const char* __restrict search, const char* __r
|
||||||
str = current;
|
str = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(result_ptr, str);
|
str_copy_short(result_ptr, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_bytes(const void* ptr, size_t size)
|
void print_bytes(const void* ptr, size_t size)
|
||||||
|
|
@ -493,18 +821,6 @@ void print_bytes(const void* ptr, size_t size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr
|
|
||||||
int32 is_eol(const char* str)
|
|
||||||
{
|
|
||||||
if (*str == '\n') {
|
|
||||||
return 1;
|
|
||||||
} else if (*str == '\r' && str[1] == '\n') {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline constexpr
|
inline constexpr
|
||||||
bool is_whitespace(char str)
|
bool is_whitespace(char str)
|
||||||
{
|
{
|
||||||
|
|
@ -639,104 +955,6 @@ void str_skip_until_list(char** __restrict str, const char* __restrict delim)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
|
||||||
void str_copy_until(const char* __restrict src, char* __restrict dest, char delim)
|
|
||||||
{
|
|
||||||
while (*src != delim && *src != '\0') {
|
|
||||||
*dest++ = *src++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
void str_copy_until(const char* __restrict src, char* __restrict dest, const char* __restrict delim, int32 len)
|
|
||||||
{
|
|
||||||
while (*src != '\0') {
|
|
||||||
for (int32 i = 0; i < len; ++i) {
|
|
||||||
if (*src == delim[i]) {
|
|
||||||
*dest = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest++ = *src++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
int32 str_copy_until(char* __restrict dest, const char* __restrict src, char delim)
|
|
||||||
{
|
|
||||||
int32 len = 0;
|
|
||||||
while (*src != delim && *src != '\0') {
|
|
||||||
*dest++ = *src++;
|
|
||||||
++len;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest = '\0';
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
int32 str_copy(char* __restrict dest, const char* __restrict src, char delim)
|
|
||||||
{
|
|
||||||
int32 len = 0;
|
|
||||||
while (*src != delim) {
|
|
||||||
*dest++ = *src++;
|
|
||||||
++len;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest = '\0';
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
void str_copy_move_until(char** __restrict src, char* __restrict dest, char delim)
|
|
||||||
{
|
|
||||||
while (**src != delim && **src != '\0') {
|
|
||||||
*dest++ = **src;
|
|
||||||
++(*src);
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
void str_copy_move_until(char** __restrict src, char* __restrict dest, const char* __restrict delim, int32 len)
|
|
||||||
{
|
|
||||||
while (**src != '\0') {
|
|
||||||
for (int32 i = 0; i < len; ++i) {
|
|
||||||
if (**src == delim[i]) {
|
|
||||||
*dest = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest++ = **src;
|
|
||||||
++(*src);
|
|
||||||
}
|
|
||||||
|
|
||||||
*dest = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
int32 strcpy_to_eol(const char* src, char* dst)
|
|
||||||
{
|
|
||||||
int32 offset = 0;
|
|
||||||
while (!is_eol(src) && *src != '\0') {
|
|
||||||
*dst++ = *src++;
|
|
||||||
++offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dst = '\0';
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void hexstr_to_rgba(v4_f32* rgba, const char* hex)
|
void hexstr_to_rgba(v4_f32* rgba, const char* hex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user