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