mirror of
https://github.com/nicbarker/clay.git
synced 2025-04-07 14:58:05 +00:00
[Core] Switch text content hashing to default behaviour (#335)
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
This commit is contained in:
parent
ad49977f1b
commit
47d1d84bc8
@ -17,6 +17,7 @@ when ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String :: struct {
|
String :: struct {
|
||||||
|
isStaticallyAllocated: c.bool,
|
||||||
length: c.int32_t,
|
length: c.int32_t,
|
||||||
chars: [^]c.char,
|
chars: [^]c.char,
|
||||||
}
|
}
|
||||||
@ -419,7 +420,13 @@ UI :: proc() -> proc (config: ElementDeclaration) -> bool {
|
|||||||
return ConfigureOpenElement
|
return ConfigureOpenElement
|
||||||
}
|
}
|
||||||
|
|
||||||
Text :: proc(text: string, config: ^TextElementConfig) {
|
Text :: proc($text: string, config: ^TextElementConfig) {
|
||||||
|
wrapped := MakeString(text)
|
||||||
|
wrapped.isStaticallyAllocated = true
|
||||||
|
_OpenTextElement(wrapped, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextDynamic :: proc(text: string, config: ^TextElementConfig) {
|
||||||
_OpenTextElement(MakeString(text), config)
|
_OpenTextElement(MakeString(text), config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -62,7 +62,7 @@ border2pxRed := clay.BorderElementConfig {
|
|||||||
color = COLOR_RED
|
color = COLOR_RED
|
||||||
}
|
}
|
||||||
|
|
||||||
LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, text: string, image: ^raylib.Texture2D) {
|
LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, $text: string, image: ^raylib.Texture2D) {
|
||||||
if clay.UI()({
|
if clay.UI()({
|
||||||
id = clay.ID("HeroBlob", index),
|
id = clay.ID("HeroBlob", index),
|
||||||
layout = { sizing = { width = clay.SizingGrow({ max = 480 }) }, padding = clay.PaddingAll(16), childGap = 16, childAlignment = clay.ChildAlignment{ y = .Center } },
|
layout = { sizing = { width = clay.SizingGrow({ max = 480 }) }, padding = clay.PaddingAll(16), childGap = 16, childAlignment = clay.ChildAlignment{ y = .Center } },
|
||||||
@ -252,7 +252,7 @@ ColorLerp :: proc(a: clay.Color, b: clay.Color, amount: f32) -> clay.Color {
|
|||||||
return clay.Color{a.r + (b.r - a.r) * amount, a.g + (b.g - a.g) * amount, a.b + (b.b - a.b) * amount, a.a + (b.a - a.a) * amount}
|
return clay.Color{a.r + (b.r - a.r) * amount, a.g + (b.g - a.g) * amount, a.b + (b.b - a.b) * amount, a.a + (b.a - a.a) * amount}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOREM_IPSUM_TEXT := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
LOREM_IPSUM_TEXT :: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
|
|
||||||
HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
|
HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
|
||||||
if clay.UI()({ id = clay.ID("PerformanceLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
if clay.UI()({ id = clay.ID("PerformanceLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||||
@ -321,7 +321,7 @@ HighPerformancePageMobile :: proc(lerpValue: f32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererButtonActive :: proc(index: i32, text: string) {
|
RendererButtonActive :: proc(index: i32, $text: string) {
|
||||||
if clay.UI()({
|
if clay.UI()({
|
||||||
layout = { sizing = { width = clay.SizingFixed(300) }, padding = clay.PaddingAll(16) },
|
layout = { sizing = { width = clay.SizingFixed(300) }, padding = clay.PaddingAll(16) },
|
||||||
backgroundColor = COLOR_RED,
|
backgroundColor = COLOR_RED,
|
||||||
@ -331,7 +331,7 @@ RendererButtonActive :: proc(index: i32, text: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RendererButtonInactive :: proc(index: u32, text: string) {
|
RendererButtonInactive :: proc(index: u32, $text: string) {
|
||||||
if clay.UI()({ border = border2pxRed }) {
|
if clay.UI()({ border = border2pxRed }) {
|
||||||
if clay.UI()({
|
if clay.UI()({
|
||||||
id = clay.ID("RendererButtonInactiveInner", index),
|
id = clay.ID("RendererButtonInactiveInner", index),
|
||||||
|
166
clay.h
166
clay.h
@ -96,9 +96,9 @@
|
|||||||
#define CLAY__ENSURE_STRING_LITERAL(x) ("" x "")
|
#define CLAY__ENSURE_STRING_LITERAL(x) ("" x "")
|
||||||
|
|
||||||
// Note: If an error led you here, it's because CLAY_STRING can only be used with string literals, i.e. CLAY_STRING("SomeString") and not CLAY_STRING(yourString)
|
// Note: If an error led you here, it's because CLAY_STRING can only be used with string literals, i.e. CLAY_STRING("SomeString") and not CLAY_STRING(yourString)
|
||||||
#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) })
|
#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) })
|
||||||
|
|
||||||
#define CLAY_STRING_CONST(string) { .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }
|
#define CLAY_STRING_CONST(string) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }
|
||||||
|
|
||||||
static uint8_t CLAY__ELEMENT_DEFINITION_LATCH;
|
static uint8_t CLAY__ELEMENT_DEFINITION_LATCH;
|
||||||
|
|
||||||
@ -185,6 +185,9 @@ extern "C" {
|
|||||||
// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
|
// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
|
||||||
// but it is also used to represent slices.
|
// but it is also used to represent slices.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
// Set this boolean to true if the char* data underlying this string will live for the entire lifetime of the program.
|
||||||
|
// This will automatically be set for strings created with CLAY_STRING, as the macro requires a string literal.
|
||||||
|
bool isStaticallyAllocated;
|
||||||
int32_t length;
|
int32_t length;
|
||||||
// The underlying character memory. Note: this will not be copied and will not extend the lifetime of the underlying memory.
|
// The underlying character memory. Note: this will not be copied and will not extend the lifetime of the underlying memory.
|
||||||
const char *chars;
|
const char *chars;
|
||||||
@ -384,10 +387,6 @@ typedef struct {
|
|||||||
// CLAY_TEXT_ALIGN_CENTER - Horizontally aligns wrapped lines of text to the center of their bounding box.
|
// CLAY_TEXT_ALIGN_CENTER - Horizontally aligns wrapped lines of text to the center of their bounding box.
|
||||||
// CLAY_TEXT_ALIGN_RIGHT - Horizontally aligns wrapped lines of text to the right hand side of their bounding box.
|
// CLAY_TEXT_ALIGN_RIGHT - Horizontally aligns wrapped lines of text to the right hand side of their bounding box.
|
||||||
Clay_TextAlignment textAlignment;
|
Clay_TextAlignment textAlignment;
|
||||||
// When set to true, clay will hash the entire text contents of this string as an identifier for its internal
|
|
||||||
// text measurement cache, rather than just the pointer and length. This will incur significant performance cost for
|
|
||||||
// long bodies of text.
|
|
||||||
bool hashStringContents;
|
|
||||||
} Clay_TextElementConfig;
|
} Clay_TextElementConfig;
|
||||||
|
|
||||||
CLAY__WRAPPER_STRUCT(Clay_TextElementConfig);
|
CLAY__WRAPPER_STRUCT(Clay_TextElementConfig);
|
||||||
@ -876,8 +875,7 @@ CLAY_DLL_EXPORT int32_t Clay_GetMaxMeasureTextCacheWordCount(void);
|
|||||||
// Modifies the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
|
// Modifies the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
|
||||||
// This may require reallocating additional memory, and re-calling Clay_Initialize();
|
// This may require reallocating additional memory, and re-calling Clay_Initialize();
|
||||||
CLAY_DLL_EXPORT void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
|
CLAY_DLL_EXPORT void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
|
||||||
// Resets Clay's internal text measurement cache, useful if memory to represent strings is being re-used.
|
// Resets Clay's internal text measurement cache. Useful if font mappings have changed or fonts have been reloaded.
|
||||||
// Similar behaviour can be achieved on an individual text element level by using Clay_TextElementConfig.hashStringContents
|
|
||||||
CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
|
CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
|
||||||
|
|
||||||
// Internal API functions required by macros ----------------------
|
// Internal API functions required by macros ----------------------
|
||||||
@ -1348,26 +1346,140 @@ Clay_ElementId Clay__HashString(Clay_String key, const uint32_t offset, const ui
|
|||||||
return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as "null id"
|
return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as "null id"
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *config) {
|
#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||||
uint32_t hash = 0;
|
static inline __m128i Clay__SIMDRotateLeft(__m128i x, int r) {
|
||||||
uintptr_t pointerAsNumber = (uintptr_t)text->chars;
|
return _mm_or_si128(_mm_slli_epi64(x, r), _mm_srli_epi64(x, 64 - r));
|
||||||
|
}
|
||||||
|
|
||||||
if (config->hashStringContents) {
|
static inline void Clay__SIMDARXMix(__m128i* a, __m128i* b) {
|
||||||
uint32_t maxLengthToHash = CLAY__MIN(text->length, 256);
|
*a = _mm_add_epi64(*a, *b);
|
||||||
for (uint32_t i = 0; i < maxLengthToHash; i++) {
|
*b = _mm_xor_si128(Clay__SIMDRotateLeft(*b, 17), *a);
|
||||||
hash += text->chars[i];
|
}
|
||||||
hash += (hash << 10);
|
|
||||||
hash ^= (hash >> 6);
|
uint64_t Clay__HashData(const uint8_t* data, size_t length) {
|
||||||
|
// Pinched these constants from the BLAKE implementation
|
||||||
|
__m128i v0 = _mm_set1_epi64x(0x6a09e667f3bcc908ULL);
|
||||||
|
__m128i v1 = _mm_set1_epi64x(0xbb67ae8584caa73bULL);
|
||||||
|
__m128i v2 = _mm_set1_epi64x(0x3c6ef372fe94f82bULL);
|
||||||
|
__m128i v3 = _mm_set1_epi64x(0xa54ff53a5f1d36f1ULL);
|
||||||
|
|
||||||
|
uint8_t overflowBuffer[16] = { 0 }; // Temporary buffer for small inputs
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
__m128i msg;
|
||||||
|
if (length >= 16) {
|
||||||
|
msg = _mm_loadu_si128((const __m128i*)data);
|
||||||
|
data += 16;
|
||||||
|
length -= 16;
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
hash += pointerAsNumber;
|
for (int i = 0; i < length; i++) {
|
||||||
|
overflowBuffer[i] = data[i];
|
||||||
|
}
|
||||||
|
msg = _mm_loadu_si128((const __m128i*)overflowBuffer);
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
v0 = _mm_xor_si128(v0, msg);
|
||||||
|
Clay__SIMDARXMix(&v0, &v1);
|
||||||
|
Clay__SIMDARXMix(&v2, &v3);
|
||||||
|
|
||||||
|
v0 = _mm_add_epi64(v0, v2);
|
||||||
|
v1 = _mm_add_epi64(v1, v3);
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay__SIMDARXMix(&v0, &v1);
|
||||||
|
Clay__SIMDARXMix(&v2, &v3);
|
||||||
|
v0 = _mm_add_epi64(v0, v2);
|
||||||
|
v1 = _mm_add_epi64(v1, v3);
|
||||||
|
|
||||||
|
uint64_t result[2];
|
||||||
|
_mm_storeu_si128((__m128i*)result, v0);
|
||||||
|
|
||||||
|
return result[0] ^ result[1];
|
||||||
|
}
|
||||||
|
#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)
|
||||||
|
static inline uint64x2_t Clay__SIMDRotateLeft(uint64x2_t x, int r) {
|
||||||
|
return vorrq_u64(vshlq_n_u64(x, 17), vshrq_n_u64(x, 64 - 17));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void Clay__SIMDARXMix(uint64x2_t* a, uint64x2_t* b) {
|
||||||
|
*a = vaddq_u64(*a, *b);
|
||||||
|
*b = veorq_u64(Clay__SIMDRotateLeft(*b, 17), *a);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Clay__HashData(const uint8_t* data, size_t length) {
|
||||||
|
// Pinched these constants from the BLAKE implementation
|
||||||
|
uint64x2_t v0 = vdupq_n_u64(0x6a09e667f3bcc908ULL);
|
||||||
|
uint64x2_t v1 = vdupq_n_u64(0xbb67ae8584caa73bULL);
|
||||||
|
uint64x2_t v2 = vdupq_n_u64(0x3c6ef372fe94f82bULL);
|
||||||
|
uint64x2_t v3 = vdupq_n_u64(0xa54ff53a5f1d36f1ULL);
|
||||||
|
|
||||||
|
uint8_t overflowBuffer[8] = { 0 };
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
uint64x2_t msg;
|
||||||
|
if (length > 16) {
|
||||||
|
msg = vld1q_u64((const uint64_t*)data);
|
||||||
|
data += 16;
|
||||||
|
length -= 16;
|
||||||
|
}
|
||||||
|
else if (length > 8) {
|
||||||
|
msg = vcombine_u64(vld1_u64((const uint64_t*)data), vdup_n_u64(0));
|
||||||
|
data += 8;
|
||||||
|
length -= 8;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
overflowBuffer[i] = data[i];
|
||||||
|
}
|
||||||
|
uint8x8_t lower = vld1_u8(overflowBuffer);
|
||||||
|
msg = vcombine_u8(lower, vdup_n_u8(0));
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
v0 = veorq_u64(v0, msg);
|
||||||
|
Clay__SIMDARXMix(&v0, &v1);
|
||||||
|
Clay__SIMDARXMix(&v2, &v3);
|
||||||
|
|
||||||
|
v0 = vaddq_u64(v0, v2);
|
||||||
|
v1 = vaddq_u64(v1, v3);
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay__SIMDARXMix(&v0, &v1);
|
||||||
|
Clay__SIMDARXMix(&v2, &v3);
|
||||||
|
v0 = vaddq_u64(v0, v2);
|
||||||
|
v1 = vaddq_u64(v1, v3);
|
||||||
|
|
||||||
|
uint64_t result[2];
|
||||||
|
vst1q_u64(result, v0);
|
||||||
|
|
||||||
|
return result[0] ^ result[1];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
uint64_t Clay__HashData(const uint8_t* data, size_t length) {
|
||||||
|
uint64_t hash = 0;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < length; i++) {
|
||||||
|
hash += data[i];
|
||||||
hash += (hash << 10);
|
hash += (hash << 10);
|
||||||
hash ^= (hash >> 6);
|
hash ^= (hash >> 6);
|
||||||
}
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
hash += text->length;
|
uint32_t Clay__HashStringContentsWithConfig(Clay_String *text, Clay_TextElementConfig *config) {
|
||||||
hash += (hash << 10);
|
uint32_t hash = 0;
|
||||||
hash ^= (hash >> 6);
|
if (text->isStaticallyAllocated) {
|
||||||
|
hash += (uintptr_t)text->chars;
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
hash += text->length;
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
} else {
|
||||||
|
hash = Clay__HashData((const uint8_t *)text->chars, text->length) % UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
hash += config->fontId;
|
hash += config->fontId;
|
||||||
hash += (hash << 10);
|
hash += (hash << 10);
|
||||||
@ -1377,18 +1489,10 @@ uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *con
|
|||||||
hash += (hash << 10);
|
hash += (hash << 10);
|
||||||
hash ^= (hash >> 6);
|
hash ^= (hash >> 6);
|
||||||
|
|
||||||
hash += config->lineHeight;
|
|
||||||
hash += (hash << 10);
|
|
||||||
hash ^= (hash >> 6);
|
|
||||||
|
|
||||||
hash += config->letterSpacing;
|
hash += config->letterSpacing;
|
||||||
hash += (hash << 10);
|
hash += (hash << 10);
|
||||||
hash ^= (hash >> 6);
|
hash ^= (hash >> 6);
|
||||||
|
|
||||||
hash += config->wrapMode;
|
|
||||||
hash += (hash << 10);
|
|
||||||
hash ^= (hash >> 6);
|
|
||||||
|
|
||||||
hash += (hash << 3);
|
hash += (hash << 3);
|
||||||
hash ^= (hash >> 11);
|
hash ^= (hash >> 11);
|
||||||
hash += (hash << 15);
|
hash += (hash << 15);
|
||||||
@ -1423,7 +1527,7 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
|
|||||||
return &Clay__MeasureTextCacheItem_DEFAULT;
|
return &Clay__MeasureTextCacheItem_DEFAULT;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
uint32_t id = Clay__HashTextWithConfig(text, config);
|
uint32_t id = Clay__HashStringContentsWithConfig(text, config);
|
||||||
uint32_t hashBucket = id % (context->maxMeasureTextCacheWordCount / 32);
|
uint32_t hashBucket = id % (context->maxMeasureTextCacheWordCount / 32);
|
||||||
int32_t elementIndexPrevious = 0;
|
int32_t elementIndexPrevious = 0;
|
||||||
int32_t elementIndex = context->measureTextHashMap.internalArray[hashBucket];
|
int32_t elementIndex = context->measureTextHashMap.internalArray[hashBucket];
|
||||||
|
Loading…
Reference in New Issue
Block a user