Skip to content

Commit d7c88d9

Browse files
author
amalxloop
committed
O(1) free-list slot allocator, comprehensive resource tests, WebGPU backend stub
1 parent d480b36 commit d7c88d9

4 files changed

Lines changed: 444 additions & 27 deletions

File tree

backends/sc_backend_vulkan.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,7 @@ SCGfxBuffer sc_vulkan_make_buffer(SCGfxContext *ctx, const SCGfxBufferDesc *desc
11021102
_SCVkState *s = (_SCVkState*)ctx->backend_data;
11031103
if (!desc->data || desc->size == 0) return h;
11041104

1105-
u32 id = _sc_gfx_alloc_slot(ctx->buf_slots, SC_GFX_MAX_BUFFERS);
1105+
u32 id = _sc_gfx_alloc_slot(ctx->buf_slots, SC_GFX_MAX_BUFFERS, &ctx->buf_free_head);
11061106
if (id == 0) return h;
11071107
h.id = id;
11081108

@@ -1115,7 +1115,7 @@ SCGfxBuffer sc_vulkan_make_buffer(SCGfxContext *ctx, const SCGfxBufferDesc *desc
11151115
VkResult r = _sc_vk_create_buf(s->device, s->phy_dev, desc->size, usage,
11161116
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
11171117
&s->buf_buffers[id], &s->buf_mems[id]);
1118-
if (r != VK_SUCCESS) { _sc_gfx_free_slot(ctx->buf_slots, id); h.id = 0; return h; }
1118+
if (r != VK_SUCCESS) { _sc_gfx_free_slot(ctx->buf_slots, id, &ctx->buf_free_head); h.id = 0; return h; }
11191119
s->buf_sizes[id] = desc->size;
11201120

11211121
/* Staging buffer for upload */
@@ -1130,7 +1130,7 @@ SCGfxBuffer sc_vulkan_make_buffer(SCGfxContext *ctx, const SCGfxBufferDesc *desc
11301130
vkFreeMemory(s->device, s->buf_mems[id], NULL);
11311131
s->buf_buffers[id] = VK_NULL_HANDLE;
11321132
s->buf_mems[id] = VK_NULL_HANDLE;
1133-
_sc_gfx_free_slot(ctx->buf_slots, id);
1133+
_sc_gfx_free_slot(ctx->buf_slots, id, &ctx->buf_free_head);
11341134
h.id = 0;
11351135
return h;
11361136
}
@@ -1253,7 +1253,7 @@ SCGfxShader sc_vulkan_make_shader(SCGfxContext *ctx, const SCGfxShaderDesc *desc
12531253
if (!ctx || !ctx->backend_data || !desc) return h;
12541254
_SCVkState *s = (_SCVkState*)ctx->backend_data;
12551255

1256-
u32 id = _sc_gfx_alloc_slot(ctx->shd_slots, SC_GFX_MAX_SHADERS);
1256+
u32 id = _sc_gfx_alloc_slot(ctx->shd_slots, SC_GFX_MAX_SHADERS, &ctx->shd_free_head);
12571257
if (id == 0) return h;
12581258
h.id = id;
12591259

@@ -1290,7 +1290,7 @@ SCGfxPipeline sc_vulkan_make_pipeline(SCGfxContext *ctx, const SCGfxPipelineDesc
12901290
if (!ctx || !ctx->backend_data || !desc) return h;
12911291
_SCVkState *s = (_SCVkState*)ctx->backend_data;
12921292

1293-
u32 id = _sc_gfx_alloc_slot(ctx->pip_slots, SC_GFX_MAX_PIPELINES);
1293+
u32 id = _sc_gfx_alloc_slot(ctx->pip_slots, SC_GFX_MAX_PIPELINES, &ctx->pip_free_head);
12941294
if (id == 0) return h;
12951295
h.id = id;
12961296

@@ -1303,7 +1303,7 @@ SCGfxPipeline sc_vulkan_make_pipeline(SCGfxContext *ctx, const SCGfxPipelineDesc
13031303
plci.setLayoutCount = 1;
13041304
plci.pSetLayouts = &s->ds_layout;
13051305
VkResult r = vkCreatePipelineLayout(s->device, &plci, NULL, &s->user_pip_layouts[id]);
1306-
if (r != VK_SUCCESS) { _sc_gfx_free_slot(ctx->pip_slots, id); h.id = 0; return h; }
1306+
if (r != VK_SUCCESS) { _sc_gfx_free_slot(ctx->pip_slots, id, &ctx->pip_free_head); h.id = 0; return h; }
13071307

13081308
/* Use shader modules from the shader handle, or fallback to built-in */
13091309
u32 shd_id = desc->shader.id;
@@ -1321,7 +1321,7 @@ SCGfxPipeline sc_vulkan_make_pipeline(SCGfxContext *ctx, const SCGfxPipelineDesc
13211321
if (r != VK_SUCCESS) {
13221322
vkDestroyPipelineLayout(s->device, s->user_pip_layouts[id], NULL);
13231323
s->user_pip_layouts[id] = VK_NULL_HANDLE;
1324-
_sc_gfx_free_slot(ctx->pip_slots, id);
1324+
_sc_gfx_free_slot(ctx->pip_slots, id, &ctx->pip_free_head);
13251325
h.id = 0;
13261326
return h;
13271327
}

backends/sc_backend_wgpu.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* sc_backend_wgpu.h -- WebGPU (wgpu-native / Dawn) backend stub
3+
*
4+
* Requires the webgpu.h header from the WebGPU native specification.
5+
* Link: -lwgpu_native (or Dawn equivalent)
6+
*
7+
* To activate:
8+
* #define SC_GFX_BACKEND_WGPU
9+
* #define SC_BACKEND_WGPU_IMPLEMENTATION
10+
*/
11+
#ifndef SC_BACKEND_WGPU_H
12+
#define SC_BACKEND_WGPU_H
13+
14+
#include "../include/sc_types.h"
15+
#include "../include/sc_gfx.h"
16+
17+
typedef struct SCWGPUDesc {
18+
bool headless;
19+
bool enable_validation;
20+
const char **instance_extensions;
21+
u32 instance_extension_count;
22+
} SCWGPUDesc;
23+
24+
SCResult sc_wgpu_init (SCGfxContext *ctx, const SCGfxDesc *desc,
25+
const SCWGPUDesc *wgpu_desc);
26+
void sc_wgpu_shutdown (SCGfxContext *ctx);
27+
void sc_wgpu_begin_frame (SCGfxContext *ctx, SCColor clear);
28+
void sc_wgpu_submit (SCGfxContext *ctx,
29+
const SCGfxDrawCmd *cmds, u32 count);
30+
void sc_wgpu_end_frame (SCGfxContext *ctx);
31+
SCGfxBuffer sc_wgpu_make_buffer (SCGfxContext *ctx, const SCGfxBufferDesc *desc);
32+
void sc_wgpu_destroy_buffer(SCGfxContext *ctx, SCGfxBuffer buf);
33+
void sc_wgpu_update_buffer (SCGfxContext *ctx, SCGfxBuffer buf,
34+
const void *data, usize size);
35+
SCGfxTexture sc_wgpu_make_texture (SCGfxContext *ctx, const SCGfxTextureDesc *desc);
36+
void sc_wgpu_destroy_texture(SCGfxContext *ctx, SCGfxTexture tex);
37+
SCGfxShader sc_wgpu_make_shader (SCGfxContext *ctx, const SCGfxShaderDesc *desc);
38+
void sc_wgpu_destroy_shader(SCGfxContext *ctx, SCGfxShader shd);
39+
SCGfxPipeline sc_wgpu_make_pipeline (SCGfxContext *ctx, const SCGfxPipelineDesc *desc);
40+
void sc_wgpu_destroy_pipeline(SCGfxContext *ctx, SCGfxPipeline pip);
41+
42+
#ifdef SC_BACKEND_WGPU_IMPLEMENTATION
43+
#include <stdio.h>
44+
45+
SCResult sc_wgpu_init(SCGfxContext *ctx, const SCGfxDesc *desc,
46+
const SCWGPUDesc *wgpu_desc) {
47+
SC_UNUSED(ctx); SC_UNUSED(desc); SC_UNUSED(wgpu_desc);
48+
fprintf(stderr, "[sc_wgpu] stub – link wgpu-native or Dawn\n");
49+
return SC_ERR_NOT_SUPPORTED;
50+
}
51+
void sc_wgpu_shutdown(SCGfxContext *ctx) { SC_UNUSED(ctx); }
52+
void sc_wgpu_begin_frame(SCGfxContext *ctx, SCColor c) { SC_UNUSED(ctx); SC_UNUSED(c); }
53+
void sc_wgpu_submit(SCGfxContext *ctx, const SCGfxDrawCmd *cmds, u32 n) {
54+
SC_UNUSED(ctx); SC_UNUSED(cmds); SC_UNUSED(n);
55+
}
56+
void sc_wgpu_end_frame(SCGfxContext *ctx) { SC_UNUSED(ctx); }
57+
SCGfxBuffer sc_wgpu_make_buffer(SCGfxContext *ctx, const SCGfxBufferDesc *desc) {
58+
SC_UNUSED(ctx); SC_UNUSED(desc); SCGfxBuffer h = {0}; return h;
59+
}
60+
void sc_wgpu_destroy_buffer(SCGfxContext *ctx, SCGfxBuffer buf) {
61+
SC_UNUSED(ctx); SC_UNUSED(buf);
62+
}
63+
void sc_wgpu_update_buffer(SCGfxContext *ctx, SCGfxBuffer buf,
64+
const void *data, usize size) {
65+
SC_UNUSED(ctx); SC_UNUSED(buf); SC_UNUSED(data); SC_UNUSED(size);
66+
}
67+
SCGfxTexture sc_wgpu_make_texture(SCGfxContext *ctx, const SCGfxTextureDesc *desc) {
68+
SC_UNUSED(ctx); SC_UNUSED(desc); SCGfxTexture h = {0}; return h;
69+
}
70+
void sc_wgpu_destroy_texture(SCGfxContext *ctx, SCGfxTexture tex) {
71+
SC_UNUSED(ctx); SC_UNUSED(tex);
72+
}
73+
SCGfxShader sc_wgpu_make_shader(SCGfxContext *ctx, const SCGfxShaderDesc *desc) {
74+
SC_UNUSED(ctx); SC_UNUSED(desc); SCGfxShader h = {0}; return h;
75+
}
76+
void sc_wgpu_destroy_shader(SCGfxContext *ctx, SCGfxShader shd) {
77+
SC_UNUSED(ctx); SC_UNUSED(shd);
78+
}
79+
SCGfxPipeline sc_wgpu_make_pipeline(SCGfxContext *ctx, const SCGfxPipelineDesc *desc) {
80+
SC_UNUSED(ctx); SC_UNUSED(desc); SCGfxPipeline h = {0}; return h;
81+
}
82+
void sc_wgpu_destroy_pipeline(SCGfxContext *ctx, SCGfxPipeline pip) {
83+
SC_UNUSED(ctx); SC_UNUSED(pip);
84+
}
85+
#endif /* SC_BACKEND_WGPU_IMPLEMENTATION */
86+
#endif /* SC_BACKEND_WGPU_H */

include/sc_gfx.h

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ struct SCGfxContext {
357357
_SCTexData tex_data[SC_GFX_MAX_TEXTURES];
358358
bool rasterize; /* if true, software rasterizes quads into framebuffer */
359359

360+
/* Free-list heads for O(1) slot allocation */
361+
u32 buf_free_head;
362+
u32 tex_free_head;
363+
u32 shd_free_head;
364+
u32 pip_free_head;
365+
360366
/* Submitted draw commands (retained for end_frame) */
361367
SCGfxDrawCmd submit_cmds[SC_GFX_MAX_CMDS];
362368
u32 submit_ccount;
@@ -377,15 +383,30 @@ SC_INLINE u8 _sc_f32_to_u8(f32 v) {
377383
return (u8)(v * 255.0f);
378384
}
379385

380-
/* ---- ID helpers -------------------------------------------------------- */
381-
static u32 _sc_gfx_alloc_slot(_SCSlot *slots, u32 max) {
382-
for (u32 i = 1; i < max; i++) {
383-
if (!slots[i].live) { slots[i].live = true; return i; }
384-
}
385-
return 0;
386+
/* ---- Free-list helpers (O(1) slot alloc/free) ------------------------- */
387+
388+
/* Thread a free-list through the gen field of inactive slots.
389+
gen[slot] = next-free-index, 0 = end-of-list. */
390+
static void _sc_gfx_init_freelist(_SCSlot *slots, u32 max, u32 *head) {
391+
*head = 1;
392+
for (u32 i = 1; i < max; i++) slots[i].gen = i + 1;
393+
slots[max - 1].gen = 0;
386394
}
387-
static void _sc_gfx_free_slot(_SCSlot *slots, u32 id) {
388-
if (id > 0) { slots[id].live = false; slots[id].gen++; }
395+
396+
static u32 _sc_gfx_alloc_slot(_SCSlot *slots, u32 max, u32 *head) {
397+
(void)max;
398+
u32 id = *head;
399+
if (id == 0) return 0;
400+
*head = slots[id].gen;
401+
slots[id].live = true;
402+
return id;
403+
}
404+
405+
static void _sc_gfx_free_slot(_SCSlot *slots, u32 id, u32 *head) {
406+
if (id == 0) return;
407+
slots[id].live = false;
408+
slots[id].gen = *head;
409+
*head = id;
389410
}
390411

391412
/* ---- Core API --------------------------------------------------------- */
@@ -400,6 +421,12 @@ SCResult sc_gfx_init(const SCGfxDesc *desc, SCGfxContext **out_ctx) {
400421
ctx->backend = desc->backend != SC_BACKEND_AUTO
401422
? desc->backend : SC_BACKEND_SOFTWARE;
402423

424+
/* Initialize free-list slot allocators */
425+
_sc_gfx_init_freelist(ctx->buf_slots, SC_GFX_MAX_BUFFERS, &ctx->buf_free_head);
426+
_sc_gfx_init_freelist(ctx->tex_slots, SC_GFX_MAX_TEXTURES, &ctx->tex_free_head);
427+
_sc_gfx_init_freelist(ctx->shd_slots, SC_GFX_MAX_SHADERS, &ctx->shd_free_head);
428+
_sc_gfx_init_freelist(ctx->pip_slots, SC_GFX_MAX_PIPELINES, &ctx->pip_free_head);
429+
403430
#ifdef SC_GFX_BACKEND_VULKAN
404431
if (ctx->backend == SC_BACKEND_VULKAN) {
405432
SCResult r = sc_vulkan_init(ctx, desc, NULL);
@@ -448,7 +475,7 @@ void sc_gfx_shutdown(SCGfxContext *ctx) {
448475
}
449476

450477
SCGfxBuffer sc_gfx_make_buffer(SCGfxContext *ctx, const SCGfxBufferDesc *desc) {
451-
u32 id = _sc_gfx_alloc_slot(ctx->buf_slots, SC_GFX_MAX_BUFFERS);
478+
u32 id = _sc_gfx_alloc_slot(ctx->buf_slots, SC_GFX_MAX_BUFFERS, &ctx->buf_free_head);
452479
SCGfxBuffer h = {id};
453480
if (id == 0) return h;
454481
#ifdef SC_GFX_BACKEND_VULKAN
@@ -464,7 +491,7 @@ SCGfxBuffer sc_gfx_make_buffer(SCGfxContext *ctx, const SCGfxBufferDesc *desc) {
464491
if (desc->data && desc->size > 0) {
465492
bd->data = (u8*)malloc(desc->size);
466493
if (!bd->data) {
467-
_sc_gfx_free_slot(ctx->buf_slots, id);
494+
_sc_gfx_free_slot(ctx->buf_slots, id, &ctx->buf_free_head);
468495
h.id = 0;
469496
return h;
470497
}
@@ -508,7 +535,7 @@ void sc_gfx_destroy_buffer(SCGfxContext *ctx, SCGfxBuffer buf) {
508535
free(ctx->buf_data[buf.id].data);
509536
ctx->buf_data[buf.id].data = NULL;
510537
}
511-
_sc_gfx_free_slot(ctx->buf_slots, buf.id);
538+
_sc_gfx_free_slot(ctx->buf_slots, buf.id, &ctx->buf_free_head);
512539
}
513540

514541
SCGfxTexture sc_gfx_make_texture(SCGfxContext *ctx, const SCGfxTextureDesc *desc) {
@@ -517,7 +544,7 @@ SCGfxTexture sc_gfx_make_texture(SCGfxContext *ctx, const SCGfxTextureDesc *desc
517544
return sc_vulkan_make_texture(ctx, desc);
518545
}
519546
#endif
520-
u32 id = _sc_gfx_alloc_slot(ctx->tex_slots, SC_GFX_MAX_TEXTURES);
547+
u32 id = _sc_gfx_alloc_slot(ctx->tex_slots, SC_GFX_MAX_TEXTURES, &ctx->tex_free_head);
521548
SCGfxTexture h = {id};
522549
if (id == 0) return h;
523550
if (ctx->backend == SC_BACKEND_SOFTWARE && desc) {
@@ -529,7 +556,7 @@ SCGfxTexture sc_gfx_make_texture(SCGfxContext *ctx, const SCGfxTextureDesc *desc
529556
usize sz = (usize)desc->width * (usize)desc->height * bpp;
530557
td->pixels = (u8*)malloc(sz);
531558
if (!td->pixels) {
532-
_sc_gfx_free_slot(ctx->tex_slots, id);
559+
_sc_gfx_free_slot(ctx->tex_slots, id, &ctx->tex_free_head);
533560
h.id = 0;
534561
return h;
535562
}
@@ -567,19 +594,19 @@ void sc_gfx_destroy_texture(SCGfxContext *ctx, SCGfxTexture tex) {
567594
#ifdef SC_GFX_BACKEND_VULKAN
568595
if (ctx->backend == SC_BACKEND_VULKAN) {
569596
sc_vulkan_destroy_texture(ctx, tex);
570-
_sc_gfx_free_slot(ctx->tex_slots, tex.id);
597+
_sc_gfx_free_slot(ctx->tex_slots, tex.id, &ctx->tex_free_head);
571598
return;
572599
}
573600
#endif
574601
if (ctx->backend == SC_BACKEND_SOFTWARE) {
575602
free(ctx->tex_data[tex.id].pixels);
576603
ctx->tex_data[tex.id].pixels = NULL;
577604
}
578-
_sc_gfx_free_slot(ctx->tex_slots, tex.id);
605+
_sc_gfx_free_slot(ctx->tex_slots, tex.id, &ctx->tex_free_head);
579606
}
580607

581608
SCGfxShader sc_gfx_make_shader(SCGfxContext *ctx, const SCGfxShaderDesc *desc) {
582-
u32 id = _sc_gfx_alloc_slot(ctx->shd_slots, SC_GFX_MAX_SHADERS);
609+
u32 id = _sc_gfx_alloc_slot(ctx->shd_slots, SC_GFX_MAX_SHADERS, &ctx->shd_free_head);
583610
SCGfxShader h = {id};
584611
if (id == 0) return h;
585612
#ifdef SC_GFX_BACKEND_VULKAN
@@ -600,11 +627,11 @@ void sc_gfx_destroy_shader(SCGfxContext *ctx, SCGfxShader shd) {
600627
return;
601628
}
602629
#endif
603-
_sc_gfx_free_slot(ctx->shd_slots, shd.id);
630+
_sc_gfx_free_slot(ctx->shd_slots, shd.id, &ctx->shd_free_head);
604631
}
605632

606633
SCGfxPipeline sc_gfx_make_pipeline(SCGfxContext *ctx, const SCGfxPipelineDesc *desc) {
607-
u32 id = _sc_gfx_alloc_slot(ctx->pip_slots, SC_GFX_MAX_PIPELINES);
634+
u32 id = _sc_gfx_alloc_slot(ctx->pip_slots, SC_GFX_MAX_PIPELINES, &ctx->pip_free_head);
608635
SCGfxPipeline h = {id};
609636
if (id == 0) return h;
610637
#ifdef SC_GFX_BACKEND_VULKAN
@@ -625,7 +652,7 @@ void sc_gfx_destroy_pipeline(SCGfxContext *ctx, SCGfxPipeline pip) {
625652
return;
626653
}
627654
#endif
628-
_sc_gfx_free_slot(ctx->pip_slots, pip.id);
655+
_sc_gfx_free_slot(ctx->pip_slots, pip.id, &ctx->pip_free_head);
629656
}
630657

631658
void sc_gfx_begin_frame(SCGfxContext *ctx, SCColor c) {

0 commit comments

Comments
 (0)