143#if defined (PICO_GFX_GL)
145#elif defined (PICO_GFX_GLES)
147#elif defined (PICO_GFX_D3D)
149#elif defined (PICO_GFX_METAL)
151#elif defined (PICO_GFX_WEBGPU)
154 #error "GFX backend must be specified"
157#include "sokol_gfx.h"
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
452#define pg_create_shader(ctx, prefix) \
453 pg_create_shader_internal( \
455 (pg_shader_internal_t) \
457 prefix##_shader_desc, \
458 prefix##_attr_slot, \
459 prefix##_image_slot, \
460 prefix##_sampler_slot, \
461 prefix##_uniformblock_slot, \
462 prefix##_uniformblock_size, \
593 int width,
int height,
594 const uint8_t* data,
size_t size,
605 int width,
int height,
674 size_t element_size);
687 size_t max_elements);
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);
752#ifdef PICO_GFX_IMPLEMENTATION
760#ifndef PICO_GFX_STACK_MAX_SIZE
761#define PICO_GFX_STACK_MAX_SIZE 16
764#ifndef PICO_GFX_HASHTABLE_KEY_SIZE
765#define PICO_GFX_HASHTABLE_KEY_SIZE 16
773 #define PICO_GFX_ASSERT(expr) ((void)0)
775 #ifndef PICO_GFX_ASSERT
777 #define PICO_GFX_ASSERT(expr) (assert(expr))
781#if !defined(PICO_GFX_MALLOC) || !defined(PICO_GFX_REALLOC) || !defined(PICO_GFX_FREE)
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))
790 #define PICO_GFX_LOG(...) (pg_log(__VA_ARGS__))
797static void pg_alloc_uniform_block(
pg_shader_t* shader,
const char* name);
801 PG_BUFFER_TYPE_VERTEX,
802 PG_BUFFER_TYPE_INDEX,
805static sg_primitive_type pg_map_primitive(
pg_primitive_t primitive);
810static sg_buffer_type pg_map_buffer_type(pg_buffer_type_t type);
812static void pg_log_sg(
const char* tag,
814 uint32_t log_item_id,
815 const char* message_or_null,
817 const char* filename_or_null,
820static void pg_log(
const char* fmt, ...);
827 typedef uint32_t pg_hash_t;
829 typedef uint64_t pg_hash_t;
832typedef struct pg_hashtable_t pg_hashtable_t;
833typedef struct pg_hashtable_iterator_t pg_hashtable_iterator_t;
835typedef void (*pg_hashtable_iterator_fn)(pg_hashtable_iterator_t* iterator,
839static pg_hashtable_t* pg_hashtable_new(
size_t capacity,
size_t key_size,
840 size_t value_size,
void* mem_ctx);
842static void pg_hashtable_free(pg_hashtable_t* ht);
844static void pg_hashtable_init_iterator(
const pg_hashtable_t* ht,
845 pg_hashtable_iterator_t* iterator);
847static bool pg_hashtable_iterator_next(pg_hashtable_iterator_t* iterator,
848 char** key,
void** value);
850static void pg_hashtable_put(pg_hashtable_t* ht,
854static void* pg_hashtable_get(
const pg_hashtable_t* ht,
const char* key);
856struct pg_hashtable_iterator_t
858 const pg_hashtable_t* ht;
867typedef struct pg_arena_t pg_arena_t;
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);
877static void* pg_malloc(
size_t size,
void* ctx)
880 return PICO_GFX_MALLOC(size, ctx);
883static void pg_free(
void* ptr,
void* ctx)
886 PICO_GFX_FREE(ptr, ctx);
889typedef struct pg_rect_t
891 int x, y, width, height;
894typedef struct pg_state_t
896 sg_color clear_color;
908 sg_swapchain swapchain;
914 sg_pass default_pass;
916 pg_state_t state_stack[PICO_GFX_STACK_MAX_SIZE];
934 const sg_shader_desc* desc;
939 pg_hashtable_t* uniform_blocks;
956 sg_image depth_handle;
957 sg_attachments attachments;
970 pg_buffer_type_t type;
982 .logger.func = pg_log_sg,
985 .alloc_fn = pg_malloc,
989 .environment.defaults.color_format = SG_PIXELFORMAT_RGBA8,
1002 if (!ctx)
return NULL;
1006 ctx->mem_ctx = mem_ctx;
1007 ctx->window_width = window_width;
1008 ctx->window_height = window_height;
1012 ctx->swapchain = (sg_swapchain)
1014 .width = window_width,
1015 .height = window_height,
1023 PICO_GFX_ASSERT(ctx);
1024 PICO_GFX_FREE(ctx, ctx->mem_ctx);
1029 #if defined (PICO_GFX_GL)
1031 #elif defined (PICO_GFX_GLES)
1033 #elif defined (PICO_GFX_D3D)
1035 #elif defined (PICO_GFX_METAL)
1037 #elif defined (PICO_GFX_WEBGPU)
1040 #error "Unknown GFX backend"
1046 ctx->swapchain.width = ctx->window_width = width;
1047 ctx->swapchain.height = ctx->window_height = height;
1059 *width = ctx->window_width;
1062 *height = ctx->window_height;
1067 PICO_GFX_ASSERT(ctx);
1068 PICO_GFX_ASSERT(!ctx->pass_active);
1070 sg_pass_action action = { 0 };
1074 sg_color color = ctx->state.clear_color;
1076 action.colors[0] = (sg_color_attachment_action)
1078 .load_action = SG_LOADACTION_CLEAR,
1079 .clear_value = color
1083 sg_pass pass = { .action = action };
1087 pass.attachments = target->attachments;
1088 ctx->target = target;
1093 pass.swapchain = ctx->swapchain;
1096 sg_begin_pass(&pass);
1101 ctx->pass_active =
true;
1108 ctx->pass_active =
false;
1119 PICO_GFX_ASSERT(ctx);
1120 PICO_GFX_ASSERT(ctx->stack_size < PICO_GFX_STACK_MAX_SIZE);
1122 ctx->state_stack[ctx->stack_size] = ctx->state;
1128 PICO_GFX_ASSERT(ctx);
1129 PICO_GFX_ASSERT(ctx->stack_size > 0);
1131 ctx->state = ctx->state_stack[ctx->stack_size - 1];
1137 PICO_GFX_ASSERT(ctx);
1138 ctx->state.clear_color = (sg_color){ r, g, b, a};
1143 PICO_GFX_ASSERT(ctx);
1149 PICO_GFX_ASSERT(ctx);
1150 ctx->state.viewport = (pg_rect_t){ x, y, w, h};
1155 PICO_GFX_ASSERT(ctx);
1170 PICO_GFX_ASSERT(ctx);
1171 ctx->state.scissor = (pg_rect_t){ x, y, w, h};
1176 PICO_GFX_ASSERT(ctx);
1185 pg_set_scissor(ctx, 0, 0, ctx->window_width, ctx->window_height);
1191 PICO_GFX_ASSERT(ctx);
1192 ctx->state.pipeline = pipeline;
1197 PICO_GFX_ASSERT(ctx);
1203 PICO_GFX_ASSERT(ctx);
1204 PICO_GFX_ASSERT(!buffer || buffer->type == PG_BUFFER_TYPE_VERTEX);
1206 PICO_GFX_ASSERT(slot >= 0);
1209 ctx->state.buffers[slot] = buffer;
1214 PICO_GFX_ASSERT(ctx);
1215 memset(&ctx->state.buffers, 0,
sizeof(ctx->state.buffers));
1220 PICO_GFX_ASSERT(ctx);
1221 PICO_GFX_ASSERT(!buffer || buffer->type == PG_BUFFER_TYPE_INDEX);
1222 ctx->state.index_buffer = buffer;
1227 PICO_GFX_ASSERT(ctx);
1228 ctx->state.index_buffer = NULL;
1233 PICO_GFX_ASSERT(shader);
1234 PICO_GFX_ASSERT(name);
1236 int slot = shader->internal.get_img_slot(name);
1238 PICO_GFX_ASSERT(slot >= 0);
1241 shader->textures[slot] = texture;
1246 PICO_GFX_ASSERT(shader);
1247 memset(shader->textures, 0,
sizeof(shader->textures));
1252 PICO_GFX_ASSERT(shader);
1253 PICO_GFX_ASSERT(name);
1255 int slot = shader->internal.get_smp_slot(name);
1257 PICO_GFX_ASSERT(slot >= 0);
1260 shader->samplers[slot] = sampler;
1265 PICO_GFX_ASSERT(shader);
1266 memset(shader->samplers, 0,
sizeof(shader->samplers));
1271 PICO_GFX_ASSERT(ctx);
1273 memset(&ctx->state, 0,
sizeof(pg_state_t));
1289 desc->layout.attrs[slot] = (sg_vertex_attr_state)
1291 .format = pg_map_vertex_format(layout->
attrs[slot].
format),
1303 int step = layout->
bufs[slot].
step;
1307 desc->layout.buffers[slot] = (sg_vertex_buffer_layout_state)
1309 .step_func = SG_VERTEXSTEP_PER_INSTANCE,
1313 desc->layout.buffers[slot].step_rate = (step >= 1) ? step : 1;
1321 PICO_GFX_ASSERT(shader);
1322 PICO_GFX_ASSERT(opts);
1326 sg_pipeline_desc desc = { 0 };
1328 pg_set_attributes(&opts->
layout, &desc);
1329 pg_set_buffers(&opts->
layout, &desc);
1331 desc.primitive_type = pg_map_primitive(opts->
primitive);
1333 pipeline->blend_enabled =
false;
1337 pipeline->blend_enabled =
true;
1338 pipeline->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);
1350 desc.colors[0].pixel_format = SG_PIXELFORMAT_RGBA8;
1353 desc.index_type = SG_INDEXTYPE_UINT32;
1355 desc.index_type = SG_INDEXTYPE_NONE;
1359 desc.depth.pixel_format = SG_PIXELFORMAT_DEPTH;
1360 desc.depth.write_enabled =
true;
1365 desc.face_winding = SG_FACEWINDING_CCW;
1366 desc.shader = shader->handle;
1368 pipeline->ctx = ctx;
1369 pipeline->handle = sg_make_pipeline(&desc);
1370 pipeline->indexed = opts->
indexed;
1371 pipeline->shader = shader;
1373 PICO_GFX_ASSERT(sg_query_pipeline_state(pipeline->handle) == SG_RESOURCESTATE_VALID);
1380 PICO_GFX_ASSERT(pipeline);
1381 sg_destroy_pipeline(pipeline->handle);
1382 PICO_GFX_FREE(pipeline, pipeline->ctx->mem_ctx);
1387 PICO_GFX_ASSERT(pipeline);
1388 return pipeline->shader;
1393 PICO_GFX_ASSERT(pipeline);
1395 if (pipeline->blend_enabled)
1396 return &pipeline->blend_mode;
1409 shader->internal = internal;
1412 PICO_GFX_ASSERT(shader->desc);
1414 shader->handle = sg_make_shader(shader->desc);
1416 shader->uniform_blocks = pg_hashtable_new(16, PICO_GFX_HASHTABLE_KEY_SIZE,
1417 sizeof(pg_uniform_block_t),
1420 shader->arena = pg_arena_new(512, ctx->mem_ctx);
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);
1436 PICO_GFX_ASSERT(shader);
1437 return shader->handle.id;
1444 PICO_GFX_ASSERT(shader);
1445 PICO_GFX_ASSERT(name);
1446 PICO_GFX_ASSERT(data);
1448 pg_uniform_block_t* block = pg_hashtable_get(shader->uniform_blocks, name);
1452 pg_alloc_uniform_block(shader, name);
1453 block = pg_hashtable_get(shader->uniform_blocks, name);
1456 PICO_GFX_ASSERT(block);
1458 memcpy(block->data, data, block->size);
1462 int width,
int height,
1463 const uint8_t* data,
size_t size,
1466 PICO_GFX_ASSERT(width > 0);
1467 PICO_GFX_ASSERT(height > 0);
1468 PICO_GFX_ASSERT(data);
1469 PICO_GFX_ASSERT(size > 0);
1474 PICO_GFX_ASSERT(opts->
mipmaps >= 0);
1478 sg_image_desc desc = { 0 };
1480 desc.pixel_format = SG_PIXELFORMAT_RGBA8;
1482 desc.width = texture->width = width;
1483 desc.height = texture->height = height;
1485 desc.num_mipmaps = opts->
mipmaps;
1486 desc.data.subimage[0][0] = (sg_range){ .ptr = data, .size = size };
1489 texture->target =
false;
1490 texture->handle = sg_make_image(&desc);
1492 PICO_GFX_ASSERT(sg_query_image_state(texture->handle) == SG_RESOURCESTATE_VALID);
1498 int width,
int height,
1501 PICO_GFX_ASSERT(width > 0);
1502 PICO_GFX_ASSERT(height > 0);
1507 PICO_GFX_ASSERT(opts->
mipmaps >= 0);
1511 sg_image_desc desc = { 0 };
1513 desc.render_target =
true;
1514 desc.pixel_format = SG_PIXELFORMAT_RGBA8;
1516 desc.width = texture->width = width;
1517 desc.height = texture->height = height;
1519 desc.num_mipmaps = opts->
mipmaps;
1522 texture->handle = sg_make_image(&desc);
1523 texture->target =
true;
1525 PICO_GFX_ASSERT(sg_query_image_state(texture->handle) == SG_RESOURCESTATE_VALID);
1527 desc.pixel_format = SG_PIXELFORMAT_DEPTH;
1528 texture->depth_handle = sg_make_image(&desc);
1530 PICO_GFX_ASSERT(sg_query_image_state(texture->depth_handle) == SG_RESOURCESTATE_VALID);
1533 texture->attachments = sg_make_attachments(&(sg_attachments_desc)
1535 .colors[0].image = texture->handle,
1536 .depth_stencil.image = texture->depth_handle,
1544 if (texture->target)
1546 sg_destroy_image(texture->depth_handle);
1549 sg_destroy_image(texture->handle);
1550 PICO_GFX_FREE(texture, texture->ctx->mem_ctx);
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);
1564 PICO_GFX_ASSERT(texture);
1565 return texture->handle.id;
1570 PICO_GFX_ASSERT(texture);
1573 *width = texture->width;
1576 *height = texture->height;
1586 sg_sampler_desc desc = { 0 };
1588 desc.min_filter = (opts->
smooth) ? SG_FILTER_LINEAR : SG_FILTER_NEAREST;
1589 desc.mag_filter = (opts->
smooth) ? SG_FILTER_LINEAR : SG_FILTER_NEAREST;
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;
1595 sampler->handle = sg_make_sampler(&desc);
1602 sg_destroy_sampler(sampler->handle);
1603 PICO_GFX_FREE(sampler, sampler->ctx->mem_ctx);
1608 PICO_GFX_ASSERT(shader);
1610 pg_hashtable_iterator_t iterator;
1611 pg_hashtable_init_iterator(shader->uniform_blocks, &iterator);
1616 while (pg_hashtable_iterator_next(&iterator, &key, &value))
1618 pg_uniform_block_t* block = (pg_uniform_block_t*)value;
1620 sg_range range = { .ptr = block->data, .size = block->size };
1622 sg_apply_uniforms(block->slot, &range);
1630 size_t max_elements,
1631 size_t element_size)
1633 PICO_GFX_ASSERT(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;
1645 buffer->handle = sg_make_buffer(&(sg_buffer_desc)
1647 .type = SG_BUFFERTYPE_VERTEXBUFFER,
1648 .usage = pg_map_usage(usage),
1649 .data = { .ptr = data, .size = count * element_size },
1650 .size = buffer->size
1653 PICO_GFX_ASSERT(sg_query_buffer_state(buffer->handle) == SG_RESOURCESTATE_VALID);
1662 size_t max_elements)
1664 PICO_GFX_ASSERT(ctx);
1669 buffer->type = PG_BUFFER_TYPE_INDEX;
1670 buffer->usage = usage;
1671 buffer->count = count;
1672 buffer->size = max_elements *
sizeof(uint32_t);
1675 buffer->handle = sg_make_buffer(&(sg_buffer_desc)
1677 .type = SG_BUFFERTYPE_INDEXBUFFER,
1678 .usage = pg_map_usage(usage),
1679 .data = { .ptr = data, .size = count *
sizeof(uint32_t) },
1680 .size = buffer->size
1683 PICO_GFX_ASSERT(sg_query_buffer_state(buffer->handle) == SG_RESOURCESTATE_VALID);
1690 PICO_GFX_ASSERT(buffer);
1691 PICO_GFX_ASSERT(data);
1692 PICO_GFX_ASSERT(count > 0);
1694 sg_update_buffer(buffer->handle, &(sg_range)
1697 .size = count * buffer->element_size
1700 buffer->count = count;
1706 PICO_GFX_ASSERT(buffer);
1707 PICO_GFX_ASSERT(data);
1708 PICO_GFX_ASSERT(count > 0);
1710 int offset = sg_append_buffer(buffer->handle, &(sg_range)
1713 .size = count * buffer->element_size
1716 buffer->count = count;
1717 buffer->offset = offset;
1724 PICO_GFX_ASSERT(buffer);
1725 return buffer->offset;
1730 PICO_GFX_ASSERT(buffer);
1731 buffer->offset = offset;
1736 PICO_GFX_ASSERT(buffer);
1738 sg_destroy_buffer(buffer->handle);
1740 buffer->handle = sg_make_buffer(&(sg_buffer_desc)
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
1748 PICO_GFX_ASSERT(sg_query_buffer_state(buffer->handle) == SG_RESOURCESTATE_VALID);
1753 PICO_GFX_ASSERT(buffer);
1754 sg_destroy_buffer(buffer->handle);
1755 PICO_GFX_FREE(buffer, buffer->ctx->mem_ctx);
1758static void pg_apply_view_state(
const pg_ctx_t* ctx)
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);
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);
1767static void pg_apply_textures(
const pg_shader_t* shader, sg_bindings* bindings)
1771 if (!shader->textures[i])
1774 bindings->images[i] = shader->textures[i]->handle;
1778static void pg_apply_samplers(
const pg_shader_t* shader, sg_bindings* bindings)
1782 if (!shader->samplers[i])
1785 bindings->samplers[i] = shader->samplers[i]->handle;
1789static void pg_apply_buffers(
const pg_ctx_t* ctx, sg_bindings* bindings)
1793 for (
int slot = 0; buffers[slot] != NULL; slot++)
1795 bindings->vertex_buffer_offsets[slot] = buffers[slot]->offset;
1796 bindings->vertex_buffers[slot] = buffers[slot]->handle;
1800void pg_draw(
const pg_ctx_t* ctx,
size_t start,
size_t count,
size_t instances)
1802 PICO_GFX_ASSERT(ctx);
1803 PICO_GFX_ASSERT(ctx->pass_active);
1805 sg_bindings bindings = { 0 };
1809 pg_apply_textures(pipeline->shader, &bindings);
1810 pg_apply_samplers(pipeline->shader, &bindings);
1812 pg_apply_buffers(ctx, &bindings);
1813 pg_apply_view_state(ctx);
1815 if (ctx->state.index_buffer)
1817 bindings.index_buffer_offset = ctx->state.index_buffer->offset;
1818 bindings.index_buffer = ctx->state.index_buffer->handle;
1821 sg_apply_pipeline(pipeline->handle);
1822 sg_apply_bindings(&bindings);
1823 pg_apply_uniforms(pipeline->shader);
1825 sg_draw(start, count, instances);
1832static void pg_alloc_uniform_block(
pg_shader_t* shader,
const char* name)
1834 PICO_GFX_ASSERT(shader);
1835 PICO_GFX_ASSERT(name);
1837 size_t size = shader->internal.get_uniformblock_size(name);
1839 pg_uniform_block_t block =
1841 .slot = shader->internal.get_uniformblock_slot(name),
1842 .data = pg_arena_alloc(shader->arena, size),
1846 pg_hashtable_put(shader->uniform_blocks, name, &block);
1849static sg_primitive_type pg_map_primitive(
pg_primitive_t primitive)
1856 case PG_POINTS:
return SG_PRIMITIVETYPE_POINTS;
1857 case PG_LINES:
return SG_PRIMITIVETYPE_LINES;
1861 default: PICO_GFX_ASSERT(
false);
return SG_PRIMITIVETYPE_TRIANGLES;
1872 case PG_ZERO:
return SG_BLENDFACTOR_ZERO;
1873 case PG_ONE:
return SG_BLENDFACTOR_ONE;
1882 default: PICO_GFX_ASSERT(
false);
return SG_BLENDFACTOR_ONE;
1893 case PG_ADD:
return SG_BLENDOP_ADD;
1896 default: PICO_GFX_ASSERT(
false);
return SG_BLENDOP_ADD;
1922 default: PICO_GFX_ASSERT(
false);
return SG_VERTEXFORMAT_INVALID;
1933 default: PICO_GFX_ASSERT(
false);
return SG_USAGE_IMMUTABLE;
1937static sg_buffer_type pg_map_buffer_type(pg_buffer_type_t type)
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;
1947static void pg_log(
const char* fmt, ...)
1949 PICO_GFX_ASSERT(fmt);
1952 va_start(args, fmt);
1959static void pg_log_sg(
const char* tag,
1961 uint32_t log_item_id,
1962 const char* message_or_null,
1964 const char* filename_or_null,
1970 static const char* level[] =
1980 if (message_or_null && !filename_or_null)
1982 PICO_GFX_LOG(
"Tag: %s, Level: %s, Message: %s",
1983 tag, level[log_level], message_or_null);
1986 if (!message_or_null && filename_or_null)
1988 PICO_GFX_LOG(
"Tag: %s, Level: %s, File: %s, Line: %d",
1989 tag, level[log_level], filename_or_null, line_nr);
1992 if (message_or_null && filename_or_null)
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);
2008} pg_hashtable_entry_t;
2010struct pg_hashtable_t
2016 pg_hashtable_entry_t* entries;
2029static size_t pg_hashtable_compute_hash(
const pg_hashtable_t* ht,
2032static bool pg_hashtable_key_equal(
const pg_hashtable_t* ht,
2036static void pg_hashtable_copy_value(pg_hashtable_t* ht,
2037 pg_hashtable_entry_t* entry,
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);
2049static pg_hashtable_t* pg_hashtable_new(
size_t capacity,
2054 bool power_of_two = (0 == (capacity & (capacity - 1)));
2056 PICO_GFX_ASSERT(capacity > 2 && power_of_two);
2057 PICO_GFX_ASSERT(key_size > 0);
2058 PICO_GFX_ASSERT(value_size > 0);
2060 if (capacity <= 2 || !power_of_two)
2063 if (0 == key_size || 0 == value_size)
2066 pg_hashtable_t* ht = PICO_GFX_MALLOC(
sizeof(pg_hashtable_t), mem_ctx);
2071 ht->mem_ctx = mem_ctx;
2072 ht->capacity = capacity;
2074 ht->key_size = key_size;
2075 ht->value_size = value_size;
2077 ht->entries = PICO_GFX_MALLOC(capacity *
sizeof(pg_hashtable_entry_t), mem_ctx);
2081 PICO_GFX_FREE(ht, mem_ctx);
2085 ht->keys = PICO_GFX_MALLOC(capacity * key_size, mem_ctx);
2089 PICO_GFX_FREE(ht->entries, mem_ctx);
2090 PICO_GFX_FREE(ht, mem_ctx);
2095 ht->values = PICO_GFX_MALLOC(capacity * value_size, mem_ctx);
2099 PICO_GFX_FREE(ht->entries, mem_ctx);
2100 PICO_GFX_FREE(ht->keys, mem_ctx);
2101 PICO_GFX_FREE(ht, mem_ctx);
2105 for (
size_t i = 0; i < capacity; i++)
2107 pg_hashtable_entry_t* entry = &ht->entries[i];
2109 entry->key = (
char*)ht->keys + i * key_size;
2110 entry->value = (
char*)ht->values + i * value_size;
2116static void pg_hashtable_free(pg_hashtable_t* ht)
2118 PICO_GFX_ASSERT(NULL != ht);
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);
2126static void pg_hashtable_init_iterator(
const pg_hashtable_t* ht,
2127 pg_hashtable_iterator_t* iterator)
2129 PICO_GFX_ASSERT(NULL != ht);
2131 iterator->index = 0;
2132 iterator->count = 0;
2135static bool pg_hashtable_iterator_next(pg_hashtable_iterator_t* iterator,
2136 char** key,
void** value)
2138 PICO_GFX_ASSERT(NULL != iterator);
2140 const pg_hashtable_t* ht = iterator->ht;
2142 if (iterator->count >= ht->capacity)
2145 while (iterator->index < ht->capacity)
2147 pg_hashtable_entry_t* entry = &ht->entries[iterator->index];
2149 if (entry->hash != 0)
2155 *value = entry->value;
2169static void pg_hashtable_put(pg_hashtable_t* ht,
2173 PICO_GFX_ASSERT(NULL != ht);
2175 if (ht->size == ht->capacity)
2177 pg_hashtable_rehash(ht);
2178 PICO_GFX_ASSERT(ht->capacity > 0);
2181 pg_hash_t hash = pg_hashtable_compute_hash(ht, key);
2183 PICO_GFX_ASSERT(hash > 0);
2185 size_t start_index = hash % ht->capacity;
2186 size_t index = start_index;
2190 pg_hashtable_entry_t* entry = &ht->entries[index];
2192 if (entry->hash == hash && pg_hashtable_key_equal(ht, key, entry->key))
2194 pg_hashtable_copy_value(ht, entry, value);
2198 if (entry->hash == 0)
2202 strncpy(entry->key, key, ht->key_size);
2203 pg_hashtable_copy_value(ht, entry, value);
2210 index = (index + 1) % ht->capacity;
2212 }
while (index != start_index);
2214 start_index = index;
2215 index = (index + 1) % ht->capacity;
2217 while (index != start_index)
2219 pg_hashtable_entry_t* entry = &ht->entries[index];
2221 if (entry->hash == hash && pg_hashtable_key_equal(ht, key, entry->key))
2228 index = (index + 1) % ht->capacity;
2232static void* pg_hashtable_get(
const pg_hashtable_t* ht,
const char* key)
2234 PICO_GFX_ASSERT(NULL != ht);
2236 pg_hash_t hash = pg_hashtable_compute_hash(ht, key);
2238 PICO_GFX_ASSERT(hash > 0);
2240 size_t start_index = hash % ht->capacity;
2241 size_t index = start_index;
2245 pg_hashtable_entry_t* entry = &ht->entries[index];
2247 if (entry->hash == hash && pg_hashtable_key_equal(ht, key, entry->key))
2249 return entry->value;
2252 index = (index + 1) % ht->capacity;
2254 }
while (index != start_index);
2263static bool pg_hashtable_key_equal(
const pg_hashtable_t* ht,
2267 return 0 == strncmp(key1, key2, ht->key_size);
2270static void pg_hashtable_copy_value(pg_hashtable_t* ht,
2271 pg_hashtable_entry_t* entry,
2274 memcpy(entry->value, value, ht->value_size);
2277static void pg_hashtable_swap_size(
size_t* a,
size_t* b)
2284static void pg_hashtable_swap_ptr(
void** a,
void** b)
2291static void pg_hashtable_swap(pg_hashtable_t* ht1, pg_hashtable_t* ht2)
2293 pg_hashtable_swap_size(&ht1->capacity, &ht2->capacity);
2294 pg_hashtable_swap_size(&ht1->size, &ht2->size);
2296 pg_hashtable_swap_ptr((
void**)&ht1->entries, (
void**)&ht2->entries);
2298 pg_hashtable_swap_size(&ht1->key_size, &ht2->key_size);
2299 pg_hashtable_swap_ptr((
void**)&ht1->keys, (
void**)&ht2->keys);
2301 pg_hashtable_swap_size(&ht1->value_size, &ht2->value_size);
2302 pg_hashtable_swap_ptr(&ht1->values, &ht2->values);
2305static void pg_hashtable_rehash(pg_hashtable_t* ht)
2307 pg_hashtable_t* new_ht = pg_hashtable_new(ht->capacity * 2,
2312 pg_hashtable_iterator_t iterator;
2313 pg_hashtable_init_iterator(ht, &iterator);
2318 while (pg_hashtable_iterator_next(&iterator, &key, &value))
2320 pg_hashtable_put(new_ht, key, value);
2323 pg_hashtable_swap(ht, new_ht);
2325 pg_hashtable_free(new_ht);
2332static size_t pg_hashtable_compute_hash(
const pg_hashtable_t* ht,
const char* key)
2335#ifdef PICO_GFX_32BIT
2336 static const uint32_t offset_basis = 0x811C9DC5;
2337 static const uint32_t prime = 0x1000193;
2339 static const uint64_t offset_basis = 0xCBF29CE484222325;
2340 static const uint64_t prime = 0x100000001B3;
2343 const char* data = key;
2345 pg_hash_t hash = offset_basis;
2347 for (
size_t i = 0; i < ht->key_size; i++) {
2348 hash ^= (pg_hash_t)data[i];
2371static pg_arena_t* pg_arena_new(
size_t size,
void* mem_ctx)
2373 PICO_GFX_ASSERT(size > 0);
2375 pg_arena_t* arena = PICO_GFX_MALLOC(
sizeof(pg_arena_t), mem_ctx);
2377 memset(arena, 0,
sizeof(pg_arena_t));
2379 arena->mem_ctx = mem_ctx;
2380 arena->capacity = size * 2;
2381 arena->block = PICO_GFX_MALLOC(arena->capacity, mem_ctx);
2387static void* pg_arena_alloc(pg_arena_t* arena,
size_t size)
2389 if (arena->size + size > arena->capacity)
2391 while (arena->size + size >= arena->capacity)
2393 arena->capacity *= 2;
2396 arena->block = PICO_GFX_REALLOC(arena->block, arena->capacity, arena->mem_ctx);
2399 void* mem = (
char*)arena->block + arena->size;
2401 arena->size += size;
2406static void pg_arena_free(pg_arena_t* arena)
2408 PICO_GFX_FREE(arena->block, arena->mem_ctx);
2409 PICO_GFX_FREE(arena, arena->mem_ctx);
2412#define SOKOL_GFX_IMPL
2413#include "sokol_gfx.h"
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