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