Pico Headers
Loading...
Searching...
No Matches
pico_gfx.h
Go to the documentation of this file.
1
139#ifndef PICO_GFX_H
140#define PICO_GFX_H
141
142// Backend conversion macros
143#if defined (PICO_GFX_GL)
144 #define SOKOL_GLCORE
145#elif defined (PICO_GFX_GLES)
146 #define SOKOL_GLES3
147#elif defined (PICO_GFX_D3D)
148 #define SOKOL_D3D11
149#elif defined (PICO_GFX_METAL)
150 #define SOKOL_METAL
151#elif defined (PICO_GFX_WEBGPU)
152 #define SOKOL_WGPU
153#else
154 #error "GFX backend must be specified"
155#endif
156
157#include "sokol_gfx.h"
158
159#include <stdbool.h>
160#include <stddef.h>
161#include <stdint.h>
162
163#define PG_MAX_VERTEX_ATTRIBUTES SG_MAX_VERTEX_ATTRIBUTES
164#define PG_MAX_VERTEX_BUFFERS SG_MAX_VERTEXBUFFER_BINDSLOTS
165#define PG_MAX_TEXTURE_SLOTS SG_MAX_IMAGE_BINDSLOTS
166#define PG_MAX_SAMPLER_SLOTS SG_MAX_SAMPLER_BINDSLOTS
167
179
192
210
221
236
240typedef struct pg_ctx_t pg_ctx_t;
241
246
251
256
261
266
276void pg_init(void);
277
283void pg_shutdown(void);
284
290pg_ctx_t* pg_create_context(int window_width, int window_height, void* mem_ctx);
291
296
301
309void pg_set_window_size(pg_ctx_t* ctx, int width, int height, bool reset);
310
314void pg_get_window_size(pg_ctx_t* ctx, int* width, int* height);
315
325void pg_begin_pass(pg_ctx_t* ctx, pg_texture_t* target, bool clear);
326
331
338
346
351
355void pg_set_clear_color(pg_ctx_t* ctx, float r, float g, float b, float a);
356
361
365void pg_set_viewport(pg_ctx_t* ctx, int x, int y, int w, int h);
366
371
375void pg_set_scissor(pg_ctx_t* ctx, int x, int y, int w, int h);
376
381
388
393
397void pg_bind_buffer(pg_ctx_t* ctx, int slot, pg_buffer_t* buffer);
398
403
410
415
422void pg_bind_texture(pg_shader_t* shader, const char* name, pg_texture_t* texture);
423
428
435void pg_bind_sampler(pg_shader_t* shader, const char* name, pg_sampler_t* sampler);
436
441
446
453#define pg_create_shader(ctx, prefix) \
454 pg_create_shader_internal( \
455 ctx, \
456 (pg_shader_internal_t) \
457 { \
458 prefix##_shader_desc, \
459 prefix##_attr_slot, \
460 prefix##_image_slot, \
461 prefix##_sampler_slot, \
462 prefix##_uniformblock_slot, \
463 prefix##_uniformblock_size, \
464 } \
465 )
466
471
475uint32_t pg_get_shader_id(const pg_shader_t* shader);
476
483void pg_set_uniform_block(pg_shader_t* shader, const char* name, const void* data);
484
509
513typedef struct
514{
516 int step;
518
528
537
550
558 pg_shader_t* shader,
559 const pg_pipeline_opts_t* opts);
560
565
570
575
583
594 int width, int height,
595 const uint8_t* data, size_t size,
596 const pg_texture_opts_t* opts);
597
606 int width, int height,
607 const pg_texture_opts_t* opts);
608
613
617uint32_t pg_get_texture_id(const pg_texture_t* texture);
618
622void pg_get_texture_size(const pg_texture_t* texture, int* width, int* height);
623
628void pg_update_texture(pg_texture_t* texture, char* data, int width, int height);
629
633typedef struct
634{
635 bool smooth;
636 bool repeat_u;
637 bool repeat_v;
639
646
651
661
671 pg_buffer_usage_t usage,
672 const void* data,
673 size_t count,
674 size_t max_elements,
675 size_t element_size);
676
685 pg_buffer_usage_t usage,
686 const void* data,
687 size_t count,
688 size_t max_elements);
689
694
699void pg_update_buffer(pg_buffer_t* buffer, void* data, size_t count);
700
705int pg_append_buffer(pg_buffer_t* buffer, void* data, size_t count);
706
711
715void pg_set_buffer_offset(pg_buffer_t* buffer, int offset);
716
721
729void pg_draw(const pg_ctx_t* ctx, size_t start, size_t count, size_t instances);
730
731/*=============================================================================
732 * Internals
733 *============================================================================*/
734
735typedef struct
736{
737 const sg_shader_desc* (*get_shader_desc)(sg_backend backend);
738 int (*get_attr_slot)(const char* attr_name);
739 int (*get_img_slot)(const char* name);
740 int (*get_smp_slot)(const char* name);
741 int (*get_uniformblock_slot)(const char* ub_name);
742 size_t (*get_uniformblock_size)(const char* ub_name);
744
746
747#endif // PICO_GFX_H
748
749/*=============================================================================
750 * Implementation
751 *============================================================================*/
752
753#ifdef PICO_GFX_IMPLEMENTATION
754
755#include <string.h>
756
757/*=============================================================================
758 * Constants
759 *============================================================================*/
760
761#ifndef PICO_GFX_STACK_MAX_SIZE
762#define PICO_GFX_STACK_MAX_SIZE 16
763#endif
764
765#ifndef PICO_GFX_HASHTABLE_KEY_SIZE
766#define PICO_GFX_HASHTABLE_KEY_SIZE 16
767#endif
768
769/*=============================================================================
770 * Macros
771 *============================================================================*/
772
773#ifdef NDEBUG
774 #define PICO_GFX_ASSERT(expr) ((void)0)
775#else
776 #ifndef PICO_GFX_ASSERT
777 #include <assert.h>
778 #define PICO_GFX_ASSERT(expr) (assert(expr))
779 #endif
780#endif
781
782#if !defined(PICO_GFX_MALLOC) || !defined(PICO_GFX_REALLOC) || !defined(PICO_GFX_FREE)
783#include <stdlib.h>
784#define PICO_GFX_MALLOC(size, ctx) (malloc(size))
785#define PICO_GFX_REALLOC(ptr, size, ctx) (realloc(ptr, size))
786#define PICO_GFX_FREE(ptr, ctx) (free(ptr))
787#endif
788
789#ifndef PICO_GFX_LOG
790 #include <stdio.h>
791 #define PICO_GFX_LOG(...) (pg_log(__VA_ARGS__))
792#endif
793
794/*=============================================================================
795 * GFX Static Funtions
796 *============================================================================*/
797
798static void pg_alloc_uniform_block(pg_shader_t* shader, const char* name);
799
800typedef enum
801{
802 PG_BUFFER_TYPE_VERTEX,
803 PG_BUFFER_TYPE_INDEX,
804} pg_buffer_type_t;
805
806static sg_primitive_type pg_map_primitive(pg_primitive_t primitive);
807static sg_blend_factor pg_map_blend_factor(pg_blend_factor_t factor);
808static sg_blend_op pg_map_blend_eq(pg_blend_eq_t eq);
809static sg_vertex_format pg_map_vertex_format(pg_vertex_format_t format);
810static sg_usage pg_map_usage(pg_buffer_usage_t format);
811static sg_buffer_type pg_map_buffer_type(pg_buffer_type_t type);
812
813static void pg_log_sg(const char* tag, // e.g. 'sg'
814 uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info
815 uint32_t log_item_id, // SG_LOGITEM_*
816 const char* message_or_null, // a message string, may be nullptr in release mode
817 uint32_t line_nr, // line number in sokol_gfx.h
818 const char* filename_or_null, // source filename, may be nullptr in release mode
819 void* user_data);
820
821static void pg_log(const char* fmt, ...);
822
823/*=============================================================================
824 * Hashtable Declarations
825 *============================================================================*/
826
827#ifdef PICO_GFX_32BIT
828 typedef uint32_t pg_hash_t;
829#else
830 typedef uint64_t pg_hash_t;
831#endif
832
833typedef struct pg_hashtable_t pg_hashtable_t;
834typedef struct pg_hashtable_iterator_t pg_hashtable_iterator_t;
835
836typedef void (*pg_hashtable_iterator_fn)(pg_hashtable_iterator_t* iterator,
837 char* key,
838 void* value);
839
840static pg_hashtable_t* pg_hashtable_new(size_t capacity, size_t key_size,
841 size_t value_size, void* mem_ctx);
842
843static void pg_hashtable_free(pg_hashtable_t* ht);
844
845static void pg_hashtable_init_iterator(const pg_hashtable_t* ht,
846 pg_hashtable_iterator_t* iterator);
847
848static bool pg_hashtable_iterator_next(pg_hashtable_iterator_t* iterator,
849 char** key, void** value);
850
851static void pg_hashtable_put(pg_hashtable_t* ht,
852 const char* key,
853 const void* value);
854
855static void* pg_hashtable_get(const pg_hashtable_t* ht, const char* key);
856
857struct pg_hashtable_iterator_t
858{
859 const pg_hashtable_t* ht;
860 size_t index;
861 size_t count;
862};
863
864/*=============================================================================
865 * Arena Allocator Declarations
866 *============================================================================*/
867
868typedef struct pg_arena_t pg_arena_t;
869
870static pg_arena_t* pg_arena_create(size_t block_size, void* mem_ctx);
871static void* pg_arena_malloc(pg_arena_t* arena, size_t size);
872//static void pg_arena_reset(pg_arena_t* arena); // Not currently used
873static void pg_arena_destroy(pg_arena_t* arena);
874
875/*=============================================================================
876 * GFX Public API Implementation
877 *============================================================================*/
878
879static void* pg_malloc(size_t size, void* ctx)
880{
881 (void)ctx;
882 return PICO_GFX_MALLOC(size, ctx);
883}
884
885static void pg_free(void* ptr, void* ctx)
886{
887 (void)ctx;
888 PICO_GFX_FREE(ptr, ctx);
889}
890
891typedef struct pg_rect_t
892{
893 int x, y, width, height;
894} pg_rect_t;
895
896typedef struct pg_state_t
897{
898 sg_color clear_color;
899 pg_pipeline_t* pipeline;
900 pg_rect_t viewport;
901 pg_rect_t scissor;
902 pg_shader_t* shader;
903 pg_buffer_t* index_buffer;
905} pg_state_t;
906
907struct pg_ctx_t
908{
909 void* mem_ctx;
910 sg_swapchain swapchain;
911 int window_width;
912 int window_height;
913 bool indexed;
914 bool pass_active;
915 pg_texture_t* target;
916 sg_pass default_pass;
917 pg_state_t state;
918 pg_state_t state_stack[PICO_GFX_STACK_MAX_SIZE];
919 int stack_size;
920};
921
922struct pg_pipeline_t
923{
924 pg_ctx_t* ctx;
925 sg_pipeline handle;
926 size_t element_size;
927 bool indexed;
928 bool blend_enabled;
929 pg_blend_mode_t blend_mode;
930 pg_shader_t* shader;
931};
932
933struct pg_shader_t
934{
935 pg_ctx_t* ctx;
936 const sg_shader_desc* desc;
937 sg_shader handle;
938 pg_shader_internal_t internal;
941 pg_hashtable_t* uniform_blocks;
942 pg_arena_t* arena;
943};
944
945typedef struct
946{
947 int slot;
948 void* data;
949 size_t size;
950} pg_uniform_block_t;
951
952struct pg_texture_t
953{
954 pg_ctx_t* ctx;
955 int width, height;
956 bool target;
957 sg_image handle;
958 sg_image depth_handle;
959 sg_attachments attachments;
960};
961
962struct pg_sampler_t
963{
964 pg_ctx_t* ctx;
965 sg_sampler handle;
966};
967
968struct pg_buffer_t
969{
970 pg_ctx_t* ctx;
971 sg_buffer handle;
972 pg_buffer_type_t type;
973 pg_buffer_usage_t usage;
974 size_t count;
975 size_t element_size;
976 size_t size;
977 size_t offset;
978};
979
980void pg_init(void)
981{
982 sg_setup(&(sg_desc)
983 {
984 .logger.func = pg_log_sg,
985 .allocator =
986 {
987 .alloc_fn = pg_malloc,
988 .free_fn = pg_free,
989 .user_data = NULL,
990 },
991 .environment.defaults.color_format = SG_PIXELFORMAT_RGBA8,
992 });
993}
994
995void pg_shutdown(void)
996{
997 sg_shutdown();
998}
999
1000pg_ctx_t* pg_create_context(int window_width, int window_height, void* mem_ctx)
1001{
1002 pg_ctx_t* ctx = PICO_GFX_MALLOC(sizeof(pg_ctx_t), mem_ctx);
1003
1004 if (!ctx) return NULL;
1005
1006 memset(ctx, 0, sizeof(pg_ctx_t));
1007
1008 ctx->mem_ctx = mem_ctx;
1009 ctx->window_width = window_width;
1010 ctx->window_height = window_height;
1011
1012 pg_reset_state(ctx);
1013
1014 ctx->swapchain = (sg_swapchain)
1015 {
1016 .width = window_width,
1017 .height = window_height,
1018 };
1019
1020 return ctx;
1021}
1022
1024{
1025 PICO_GFX_ASSERT(ctx);
1026 PICO_GFX_FREE(ctx, ctx->mem_ctx);
1027}
1028
1030{
1031 #if defined (PICO_GFX_GL)
1032 return PG_BACKEND_GL;
1033 #elif defined (PICO_GFX_GLES)
1034 return PG_BACKEND_GLES;
1035 #elif defined (PICO_GFX_D3D)
1036 return PG_BACKEND_D3D;
1037 #elif defined (PICO_GFX_METAL)
1038 return PG_BACKEND_METAL;
1039 #elif defined (PICO_GFX_WEBGPU)
1040 return PG_BACKEND_WGPU;
1041 #else
1042 #error "Unknown GFX backend"
1043 #endif
1044}
1045
1046void pg_set_window_size(pg_ctx_t* ctx, int width, int height, bool reset)
1047{
1048 ctx->swapchain.width = ctx->window_width = width;
1049 ctx->swapchain.height = ctx->window_height = height;
1050
1051 if (reset)
1052 {
1053 pg_reset_viewport(ctx);
1054 pg_reset_scissor(ctx);
1055 }
1056}
1057
1058void pg_get_window_size(pg_ctx_t* ctx, int* width, int* height)
1059{
1060 if (width)
1061 *width = ctx->window_width;
1062
1063 if (height)
1064 *height = ctx->window_height;
1065}
1066
1067void pg_begin_pass(pg_ctx_t* ctx, pg_texture_t* target, bool clear)
1068{
1069 PICO_GFX_ASSERT(ctx);
1070 PICO_GFX_ASSERT(!ctx->pass_active);
1071
1072 sg_pass_action action = { 0 };
1073
1074 if (clear)
1075 {
1076 sg_color color = ctx->state.clear_color;
1077
1078 action.colors[0] = (sg_color_attachment_action)
1079 {
1080 .load_action = SG_LOADACTION_CLEAR,
1081 .clear_value = color
1082 };
1083 }
1084
1085 sg_pass pass = { .action = action };
1086
1087 if (target)
1088 {
1089 pass.attachments = target->attachments;
1090 ctx->target = target;
1091 }
1092 else
1093 {
1094 //TODO: Do more research on swapchains
1095 pass.swapchain = ctx->swapchain;
1096 }
1097
1098 sg_begin_pass(&pass);
1099
1100 pg_reset_viewport(ctx);
1101 pg_reset_scissor(ctx);
1102
1103 ctx->pass_active = true;
1104}
1105
1106void pg_end_pass(pg_ctx_t* ctx)
1107{
1108 sg_end_pass();
1109 ctx->target = NULL;
1110 ctx->pass_active = false;
1111}
1112
1113void pg_flush(pg_ctx_t* ctx)
1114{
1115 (void)ctx;
1116 sg_commit();
1117}
1118
1119void pg_push_state(pg_ctx_t* ctx)
1120{
1121 PICO_GFX_ASSERT(ctx);
1122 PICO_GFX_ASSERT(ctx->stack_size < PICO_GFX_STACK_MAX_SIZE);
1123
1124 ctx->state_stack[ctx->stack_size] = ctx->state;
1125 ctx->stack_size++;
1126}
1127
1128void pg_pop_state(pg_ctx_t* ctx)
1129{
1130 PICO_GFX_ASSERT(ctx);
1131 PICO_GFX_ASSERT(ctx->stack_size > 0);
1132
1133 ctx->state = ctx->state_stack[ctx->stack_size - 1];
1134 ctx->stack_size--;
1135}
1136
1137void pg_set_clear_color(pg_ctx_t* ctx, float r, float g, float b, float a)
1138{
1139 PICO_GFX_ASSERT(ctx);
1140 ctx->state.clear_color = (sg_color){ r, g, b, a};
1141}
1142
1144{
1145 PICO_GFX_ASSERT(ctx);
1146 pg_set_clear_color(ctx, 0.f, 0.f, 0.f, 0.f);
1147}
1148
1149void pg_set_viewport(pg_ctx_t* ctx, int x, int y, int w, int h)
1150{
1151 PICO_GFX_ASSERT(ctx);
1152 ctx->state.viewport = (pg_rect_t){ x, y, w, h};
1153}
1154
1155void pg_reset_viewport(pg_ctx_t* ctx)
1156{
1157 PICO_GFX_ASSERT(ctx);
1158
1159 if (ctx->target)
1160 {
1161 const pg_texture_t* texture = ctx->target;
1162 pg_set_viewport(ctx, 0, 0, texture->width, texture->height);
1163 }
1164 else
1165 {
1166 pg_set_viewport(ctx, 0, 0, ctx->window_width, ctx->window_height);
1167 }
1168}
1169
1170void pg_set_scissor(pg_ctx_t* ctx, int x, int y, int w, int h)
1171{
1172 PICO_GFX_ASSERT(ctx);
1173 ctx->state.scissor = (pg_rect_t){ x, y, w, h};
1174}
1175
1176void pg_reset_scissor(pg_ctx_t* ctx)
1177{
1178 PICO_GFX_ASSERT(ctx);
1179
1180 if (ctx->target)
1181 {
1182 const pg_texture_t* texture = ctx->target;
1183 pg_set_scissor(ctx, 0, 0, texture->width, texture->height);
1184 }
1185 else
1186 {
1187 pg_set_scissor(ctx, 0, 0, ctx->window_width, ctx->window_height);
1188 }
1189}
1190
1191void pg_set_pipeline(pg_ctx_t* ctx, pg_pipeline_t* pipeline)
1192{
1193 PICO_GFX_ASSERT(ctx);
1194 ctx->state.pipeline = pipeline;
1195}
1196
1197void pg_reset_pipeline(pg_ctx_t* ctx)
1198{
1199 PICO_GFX_ASSERT(ctx);
1200 pg_set_pipeline(ctx, NULL);
1201}
1202
1203void pg_bind_buffer(pg_ctx_t* ctx, int slot, pg_buffer_t* buffer)
1204{
1205 PICO_GFX_ASSERT(ctx);
1206 PICO_GFX_ASSERT(!buffer || buffer->type == PG_BUFFER_TYPE_VERTEX);
1207
1208 PICO_GFX_ASSERT(slot >= 0);
1209 PICO_GFX_ASSERT(slot < PG_MAX_VERTEX_BUFFERS);
1210
1211 ctx->state.buffers[slot] = buffer;
1212}
1213
1214void pg_reset_buffers(pg_ctx_t* ctx)
1215{
1216 PICO_GFX_ASSERT(ctx);
1217 memset(&ctx->state.buffers, 0, sizeof(ctx->state.buffers));
1218}
1219
1220void pg_set_index_buffer(pg_ctx_t* ctx, pg_buffer_t* buffer)
1221{
1222 PICO_GFX_ASSERT(ctx);
1223 PICO_GFX_ASSERT(!buffer || buffer->type == PG_BUFFER_TYPE_INDEX);
1224 ctx->state.index_buffer = buffer;
1225}
1226
1228{
1229 PICO_GFX_ASSERT(ctx);
1230 ctx->state.index_buffer = NULL;
1231}
1232
1233void pg_bind_texture(pg_shader_t* shader, const char* name, pg_texture_t* texture)
1234{
1235 PICO_GFX_ASSERT(shader);
1236 PICO_GFX_ASSERT(name);
1237
1238 int slot = shader->internal.get_img_slot(name);
1239
1240 PICO_GFX_ASSERT(slot >= 0);
1241 PICO_GFX_ASSERT(slot < PG_MAX_TEXTURE_SLOTS);
1242
1243 shader->textures[slot] = texture;
1244}
1245
1246void pg_reset_textures(pg_shader_t* shader)
1247{
1248 PICO_GFX_ASSERT(shader);
1249 memset(shader->textures, 0, sizeof(shader->textures));
1250}
1251
1252void pg_bind_sampler(pg_shader_t* shader, const char* name, pg_sampler_t* sampler)
1253{
1254 PICO_GFX_ASSERT(shader);
1255 PICO_GFX_ASSERT(name);
1256
1257 int slot = shader->internal.get_smp_slot(name);
1258
1259 PICO_GFX_ASSERT(slot >= 0);
1260 PICO_GFX_ASSERT(slot < PG_MAX_TEXTURE_SLOTS);
1261
1262 shader->samplers[slot] = sampler;
1263}
1264
1265void pg_reset_samplers(pg_shader_t* shader)
1266{
1267 PICO_GFX_ASSERT(shader);
1268 memset(shader->samplers, 0, sizeof(shader->samplers));
1269}
1270
1271void pg_reset_state(pg_ctx_t* ctx)
1272{
1273 PICO_GFX_ASSERT(ctx);
1274
1275 memset(&ctx->state, 0, sizeof(pg_state_t));
1276
1278 pg_reset_pipeline(ctx);
1279 pg_reset_viewport(ctx);
1280 pg_reset_scissor(ctx);
1281 pg_reset_buffers(ctx);
1283}
1284
1285static void pg_set_attributes(const pg_pipeline_layout_t* layout, sg_pipeline_desc* desc)
1286{
1287 for (int slot = 0; slot < PG_MAX_VERTEX_ATTRIBUTES; slot++)
1288 {
1289 if (layout->attrs[slot].format != PG_VERTEX_FORMAT_INVALID)
1290 {
1291 desc->layout.attrs[slot] = (sg_vertex_attr_state)
1292 {
1293 .format = pg_map_vertex_format(layout->attrs[slot].format),
1294 .offset = layout->attrs[slot].offset,
1295 .buffer_index = layout->attrs[slot].buffer_index,
1296 };
1297 }
1298 }
1299}
1300
1301static void pg_set_buffers(const pg_pipeline_layout_t* layout, sg_pipeline_desc* desc)
1302{
1303 for (int slot = 0; slot < PG_MAX_VERTEX_BUFFERS; slot++)
1304 {
1305 int step = layout->bufs[slot].step;
1306
1307 if (layout->bufs[slot].instanced)
1308 {
1309 desc->layout.buffers[slot] = (sg_vertex_buffer_layout_state)
1310 {
1311 .step_func = SG_VERTEXSTEP_PER_INSTANCE,
1312 };
1313 }
1314
1315 desc->layout.buffers[slot].step_rate = (step >= 1) ? step : 1;
1316 }
1317}
1318
1320 pg_shader_t* shader,
1321 const pg_pipeline_opts_t* opts)
1322{
1323 PICO_GFX_ASSERT(shader);
1324 PICO_GFX_ASSERT(opts);
1325
1326 pg_pipeline_t* pipeline = PICO_GFX_MALLOC(sizeof(pg_pipeline_t), ctx->mem_ctx);
1327
1328 sg_pipeline_desc desc = { 0 };
1329
1330 pg_set_attributes(&opts->layout, &desc);
1331 pg_set_buffers(&opts->layout, &desc);
1332
1333 desc.primitive_type = pg_map_primitive(opts->primitive);
1334
1335 pipeline->blend_enabled = false;
1336
1337 if (opts->blend_enabled)
1338 {
1339 pipeline->blend_enabled = true;
1340 pipeline->blend_mode = opts->blend;
1341
1342 const pg_blend_mode_t* blend_mode = &opts->blend;
1343 desc.colors[0].blend.enabled = true;
1344 desc.colors[0].blend.src_factor_rgb = pg_map_blend_factor(blend_mode->color_src);
1345 desc.colors[0].blend.dst_factor_rgb = pg_map_blend_factor(blend_mode->color_dst);
1346 desc.colors[0].blend.src_factor_alpha = pg_map_blend_factor(blend_mode->alpha_src);
1347 desc.colors[0].blend.dst_factor_alpha = pg_map_blend_factor(blend_mode->alpha_dst);
1348 desc.colors[0].blend.op_rgb = pg_map_blend_eq(blend_mode->color_eq);
1349 desc.colors[0].blend.op_alpha = pg_map_blend_eq(blend_mode->alpha_eq);
1350 }
1351
1352 desc.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8;
1353
1354 if (opts->indexed)
1355 desc.index_type = SG_INDEXTYPE_UINT32;
1356 else
1357 desc.index_type = SG_INDEXTYPE_NONE;
1358
1359 if (opts->target)
1360 {
1361 desc.depth.pixel_format = SG_PIXELFORMAT_DEPTH;
1362 desc.depth.write_enabled = true;
1363 }
1364
1365 //TODO: Culling
1366
1367 desc.face_winding = SG_FACEWINDING_CCW;
1368 desc.shader = shader->handle;
1369
1370 pipeline->ctx = ctx;
1371 pipeline->handle = sg_make_pipeline(&desc);
1372 pipeline->indexed = opts->indexed;
1373 pipeline->shader = shader;
1374
1375 PICO_GFX_ASSERT(sg_query_pipeline_state(pipeline->handle) == SG_RESOURCESTATE_VALID);
1376
1377 return pipeline;
1378}
1379
1380void pg_destroy_pipeline(pg_pipeline_t* pipeline)
1381{
1382 PICO_GFX_ASSERT(pipeline);
1383 sg_destroy_pipeline(pipeline->handle);
1384 PICO_GFX_FREE(pipeline, pipeline->ctx->mem_ctx);
1385}
1386
1387pg_shader_t* pg_get_shader(const pg_pipeline_t* pipeline)
1388{
1389 PICO_GFX_ASSERT(pipeline);
1390 return pipeline->shader;
1391}
1392
1393const pg_blend_mode_t* pg_get_blend_mode(const pg_pipeline_t* pipeline)
1394{
1395 PICO_GFX_ASSERT(pipeline);
1396
1397 if (pipeline->blend_enabled)
1398 return &pipeline->blend_mode;
1399 else
1400 return NULL;
1401}
1402
1404{
1405 pg_shader_t* shader = PICO_GFX_MALLOC(sizeof(pg_shader_t), ctx->mem_ctx);
1406
1407 pg_reset_textures(shader);
1408 pg_reset_samplers(shader);
1409
1410 shader->ctx = ctx;
1411 shader->internal = internal;
1412 shader->desc = internal.get_shader_desc(sg_query_backend());
1413
1414 PICO_GFX_ASSERT(shader->desc);
1415
1416 shader->handle = sg_make_shader(shader->desc);
1417
1418 shader->uniform_blocks = pg_hashtable_new(16, PICO_GFX_HASHTABLE_KEY_SIZE,
1419 sizeof(pg_uniform_block_t),
1420 ctx->mem_ctx);
1421
1422 shader->arena = pg_arena_create(512, ctx->mem_ctx);
1423
1424 return shader;
1425}
1426
1427void pg_destroy_shader(pg_shader_t* shader)
1428{
1429 PICO_GFX_ASSERT(shader);
1430 sg_destroy_shader(shader->handle);
1431 pg_hashtable_free(shader->uniform_blocks);
1432 pg_arena_destroy(shader->arena);
1433 PICO_GFX_FREE(shader, shader->ctx->mem_ctx);
1434}
1435
1436uint32_t pg_get_shader_id(const pg_shader_t* shader)
1437{
1438 PICO_GFX_ASSERT(shader);
1439 return shader->handle.id;
1440}
1441
1443 const char* name,
1444 const void* data)
1445{
1446 PICO_GFX_ASSERT(shader);
1447 PICO_GFX_ASSERT(name);
1448 PICO_GFX_ASSERT(data);
1449
1450 pg_uniform_block_t* block = pg_hashtable_get(shader->uniform_blocks, name);
1451
1452 if (!block)
1453 {
1454 pg_alloc_uniform_block(shader, name);
1455 block = pg_hashtable_get(shader->uniform_blocks, name);
1456 }
1457
1458 PICO_GFX_ASSERT(block);
1459
1460 memcpy(block->data, data, block->size);
1461}
1462
1464 int width, int height,
1465 const uint8_t* data, size_t size,
1466 const pg_texture_opts_t* opts)
1467{
1468 PICO_GFX_ASSERT(width > 0);
1469 PICO_GFX_ASSERT(height > 0);
1470 PICO_GFX_ASSERT(data);
1471 PICO_GFX_ASSERT(size > 0);
1472
1473 if (opts == NULL)
1474 opts = &(pg_texture_opts_t){ 0 };
1475
1476 PICO_GFX_ASSERT(opts->mipmaps >= 0);
1477
1478 pg_texture_t* texture = PICO_GFX_MALLOC(sizeof(pg_texture_t), ctx->mem_ctx);
1479
1480 sg_image_desc desc = { 0 };
1481
1482 desc.pixel_format = SG_PIXELFORMAT_RGBA8;
1483
1484 desc.width = texture->width = width;
1485 desc.height = texture->height = height;
1486
1487 desc.num_mipmaps = opts->mipmaps;
1488 desc.data.subimage[0][0] = (sg_range){ .ptr = data, .size = size };
1489
1490 texture->ctx = ctx;
1491 texture->target = false;
1492 texture->handle = sg_make_image(&desc);
1493
1494 PICO_GFX_ASSERT(sg_query_image_state(texture->handle) == SG_RESOURCESTATE_VALID);
1495
1496 return texture;
1497}
1498
1500 int width, int height,
1501 const pg_texture_opts_t* opts)
1502{
1503 PICO_GFX_ASSERT(width > 0);
1504 PICO_GFX_ASSERT(height > 0);
1505
1506 if (opts == NULL)
1507 opts = &(pg_texture_opts_t){ 0 };
1508
1509 PICO_GFX_ASSERT(opts->mipmaps >= 0);
1510
1511 pg_texture_t* texture = PICO_GFX_MALLOC(sizeof(pg_texture_t), ctx->mem_ctx);
1512
1513 sg_image_desc desc = { 0 };
1514
1515 desc.render_target = true;
1516 desc.pixel_format = SG_PIXELFORMAT_RGBA8;
1517
1518 desc.width = texture->width = width;
1519 desc.height = texture->height = height;
1520
1521 desc.num_mipmaps = opts->mipmaps;
1522
1523 texture->ctx = ctx;
1524 texture->handle = sg_make_image(&desc);
1525 texture->target = true;
1526
1527 PICO_GFX_ASSERT(sg_query_image_state(texture->handle) == SG_RESOURCESTATE_VALID);
1528
1529 desc.pixel_format = SG_PIXELFORMAT_DEPTH;
1530 texture->depth_handle = sg_make_image(&desc);
1531
1532 PICO_GFX_ASSERT(sg_query_image_state(texture->depth_handle) == SG_RESOURCESTATE_VALID);
1533
1534 //TODO: Research multiple color attachments
1535 texture->attachments = sg_make_attachments(&(sg_attachments_desc)
1536 {
1537 .colors[0].image = texture->handle,
1538 .depth_stencil.image = texture->depth_handle,
1539 });
1540
1541 return texture;
1542}
1543
1544void pg_destroy_texture(pg_texture_t* texture)
1545{
1546 if (texture->target)
1547 {
1548 sg_destroy_image(texture->depth_handle);
1549 }
1550
1551 sg_destroy_image(texture->handle);
1552 PICO_GFX_FREE(texture, texture->ctx->mem_ctx);
1553}
1554
1555void pg_update_texture(pg_texture_t* texture, char* data, int width, int height)
1556{
1557 // NOTE: Replaces all existing data
1558 sg_image_data img_data = { 0 };
1559 img_data.subimage[0][0].ptr = data;
1560 img_data.subimage[0][0].size = (size_t)(width * height);
1561 sg_update_image(texture->handle, &img_data);
1562}
1563
1564uint32_t pg_get_texture_id(const pg_texture_t* texture)
1565{
1566 PICO_GFX_ASSERT(texture);
1567 return texture->handle.id;
1568}
1569
1570void pg_get_texture_size(const pg_texture_t* texture, int* width, int* height)
1571{
1572 PICO_GFX_ASSERT(texture);
1573
1574 if (width)
1575 *width = texture->width;
1576
1577 if (height)
1578 *height = texture->height;
1579}
1580
1582{
1583 if (opts == NULL)
1584 opts = &(pg_sampler_opts_t){ 0 };
1585
1586 pg_sampler_t* sampler = PICO_GFX_MALLOC(sizeof(pg_sampler_t), ctx->mem_ctx);
1587
1588 sg_sampler_desc desc = { 0 };
1589
1590 desc.min_filter = (opts->smooth) ? SG_FILTER_LINEAR : SG_FILTER_NEAREST;
1591 desc.mag_filter = (opts->smooth) ? SG_FILTER_LINEAR : SG_FILTER_NEAREST;
1592
1593 desc.wrap_u = (opts->repeat_u) ? SG_WRAP_REPEAT : SG_WRAP_CLAMP_TO_EDGE;
1594 desc.wrap_v = (opts->repeat_v) ? SG_WRAP_REPEAT : SG_WRAP_CLAMP_TO_EDGE;
1595
1596 sampler->ctx = ctx;
1597 sampler->handle = sg_make_sampler(&desc);
1598
1599 return sampler;
1600}
1601
1602void pg_destroy_sampler(pg_sampler_t* sampler)
1603{
1604 sg_destroy_sampler(sampler->handle);
1605 PICO_GFX_FREE(sampler, sampler->ctx->mem_ctx);
1606}
1607
1608static void pg_apply_uniforms(pg_shader_t* shader)
1609{
1610 PICO_GFX_ASSERT(shader);
1611
1612 pg_hashtable_iterator_t iterator;
1613 pg_hashtable_init_iterator(shader->uniform_blocks, &iterator);
1614
1615 char* key = NULL;
1616 void* value = NULL;
1617
1618 while (pg_hashtable_iterator_next(&iterator, &key, &value))
1619 {
1620 pg_uniform_block_t* block = (pg_uniform_block_t*)value;
1621
1622 sg_range range = { .ptr = block->data, .size = block->size };
1623
1624 sg_apply_uniforms(block->slot, &range);
1625 }
1626}
1627
1629 pg_buffer_usage_t usage,
1630 const void* data,
1631 size_t count,
1632 size_t max_elements,
1633 size_t element_size)
1634{
1635 PICO_GFX_ASSERT(ctx);
1636
1637 pg_buffer_t* buffer = PICO_GFX_MALLOC(sizeof(pg_buffer_t), ctx->mem_ctx);
1638
1639 buffer->ctx = ctx;
1640 buffer->type = PG_BUFFER_TYPE_VERTEX;
1641 buffer->usage = usage;
1642 buffer->count = count;
1643 buffer->element_size = element_size;
1644 buffer->size = max_elements * element_size;
1645 buffer->offset = 0;
1646
1647 buffer->handle = sg_make_buffer(&(sg_buffer_desc)
1648 {
1649 .type = SG_BUFFERTYPE_VERTEXBUFFER,
1650 .usage = pg_map_usage(usage),
1651 .data = { .ptr = data, .size = count * element_size },
1652 .size = buffer->size
1653 });
1654
1655 PICO_GFX_ASSERT(sg_query_buffer_state(buffer->handle) == SG_RESOURCESTATE_VALID);
1656
1657 return buffer;
1658}
1659
1661 pg_buffer_usage_t usage,
1662 const void* data,
1663 size_t count,
1664 size_t max_elements)
1665{
1666 PICO_GFX_ASSERT(ctx);
1667
1668 pg_buffer_t* buffer = PICO_GFX_MALLOC(sizeof(pg_buffer_t), ctx->mem_ctx);
1669
1670 buffer->ctx = ctx;
1671 buffer->type = PG_BUFFER_TYPE_INDEX;
1672 buffer->usage = usage;
1673 buffer->count = count;
1674 buffer->size = max_elements * sizeof(uint32_t);
1675 buffer->offset = 0;
1676
1677 buffer->handle = sg_make_buffer(&(sg_buffer_desc)
1678 {
1679 .type = SG_BUFFERTYPE_INDEXBUFFER,
1680 .usage = pg_map_usage(usage),
1681 .data = { .ptr = data, .size = count * sizeof(uint32_t) },
1682 .size = buffer->size
1683 });
1684
1685 PICO_GFX_ASSERT(sg_query_buffer_state(buffer->handle) == SG_RESOURCESTATE_VALID);
1686
1687 return buffer;
1688}
1689
1690void pg_update_buffer(pg_buffer_t* buffer, void* data, size_t count)
1691{
1692 PICO_GFX_ASSERT(buffer);
1693 PICO_GFX_ASSERT(data);
1694 PICO_GFX_ASSERT(count > 0);
1695
1696 sg_update_buffer(buffer->handle, &(sg_range)
1697 {
1698 .ptr = data,
1699 .size = count * buffer->element_size
1700 });
1701
1702 buffer->count = count;
1703 buffer->offset = 0;
1704}
1705
1706int pg_append_buffer(pg_buffer_t* buffer, void* data, size_t count)
1707{
1708 PICO_GFX_ASSERT(buffer);
1709 PICO_GFX_ASSERT(data);
1710 PICO_GFX_ASSERT(count > 0);
1711
1712 int offset = sg_append_buffer(buffer->handle, &(sg_range)
1713 {
1714 .ptr = data,
1715 .size = count * buffer->element_size
1716 });
1717
1718 buffer->count = count;
1719 buffer->offset = offset;
1720
1721 return offset;
1722}
1723
1725{
1726 PICO_GFX_ASSERT(buffer);
1727 return buffer->offset;
1728}
1729
1730void pg_set_buffer_offset(pg_buffer_t* buffer, int offset)
1731{
1732 PICO_GFX_ASSERT(buffer);
1733 buffer->offset = offset;
1734}
1735
1736void pg_reset_buffer(pg_buffer_t* buffer)
1737{
1738 PICO_GFX_ASSERT(buffer);
1739
1740 sg_destroy_buffer(buffer->handle);
1741
1742 buffer->handle = sg_make_buffer(&(sg_buffer_desc)
1743 {
1744 .type = pg_map_buffer_type(buffer->type),
1745 .usage = pg_map_usage(buffer->usage),
1746 .data = { .ptr = NULL, .size = 0 },
1747 .size = buffer->size
1748 });
1749
1750 PICO_GFX_ASSERT(sg_query_buffer_state(buffer->handle) == SG_RESOURCESTATE_VALID);
1751}
1752
1753void pg_destroy_buffer(pg_buffer_t* buffer)
1754{
1755 PICO_GFX_ASSERT(buffer);
1756 sg_destroy_buffer(buffer->handle);
1757 PICO_GFX_FREE(buffer, buffer->ctx->mem_ctx);
1758}
1759
1760static void pg_apply_view_state(const pg_ctx_t* ctx)
1761{
1762 const pg_rect_t* vp_rect = &ctx->state.viewport;
1763 sg_apply_viewport(vp_rect->x, vp_rect->y, vp_rect->width, vp_rect->height, true);
1764
1765 const pg_rect_t* s_rect = &ctx->state.scissor;
1766 sg_apply_scissor_rect(s_rect->x, s_rect->y, s_rect->width, s_rect->height, true);
1767}
1768
1769static void pg_apply_textures(const pg_shader_t* shader, sg_bindings* bindings)
1770{
1771 for (int i = 0; i < PG_MAX_TEXTURE_SLOTS; i++)
1772 {
1773 if (!shader->textures[i])
1774 continue;
1775
1776 bindings->images[i] = shader->textures[i]->handle;
1777 }
1778}
1779
1780static void pg_apply_samplers(const pg_shader_t* shader, sg_bindings* bindings)
1781{
1782 for (int i = 0; i < PG_MAX_SAMPLER_SLOTS; i++)
1783 {
1784 if (!shader->samplers[i])
1785 continue;
1786
1787 bindings->samplers[i] = shader->samplers[i]->handle;
1788 }
1789}
1790
1791static void pg_apply_buffers(const pg_ctx_t* ctx, sg_bindings* bindings)
1792{
1793 pg_buffer_t* const* buffers = ctx->state.buffers;
1794
1795 for (int slot = 0; buffers[slot] != NULL; slot++)
1796 {
1797 bindings->vertex_buffer_offsets[slot] = buffers[slot]->offset;
1798 bindings->vertex_buffers[slot] = buffers[slot]->handle;
1799 }
1800}
1801
1802void pg_draw(const pg_ctx_t* ctx, size_t start, size_t count, size_t instances)
1803{
1804 PICO_GFX_ASSERT(ctx);
1805 PICO_GFX_ASSERT(ctx->pass_active);
1806
1807 sg_bindings bindings = { 0 };
1808
1809 pg_pipeline_t* pipeline = ctx->state.pipeline;
1810
1811 pg_apply_textures(pipeline->shader, &bindings);
1812 pg_apply_samplers(pipeline->shader, &bindings);
1813
1814 pg_apply_buffers(ctx, &bindings);
1815 pg_apply_view_state(ctx);
1816
1817 if (ctx->state.index_buffer)
1818 {
1819 bindings.index_buffer_offset = ctx->state.index_buffer->offset;
1820 bindings.index_buffer = ctx->state.index_buffer->handle;
1821 }
1822
1823 sg_apply_pipeline(pipeline->handle);
1824 sg_apply_bindings(&bindings);
1825 pg_apply_uniforms(pipeline->shader);
1826
1827 sg_draw(start, count, instances);
1828}
1829
1830/*==============================================================================
1831 * GFX Static Functions
1832 *============================================================================*/
1833
1834static void pg_alloc_uniform_block(pg_shader_t* shader, const char* name)
1835{
1836 PICO_GFX_ASSERT(shader);
1837 PICO_GFX_ASSERT(name);
1838
1839 size_t size = shader->internal.get_uniformblock_size(name);
1840
1841 pg_uniform_block_t block =
1842 {
1843 .slot = shader->internal.get_uniformblock_slot(name),
1844 .data = pg_arena_malloc(shader->arena, size),
1845 .size = size,
1846 };
1847
1848 pg_hashtable_put(shader->uniform_blocks, name, &block);
1849}
1850
1851static sg_primitive_type pg_map_primitive(pg_primitive_t primitive)
1852{
1853 if (primitive == PG_DEFAULT_PRIMITIVE)
1854 primitive = PG_TRIANGLES;
1855
1856 switch (primitive)
1857 {
1858 case PG_POINTS: return SG_PRIMITIVETYPE_POINTS;
1859 case PG_LINES: return SG_PRIMITIVETYPE_LINES;
1860 case PG_LINE_STRIP: return SG_PRIMITIVETYPE_LINE_STRIP;
1861 case PG_TRIANGLES: return SG_PRIMITIVETYPE_TRIANGLES;
1862 case PG_TRIANGLE_STRIP: return SG_PRIMITIVETYPE_TRIANGLE_STRIP;
1863 default: PICO_GFX_ASSERT(false); return SG_PRIMITIVETYPE_TRIANGLES;
1864 }
1865}
1866
1867static sg_blend_factor pg_map_blend_factor(pg_blend_factor_t factor)
1868{
1869 if (factor == PG_DEFAULT_BLEND_FACTOR)
1870 factor = PG_ONE;
1871
1872 switch (factor)
1873 {
1874 case PG_ZERO: return SG_BLENDFACTOR_ZERO;
1875 case PG_ONE: return SG_BLENDFACTOR_ONE;
1876 case PG_SRC_COLOR: return SG_BLENDFACTOR_SRC_COLOR;
1877 case PG_ONE_MINUS_SRC_COLOR: return SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR;
1878 case PG_DST_COLOR: return SG_BLENDFACTOR_DST_COLOR;
1879 case PG_ONE_MINUS_DST_COLOR: return SG_BLENDFACTOR_ONE_MINUS_DST_COLOR;
1880 case PG_SRC_ALPHA: return SG_BLENDFACTOR_SRC_ALPHA;
1881 case PG_ONE_MINUS_SRC_ALPHA: return SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
1882 case PG_DST_ALPHA: return SG_BLENDFACTOR_DST_ALPHA;
1883 case PG_ONE_MINUS_DST_ALPHA: return SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA;
1884 default: PICO_GFX_ASSERT(false); return SG_BLENDFACTOR_ONE;
1885 }
1886}
1887
1888static sg_blend_op pg_map_blend_eq(pg_blend_eq_t eq)
1889{
1890 if (eq == PG_DEFAULT_BLEND_EQ)
1891 eq = PG_ADD;
1892
1893 switch (eq)
1894 {
1895 case PG_ADD: return SG_BLENDOP_ADD;
1896 case PG_SUBTRACT: return SG_BLENDOP_SUBTRACT;
1897 case PG_REVERSE_SUBTRACT: return SG_BLENDOP_REVERSE_SUBTRACT;
1898 default: PICO_GFX_ASSERT(false); return SG_BLENDOP_ADD;
1899 }
1900}
1901
1902static sg_vertex_format pg_map_vertex_format(pg_vertex_format_t format)
1903{
1904 switch (format)
1905 {
1906 case PG_VERTEX_FORMAT_INVALID: return SG_VERTEXFORMAT_INVALID;
1907 case PG_VERTEX_FORMAT_FLOAT: return SG_VERTEXFORMAT_FLOAT;
1908 case PG_VERTEX_FORMAT_FLOAT2: return SG_VERTEXFORMAT_FLOAT2;
1909 case PG_VERTEX_FORMAT_FLOAT3: return SG_VERTEXFORMAT_FLOAT3;
1910 case PG_VERTEX_FORMAT_FLOAT4: return SG_VERTEXFORMAT_FLOAT4;
1911 case PG_VERTEX_FORMAT_BYTE4: return SG_VERTEXFORMAT_BYTE4;
1912 case PG_VERTEX_FORMAT_BYTE4N: return SG_VERTEXFORMAT_BYTE4N;
1913 case PG_VERTEX_FORMAT_UBYTE4: return SG_VERTEXFORMAT_UBYTE4;
1914 case PG_VERTEX_FORMAT_UBYTE4N: return SG_VERTEXFORMAT_UBYTE4N;
1915 case PG_VERTEX_FORMAT_SHORT2: return SG_VERTEXFORMAT_SHORT2;
1916 case PG_VERTEX_FORMAT_SHORT2N: return SG_VERTEXFORMAT_SHORT2N;
1917 case PG_VERTEX_FORMAT_USHORT2N: return SG_VERTEXFORMAT_USHORT2N;
1918 case PG_VERTEX_FORMAT_SHORT4: return SG_VERTEXFORMAT_USHORT2N;
1919 case PG_VERTEX_FORMAT_SHORT4N: return SG_VERTEXFORMAT_SHORT4;
1920 case PG_VERTEX_FORMAT_USHORT4N: return SG_VERTEXFORMAT_SHORT4N;
1921 case PG_VERTEX_FORMAT_UINT10_N2: return SG_VERTEXFORMAT_USHORT4N;
1922 case PG_VERTEX_FORMAT_HALF2: return SG_VERTEXFORMAT_HALF2;
1923 case PG_VERTEX_FORMAT_HALF4: return SG_VERTEXFORMAT_HALF4;
1924 default: PICO_GFX_ASSERT(false); return SG_VERTEXFORMAT_INVALID;
1925 }
1926}
1927
1928static sg_usage pg_map_usage(pg_buffer_usage_t format)
1929{
1930 switch (format)
1931 {
1932 case PG_USAGE_STATIC: return SG_USAGE_IMMUTABLE;
1933 case PG_USAGE_DYNAMIC: return SG_USAGE_DYNAMIC;
1934 case PG_USAGE_STREAM: return SG_USAGE_STREAM;
1935 default: PICO_GFX_ASSERT(false); return SG_USAGE_IMMUTABLE;
1936 }
1937}
1938
1939static sg_buffer_type pg_map_buffer_type(pg_buffer_type_t type)
1940{
1941 switch (type)
1942 {
1943 case PG_BUFFER_TYPE_VERTEX: return SG_BUFFERTYPE_VERTEXBUFFER;
1944 case PG_BUFFER_TYPE_INDEX: return SG_BUFFERTYPE_INDEXBUFFER;
1945 default: PICO_GFX_ASSERT(false); return SG_BUFFERTYPE_VERTEXBUFFER;
1946 }
1947}
1948
1949static void pg_log(const char* fmt, ...)
1950{
1951 PICO_GFX_ASSERT(fmt);
1952
1953 va_list args;
1954 va_start(args, fmt);
1955 vprintf(fmt, args);
1956 va_end(args);
1957 printf("\n");
1958 fflush(stdout);
1959}
1960
1961static void pg_log_sg(const char* tag, // e.g. 'sg'
1962 uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info
1963 uint32_t log_item_id, // SG_LOGITEM_*
1964 const char* message_or_null, // a message string, may be nullptr in release mode
1965 uint32_t line_nr, // line number in sokol_gfx.h
1966 const char* filename_or_null, // source filename, may be nullptr in release mode
1967 void* user_data)
1968{
1969 (void)log_item_id;
1970 (void)user_data;
1971
1972 static const char* level[] =
1973 {
1974 "Panic",
1975 "Error",
1976 "Warn",
1977 "Info",
1978 };
1979
1980 // FIXME: Handle non-debug case
1981
1982 if (message_or_null && !filename_or_null)
1983 {
1984 PICO_GFX_LOG("Tag: %s, Level: %s, Message: %s",
1985 tag, level[log_level], message_or_null);
1986 }
1987
1988 if (!message_or_null && filename_or_null)
1989 {
1990 PICO_GFX_LOG("Tag: %s, Level: %s, File: %s, Line: %d",
1991 tag, level[log_level], filename_or_null, line_nr);
1992 }
1993
1994 if (message_or_null && filename_or_null)
1995 {
1996 PICO_GFX_LOG("Tag: %s, Level: %s, File: %s, Line: %d, Message: %s",
1997 tag, level[log_level], filename_or_null, line_nr, message_or_null);
1998 }
1999}
2000
2001/*==============================================================================
2002 * Hashtable Data Structures
2003 *============================================================================*/
2004
2005typedef struct
2006{
2007 pg_hash_t hash;
2008 char* key;
2009 void* value;
2010} pg_hashtable_entry_t;
2011
2012struct pg_hashtable_t
2013{
2014 void* mem_ctx;
2015 size_t capacity;
2016 size_t size;
2017
2018 pg_hashtable_entry_t* entries;
2019
2020 size_t key_size;
2021 char* keys;
2022
2023 size_t value_size;
2024 void* values;
2025};
2026
2027/*==============================================================================
2028 * Hashtable Internal Declarations
2029 *============================================================================*/
2030
2031static size_t pg_hashtable_compute_hash(const pg_hashtable_t* ht,
2032 const char* key);
2033
2034static bool pg_hashtable_key_equal(const pg_hashtable_t* ht,
2035 const char* key1,
2036 const char* key2);
2037
2038static void pg_hashtable_copy_value(pg_hashtable_t* ht,
2039 pg_hashtable_entry_t* entry,
2040 const void* value);
2041
2042static void pg_hashtable_swap_size(size_t* a, size_t* b);
2043static void pg_hashtable_swap_ptr(void** a, void** b);
2044static void pg_hashtable_swap(pg_hashtable_t* ht1, pg_hashtable_t* ht2);
2045static void pg_hashtable_rehash(pg_hashtable_t* ht);
2046
2047/*==============================================================================
2048 * Hashtable Public Implementation
2049 *============================================================================*/
2050
2051static pg_hashtable_t* pg_hashtable_new(size_t capacity,
2052 size_t key_size,
2053 size_t value_size,
2054 void* mem_ctx)
2055{
2056 bool power_of_two = (0 == (capacity & (capacity - 1)));
2057
2058 PICO_GFX_ASSERT(capacity > 2 && power_of_two);
2059 PICO_GFX_ASSERT(key_size > 0);
2060 PICO_GFX_ASSERT(value_size > 0);
2061
2062 if (capacity <= 2 || !power_of_two)
2063 return NULL;
2064
2065 if (0 == key_size || 0 == value_size)
2066 return NULL;
2067
2068 pg_hashtable_t* ht = PICO_GFX_MALLOC(sizeof(pg_hashtable_t), mem_ctx);
2069
2070 if (!ht)
2071 return NULL;
2072
2073 ht->mem_ctx = mem_ctx;
2074 ht->capacity = capacity;
2075 ht->size = 0;
2076 ht->key_size = key_size;
2077 ht->value_size = value_size;
2078
2079 ht->entries = PICO_GFX_MALLOC(capacity * sizeof(pg_hashtable_entry_t), mem_ctx);
2080
2081 if (!ht->entries)
2082 {
2083 PICO_GFX_FREE(ht, mem_ctx);
2084 return NULL;
2085 }
2086
2087 ht->keys = PICO_GFX_MALLOC(capacity * key_size, mem_ctx);
2088
2089 if (!ht->keys)
2090 {
2091 PICO_GFX_FREE(ht->entries, mem_ctx);
2092 PICO_GFX_FREE(ht, mem_ctx);
2093
2094 return NULL;
2095 }
2096
2097 ht->values = PICO_GFX_MALLOC(capacity * value_size, mem_ctx);
2098
2099 if (!ht->values)
2100 {
2101 PICO_GFX_FREE(ht->entries, mem_ctx);
2102 PICO_GFX_FREE(ht->keys, mem_ctx);
2103 PICO_GFX_FREE(ht, mem_ctx);
2104 return NULL;
2105 }
2106
2107 for (size_t i = 0; i < capacity; i++)
2108 {
2109 pg_hashtable_entry_t* entry = &ht->entries[i];
2110 entry->hash = 0;
2111 entry->key = (char*)ht->keys + i * key_size;
2112 entry->value = (char*)ht->values + i * value_size;
2113 }
2114
2115 return ht;
2116}
2117
2118static void pg_hashtable_free(pg_hashtable_t* ht)
2119{
2120 PICO_GFX_ASSERT(NULL != ht);
2121
2122 PICO_GFX_FREE(ht->entries, ht->mem_ctx);
2123 PICO_GFX_FREE(ht->keys, ht->mem_ctx);
2124 PICO_GFX_FREE(ht->values, ht->mem_ctx);
2125 PICO_GFX_FREE(ht, ht->mem_ctx);
2126}
2127
2128static void pg_hashtable_init_iterator(const pg_hashtable_t* ht,
2129 pg_hashtable_iterator_t* iterator)
2130{
2131 PICO_GFX_ASSERT(NULL != ht);
2132 iterator->ht = ht;
2133 iterator->index = 0;
2134 iterator->count = 0;
2135}
2136
2137static bool pg_hashtable_iterator_next(pg_hashtable_iterator_t* iterator,
2138 char** key, void** value)
2139{
2140 PICO_GFX_ASSERT(NULL != iterator);
2141
2142 const pg_hashtable_t* ht = iterator->ht;
2143
2144 if (iterator->count >= ht->capacity)
2145 return false;
2146
2147 while (iterator->index < ht->capacity)
2148 {
2149 pg_hashtable_entry_t* entry = &ht->entries[iterator->index];
2150
2151 if (entry->hash != 0)
2152 {
2153 if (key)
2154 *key = entry->key;
2155
2156 if (value)
2157 *value = entry->value;
2158
2159 iterator->count++;
2160 iterator->index++;
2161
2162 return true;
2163 }
2164
2165 iterator->index++;
2166 }
2167
2168 return false;
2169}
2170
2171static void pg_hashtable_put(pg_hashtable_t* ht,
2172 const char* key,
2173 const void* value)
2174{
2175 PICO_GFX_ASSERT(NULL != ht);
2176
2177 if (ht->size == ht->capacity)
2178 {
2179 pg_hashtable_rehash(ht);
2180 PICO_GFX_ASSERT(ht->capacity > 0);
2181 }
2182
2183 pg_hash_t hash = pg_hashtable_compute_hash(ht, key);
2184
2185 PICO_GFX_ASSERT(hash > 0);
2186
2187 size_t start_index = hash % ht->capacity;
2188 size_t index = start_index;
2189
2190 do
2191 {
2192 pg_hashtable_entry_t* entry = &ht->entries[index];
2193
2194 if (entry->hash == hash && pg_hashtable_key_equal(ht, key, entry->key))
2195 {
2196 pg_hashtable_copy_value(ht, entry, value);
2197 break;
2198 }
2199
2200 if (entry->hash == 0)
2201 {
2202 entry->hash = hash;
2203
2204 strncpy(entry->key, key, ht->key_size);
2205 pg_hashtable_copy_value(ht, entry, value);
2206
2207 ht->size++;
2208
2209 break;
2210 }
2211
2212 index = (index + 1) % ht->capacity;
2213
2214 } while (index != start_index);
2215
2216 start_index = index;
2217 index = (index + 1) % ht->capacity;
2218
2219 while (index != start_index)
2220 {
2221 pg_hashtable_entry_t* entry = &ht->entries[index];
2222
2223 if (entry->hash == hash && pg_hashtable_key_equal(ht, key, entry->key))
2224 {
2225 entry->hash = 0;
2226 ht->size--;
2227 return;
2228 }
2229
2230 index = (index + 1) % ht->capacity;
2231 }
2232}
2233
2234static void* pg_hashtable_get(const pg_hashtable_t* ht, const char* key)
2235{
2236 PICO_GFX_ASSERT(NULL != ht);
2237
2238 pg_hash_t hash = pg_hashtable_compute_hash(ht, key);
2239
2240 PICO_GFX_ASSERT(hash > 0);
2241
2242 size_t start_index = hash % ht->capacity;
2243 size_t index = start_index;
2244
2245 do
2246 {
2247 pg_hashtable_entry_t* entry = &ht->entries[index];
2248
2249 if (entry->hash == hash && pg_hashtable_key_equal(ht, key, entry->key))
2250 {
2251 return entry->value;
2252 }
2253
2254 index = (index + 1) % ht->capacity;
2255
2256 } while (index != start_index);
2257
2258 return NULL;
2259}
2260
2261/*==============================================================================
2262 * Hashtable Internal API
2263 *============================================================================*/
2264
2265static bool pg_hashtable_key_equal(const pg_hashtable_t* ht,
2266 const char* key1,
2267 const char* key2)
2268{
2269 return 0 == strncmp(key1, key2, ht->key_size);
2270}
2271
2272static void pg_hashtable_copy_value(pg_hashtable_t* ht,
2273 pg_hashtable_entry_t* entry,
2274 const void* value)
2275{
2276 memcpy(entry->value, value, ht->value_size);
2277}
2278
2279static void pg_hashtable_swap_size(size_t* a, size_t* b)
2280{
2281 size_t tmp = *a;
2282 *a = *b;
2283 *b = tmp;
2284}
2285
2286static void pg_hashtable_swap_ptr(void** a, void** b)
2287{
2288 void* tmp = *a;
2289 *a = *b;
2290 *b = tmp;
2291}
2292
2293static void pg_hashtable_swap(pg_hashtable_t* ht1, pg_hashtable_t* ht2)
2294{
2295 pg_hashtable_swap_size(&ht1->capacity, &ht2->capacity);
2296 pg_hashtable_swap_size(&ht1->size, &ht2->size);
2297
2298 pg_hashtable_swap_ptr((void**)&ht1->entries, (void**)&ht2->entries);
2299
2300 pg_hashtable_swap_size(&ht1->key_size, &ht2->key_size);
2301 pg_hashtable_swap_ptr((void**)&ht1->keys, (void**)&ht2->keys);
2302
2303 pg_hashtable_swap_size(&ht1->value_size, &ht2->value_size);
2304 pg_hashtable_swap_ptr(&ht1->values, &ht2->values);
2305}
2306
2307static void pg_hashtable_rehash(pg_hashtable_t* ht)
2308{
2309 pg_hashtable_t* new_ht = pg_hashtable_new(ht->capacity * 2,
2310 ht->key_size,
2311 ht->size,
2312 ht->mem_ctx);
2313
2314 pg_hashtable_iterator_t iterator;
2315 pg_hashtable_init_iterator(ht, &iterator);
2316
2317 char* key;
2318 void* value;
2319
2320 while (pg_hashtable_iterator_next(&iterator, &key, &value))
2321 {
2322 pg_hashtable_put(new_ht, key, value);
2323 }
2324
2325 pg_hashtable_swap(ht, new_ht);
2326
2327 pg_hashtable_free(new_ht);
2328}
2329
2330/*==============================================================================
2331 * Hash Functions
2332 *============================================================================*/
2333
2334static size_t pg_hashtable_compute_hash(const pg_hashtable_t* ht, const char* key)
2335{
2336
2337#ifdef PICO_GFX_32BIT
2338 static const uint32_t offset_basis = 0x811C9DC5;
2339 static const uint32_t prime = 0x1000193;
2340#else
2341 static const uint64_t offset_basis = 0xCBF29CE484222325;
2342 static const uint64_t prime = 0x100000001B3;
2343#endif
2344
2345 const char* data = key;
2346
2347 pg_hash_t hash = offset_basis;
2348
2349 for (size_t i = 0; i < ht->key_size; i++) {
2350 hash ^= (pg_hash_t)data[i];
2351 hash *= prime;
2352 }
2353
2354 // Ensure hash is never zero
2355 if (hash == 0)
2356 hash++;
2357
2358 return hash;
2359}
2360
2361/*==============================================================================
2362 * Arena Allocator
2363 *============================================================================*/
2364
2365// Arena block structure
2366typedef struct pg_block_t
2367{
2368 struct pg_block_t* next;
2369 size_t size;
2370 size_t used;
2371 unsigned char data[];
2372} pg_block_t;
2373
2374// Arena structure
2375struct pg_arena_t
2376{
2377 pg_block_t* first;
2378 pg_block_t* current;
2379 size_t block_size;
2380 void* mem_ctx;
2381};
2382
2383pg_arena_t* pg_arena_create(size_t block_size, void* mem_ctx)
2384{
2385 PICO_GFX_ASSERT(block_size > 0);
2386
2387 pg_arena_t* arena = PICO_GFX_MALLOC(sizeof(*arena), mem_ctx);
2388
2389 if (!arena)
2390 {
2391 return NULL;
2392 }
2393
2394 arena->first = NULL;
2395 arena->current = NULL;
2396 arena->block_size = block_size;
2397 arena->mem_ctx = mem_ctx;
2398
2399 return arena;
2400}
2401
2402void pg_arena_destroy(pg_arena_t* arena) {
2403 PICO_GFX_ASSERT(arena);
2404
2405 if (!arena)
2406 {
2407 return;
2408 }
2409
2410 pg_block_t* block = arena->first;
2411
2412 while (block)
2413 {
2414 pg_block_t* next = block->next;
2415 PICO_GFX_FREE(block, arena->mem_ctx);
2416 block = next;
2417 }
2418
2419 PICO_GFX_FREE(arena, arena->mem_ctx);
2420}
2421
2422void* pg_arena_malloc(pg_arena_t* arena, size_t size)
2423{
2424 if (!arena || size == 0)
2425 {
2426 return NULL;
2427 }
2428
2429 // Align size to 8 bytes
2430 size = (size + 7) & ~7;
2431
2432 // Check if current block has enough space
2433 if (arena->current && arena->current->used + size <= arena->current->size)
2434 {
2435 void* ptr = arena->current->data + arena->current->used;
2436 arena->current->used += size;
2437 return ptr;
2438 }
2439
2440 // Allocate new block
2441 size_t block_size = size > arena->block_size ? size : arena->block_size;
2442 pg_block_t* block = PICO_GFX_MALLOC(sizeof(pg_block_t) + block_size, arena->mem_ctx);
2443
2444 if (!block)
2445 {
2446 return NULL;
2447 }
2448
2449 block->next = NULL;
2450 block->size = block_size;
2451 block->used = size;
2452
2453 // Link block
2454 if (!arena->first)
2455 {
2456 arena->first = block;
2457 }
2458 else
2459 {
2460 arena->current->next = block;
2461 }
2462
2463 arena->current = block;
2464
2465 return block->data;
2466}
2467
2468#if 0
2469
2470void pg_arena_reset(pg_arena_t* arena)
2471{
2472 if (!arena)
2473 {
2474 return;
2475 }
2476
2477 pg_block_t* block = arena->first;
2478
2479 while (block)
2480 {
2481 block->used = 0;
2482 block = block->next;
2483 }
2484
2485 arena->current = arena->first;
2486}
2487
2488#endif
2489
2490#define SOKOL_GFX_IMPL
2491#include "sokol_gfx.h"
2492
2493#endif //PICO_GFX_IMPLEMENTATION
2494
2495/*
2496 ----------------------------------------------------------------------------
2497 This software is available under two licenses (A) or (B). You may choose
2498 either one as you wish:
2499 ----------------------------------------------------------------------------
2500
2501 (A) The zlib License
2502
2503 Copyright (c) 2023 James McLean
2504
2505 This software is provided 'as-is', without any express or implied warranty.
2506 In no event will the authors be held liable for any damages arising from the
2507 use of this software.
2508
2509 Permission is granted to anyone to use this software for any purpose,
2510 including commercial applications, and to alter it and redistribute it
2511 freely, subject to the following restrictions:
2512
2513 1. The origin of this software must not be misrepresented; you must not
2514 claim that you wrote the original software. If you use this software in a
2515 product, an acknowledgment in the product documentation would be appreciated
2516 but is not required.
2517
2518 2. Altered source versions must be plainly marked as such, and must not be
2519 misrepresented as being the original software.
2520
2521 3. This notice may not be removed or altered from any source distribution.
2522
2523 ----------------------------------------------------------------------------
2524
2525 (B) Public Domain (www.unlicense.org)
2526
2527 This is free and unencumbered software released into the public domain.
2528
2529 Anyone is free to copy, modify, publish, use, compile, sell, or distribute
2530 this software, either in source code form or as a compiled binary, for any
2531 purpose, commercial or non-commercial, and by any means.
2532
2533 In jurisdictions that recognize copyright laws, the author or authors of
2534 this software dedicate any and all copyright interest in the software to the
2535 public domain. We make this dedication for the benefit of the public at
2536 large and to the detriment of our heirs and successors. We intend this
2537 dedication to be an overt act of relinquishment in perpetuity of all present
2538 and future rights to this software under copyright law.
2539
2540 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2541 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2542 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2543 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2544 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2545 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2546*/
2547
2548// EoF
pg_backend_t
Graphics backends.
Definition pico_gfx.h:172
@ PG_BACKEND_WGPU
Definition pico_gfx.h:177
@ PG_BACKEND_D3D
Definition pico_gfx.h:175
@ PG_BACKEND_METAL
Definition pico_gfx.h:176
@ PG_BACKEND_GL
Definition pico_gfx.h:173
@ PG_BACKEND_GLES
Definition pico_gfx.h:174
pg_shader_t * pg_get_shader(const pg_pipeline_t *pipeline)
Returns the shader associated with the pipeline.
#define PG_MAX_TEXTURE_SLOTS
Definition pico_gfx.h:165
void pg_destroy_context(pg_ctx_t *ctx)
Destroys a graphics context.
struct pg_sampler_t pg_sampler_t
Represents sampler.
Definition pico_gfx.h:260
#define PG_MAX_VERTEX_ATTRIBUTES
Definition pico_gfx.h:163
pg_vertex_format_t
Vertex attribute pixel formats.
Definition pico_gfx.h:489
@ PG_VERTEX_FORMAT_INVALID
Definition pico_gfx.h:490
@ PG_VERTEX_FORMAT_BYTE4
Definition pico_gfx.h:495
@ PG_VERTEX_FORMAT_FLOAT
Definition pico_gfx.h:491
@ PG_VERTEX_FORMAT_USHORT4N
Definition pico_gfx.h:504
@ PG_VERTEX_FORMAT_FLOAT2
Definition pico_gfx.h:492
@ PG_VERTEX_FORMAT_FLOAT4
Definition pico_gfx.h:494
@ PG_VERTEX_FORMAT_HALF2
Definition pico_gfx.h:506
@ PG_VERTEX_FORMAT_SHORT2
Definition pico_gfx.h:499
@ PG_VERTEX_FORMAT_UBYTE4N
Definition pico_gfx.h:498
@ PG_VERTEX_FORMAT_UINT10_N2
Definition pico_gfx.h:505
@ PG_VERTEX_FORMAT_SHORT4
Definition pico_gfx.h:502
@ PG_VERTEX_FORMAT_USHORT2N
Definition pico_gfx.h:501
@ PG_VERTEX_FORMAT_HALF4
Definition pico_gfx.h:507
@ PG_VERTEX_FORMAT_FLOAT3
Definition pico_gfx.h:493
@ PG_VERTEX_FORMAT_UBYTE4
Definition pico_gfx.h:497
@ PG_VERTEX_FORMAT_SHORT2N
Definition pico_gfx.h:500
@ PG_VERTEX_FORMAT_BYTE4N
Definition pico_gfx.h:496
@ PG_VERTEX_FORMAT_SHORT4N
Definition pico_gfx.h:503
void pg_destroy_texture(pg_texture_t *texture)
Destroys a texture.
void pg_reset_textures(pg_shader_t *shader)
Resets the texture bindings for the current state.
pg_buffer_t * pg_create_index_buffer(pg_ctx_t *ctx, pg_buffer_usage_t usage, const void *data, size_t count, size_t max_elements)
Creates a vertex buffer.
pg_buffer_usage_t
Buffer update criteria.
Definition pico_gfx.h:656
@ PG_USAGE_STATIC
Buffer is immutable (cannot be updated)
Definition pico_gfx.h:657
@ PG_USAGE_STREAM
Buffer is updated possibly more than once per frame.
Definition pico_gfx.h:659
@ PG_USAGE_DYNAMIC
Buffer is updated on average less than once per frame.
Definition pico_gfx.h:658
void pg_draw(const pg_ctx_t *ctx, size_t start, size_t count, size_t instances)
Draws from the buffers that are bound to the current state.
void pg_end_pass(pg_ctx_t *ctx)
Ends a render pass (mandatory)
void pg_set_pipeline(pg_ctx_t *ctx, pg_pipeline_t *pipeline)
Sets the pipeline state.
int pg_get_buffer_offset(pg_buffer_t *buffer)
Returns the buffer offset.
void pg_reset_state(pg_ctx_t *ctx)
Resets the active state to defaults.
void pg_reset_buffers(pg_ctx_t *ctx)
Clears buffer bindings.
void pg_set_scissor(pg_ctx_t *ctx, int x, int y, int w, int h)
Sets the scissor state to be placed at the top of the state stack.
uint32_t pg_get_shader_id(const pg_shader_t *shader)
Returns a shader ID.
void pg_init(void)
Loads pico_gfx and sokol_gfx.
void pg_set_uniform_block(pg_shader_t *shader, const char *name, const void *data)
Sets a uniform block (UB)
void pg_reset_samplers(pg_shader_t *shader)
Resets the sampler bindings for the current state.
void pg_reset_buffer(pg_buffer_t *buffer)
Destroys and recreates buffer.
void pg_push_state(pg_ctx_t *ctx)
Pushes the active state onto the stack.
void pg_shutdown(void)
Tears down pico_gfx and sokol_gfx.
pg_shader_t * pg_create_shader_internal(pg_ctx_t *ctx, pg_shader_internal_t internal)
const pg_blend_mode_t * pg_get_blend_mode(const pg_pipeline_t *pipeline)
Returns the blend mode associated with the pipeline.
void pg_get_texture_size(const pg_texture_t *texture, int *width, int *height)
Gets a texture's dimensions.
struct pg_pipeline_t pg_pipeline_t
Render state information.
Definition pico_gfx.h:245
void pg_reset_clear_color(pg_ctx_t *ctx)
pg_texture_t * pg_create_texture(pg_ctx_t *ctx, int width, int height, const uint8_t *data, size_t size, const pg_texture_opts_t *opts)
Creates a texture from an RGBA8 image.
pg_buffer_t * pg_create_vertex_buffer(pg_ctx_t *ctx, pg_buffer_usage_t usage, const void *data, size_t count, size_t max_elements, size_t element_size)
Creates a vertex buffer.
pg_backend_t pg_backend(void)
Returns the backend in use at runtime.
void pg_set_window_size(pg_ctx_t *ctx, int width, int height, bool reset)
Sets the window dimensions.
#define PG_MAX_VERTEX_BUFFERS
Definition pico_gfx.h:164
struct pg_buffer_t pg_buffer_t
A vertex or index array buffer.
Definition pico_gfx.h:265
void pg_reset_viewport(pg_ctx_t *ctx)
void pg_reset_scissor(pg_ctx_t *ctx)
void pg_destroy_pipeline(pg_pipeline_t *pipeline)
Destroys a render pipeline.
void pg_set_clear_color(pg_ctx_t *ctx, float r, float g, float b, float a)
Sets the clear color state to be placed at the top of the state stack.
pg_sampler_t * pg_create_sampler(pg_ctx_t *ctx, const pg_sampler_opts_t *opts)
Creates a sampler represents an object that can control how shaders transform and filter texture reso...
void pg_bind_sampler(pg_shader_t *shader, const char *name, pg_sampler_t *sampler)
Binds a sampler to a slot in the current state.
void pg_flush(pg_ctx_t *ctx)
Flush commands.
void pg_set_viewport(pg_ctx_t *ctx, int x, int y, int w, int h)
Sets the viewport state to be placed at the top of the state stack.
void pg_destroy_buffer(pg_buffer_t *buffer)
Destroys a vertex or index buffer.
void pg_begin_pass(pg_ctx_t *ctx, pg_texture_t *target, bool clear)
Starts a render pass (mandatory)
struct pg_texture_t pg_texture_t
Represents an image or render target in VRAM.
Definition pico_gfx.h:255
struct pg_shader_t pg_shader_t
Vertex/fragment shader program.
Definition pico_gfx.h:250
pg_blend_factor_t
Blend factors.
Definition pico_gfx.h:197
@ PG_ONE_MINUS_SRC_COLOR
(1, 1, 1, 1) - (src.r, src.g, src.b, src.a)
Definition pico_gfx.h:202
@ PG_ONE
(1, 1, 1, 1)
Definition pico_gfx.h:200
@ PG_SRC_ALPHA
(src.a, src.a, src.a, src.a)
Definition pico_gfx.h:205
@ PG_DEFAULT_BLEND_FACTOR
Definition pico_gfx.h:198
@ PG_DST_COLOR
(dst.r, dst.g, dst.b, dst.a)
Definition pico_gfx.h:203
@ PG_ZERO
(0, 0, 0, 0)
Definition pico_gfx.h:199
@ PG_DST_ALPHA
(dst.a, dst.a, dst.a, dst.a)
Definition pico_gfx.h:207
@ PG_SRC_COLOR
(src.r, src.g, src.b, src.a)
Definition pico_gfx.h:201
@ PG_ONE_MINUS_DST_ALPHA
(1, 1, 1, 1) - (dst.a, dst.a, dst.a, dst.a)
Definition pico_gfx.h:208
@ PG_ONE_MINUS_DST_COLOR
(1, 1, 1, 1) - (dst.r, dst.g, dst.b, dst.a)
Definition pico_gfx.h:204
@ PG_ONE_MINUS_SRC_ALPHA
(1, 1, 1, 1) - (src.a, src.a, src.a, src.a)
Definition pico_gfx.h:206
void pg_reset_pipeline(pg_ctx_t *ctx)
void pg_update_texture(pg_texture_t *texture, char *data, int width, int height)
Updates a texture with the given data. This can only be called once per frame.
int pg_append_buffer(pg_buffer_t *buffer, void *data, size_t count)
Appends data to a buffer. This can happen more than once per frame, and cannot happen after an update...
pg_texture_t * pg_create_render_texture(pg_ctx_t *ctx, int width, int height, const pg_texture_opts_t *opts)
Creates a render target.
void pg_set_buffer_offset(pg_buffer_t *buffer, int offset)
Sets the buffer offset.
void pg_destroy_shader(pg_shader_t *shader)
Destroys a shader.
struct pg_ctx_t pg_ctx_t
Contains core data/state for an instance of the graphics library.
Definition pico_gfx.h:240
void pg_pop_state(pg_ctx_t *ctx)
Pops a state off the stack and makes it the active state.
void pg_get_window_size(pg_ctx_t *ctx, int *width, int *height)
Gets the window size.
void pg_bind_buffer(pg_ctx_t *ctx, int slot, pg_buffer_t *buffer)
Binds a buffer to the specified slot.
pg_pipeline_t * pg_create_pipeline(pg_ctx_t *ctx, pg_shader_t *shader, const pg_pipeline_opts_t *opts)
Creates a rendering pipeline (encapsulates render state)
pg_primitive_t
Drawing primitives.
Definition pico_gfx.h:184
@ PG_TRIANGLES
Each adjacent triple forms an individual triangle.
Definition pico_gfx.h:189
@ PG_TRIANGLE_STRIP
Array of points where every triple forms a triangle.
Definition pico_gfx.h:190
@ PG_POINTS
Array of points.
Definition pico_gfx.h:186
@ PG_LINE_STRIP
Array of points where every pair forms a lines.
Definition pico_gfx.h:188
@ PG_DEFAULT_PRIMITIVE
Definition pico_gfx.h:185
@ PG_LINES
Each adjacent pair of points forms a line.
Definition pico_gfx.h:187
void pg_update_buffer(pg_buffer_t *buffer, void *data, size_t count)
void pg_bind_texture(pg_shader_t *shader, const char *name, pg_texture_t *texture)
Binds a texture to a slot in the current state.
void pg_set_index_buffer(pg_ctx_t *ctx, pg_buffer_t *buffer)
Sets the active index buffer.
void pg_destroy_sampler(pg_sampler_t *sampler)
Destroys a sampler object.
pg_blend_eq_t
Blend equations.
Definition pico_gfx.h:215
@ PG_DEFAULT_BLEND_EQ
Definition pico_gfx.h:216
@ PG_ADD
result = src * src_factor + dst * dst_factor
Definition pico_gfx.h:217
@ PG_REVERSE_SUBTRACT
result = dst * dst_factor - src * src_factor
Definition pico_gfx.h:219
@ PG_SUBTRACT
result = src * src_factor - dst * dst_factor
Definition pico_gfx.h:218
uint32_t pg_get_texture_id(const pg_texture_t *texture)
Returns a texture ID.
pg_ctx_t * pg_create_context(int window_width, int window_height, void *mem_ctx)
Creates a graphics context.
#define PG_MAX_SAMPLER_SLOTS
Definition pico_gfx.h:166
void pg_reset_index_buffer(pg_ctx_t *ctx)
Disables indexed rednering.
Blend mode.
Definition pico_gfx.h:228
pg_blend_eq_t color_eq
Equation for blending colors.
Definition pico_gfx.h:231
pg_blend_eq_t alpha_eq
Equation for blending alpha values.
Definition pico_gfx.h:234
pg_blend_factor_t color_dst
Color dsestination blending factor.
Definition pico_gfx.h:230
pg_blend_factor_t color_src
Color source blending factor.
Definition pico_gfx.h:229
pg_blend_factor_t alpha_dst
Alpha destination blending factor.
Definition pico_gfx.h:233
pg_blend_factor_t alpha_src
Alpha source blending factor.
Definition pico_gfx.h:232
Pipeline layout.
Definition pico_gfx.h:533
pg_vertex_buf_t bufs[PG_MAX_VERTEX_BUFFERS]
Vertex buffer descriptions.
Definition pico_gfx.h:534
pg_vertex_attr_t attrs[PG_MAX_VERTEX_ATTRIBUTES]
Vertex buffer attribute definitions.
Definition pico_gfx.h:535
Pipeline creation options.
Definition pico_gfx.h:542
pg_pipeline_layout_t layout
Attribute information.
Definition pico_gfx.h:544
bool indexed
Indexed drawing.
Definition pico_gfx.h:546
pg_blend_mode_t blend
Blend mode.
Definition pico_gfx.h:548
bool blend_enabled
Enables blending.
Definition pico_gfx.h:547
bool target
Drawing to render target.
Definition pico_gfx.h:545
pg_primitive_t primitive
Rendering primitive.
Definition pico_gfx.h:543
Sampler options.
Definition pico_gfx.h:634
bool repeat_u
Repeat if true, clamp-to-edge otherwise.
Definition pico_gfx.h:636
bool smooth
Linear filtering if true, nearest otherwise.
Definition pico_gfx.h:635
bool repeat_v
Repeat if true, clamp-to-edge otherwise.
Definition pico_gfx.h:637
Definition pico_gfx.h:736
const sg_shader_desc *(* get_shader_desc)(sg_backend backend)
Definition pico_gfx.h:737
Texture creation options.
Definition pico_gfx.h:580
int mipmaps
Mipmap level.
Definition pico_gfx.h:581
Vertex attribute description.
Definition pico_gfx.h:523
int offset
Attribute offset into the vertex buffer.
Definition pico_gfx.h:526
pg_vertex_format_t format
Vertex pixel format (see above)
Definition pico_gfx.h:525
int buffer_index
The vertex buffer bind slot.
Definition pico_gfx.h:524
Vertex buffer description.
Definition pico_gfx.h:514
bool instanced
True if the buffer will be used for instanced rendering.
Definition pico_gfx.h:515
int step
The step rate (default is 1)
Definition pico_gfx.h:516