fixed allocator and asset system bug, still experiencing an awful asset cleanup bug though
This commit is contained in:
parent
f0e44b5c37
commit
0640032505
@ -480,7 +480,10 @@ static void FreeListInsert(FLNode **head, FLNode *prev_node, FLNode *new_node)
|
|||||||
if (prev_node == &FL_NIL_NODE)
|
if (prev_node == &FL_NIL_NODE)
|
||||||
{
|
{
|
||||||
if (*head != &FL_NIL_NODE)
|
if (*head != &FL_NIL_NODE)
|
||||||
|
{
|
||||||
new_node->next = *head;
|
new_node->next = *head;
|
||||||
|
*head = new_node;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
*head = new_node;
|
*head = new_node;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ typedef enum AssetType_e : u32
|
|||||||
|
|
||||||
typedef enum ShaderAsset_e : u32
|
typedef enum ShaderAsset_e : u32
|
||||||
{
|
{
|
||||||
SHADER_ASSET_NONE,
|
|
||||||
QUAD_FRAG_SPIRV_SHADER,
|
QUAD_FRAG_SPIRV_SHADER,
|
||||||
QUAD_VERT_SPIRV_SHADER,
|
QUAD_VERT_SPIRV_SHADER,
|
||||||
GUI_FRAG_SPIRV_SHADER,
|
GUI_FRAG_SPIRV_SHADER,
|
||||||
@ -30,7 +29,6 @@ typedef enum ShaderAsset_e : u32
|
|||||||
|
|
||||||
typedef enum TextureAsset_e : u32
|
typedef enum TextureAsset_e : u32
|
||||||
{
|
{
|
||||||
TEXTURE_ASSET_NONE,
|
|
||||||
PATTERMON_OBESE,
|
PATTERMON_OBESE,
|
||||||
PATTERMON_PURPLOID,
|
PATTERMON_PURPLOID,
|
||||||
PATTERMON_YUKATA,
|
PATTERMON_YUKATA,
|
||||||
|
|||||||
31
src/ds.c
31
src/ds.c
@ -511,7 +511,7 @@ static HashNode *HashTablePushU64Rawptr(HashTable *table, u64 key, rawptr value)
|
|||||||
static HashNode *HashTablePushU64U64Split(HashTable *table, u64 key, u32 upper, u32 lower)
|
static HashNode *HashTablePushU64U64Split(HashTable *table, u64 key, u32 upper, u32 lower)
|
||||||
{
|
{
|
||||||
u64 hash = HashFromString(String8Struct(&key));
|
u64 hash = HashFromString(String8Struct(&key));
|
||||||
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_u64_upper = upper, .value_u64_lower = lower });
|
return HashTablePush(table, hash, (KeyValuePair){ .key_u64 = key, .value_u64_split = { .upper = upper, .lower = lower }});
|
||||||
}
|
}
|
||||||
|
|
||||||
static HashNode *HashTablePushRawptrU64(HashTable *table, rawptr key, u64 value)
|
static HashNode *HashTablePushRawptrU64(HashTable *table, rawptr key, u64 value)
|
||||||
@ -606,6 +606,35 @@ static rawptr HashTableDeleteU64Rawptr(HashTable *table, u64 key)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static U64Split HashTableDeleteU64U64Split(HashTable *table, u64 key)
|
||||||
|
{
|
||||||
|
U64Split value = { .upper = UINT32_MAX };
|
||||||
|
|
||||||
|
u64 hash = HashFromString(String8Struct(&key));
|
||||||
|
u64 index = hash % table->cap;
|
||||||
|
HashList *list = table->lists + index;
|
||||||
|
HashNode *prev = P_HT_NIL;
|
||||||
|
for (HashNode *node = list->first; node != P_HT_NIL; node = node->next)
|
||||||
|
{
|
||||||
|
if (node->v.key_u64 == key)
|
||||||
|
{
|
||||||
|
if (prev != P_HT_NIL)
|
||||||
|
prev->next = node->next;
|
||||||
|
|
||||||
|
value.upper = node->v.value_u64_split.upper;
|
||||||
|
value.lower = node->v.value_u64_split.lower;
|
||||||
|
|
||||||
|
node->v.key_u64 = 0;
|
||||||
|
node->v.value_u64_split.upper = 0;
|
||||||
|
node->v.value_u64_split.lower = 0;
|
||||||
|
HTQueuePush(table->free_lists.first, table->free_lists.last, node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
// ::DataStructures::HashTable::Functions::End::
|
// ::DataStructures::HashTable::Functions::End::
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
7
src/ds.h
7
src/ds.h
@ -86,11 +86,7 @@ typedef struct KeyValuePair
|
|||||||
rawptr value_rawptr;
|
rawptr value_rawptr;
|
||||||
u32 value_u32;
|
u32 value_u32;
|
||||||
u64 value_u64;
|
u64 value_u64;
|
||||||
union
|
U64Split value_u64_split;
|
||||||
{
|
|
||||||
u32 value_u64_upper;
|
|
||||||
u32 value_u64_lower;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
} KeyValuePair;
|
} KeyValuePair;
|
||||||
|
|
||||||
@ -127,5 +123,6 @@ static HashNode *HashTablePushU64U64(HashTable *table, u64 key, u64 value);
|
|||||||
static HashNode *HashTablePushU64String8(HashTable *table, u64 key, String8 value);
|
static HashNode *HashTablePushU64String8(HashTable *table, u64 key, String8 value);
|
||||||
static HashNode *HashTablePushU64Rawptr(HashTable *table, u64 key, rawptr value);
|
static HashNode *HashTablePushU64Rawptr(HashTable *table, u64 key, rawptr value);
|
||||||
static HashNode *HashTablePushU64U64Split(HashTable *table, u64 key, u32 upper, u32 lower);
|
static HashNode *HashTablePushU64U64Split(HashTable *table, u64 key, u32 upper, u32 lower);
|
||||||
|
static rawptr HashTableDeleteU64Rawptr(HashTable *table, u64 key);
|
||||||
static void HashTableDeleteU64(HashTable *table, u64 key);
|
static void HashTableDeleteU64(HashTable *table, u64 key);
|
||||||
|
|
||||||
|
|||||||
30
src/game.c
30
src/game.c
@ -59,6 +59,14 @@ static void gRunCycle(gGameCtx *ctx, pGameInput *inputs, u32 i_count)
|
|||||||
|
|
||||||
gHandleInputs(inputs, i_count);
|
gHandleInputs(inputs, i_count);
|
||||||
|
|
||||||
|
rFrameBegin();
|
||||||
|
|
||||||
|
|
||||||
|
// This is fucked up, it's triggering before even being set
|
||||||
|
vTextureCleanUp();
|
||||||
|
|
||||||
|
Printfln("post clean up");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (gButton(ctx, "Show 2", 150.0f, 150.0f, 200.0f, 200.0f))
|
if (gButton(ctx, "Show 2", 150.0f, 150.0f, 200.0f, 200.0f))
|
||||||
{
|
{
|
||||||
@ -76,19 +84,19 @@ static void gRunCycle(gGameCtx *ctx, pGameInput *inputs, u32 i_count)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
u64 index = vFrameIndex();
|
||||||
|
rDescHandle texture;
|
||||||
|
if (index == 0)
|
||||||
|
texture = rTextureLoad(HAMSMOKER);
|
||||||
|
else
|
||||||
|
texture = rTextureLoad(HOG);
|
||||||
|
|
||||||
|
rTextureUnload(texture);
|
||||||
|
|
||||||
rViewportSize(&ctx->pc.res);
|
rViewportSize(&ctx->pc.res);
|
||||||
ctx->pc.time = (f32)pCPUTimerRead();
|
ctx->pc.time = (f32)pCPUTimerRead();
|
||||||
|
|
||||||
rDescHandle pattermon = rTextureLoad(CHEESOID);
|
gWindow(ctx, "texture", 100.0f, 100.0f, 300.0f, 300.0f, texture);
|
||||||
rDescHandle pattermon2 = rTextureLoad(HAMSTER);
|
|
||||||
rDescHandle purplemon = rTextureLoad(HAMSMOKER);
|
|
||||||
rDescHandle hog = rTextureLoad(HOG);
|
|
||||||
|
|
||||||
gWindow(ctx, "Pattermon", 100.0f, 100.0f, 300.0f, 300.0f, pattermon);
|
|
||||||
gWindow(ctx, "Pattermon2", 350.0f, 350.0f, 550.0f, 550.0f, purplemon);
|
|
||||||
gWindow(ctx, "Pattermon3", 600.0f, 100.0f, 800.0f, 300.0f, pattermon2);
|
|
||||||
gWindow(ctx, "ham", 100.0f, 600.0f, 300.0f, 800.0f, pattermon2);
|
|
||||||
gWindow(ctx, "hog", 150.0f, 820.0f, 450.0f, 1020.0f, hog);
|
|
||||||
|
|
||||||
rawptr vert_buffer = rBufferGUIVertMapping();
|
rawptr vert_buffer = rBufferGUIVertMapping();
|
||||||
rawptr idx_buffer = rBufferGUIIdxMapping();
|
rawptr idx_buffer = rBufferGUIIdxMapping();
|
||||||
@ -98,8 +106,6 @@ static void gRunCycle(gGameCtx *ctx, pGameInput *inputs, u32 i_count)
|
|||||||
|
|
||||||
vBufferQueueWait();
|
vBufferQueueWait();
|
||||||
|
|
||||||
rFrameBegin();
|
|
||||||
|
|
||||||
rPipelineBind(R_PIPELINE_GUI, rPT_GRAPHICS);
|
rPipelineBind(R_PIPELINE_GUI, rPT_GRAPHICS);
|
||||||
|
|
||||||
rPushConstantsSet(&ctx->pc);
|
rPushConstantsSet(&ctx->pc);
|
||||||
|
|||||||
@ -132,6 +132,8 @@ static inline u64 pCPUTimerRead();
|
|||||||
|
|
||||||
// ::Platform::Atomics::Header::
|
// ::Platform::Atomics::Header::
|
||||||
|
|
||||||
|
static inline void AtomicSignalFenceSeqCst();
|
||||||
|
|
||||||
#define DefSigAtomicFetchIncr(T) static inline T p##T##AtomicFetchIncr(T volatile *ptr)
|
#define DefSigAtomicFetchIncr(T) static inline T p##T##AtomicFetchIncr(T volatile *ptr)
|
||||||
#define DefSigAtomicFetchSub(T) static inline T p##T##AtomicFetchSub(T volatile *ptr, T count)
|
#define DefSigAtomicFetchSub(T) static inline T p##T##AtomicFetchSub(T volatile *ptr, T count)
|
||||||
#define DefSigAtomicIncr(T) static inline void p##T##AtomicIncr(T volatile *ptr)
|
#define DefSigAtomicIncr(T) static inline void p##T##AtomicIncr(T volatile *ptr)
|
||||||
|
|||||||
@ -334,6 +334,11 @@ static inline u64 pCPUTimerRead()
|
|||||||
|
|
||||||
// ::Platform::Atomics::Functions::Start::
|
// ::Platform::Atomics::Functions::Start::
|
||||||
|
|
||||||
|
static inline void AtomicSignalFenceSeqCst()
|
||||||
|
{
|
||||||
|
__atomic_signal_fence(__ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
DefScalarImpl(AtomicFetchIncr);
|
DefScalarImpl(AtomicFetchIncr);
|
||||||
DefScalarImpl(AtomicIncr);
|
DefScalarImpl(AtomicIncr);
|
||||||
DefScalarImpl(AtomicStore);
|
DefScalarImpl(AtomicStore);
|
||||||
|
|||||||
@ -401,18 +401,27 @@ static b32 vImageViewCreate(vImageView *view, u32 width, u32 height, u32 channel
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vTextureCleanUp(vImageView *view)
|
static void vTextureCleanUp()
|
||||||
{
|
{
|
||||||
VkDevice device = v_Renderer.handles.device;
|
VkDevice device = v_Renderer.handles.device;
|
||||||
VmaAllocator vma_alloc = v_Renderer.handles.vma_alloc;
|
VmaAllocator vma_alloc = v_Renderer.handles.vma_alloc;
|
||||||
HashTable *table = &v_Renderer.buffers.images;
|
HashTable *table = &v_Renderer.buffers.images;
|
||||||
b8 *queue = vFrameTexDestroyQueue();
|
b8 *queue = vFrameTexDestroyQueue();
|
||||||
|
|
||||||
|
Printfln("frame: %llu", v_Renderer.state.renderer.frame_count);
|
||||||
|
|
||||||
|
// NOTE: might need a mutex here at some point, check if crashes related to image access
|
||||||
for (u64 i = 0; i < TEXTURE_ASSET_MAX; i++)
|
for (u64 i = 0; i < TEXTURE_ASSET_MAX; i++)
|
||||||
{
|
{
|
||||||
if (queue[i])
|
if (queue[i])
|
||||||
{
|
{
|
||||||
vImageView *view = HashTableDeleteU64Rawptr(table, i);
|
Printfln("fetching handle for id: %llu", i);
|
||||||
|
|
||||||
|
rDescHandle handle = vDescHandlePop(vDT_SAMPLED_IMAGE, (u32)i);
|
||||||
|
vDescIndexPush(vDT_SAMPLED_IMAGE, handle.desc_index);
|
||||||
|
|
||||||
|
Printfln("queue %llu %d", i, queue[i]);
|
||||||
|
vImageView *view = vImagePop(i);
|
||||||
Assert(view != NULL, "rTextureUnload failure: value not in hash table");
|
Assert(view != NULL, "rTextureUnload failure: value not in hash table");
|
||||||
|
|
||||||
vkDestroyImageView(device, view->view, NULL);
|
vkDestroyImageView(device, view->view, NULL);
|
||||||
@ -423,17 +432,19 @@ static void vTextureCleanUp(vImageView *view)
|
|||||||
queue[i] = false;
|
queue[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AtomicSignalFenceSeqCst();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vImagePush(TextureAsset asset_id, vImageView *view)
|
static void vImagePush(TextureAsset asset_id, vImageView *view)
|
||||||
{
|
{
|
||||||
Assert(asset_id != 0, "asset_id is TEXTURE_ASSET_NONE");
|
|
||||||
HashTablePushU64Rawptr(&v_Renderer.buffers.images, asset_id, view);
|
HashTablePushU64Rawptr(&v_Renderer.buffers.images, asset_id, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vImagePop(TextureAsset asset_id)
|
static vImageView *vImagePop(TextureAsset asset_id)
|
||||||
{
|
{
|
||||||
|
Printfln("popping id: %llu", asset_id);
|
||||||
|
return (vImageView *)HashTableDeleteU64Rawptr(&v_Renderer.buffers.images, asset_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static vImageView *vImageSearch(TextureAsset asset_id)
|
static vImageView *vImageSearch(TextureAsset asset_id)
|
||||||
@ -507,24 +518,41 @@ static rDescHandle vDescHandleSearch(vDescType type, u32 asset_id)
|
|||||||
.asset_id = UINT32_MAX,
|
.asset_id = UINT32_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Printfln("search asset_id: %llu", asset_id);
|
||||||
|
|
||||||
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
||||||
|
|
||||||
KeyValuePair *kv_pair = HashTableSearchU64(table, asset_id);
|
KeyValuePair *kv_pair = HashTableSearchU64(table, asset_id);
|
||||||
if (kv_pair != NULL)
|
if (kv_pair != NULL)
|
||||||
{
|
{
|
||||||
asset_info.asset_id = kv_pair->value_u64_upper;
|
asset_info.asset_id = kv_pair->value_u64_split.upper;
|
||||||
asset_info.desc_index = kv_pair->value_u64_lower;
|
asset_info.desc_index = kv_pair->value_u64_split.lower;
|
||||||
}
|
}
|
||||||
|
|
||||||
return asset_info;
|
return asset_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vDescHandleInsert(vDescType type, rDescHandle handle)
|
static void vDescHandlePush(vDescType type, rDescHandle handle)
|
||||||
{
|
{
|
||||||
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
||||||
HashTablePushU64U64Split(table, handle.asset_id, handle.asset_id, handle.desc_index);
|
HashTablePushU64U64Split(table, handle.asset_id, handle.asset_id, handle.desc_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static rDescHandle vDescHandlePop(vDescType type, u32 asset_id)
|
||||||
|
{
|
||||||
|
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
||||||
|
|
||||||
|
U64Split split = HashTableDeleteU64U64Split(table, (u64)asset_id);
|
||||||
|
Assert(split.upper != UINT32_MAX, "vDescHandlePop failure: unable to find asset handle");
|
||||||
|
|
||||||
|
rDescHandle handle = {
|
||||||
|
.asset_id = split.upper,
|
||||||
|
.desc_index = split.lower,
|
||||||
|
};
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
static void vDescHandleDelete(vDescType type, u32 asset_id)
|
static void vDescHandleDelete(vDescType type, u32 asset_id)
|
||||||
{
|
{
|
||||||
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
HashTable *table = &v_Renderer.desc_bindings[type].lookup_table;
|
||||||
@ -1433,6 +1461,7 @@ static b32 vDescriptorsInit()
|
|||||||
{
|
{
|
||||||
for (u32 i = 0; i < vDT_MAX; i++)
|
for (u32 i = 0; i < vDT_MAX; i++)
|
||||||
{
|
{
|
||||||
|
// FREE MIGHT BE NULL
|
||||||
bindings[i].free = ArenaAlloc(v_Renderer.mem.perm_arena, sizeof(u32) * DESC_MAX_BINDINGS);
|
bindings[i].free = ArenaAlloc(v_Renderer.mem.perm_arena, sizeof(u32) * DESC_MAX_BINDINGS);
|
||||||
|
|
||||||
HashTableInit(&bindings[i].lookup_table, 6);
|
HashTableInit(&bindings[i].lookup_table, 6);
|
||||||
@ -1626,6 +1655,7 @@ static b32 vBuffersInit()
|
|||||||
InitArrayType(buf->frame_images[i], arena, vImageView *, 128);
|
InitArrayType(buf->frame_images[i], arena, vImageView *, 128);
|
||||||
|
|
||||||
buf->tex_destroy_queue[i] = MakeArray(arena, b8, TEXTURE_ASSET_MAX);
|
buf->tex_destroy_queue[i] = MakeArray(arena, b8, TEXTURE_ASSET_MAX);
|
||||||
|
MemZero(buf->tex_destroy_queue, sizeof(b8) * TEXTURE_ASSET_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -550,8 +550,9 @@ static void vSwapchainResize();
|
|||||||
// ::Vulkan::Images::Functions::Header::
|
// ::Vulkan::Images::Functions::Header::
|
||||||
|
|
||||||
static b32 vImageViewCreate(vImageView *view, u32 width, u32 height, u32 channels);
|
static b32 vImageViewCreate(vImageView *view, u32 width, u32 height, u32 channels);
|
||||||
static void vTextureCleanUp(vImageView *view);
|
static void vTextureCleanUp();
|
||||||
static void vImagePush(TextureAsset asset_id, vImageView *view);
|
static void vImagePush(TextureAsset asset_id, vImageView *view);
|
||||||
|
static vImageView *vImagePop(TextureAsset asset_id);
|
||||||
static vImageView *vImageSearch(TextureAsset asset_id);
|
static vImageView *vImageSearch(TextureAsset asset_id);
|
||||||
|
|
||||||
// ::Vulkan::Descriptors::Functions::Header::
|
// ::Vulkan::Descriptors::Functions::Header::
|
||||||
@ -559,7 +560,8 @@ static vImageView *vImageSearch(TextureAsset asset_id);
|
|||||||
static void vDescIndexPush(vDescType type, u32 index);
|
static void vDescIndexPush(vDescType type, u32 index);
|
||||||
static u32 vDescIndexPop(vDescType type);
|
static u32 vDescIndexPop(vDescType type);
|
||||||
static rDescHandle vDescHandleSearch(vDescType type, u32 asset_id);
|
static rDescHandle vDescHandleSearch(vDescType type, u32 asset_id);
|
||||||
static void vDescHandleInsert(vDescType type, rDescHandle handle);
|
static void vDescHandlePush(vDescType type, rDescHandle handle);
|
||||||
|
static rDescHandle vDescHandlePop(vDescType type, u32 asset_id);
|
||||||
static void vDescHandleDelete(vDescType type, u32 asset_id);
|
static void vDescHandleDelete(vDescType type, u32 asset_id);
|
||||||
static u32 vDescPushImage(vImageView *view);
|
static u32 vDescPushImage(vImageView *view);
|
||||||
|
|
||||||
|
|||||||
@ -139,6 +139,7 @@ static rDescHandle rTextureLoad(TextureAsset asset_id)
|
|||||||
// TODO: handle errors instead of failing
|
// TODO: handle errors instead of failing
|
||||||
Assert(vImageViewCreate(view, meta.w, meta.h, meta.ch), "rTextureLoad failure: vImageViewCreate failed");
|
Assert(vImageViewCreate(view, meta.w, meta.h, meta.ch), "rTextureLoad failure: vImageViewCreate failed");
|
||||||
|
|
||||||
|
Printfln("Loading asset: %llu", asset_id);
|
||||||
handle.asset_id = asset_id;
|
handle.asset_id = asset_id;
|
||||||
handle.desc_index = vDescPushImage(view);
|
handle.desc_index = vDescPushImage(view);
|
||||||
|
|
||||||
@ -160,7 +161,7 @@ static rDescHandle rTextureLoad(TextureAsset asset_id)
|
|||||||
|
|
||||||
v_Renderer.upload.transfers[job_idx] = transfer;
|
v_Renderer.upload.transfers[job_idx] = transfer;
|
||||||
|
|
||||||
vDescHandleInsert(vDT_SAMPLED_IMAGE, handle);
|
vDescHandlePush(vDT_SAMPLED_IMAGE, handle);
|
||||||
vImagePush(asset_id, view);
|
vImagePush(asset_id, view);
|
||||||
|
|
||||||
TicketMutUnlock(&v_Renderer.upload.mut);
|
TicketMutUnlock(&v_Renderer.upload.mut);
|
||||||
@ -177,9 +178,9 @@ static void rTextureUnload(rDescHandle current_handle)
|
|||||||
|
|
||||||
if (handle.asset_id != UINT32_MAX)
|
if (handle.asset_id != UINT32_MAX)
|
||||||
{
|
{
|
||||||
b8 *queue = vFrameNextTexDestroyQueue();
|
b8 *queue = vFrameTexDestroyQueue();
|
||||||
|
|
||||||
queue[handle.asset_id] = true;
|
queue[current_handle.asset_id] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,6 +77,12 @@ typedef uint32_t usize;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct { u32 upper, lower; };
|
||||||
|
u64 full;
|
||||||
|
} U64Split;
|
||||||
|
|
||||||
typedef union
|
typedef union
|
||||||
{
|
{
|
||||||
struct { f32 r, g; };
|
struct { f32 r, g; };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user