SBgl 0.1.0
A graphics framework in C99
Loading...
Searching...
No Matches
sbgl_core.c
Go to the documentation of this file.
1#include "sbgl.h"
2#include "sbgl_types.h"
3
4#define SBL_ARENA_IMPLEMENTATION
5#include "core/sbl_arena.h"
6
8#include "core/sbgl_batcher.h"
11#include "core/sbgl_platform.h"
12#include "core/sbgl_sort.h"
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17
32
61
63 if (!ctx || !ctx->inner)
64 return NULL;
66 return &inner->arena;
67}
68
69// --- Error Inspection API ---
70
72 if (!ctx) {
74 }
75 return ctx->result;
76}
77
79 sbgl_ErrorDetail detail = {
80 .type = SBGL_BACKEND_VULKAN,
81 .coreResult = SBGL_SUCCESS,
82 .vkResult = 0,
83 .extension = 0,
84 };
85
86 if (!ctx) {
88 return detail;
89 }
90
91 detail.coreResult = ctx->result;
92
93 if (ctx->inner) {
95 if (inner->gfx) {
96 detail.vkResult = sbgl_gfx_GetLastVkResult(inner->gfx);
97 }
98 }
99
100 return detail;
101}
102
104 if (!ctx) {
105 return;
106 }
107 ctx->result = SBGL_SUCCESS;
108}
109
110// --- Initialization ---
111
113 sbgl_InitResult res = { .ctx = NULL, .error = SBGL_SUCCESS };
114
115 if (!config) {
118 "sbgl_InitWithConfig: NULL config passed");
119 return res;
120 }
121
122 SblArena main_arena;
123 if (!sbl_arena_init(&main_arena, 4 * 1024 * 1024)) {
126 "sbgl_InitWithConfig: arena initialization failed");
127 return res;
128 }
129
131 if (!ctx) {
132 sbl_arena_free(&main_arena);
135 "sbgl_InitWithConfig: context allocation failed");
136 return res;
137 }
138
140 if (!inner) {
141 sbl_arena_free(&main_arena);
144 "sbgl_InitWithConfig: inner context allocation failed");
145 return res;
146 }
147
148 inner->arena = main_arena;
149 sbl_arena_init(&inner->transientArena, 16 * 1024 * 1024);
150 inner->clearColor[0] = 0.0f;
151 inner->clearColor[1] = 0.0f;
152 inner->clearColor[2] = 0.0f;
153 inner->clearColor[3] = 0.0f;
154 inner->state.isIdle = 1;
156 inner->state.wasFocused = 0;
157
158 ctx->inner = inner;
159 ctx->result = SBGL_SUCCESS;
160
161 const char* title = config->windowTitle ? config->windowTitle : "SBgl";
163 &inner->arena, &inner->input,
164 config->windowWidth, config->windowHeight, title,
165 &inner->window
166 );
167
168 if (platformRes != SBGL_PLATFORM_SUCCESS) {
171 "sbgl_InitWithConfig: window creation failed");
172 sbl_arena_free(&inner->arena);
173 return res;
174 }
175
176 inner->gfx = sbgl_gfx_Init(inner->window, &inner->arena, &config->limits, config->enableValidation);
177 if (!inner->gfx) {
180 "sbgl_InitWithConfig: graphics initialization failed");
182 sbl_arena_free(&inner->arena);
183 return res;
184 }
185
187 "sbgl_InitWithConfig: initialization successful");
188
189 res.ctx = ctx;
190 return res;
191}
192
193sbgl_InitResult sbgl_Init(int w, int h, const char* title) {
194 sbgl_InitConfig config = {
195 .windowWidth = w,
196 .windowHeight = h,
197 .windowTitle = title,
198 .limits = { .maxBuffers = 1024, .maxShaders = 256, .maxPipelines = 256 },
199 .enableValidation = true
200 };
201 return sbgl_InitWithConfig(&config);
202}
203
205 if (!ctx) {
207 "sbgl_Shutdown: NULL context");
208 return;
209 }
210 if (!ctx->inner) {
213 "sbgl_Shutdown: uninitialized context");
214 return;
215 }
217
218 sbgl_log_impl(SBGL_LOG_INFO, SBGL_LOG_CAT_CORE, "Initiating shutdown...");
219
220 if (!inner->state.isIdle) {
222 }
223
224 if (inner->gfx)
225 sbgl_gfx_Shutdown(inner->gfx);
226 if (inner->window)
228
230
231 // The context itself is allocated within inner->arena,
232 // so we cannot access it after the arena is freed.
233 ctx->result = SBGL_SUCCESS;
234
235 sbl_arena_free(&inner->arena);
236}
237
239 if (!ctx || !ctx->inner)
240 return true;
242 return sbgl_os_WindowShouldClose(inner->window);
243}
244
246 if (!ctx || !ctx->inner)
247 return 0.0;
249 return (double)sbgl_os_GetPerfCount(inner->window) / (double)sbgl_os_GetPerfFreq(inner->window);
250}
251
252void sbgl_GetWindowSize(sbgl_Context* ctx, int* w, int* h) {
253 if (w)
254 *w = 0;
255 if (h)
256 *h = 0;
257 if (!ctx || !ctx->inner)
258 return;
260 sbgl_os_GetWindowSize(inner->window, w, h);
261}
263 if (!ctx || !ctx->inner) return;
265
266 // Reset compute handle to prevent cross-phase push constant delivery
268
269 if (!inner->state.hasStartedFrame) {
271
272 // Detect focus transitions to re-apply mouse locking if necessary.
273 bool currentlyFocused = sbgl_os_IsWindowFocused(inner->window);
274 if (currentlyFocused && !inner->state.wasFocused) {
275 if (inner->mouseMode == SBGL_MOUSE_MODE_CAPTURED) {
276 sbgl_os_SetCursorVisible(inner->window, false);
277 sbgl_os_SetCursorLocked(inner->window, true);
278 }
279 }
280 inner->state.wasFocused = currentlyFocused;
281
283
284 // Performance counters are reset at the start of every frame
285 inner->currentFrame.draw_calls = 0;
286 inner->currentFrame.instance_count = 0;
287 inner->currentFrame.cpu_sort_time = 0.0f;
288
289 if (sbgl_gfx_BeginFrame(inner->gfx)) {
290 inner->state.hasStartedFrame = 1;
291
292 /* GPU performance data for the previous frame in this slot is now guaranteed
293 to be ready. Telemetry is skipped for the very first frames to allow
294 the pipeline to prime and avoid uninitialized query errors. */
295 if (inner->frameCount > 2) {
297 }
298 } else {
300 return;
301 }
302 }
303
305 inner->gfx,
306 inner->clearColor[0],
307 inner->clearColor[1],
308 inner->clearColor[2],
309 inner->clearColor[3]
310 );
311 inner->state.isDrawing = 1;
312 ctx->result = SBGL_SUCCESS;
313}
314
316 if (!ctx || !ctx->inner)
317 return;
319
320 if (inner->state.isDrawing) {
322 inner->state.isDrawing = 0;
323 }
324
325 if (inner->state.hasStartedFrame) {
326 sbgl_gfx_EndFrame(inner->gfx);
327 inner->state.hasStartedFrame = 0;
328 inner->state.isIdle = 0;
329 inner->frameCount++;
330
331 uint64_t frameEndTicks = sbgl_os_GetPerfCount(inner->window);
333 (float)((double)(frameEndTicks - inner->frameStartTicks) * 1000.0 / (double)sbgl_os_GetPerfFreq(inner->window));
334
335 // The completed telemetry data is archived for user retrieval
336 inner->lastFrame = inner->currentFrame;
337 }
338
339 // Reset pressed states for next frame
340 memset(inner->input.keysPressed, 0, sizeof(inner->input.keysPressed));
341 ctx->result = SBGL_SUCCESS;
342}
343
345 if (!ctx || !ctx->inner)
346 return;
348
349 // Reset graphics handle to prevent cross-phase push constant delivery
351
352 /* If a frame has not yet been initiated, the system handles event polling,
353 focus management, and starts the GPU command stream. */
354 if (!inner->state.hasStartedFrame) {
356
357 bool currentlyFocused = sbgl_os_IsWindowFocused(inner->window);
358 if (currentlyFocused && !inner->state.wasFocused) {
359 if (inner->mouseMode == SBGL_MOUSE_MODE_CAPTURED) {
360 sbgl_os_SetCursorVisible(inner->window, false);
361 sbgl_os_SetCursorLocked(inner->window, true);
362 }
363 }
364 inner->state.wasFocused = currentlyFocused;
365
367
368 inner->currentFrame.draw_calls = 0;
369 inner->currentFrame.instance_count = 0;
370 inner->currentFrame.cpu_sort_time = 0.0f;
371
372 if (sbgl_gfx_BeginFrame(inner->gfx)) {
373 inner->state.hasStartedFrame = 1;
374 if (inner->frameCount > 2) {
376 }
377 } else {
379 return;
380 }
381 }
382 ctx->result = SBGL_SUCCESS;
383}
384
386 /* The compute phase finalize is currently a no-op at the core level,
387 as command buffer submission is deferred until the main frame end. */
388 (void)ctx;
389}
390
392 if (!ctx || !ctx->inner)
393 return;
394
397 inner->state.isIdle = 1;
398 ctx->result = SBGL_SUCCESS;
399}
400
401void sbgl_SetClearColor(sbgl_Context* ctx, float r, float g, float b, float a) {
402 if (!ctx || !ctx->inner)
403 return;
404
406 inner->clearColor[0] = r;
407 inner->clearColor[1] = g;
408 inner->clearColor[2] = b;
409 inner->clearColor[3] = a;
410 ctx->result = SBGL_SUCCESS;
411}
412
414 static const sbgl_InputState dummy = { 0 };
415 if (!ctx || !ctx->inner)
416 return &dummy;
417
419 return &inner->input;
420}
421
423 if (!ctx || !ctx->inner)
424 return;
426 inner->mouseMode = mode;
427
428 bool visible = (mode == SBGL_MOUSE_MODE_NORMAL);
429 bool locked = (mode == SBGL_MOUSE_MODE_CAPTURED);
430
431 sbgl_os_SetCursorVisible(inner->window, visible);
432 sbgl_os_SetCursorLocked(inner->window, locked);
433 ctx->result = SBGL_SUCCESS;
434}
435
437 if (!ctx || !ctx->inner) {
438 return (sbgl_Telemetry){ 0 };
439 }
441 return inner->lastFrame;
442}
443
445sbgl_CreateBuffer(sbgl_Context* ctx, sbgl_BufferUsage usage, size_t size, const void* data) {
446 if (!ctx || !ctx->inner)
447 return SBGL_INVALID_HANDLE;
448
450 sbgl_Buffer res = sbgl_gfx_CreateBuffer(inner->gfx, usage, size, data);
451
452 ctx->result =
454 return res;
455}
456
458 if (!ctx || !ctx->inner)
459 return;
460
462
463 if (!inner->state.isIdle) {
467 "Buffer destroyed while GPU is busy! Missing sbgl_DeviceWaitIdle."
468 );
469
471
472 // Force a wait to safely destroy it anyway and prevent driver crash
474 }
475
476 sbgl_gfx_DestroyBuffer(inner->gfx, buffer);
477 ctx->result = SBGL_SUCCESS;
478}
479
480void sbgl_FillBuffer(sbgl_Context* ctx, sbgl_Buffer buffer, size_t offset, size_t size, uint32_t value) {
481 if (!ctx || !ctx->inner)
482 return;
483
485 sbgl_gfx_FillBuffer(inner->gfx, buffer, offset, size, value);
486 ctx->result = SBGL_SUCCESS;
487}
488
490 if (!ctx || !ctx->inner)
491 return 0;
492
494 return sbgl_gfx_GetFrameIndex(inner->gfx);
495}
496
498 /* The system maps the specified GPU buffer into the CPU's address space,
499 allowing for direct memory access. This is essential for reading back
500 results from compute shaders or updating dynamic vertex data. */
501 if (!ctx || !ctx->inner)
502 return NULL;
503
505 void* ptr = sbgl_gfx_MapBuffer(inner->gfx, buffer);
506
507 ctx->result = (ptr != NULL) ? SBGL_SUCCESS : SBGL_ERROR_GRAPHICS_FAILED;
508 return ptr;
509}
510
512 /* The mapping between the CPU and GPU buffer is released, finalizing any
513 pending memory writes and ensuring synchronization for subsequent GPU operations. */
514 if (!ctx || !ctx->inner)
515 return;
516
518 sbgl_gfx_UnmapBuffer(inner->gfx, buffer);
519 ctx->result = SBGL_SUCCESS;
520}
521
523sbgl_LoadShader(sbgl_Context* ctx, sbgl_ShaderStage stage, const uint32_t* bytecode, size_t size) {
524 if (!ctx || !ctx->inner)
525 return SBGL_INVALID_HANDLE;
527 sbgl_Shader res = sbgl_gfx_LoadShader(inner->gfx, stage, bytecode, size);
528 ctx->result =
530 return res;
531}
532
534sbgl_LoadShaderFromFile(sbgl_Context* ctx, sbgl_ShaderStage stage, const char* filename) {
535 FILE* file = fopen(filename, "rb");
536 if (!file) {
537 sbgl_log_impl(SBGL_LOG_ERROR, SBGL_LOG_CAT_GFX, "Failed to open shader file.");
538 return SBGL_INVALID_HANDLE;
539 }
540
541 fseek(file, 0, SEEK_END);
542 size_t size = ftell(file);
543 fseek(file, 0, SEEK_SET);
544
545 uint32_t* buffer = malloc(size);
546 if (!buffer) {
547 fclose(file);
548 return SBGL_INVALID_HANDLE;
549 }
550
551 size_t read = fread(buffer, 1, size, file);
552 fclose(file);
553
554 if (read != size) {
555 free(buffer);
556 return SBGL_INVALID_HANDLE;
557 }
558
559 sbgl_Shader shader = sbgl_LoadShader(ctx, stage, buffer, size);
560 free(buffer);
561
562 return shader;
563}
564
566 if (!ctx || !ctx->inner)
567 return;
569
570 if (!inner->state.isIdle) {
574 "Destroy Shader called while GPU is busy! Missing sbgl_DeviceWaitIdle."
575 );
576
578
579 // Force a wait to safely destroy it anyway and prevent driver crash
581 }
582
583 sbgl_gfx_DestroyShader(inner->gfx, shader);
584 ctx->result = SBGL_SUCCESS;
585}
586
588 if (!ctx || !ctx->inner)
589 return SBGL_INVALID_HANDLE;
591 sbgl_Pipeline res = sbgl_gfx_CreatePipeline(inner->gfx, config);
592 ctx->result =
594 return res;
595}
596
598 if (!ctx || !ctx->inner)
599 return;
601
602 if (!inner->state.isIdle) {
606 "Destroy Pipeline called while GPU is busy! Missing sbgl_DeviceWaitIdle."
607 );
608
610
611 // Force a wait to safely destroy it anyway and prevent driver crash
613 }
614
615 sbgl_gfx_DestroyPipeline(inner->gfx, pipeline);
616 ctx->result = SBGL_SUCCESS;
617}
618
628
630 if (!ctx || !ctx->inner)
631 return;
633
634 if (!inner->state.isIdle) {
638 "Destroy Compute Pipeline called while GPU is busy! Missing sbgl_DeviceWaitIdle."
639 );
640
643 }
644
645 sbgl_gfx_DestroyComputePipeline(inner->gfx, pipeline);
646 ctx->result = SBGL_SUCCESS;
647}
648
650 if (!ctx || !ctx->inner)
651 return;
653 sbgl_gfx_BindComputePipeline(inner->gfx, pipeline);
654 ctx->result = SBGL_SUCCESS;
655}
656
657void sbgl_DispatchCompute(sbgl_Context* ctx, uint32_t x, uint32_t y, uint32_t z) {
658 if (!ctx || !ctx->inner)
659 return;
661 sbgl_gfx_DispatchCompute(inner->gfx, x, y, z);
662 ctx->result = SBGL_SUCCESS;
663}
664
666 if (!ctx || !ctx->inner)
667 return;
669 sbgl_gfx_MemoryBarrier(inner->gfx, type);
670 ctx->result = SBGL_SUCCESS;
671}
672
674 if (!ctx || !ctx->inner)
675 return;
677 sbgl_gfx_BindPipeline(inner->gfx, pipeline);
678 ctx->result = SBGL_SUCCESS;
679}
680
682 if (!ctx || !ctx->inner)
683 return;
685 sbgl_gfx_BindBuffer(inner->gfx, buffer, usage);
686 ctx->result = SBGL_SUCCESS;
687}
688
690 if (!ctx || !ctx->inner)
691 return 0;
693 uint64_t addr = sbgl_gfx_GetBufferDeviceAddress(inner->gfx, buffer);
694 ctx->result = (addr != 0) ? SBGL_SUCCESS : SBGL_ERROR_GRAPHICS_FAILED;
695 return addr;
696}
697
698void sbgl_Draw(sbgl_Context* ctx, uint32_t vertexCount, uint32_t firstVertex, uint32_t instanceCount) {
699 if (!ctx || !ctx->inner)
700 return;
702 sbgl_gfx_Draw(inner->gfx, vertexCount, firstVertex, instanceCount);
703 ctx->result = SBGL_SUCCESS;
704}
705
707 sbgl_Context* ctx,
708 uint32_t indexCount,
709 uint32_t firstIndex,
710 int32_t vertexOffset,
711 uint32_t instanceCount
712) {
713 if (!ctx || !ctx->inner)
714 return;
716 sbgl_gfx_DrawIndexed(inner->gfx, indexCount, firstIndex, vertexOffset, instanceCount);
717 ctx->result = SBGL_SUCCESS;
718}
719
721 sbgl_Context* ctx,
722 sbgl_Buffer buffer,
723 size_t offset,
724 uint32_t drawCount
725) {
726 if (!ctx || !ctx->inner)
727 return;
729 sbgl_gfx_DrawIndirect(inner->gfx, buffer, offset, drawCount);
730 ctx->result = SBGL_SUCCESS;
731}
732
733void sbgl_PushConstants(sbgl_Context* ctx, size_t size, const void* data) {
734 if (!ctx || !ctx->inner)
735 return;
737 sbgl_gfx_PushConstants(inner->gfx, size, data);
738 ctx->result = SBGL_SUCCESS;
739}
740
741// --- Automated Batching API ---
742
744 if (!ctx || !arena) {
745 return NULL;
746 }
747
748 // Allocate the queue structure from the provided arena
750 if (!queue) {
752 return NULL;
753 }
754
755 // Initialize the queue with a default capacity of 16,384 packets
756 /* Initialize the queue with a high capacity to support massive batching (e.g., voxels). */
757 queue->capacity = 131072;
758 queue->count = 0;
759 queue->arena = arena;
762
763 if (!queue->packets || !queue->instances) {
765 return NULL;
766 }
767
768 ctx->result = SBGL_SUCCESS;
769 return queue;
770}
771
773 sbgl_RenderQueue* queue,
774 uint32_t mesh,
775 uint32_t material,
776 uint32_t blendMode,
777 uint32_t sidedness,
778 uint32_t tags,
779 sbgl_SortKey key,
780 const sbgl_InstanceData* data
781) {
782 if (!queue) {
783 return;
784 }
785
786 // Verify the queue has not reached maximum capacity
787 if (queue->count >= queue->capacity) {
788 return;
789 }
790
791 // The system appends the draw packet to the contiguous array
792 uint32_t index = queue->count++;
793 sbgl_DrawPacket* packet = &queue->packets[index];
794 packet->key = key;
795 packet->header = SBGL_PACK_HEADER(mesh, material, blendMode, sidedness, tags);
796
797 // Per-instance data is copied into the parallel array
798 if (data) {
799 queue->instances[index] = *data;
800 } else {
801 // An identity transform and white color are used if no data is provided
802 memset(&queue->instances[index], 0, sizeof(sbgl_InstanceData));
803 queue->instances[index].transform = sbgl_Mat4Identity();
804 queue->instances[index].color = sbgl_Vec4Set(1, 1, 1, 1);
805 }
806}
807
809 sbgl_Context* ctx,
810 sbgl_RenderQueue** queues,
811 uint32_t queueCount,
812 const sbgl_Mat4* viewProj
813) {
814 sbgl_RenderQueuesEx(ctx, queues, queueCount, viewProj, 0);
815}
816
818 sbgl_Context* ctx,
819 sbgl_RenderQueue** queues,
820 uint32_t queueCount,
821 const sbgl_Mat4* viewProj,
822 uint64_t userAddress
823) {
824 if (!ctx || !ctx->inner || !queues || queueCount == 0) {
825 return;
826 }
827
829
830 // Total packet count is calculated across all queues to determine buffer requirements
831 uint32_t totalPackets = 0;
832 for (uint32_t i = 0; i < queueCount; ++i) {
833 if (queues[i]) {
834 totalPackets += queues[i]->count;
835 }
836 }
837
838 if (totalPackets == 0) {
839 return;
840 }
841
842 // The transient arena is reset for this frame's batching work
844
846
847 // Temporary host memory is allocated for merging and sorting from the transient arena
848 sbgl_DrawPacket* mergedPackets =
850 sbgl_InstanceData* mergedInstances =
852 sbgl_SortKey* keys = SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_SortKey, totalPackets);
853 uint32_t* indices = SBL_ARENA_PUSH_ARRAY(&inner->transientArena, uint32_t, totalPackets);
854
855 if (!mergedPackets || !mergedInstances || !keys || !indices) {
857 return;
858 }
859
860 // Packets and instance data are merged from all queues
861 uint32_t current = 0;
862 for (uint32_t i = 0; i < queueCount; ++i) {
863 if (!queues[i])
864 continue;
865 for (uint32_t j = 0; j < queues[i]->count; ++j) {
866 mergedPackets[current] = queues[i]->packets[j];
867 mergedInstances[current] = queues[i]->instances[j];
868 keys[current] = mergedPackets[current].key;
869 indices[current] = current;
870 current++;
871 }
872 // The queue is cleared for the next frame
873 queues[i]->count = 0;
874 }
875
876 // A stable radix sort is performed on the packets based on sort keys
877 sbgl_SortKey* temp_keys =
878 SBL_ARENA_PUSH_ARRAY(&inner->transientArena, sbgl_SortKey, totalPackets);
879 uint32_t* temp_indices = SBL_ARENA_PUSH_ARRAY(&inner->transientArena, uint32_t, totalPackets);
880 if (!temp_keys || !temp_indices) {
882 return;
883 }
884
885 sbgl_radix_sort(keys, indices, totalPackets, temp_keys, temp_indices);
886
887 // Packets and instances are reordered according to the sorted indices
888 sbgl_DrawPacket* sortedPackets =
890 sbgl_InstanceData* sortedInstances =
892 if (!sortedPackets || !sortedInstances) {
894 return;
895 }
896
897 for (uint32_t i = 0; i < totalPackets; ++i) {
898 sortedPackets[i] = mergedPackets[indices[i]];
899 sortedInstances[i] = mergedInstances[indices[i]];
900 }
901
902 // Sorted packets are baked into optimized indirect draw commands
903 sbgl_IndirectCommand* commands =
905 if (!commands) {
907 return;
908 }
909
910 uint32_t commandCount = sbgl_bake_commands(sortedPackets, totalPackets, commands, totalPackets);
911
912 uint64_t sortEndTicks = sbgl_os_GetPerfCount(inner->window);
914 (float)((double)(sortEndTicks - inner->sortStartTicks) * 1000.0 / (double)sbgl_os_GetPerfFreq(inner->window));
915
916 inner->currentFrame.draw_calls += commandCount;
917 inner->currentFrame.instance_count += totalPackets;
918
919 if (commandCount > 0) {
920 // Allocate transient GPU space for packed instance data
922 inner->gfx,
923 sizeof(sbgl_InstanceData) * totalPackets,
924 256 // Storage buffer alignment
925 );
926
927 if (instanceAlloc.buffer != SBGL_INVALID_HANDLE) {
928 memcpy(instanceAlloc.mapped, sortedInstances, sizeof(sbgl_InstanceData) * totalPackets);
929
930 // Retrieve BDA address and update push constants
931 uint64_t bdaAddress = instanceAlloc.deviceAddress;
932
933 struct {
934 sbgl_Mat4 viewProj;
935 uint64_t instanceAddress;
936 uint64_t userAddress;
937 } pc;
938 pc.viewProj = viewProj ? *viewProj : sbgl_Mat4Identity();
939 pc.instanceAddress = bdaAddress;
940 pc.userAddress = userAddress;
941
942 sbgl_gfx_PushConstants(inner->gfx, sizeof(pc), &pc);
943
944 // Allocate transient GPU space for baked commands and dispatch MDI
946 inner->gfx,
947 sizeof(sbgl_IndirectCommand) * commandCount,
948 16 // Indirect buffer alignment
949 );
950
951 if (indirectAlloc.buffer != SBGL_INVALID_HANDLE) {
952 memcpy(indirectAlloc.mapped, commands, sizeof(sbgl_IndirectCommand) * commandCount);
954 inner->gfx,
955 indirectAlloc.buffer,
956 indirectAlloc.offset,
957 commandCount
958 );
959 }
960 }
961 }
962
963 ctx->result = SBGL_SUCCESS;
964}
sbgl_MouseMode
Mouse behavior modes.
Definition sbgl_input.h:153
@ SBGL_MOUSE_MODE_NORMAL
Definition sbgl_input.h:154
@ SBGL_MOUSE_MODE_CAPTURED
Definition sbgl_input.h:156
API for the SiputBiru Graphics Library (SBgl).
void sbgl_gfx_Shutdown(sbgl_GfxContext *ctx)
void sbgl_gfx_BindComputePipeline(sbgl_GfxContext *ctx, sbgl_ComputePipeline handle)
void sbgl_gfx_Draw(sbgl_GfxContext *ctx, uint32_t vertexCount, uint32_t firstVertex, uint32_t instanceCount)
uint64_t sbgl_gfx_GetBufferDeviceAddress(sbgl_GfxContext *ctx, sbgl_Buffer handle)
Retrieves the 64-bit GPU virtual address for a buffer.
sbgl_GfxContext * sbgl_gfx_Init(sbgl_Window *window, struct SblArena *arena, const sbgl_ResourceLimits *limits, bool enableValidation)
Initializes the graphics backend with configurable resource limits.
sbgl_Buffer sbgl_gfx_CreateBuffer(sbgl_GfxContext *ctx, sbgl_BufferUsage usage, size_t size, const void *data)
void sbgl_gfx_EndRenderPass(sbgl_GfxContext *ctx)
Ends the current graphics rendering pass.
void sbgl_gfx_DestroyShader(sbgl_GfxContext *ctx, sbgl_Shader handle)
void sbgl_gfx_DispatchCompute(sbgl_GfxContext *ctx, uint32_t x, uint32_t y, uint32_t z)
void sbgl_gfx_DestroyPipeline(sbgl_GfxContext *ctx, sbgl_Pipeline handle)
void sbgl_gfx_BindPipeline(sbgl_GfxContext *ctx, sbgl_Pipeline handle)
void * sbgl_gfx_MapBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle)
bool sbgl_gfx_BeginFrame(sbgl_GfxContext *ctx)
Starts a new frame, acquiring an image and starting the command buffer.
void sbgl_gfx_MemoryBarrier(sbgl_GfxContext *ctx, sbgl_BarrierType type)
float sbgl_gfx_GetGpuTime(sbgl_GfxContext *ctx)
Retrieves the elapsed GPU time for the previous frame in milliseconds.
void sbgl_gfx_UnmapBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle)
void sbgl_gfx_DrawIndexed(sbgl_GfxContext *ctx, uint32_t indexCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t instanceCount)
sbgl_Shader sbgl_gfx_LoadShader(sbgl_GfxContext *ctx, sbgl_ShaderStage stage, const uint32_t *bytecode, size_t size)
sbgl_Pipeline sbgl_gfx_CreatePipeline(sbgl_GfxContext *ctx, const sbgl_PipelineConfig *config)
void sbgl_gfx_PushConstants(sbgl_GfxContext *ctx, size_t size, const void *data)
sbgl_GfxTransientAllocation sbgl_gfx_AllocateTransient(sbgl_GfxContext *ctx, size_t size, uint32_t alignment)
Allocates a slice of GPU-visible memory for transient per-frame data.
void sbgl_gfx_DestroyBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle)
void sbgl_gfx_DeviceWaitIdle(sbgl_GfxContext *ctx)
void sbgl_gfx_DrawIndirect(sbgl_GfxContext *ctx, sbgl_Buffer handle, size_t offset, uint32_t drawCount)
Submits a batch of draw calls stored in a GPU buffer.
int32_t sbgl_gfx_GetLastVkResult(sbgl_GfxContext *ctx)
Retrieves the last VkResult from the backend for error inspection.
void sbgl_gfx_FillBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle, size_t offset, size_t size, uint32_t value)
Performs a hardware-accelerated buffer fill.
sbgl_ComputePipeline sbgl_gfx_CreateComputePipeline(sbgl_GfxContext *ctx, sbgl_Shader handle)
uint32_t sbgl_gfx_GetFrameIndex(sbgl_GfxContext *ctx)
Retrieves the current backend frame index.
void sbgl_gfx_DestroyComputePipeline(sbgl_GfxContext *ctx, sbgl_ComputePipeline handle)
void sbgl_gfx_BindBuffer(sbgl_GfxContext *ctx, sbgl_Buffer handle, sbgl_BufferUsage usage)
void sbgl_gfx_EndFrame(sbgl_GfxContext *ctx)
Submits the current frame's commands and presents the image.
void sbgl_gfx_BeginRenderPass(sbgl_GfxContext *ctx, float r, float g, float b, float a)
Starts a graphics rendering pass.
uint32_t sbgl_bake_commands(const sbgl_DrawPacket *packets, uint32_t packetCount, sbgl_IndirectCommand *outCommands, uint32_t maxCommands)
Definition sbgl_batcher.c:3
The baking pipeline for compressing draw packets into indirect commands.
Internal helpers for accessing context state from library subsystems.
void sbgl_SetClearColor(sbgl_Context *ctx, float r, float g, float b, float a)
Sets the clear color for the next frame.
Definition sbgl_core.c:401
sbgl_Result sbgl_GetResult(sbgl_Context *ctx)
Retrieves the last result code from the context.
Definition sbgl_core.c:71
void sbgl_RenderQueuesEx(sbgl_Context *ctx, sbgl_RenderQueue **queues, uint32_t queueCount, const sbgl_Mat4 *viewProj, uint64_t userAddress)
Extended version of sbgl_RenderQueues with user metadata.
Definition sbgl_core.c:817
void sbgl_DestroyComputePipeline(sbgl_Context *ctx, sbgl_ComputePipeline pipeline)
Destroys a compute pipeline.
Definition sbgl_core.c:629
void sbgl_DispatchCompute(sbgl_Context *ctx, uint32_t x, uint32_t y, uint32_t z)
Dispatches a compute workload.
Definition sbgl_core.c:657
void sbgl_FillBuffer(sbgl_Context *ctx, sbgl_Buffer buffer, size_t offset, size_t size, uint32_t value)
Fills a region of a GPU buffer with a fixed 32-bit value.
Definition sbgl_core.c:480
void sbgl_DrawIndirect(sbgl_Context *ctx, sbgl_Buffer buffer, size_t offset, uint32_t drawCount)
Submits a batch of draw calls stored in a GPU buffer.
Definition sbgl_core.c:720
sbgl_InitResult sbgl_Init(int w, int h, const char *title)
Initializes the engine and opens a window.
Definition sbgl_core.c:193
sbgl_Shader sbgl_LoadShaderFromFile(sbgl_Context *ctx, sbgl_ShaderStage stage, const char *filename)
Helper function to load a shader directly from a SPIR-V file.
Definition sbgl_core.c:534
void sbgl_SubmitDraw(sbgl_RenderQueue *queue, uint32_t mesh, uint32_t material, uint32_t blendMode, uint32_t sidedness, uint32_t tags, sbgl_SortKey key, const sbgl_InstanceData *data)
Appends a draw command to the render queue.
Definition sbgl_core.c:772
void sbgl_GetWindowSize(sbgl_Context *ctx, int *w, int *h)
Retrieves the current window dimensions.
Definition sbgl_core.c:252
sbgl_Telemetry sbgl_GetTelemetry(sbgl_Context *ctx)
Retrieves the performance telemetry data for the previous frame.
Definition sbgl_core.c:436
bool sbgl_WindowShouldClose(sbgl_Context *ctx)
Checks if the user or OS has requested to close the window.
Definition sbgl_core.c:238
sbgl_InitResult sbgl_InitWithConfig(const sbgl_InitConfig *config)
Initializes the engine and opens a window with explicit configuration.
Definition sbgl_core.c:112
const sbgl_InputState * sbgl_GetInputState(sbgl_Context *ctx)
Retrieves the input state for the current frame.
Definition sbgl_core.c:413
sbgl_Pipeline sbgl_CreatePipeline(sbgl_Context *ctx, const sbgl_PipelineConfig *config)
Creates a graphics pipeline.
Definition sbgl_core.c:587
void sbgl_DestroyShader(sbgl_Context *ctx, sbgl_Shader shader)
Destroys a shader module.
Definition sbgl_core.c:565
sbgl_Shader sbgl_LoadShader(sbgl_Context *ctx, sbgl_ShaderStage stage, const uint32_t *bytecode, size_t size)
Loads a shader from SPIR-V bytecode.
Definition sbgl_core.c:523
void sbgl_SetMouseMode(sbgl_Context *ctx, sbgl_MouseMode mode)
Sets the cursor behavior and visibility.
Definition sbgl_core.c:422
void sbgl_PushConstants(sbgl_Context *ctx, size_t size, const void *data)
Updates push constants for the currently bound pipeline.
Definition sbgl_core.c:733
void sbgl_EndDrawing(sbgl_Context *ctx)
Finalizes the current frame and presents it to the screen.
Definition sbgl_core.c:315
void sbgl_BindComputePipeline(sbgl_Context *ctx, sbgl_ComputePipeline pipeline)
Binds a compute pipeline for subsequent dispatch calls.
Definition sbgl_core.c:649
void sbgl_EndCompute(sbgl_Context *ctx)
Finalizes the compute phase.
Definition sbgl_core.c:385
sbgl_RenderQueue * sbgl_CreateRenderQueue(sbgl_Context *ctx, SblArena *arena)
Creates a thread-local render queue for collecting draw commands.
Definition sbgl_core.c:743
void sbgl_BindPipeline(sbgl_Context *ctx, sbgl_Pipeline pipeline)
Binds a graphics pipeline for subsequent draw calls.
Definition sbgl_core.c:673
void sbgl_DestroyBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Destroys a GPU buffer.
Definition sbgl_core.c:457
void sbgl_BeginCompute(sbgl_Context *ctx)
Prepares the engine for compute operations before the main drawing pass.
Definition sbgl_core.c:344
sbgl_ComputePipeline sbgl_CreateComputePipeline(sbgl_Context *ctx, sbgl_Shader shader)
Creates a compute pipeline.
Definition sbgl_core.c:619
void * sbgl_MapBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Maps a GPU buffer into the CPU's address space.
Definition sbgl_core.c:497
SblArena * sbgl_GetContextArena(sbgl_Context *ctx)
Retrieves the persistent arena associated with a context.
Definition sbgl_core.c:62
void sbgl_ClearResult(sbgl_Context *ctx)
Clears the result code to SBGL_SUCCESS.
Definition sbgl_core.c:103
void sbgl_MemoryBarrier(sbgl_Context *ctx, sbgl_BarrierType type)
Injects a memory barrier to synchronize compute and graphics operations.
Definition sbgl_core.c:665
void sbgl_UnmapBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Unmaps a previously mapped GPU buffer.
Definition sbgl_core.c:511
sbgl_ErrorDetail sbgl_GetErrorDetail(sbgl_Context *ctx)
Retrieves detailed error information including backend-specific codes.
Definition sbgl_core.c:78
void sbgl_DeviceWaitIdle(sbgl_Context *ctx)
Synchronizes the CPU with the GPU, waiting for all commands to complete.
Definition sbgl_core.c:391
uint32_t sbgl_GetFrameIndex(sbgl_Context *ctx)
Retrieves the current frame index for double/triple buffering.
Definition sbgl_core.c:489
void sbgl_DrawIndexed(sbgl_Context *ctx, uint32_t indexCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t instanceCount)
Submits an indexed draw command.
Definition sbgl_core.c:706
double sbgl_GetTime(sbgl_Context *ctx)
Retrieves the current monotonic system time in seconds.
Definition sbgl_core.c:245
void sbgl_Draw(sbgl_Context *ctx, uint32_t vertexCount, uint32_t firstVertex, uint32_t instanceCount)
Submits a non-indexed draw command.
Definition sbgl_core.c:698
uint64_t sbgl_GetBufferDeviceAddress(sbgl_Context *ctx, sbgl_Buffer buffer)
Retrieves the 64-bit GPU virtual address for a buffer.
Definition sbgl_core.c:689
void sbgl_Shutdown(sbgl_Context *ctx)
Gracefully shuts down the engine and releases all resources.
Definition sbgl_core.c:204
void sbgl_BeginDrawing(sbgl_Context *ctx)
Prepares the engine for a new frame of drawing.
Definition sbgl_core.c:262
void sbgl_BindBuffer(sbgl_Context *ctx, sbgl_Buffer buffer, sbgl_BufferUsage usage)
Binds a buffer to the pipeline.
Definition sbgl_core.c:681
void sbgl_DestroyPipeline(sbgl_Context *ctx, sbgl_Pipeline pipeline)
Destroys a graphics pipeline.
Definition sbgl_core.c:597
sbgl_Buffer sbgl_CreateBuffer(sbgl_Context *ctx, sbgl_BufferUsage usage, size_t size, const void *data)
Creates a GPU buffer.
Definition sbgl_core.c:445
void sbgl_RenderQueues(sbgl_Context *ctx, sbgl_RenderQueue **queues, uint32_t queueCount, const sbgl_Mat4 *viewProj)
Merges, sorts, and submits pending draw commands to the GPU.
Definition sbgl_core.c:808
@ SBGL_LOG_CAT_PLATFORM
@ SBGL_LOG_CAT_GFX
@ SBGL_LOG_CAT_CORE
@ SBGL_LOG_WARN
@ SBGL_LOG_ERROR
@ SBGL_LOG_INFO
#define sbgl_log_impl(level, category, msg)
static sbgl_Mat4 sbgl_Mat4Identity(void)
Returns an identity matrix.
Definition sbgl_math.h:200
static sbgl_Vec4 sbgl_Vec4Set(float x, float y, float z, float w)
Creates a Vec4.
Definition sbgl_math.h:98
Internal Platform Abstraction Layer (HAL).
uint64_t sbgl_os_GetPerfCount(sbgl_Window *window)
Gets the high-resolution performance counter.
void sbgl_os_PollEvents(sbgl_Window *window)
Dispatches OS events (messages/protocol requests).
void sbgl_os_SetCursorLocked(sbgl_Window *window, bool locked)
Locks or unlocks the cursor within the window bounds.
uint64_t sbgl_os_GetPerfFreq(sbgl_Window *window)
Gets the performance counter frequency.
void sbgl_os_SetCursorVisible(sbgl_Window *window, bool visible)
Sets the visibility of the OS cursor for the given window.
bool sbgl_os_WindowShouldClose(sbgl_Window *window)
Checks the window's close flag.
void sbgl_os_GetWindowSize(sbgl_Window *window, int *w, int *h)
Retrieves the current client area size.
sbgl_platform_Result sbgl_os_CreateWindow(struct SblArena *arena, sbgl_InputState *input, int width, int height, const char *title, sbgl_Window **outWindow)
Creates a native OS window.
bool sbgl_os_IsWindowFocused(sbgl_Window *window)
Checks if the window currently has input focus.
void sbgl_os_DestroyWindow(sbgl_Window *window)
Destroys a native window.
void sbgl_radix_sort(sbgl_SortKey *keys, uint32_t *values, uint32_t count, sbgl_SortKey *temp_keys, uint32_t *temp_values)
Performs a stable radix sort on an array of 64-bit keys and associated 32-bit values.
Definition sbgl_sort.c:8
Sorting utilities for the SBgl batching system.
sbgl_platform_Result
Result codes for platform layer operations.
Definition sbgl_types.h:233
@ SBGL_PLATFORM_SUCCESS
Definition sbgl_types.h:234
sbgl_BufferUsage
Buffer usage flags.
Definition sbgl_types.h:122
sbgl_Result
Result codes for engine operations.
Definition sbgl_types.h:214
@ SBGL_ERROR_DEVICE_BUSY
Definition sbgl_types.h:222
@ SBGL_ERROR_OUT_OF_MEMORY
Definition sbgl_types.h:221
@ SBGL_SUCCESS
Definition sbgl_types.h:215
@ SBGL_ERROR_WINDOW_CREATION_FAILED
Definition sbgl_types.h:219
@ SBGL_ERROR_GRAPHICS_FAILED
Definition sbgl_types.h:220
@ SBGL_ERROR_INVALID_ARGUMENT
Definition sbgl_types.h:217
@ SBGL_ERROR_NULL_CONTEXT
Definition sbgl_types.h:216
sbgl_BarrierType
Memory barrier types for compute synchronization.
Definition sbgl_types.h:142
uint32_t sbgl_Buffer
Handle for a GPU-side buffer.
Definition sbgl_types.h:37
uint32_t sbgl_Shader
Handle for a shader module.
Definition sbgl_types.h:42
uint64_t sbgl_SortKey
Bit-packed key used for sorting draw commands to minimize state changes.
Definition sbgl_types.h:57
sbgl_ShaderStage
Shader stage flags.
Definition sbgl_types.h:133
uint32_t sbgl_ComputePipeline
Handle for a compute pipeline.
Definition sbgl_types.h:52
uint32_t sbgl_Pipeline
Handle for a graphics pipeline.
Definition sbgl_types.h:47
@ SBGL_BACKEND_VULKAN
Definition sbgl_types.h:244
#define SBGL_PACK_HEADER(mesh, mat, blend, sided, tags)
Definition sbgl_types.h:82
#define SBGL_INVALID_HANDLE
Definition sbgl_types.h:8
Arena allocator implementation.
#define SBL_ARENA_PUSH_ARRAY_ZERO(arena, type, count)
Definition sbl_arena.h:23
SBL_ARENA_DEF void sbl_arena_reset(SblArena *arena)
#define SBL_ARENA_PUSH_STRUCT_ZERO(arena, type)
Definition sbl_arena.h:20
SBL_ARENA_DEF void sbl_arena_free(SblArena *arena)
SBL_ARENA_DEF bool sbl_arena_init(SblArena *arena, uint64_t initial_size)
#define SBL_ARENA_PUSH_ARRAY(arena, type, count)
Definition sbl_arena.h:21
Arena allocator.
Definition sbl_arena.h:47
Engine context.
Definition sbgl_types.h:268
void * inner
Opaque pointer to the internal engine state.
Definition sbgl_types.h:278
sbgl_Result result
Status of the last major operation.
Definition sbgl_types.h:286
Encapsulates all data required to submit a single draw call. Optimized for cache density (16 bytes).
Definition sbgl_types.h:97
sbgl_SortKey key
Definition sbgl_types.h:98
uint32_t header
Definition sbgl_types.h:99
Detailed error information for debugging.
Definition sbgl_types.h:253
sbgl_Result coreResult
Definition sbgl_types.h:255
Represents a slice of a persistent GPU buffer used for transient data.
Standard Vulkan Indirect Draw command layout.
Definition sbgl_types.h:111
Configuration for engine initialization.
Definition sbgl_types.h:308
uint32_t windowWidth
Definition sbgl_types.h:309
sbgl_ResourceLimits limits
Definition sbgl_types.h:312
const char * windowTitle
Definition sbgl_types.h:311
uint32_t windowHeight
Definition sbgl_types.h:310
Result structure for initialization.
Definition sbgl_types.h:339
sbgl_Context * ctx
Definition sbgl_types.h:340
sbgl_Result error
Definition sbgl_types.h:341
Represents the real-time state of physical inputs.
Definition sbgl_input.h:165
bool keysPressed[SBGL_SCANCODE_MAX]
Definition sbgl_input.h:167
Per-instance data for automated batching.
Definition sbgl_types.h:19
sbgl_Mat4 transform
Definition sbgl_types.h:20
sbgl_Vec4 color
Definition sbgl_types.h:21
Internal state for the engine context.
Definition sbgl_core.c:40
uint64_t frameStartTicks
Definition sbgl_core.c:58
sbgl_Telemetry lastFrame
Definition sbgl_core.c:55
sbgl_Telemetry currentFrame
Definition sbgl_core.c:56
sbgl_GfxContext * gfx
Definition sbgl_core.c:44
uint32_t hasStartedFrame
Definition sbgl_core.c:48
sbgl_Window * window
Definition sbgl_core.c:43
struct sbgl_InternalContext::@8 state
sbgl_MouseMode mouseMode
Definition sbgl_core.c:54
SblArena transientArena
Definition sbgl_core.c:42
sbgl_InputState input
Definition sbgl_core.c:53
uint64_t sortStartTicks
Definition sbgl_core.c:59
4x4 Matrix, 16-byte aligned, column-major.
Definition sbgl_math.h:83
Configuration for creating a graphics pipeline.
Definition sbgl_types.h:204
Internal storage for draw packets awaiting submission.
Definition sbgl_core.c:25
uint32_t capacity
Definition sbgl_core.c:29
uint32_t count
Definition sbgl_core.c:28
sbgl_InstanceData * instances
Definition sbgl_core.c:27
struct SblArena * arena
Definition sbgl_core.c:30
sbgl_DrawPacket * packets
Definition sbgl_core.c:26
Performance telemetry data for a single frame.
Definition sbgl_types.h:175
float gpu_render_time
Definition sbgl_types.h:178
float cpu_sort_time
Definition sbgl_types.h:177
uint32_t draw_calls
Definition sbgl_types.h:179
uint32_t instance_count
Definition sbgl_types.h:180
float cpu_frame_time
Definition sbgl_types.h:176
Native X11 window state.