Browse Source

sbatch_pipeline API

the sbatch_pipeline helps users make a pipeline object that is correct and usable for the sbatch rendering API.
Stuart Adams 4 years ago
parent
commit
36e9b36b92
1 changed files with 171 additions and 42 deletions
  1. 171 42
      util/sokol_spritebatch.h

+ 171 - 42
util/sokol_spritebatch.h

@@ -70,6 +70,7 @@ extern "C" {
 
     typedef struct sbatch_desc {
         int context_pool_size;
+        int pipeline_pool_size;
         sg_pixel_format color_format;
         sg_pixel_format depth_format;
         int sample_count;
@@ -79,17 +80,15 @@ extern "C" {
         uint32_t id;
     } sbatch_context;
 
-    enum
-    {
-        SBATCH_MAX_SPRITES = (1 << 16) / 4,
-        SBATCH_DEFAULT_SPRITES = SBATCH_MAX_SPRITES / 4
-    };
+    typedef struct sbatch_pipeline {
+        uint32_t id;
+    } sbatch_pipeline;
 
     typedef struct sbatch_context_desc {
         int canvas_width;
         int canvas_height;
         int max_sprites;
-        sg_pipeline pipeline;
+        sbatch_pipeline pipeline;
         const char* label;
     } sbatch_context_desc;
 
@@ -97,6 +96,9 @@ extern "C" {
     SOKOL_SPRITEBATCH_API_DECL void sbatch_shutdown(void);
     SOKOL_SPRITEBATCH_API_DECL int sbatch_frame();
 
+    SOKOL_SPRITEBATCH_API_DECL sbatch_pipeline sbatch_make_pipeline(const sg_pipeline_desc* desc);
+    SOKOL_SPRITEBATCH_API_DECL void sbatch_destroy_pipeline(sbatch_pipeline context);
+
     SOKOL_SPRITEBATCH_API_DECL sbatch_context sbatch_make_context(const sbatch_context_desc* desc);
     SOKOL_SPRITEBATCH_API_DECL void sbatch_destroy_context(sbatch_context context);
 
@@ -199,7 +201,7 @@ typedef struct {
     _sbatch_vertex* vertices;
     sg_image* images;
     sg_buffer vertex_buffer;
-    sg_pipeline pipeline;
+    sbatch_pipeline pipeline;
     int update_frame_index;
     _sbatch_fs_uniform_state fs_uniform_state;
 } _sbatch_context;
@@ -216,6 +218,18 @@ typedef struct {
     _sbatch_context* contexts;
 } _sbatch_context_pool;
 
+typedef struct {
+    _sbatch_str label;
+    _sbatch_slot slot;
+    sg_pipeline_desc desc;
+    sg_pipeline pipeline;
+} _sbatch_pipeline;
+
+typedef struct {
+    _sbatch_pool pool;
+    _sbatch_pipeline* pipelines;
+} _sbatch_pipeline_pool;
+
 typedef struct {
     sg_image image;
     int      width;
@@ -234,9 +248,10 @@ typedef struct {
     _sbatch_slot slot;
     sg_bindings bindings;
     sg_shader shader;
-    sg_pipeline pipeline;
+    sbatch_pipeline pipeline;
     sbatch_context ctx_id;
     _sbatch_context_pool context_pool;
+    _sbatch_pipeline_pool pipeline_pool;
     _sbatch_sprite_pool sprite_pool;
     sg_buffer index_buffer;
     int frame_index;
@@ -402,7 +417,6 @@ static _sbatch_str _sbatch_make_str(const char* str) {
     return res;
 }
 
-/*=== CONTEXT POOL ===========================================================*/
 static void _sbatch_init_pool(_sbatch_pool* pool, int num) {
     SOKOL_ASSERT(pool && (num >= 1));
     /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */
@@ -461,24 +475,6 @@ static void _sbatch_pool_free_index(_sbatch_pool* pool, int slot_index) {
     SOKOL_ASSERT(pool->queue_top <= (pool->size - 1));
 }
 
-static void _sbatch_setup_context_pool(const sbatch_desc* desc) {
-    SOKOL_ASSERT(desc);
-    /* note: the pool will have an additional item, since slot 0 is reserved */
-    SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SBATCH_MAX_POOL_SIZE));
-    _sbatch_init_pool(&_sbatch.context_pool.pool, desc->context_pool_size);
-    const size_t pool_byte_size = sizeof(_sbatch_context) * (size_t)_sbatch.context_pool.pool.size;
-    _sbatch.context_pool.contexts = (_sbatch_context*)SOKOL_MALLOC(pool_byte_size);
-    SOKOL_ASSERT(_sbatch.context_pool.contexts);
-    memset(_sbatch.context_pool.contexts, 0, pool_byte_size);
-}
-
-static void _sbatch_discard_context_pool(void) {
-    SOKOL_ASSERT(_sbatch.context_pool.contexts);
-    SOKOL_FREE(_sbatch.context_pool.contexts);
-    _sbatch.context_pool.contexts = 0;
-    _sbatch_discard_pool(&_sbatch.context_pool.pool);
-}
-
 /* allocate the slot at slot_index:
     - bump the slot's generation counter
     - create a resource id from the generation counter and slot index
@@ -507,6 +503,24 @@ static int _sbatch_slot_index(uint32_t id) {
     return slot_index;
 }
 
+static void _sbatch_setup_context_pool(const sbatch_desc* desc) {
+    SOKOL_ASSERT(desc);
+    /* note: the pool will have an additional item, since slot 0 is reserved */
+    SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SBATCH_MAX_POOL_SIZE));
+    _sbatch_init_pool(&_sbatch.context_pool.pool, desc->context_pool_size);
+    const size_t pool_byte_size = sizeof(_sbatch_context) * (size_t)_sbatch.context_pool.pool.size;
+    _sbatch.context_pool.contexts = (_sbatch_context*)SOKOL_MALLOC(pool_byte_size);
+    SOKOL_ASSERT(_sbatch.context_pool.contexts);
+    memset(_sbatch.context_pool.contexts, 0, pool_byte_size);
+}
+
+static void _sbatch_discard_context_pool(void) {
+    SOKOL_ASSERT(_sbatch.context_pool.contexts);
+    SOKOL_FREE(_sbatch.context_pool.contexts);
+    _sbatch.context_pool.contexts = 0;
+    _sbatch_discard_pool(&_sbatch.context_pool.pool);
+}
+
 /* get context pointer without id-check */
 static _sbatch_context* _sbatch_context_at(uint32_t ctx_id) {
     SOKOL_ASSERT(SG_INVALID_ID != ctx_id);
@@ -546,6 +560,65 @@ static sbatch_context _sbatch_alloc_context(void) {
     return ctx_id;
 }
 
+///////////////////////////////
+
+static void _sbatch_setup_pipeline_pool(const sbatch_desc* desc) {
+    SOKOL_ASSERT(desc);
+    /* note: the pool will have an additional item, since slot 0 is reserved */
+    SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SBATCH_MAX_POOL_SIZE));
+    _sbatch_init_pool(&_sbatch.pipeline_pool.pool, desc->pipeline_pool_size);
+    const size_t pool_byte_size = sizeof(_sbatch_pipeline) * (size_t)_sbatch.pipeline_pool.pool.size;
+    _sbatch.pipeline_pool.pipelines = (_sbatch_pipeline*)SOKOL_MALLOC(pool_byte_size);
+    SOKOL_ASSERT(_sbatch.pipeline_pool.pipelines);
+    memset(_sbatch.pipeline_pool.pipelines, 0, pool_byte_size);
+}
+
+static void _sbatch_discard_pipeline_pool(void) {
+    SOKOL_ASSERT(_sbatch.pipeline_pool.pipelines);
+    SOKOL_FREE(_sbatch.pipeline_pool.pipelines);
+    _sbatch.pipeline_pool.pipelines = 0;
+    _sbatch_discard_pool(&_sbatch.pipeline_pool.pool);
+}
+
+/* get pipeline pointer without id-check */
+static _sbatch_pipeline* _sbatch_pipeline_at(uint32_t pip_id) {
+    SOKOL_ASSERT(SG_INVALID_ID != pip_id);
+    const int slot_index = _sbatch_slot_index(pip_id);
+    SOKOL_ASSERT((slot_index > _SBATCH_INVALID_SLOT_INDEX) && (slot_index < _sbatch.pipeline_pool.pool.size));
+    return &_sbatch.pipeline_pool.pipelines[slot_index];
+}
+
+/* get pipeline pointer with id-check, returns 0 if no match */
+static _sbatch_pipeline* _sbatch_lookup_pipeline(uint32_t pip_id) {
+    if (SG_INVALID_ID != pip_id) {
+        _sbatch_pipeline* pip = _sbatch_pipeline_at(pip_id);
+        if (pip->slot.id == pip_id) {
+            return pip;
+        }
+    }
+    return NULL;
+}
+
+/* make pipeline handle from raw uint32_t id */
+static sbatch_pipeline _sbatch_make_pip_id(uint32_t pip_id) {
+    sbatch_pipeline pip;
+    pip.id = pip_id;
+    return pip;
+}
+
+static sbatch_pipeline _sbatch_alloc_pipeline(void) {
+    sbatch_pipeline pip_id;
+    const int slot_index = _sbatch_pool_alloc_index(&_sbatch.pipeline_pool.pool);
+    if (_SBATCH_INVALID_SLOT_INDEX != slot_index) {
+        pip_id = _sbatch_make_pip_id(_sbatch_slot_alloc(&_sbatch.pipeline_pool.pool, &_sbatch.pipeline_pool.pipelines[slot_index].slot, slot_index));
+    }
+    else {
+        /* pool is exhausted */
+        pip_id = _sbatch_make_pip_id(SG_INVALID_ID);
+    }
+    return pip_id;
+}
+
 static sbatch_context_desc _sbatch_context_desc_defaults(const sbatch_context_desc* desc) {
     sbatch_context_desc res = *desc;
     res.max_sprites = _SBATCH_DEFAULT(res.max_sprites, 4096);
@@ -598,7 +671,6 @@ static void _sbatch_init_context(sbatch_context ctx_id, const sbatch_context_des
 static void _sbatch_destroy_context(sbatch_context ctx_id) {
     _sbatch_context* ctx = _sbatch_lookup_context(ctx_id.id);
     if (ctx) {
-        ctx->sprite_count = 0;
         if (ctx->vertices) {
             SOKOL_FREE(ctx->vertices);
         }
@@ -613,6 +685,59 @@ static void _sbatch_destroy_context(sbatch_context ctx_id) {
     }
 }
 
+
+static sg_pipeline_desc _sbatch_pipeline_desc_defaults(const sg_pipeline_desc* desc) {
+    sg_pipeline_desc pipeline_desc = *desc;
+
+    pipeline_desc.color_count = _SBATCH_DEFAULT(pipeline_desc.color_count, 1);
+    if (pipeline_desc.color_count == 1) {
+        pipeline_desc.colors[0].blend.enabled = _SBATCH_DEFAULT(desc->colors[0].blend.enabled, true);
+        pipeline_desc.colors[0].blend.src_factor_rgb = _SBATCH_DEFAULT(desc->colors[0].blend.src_factor_rgb, SG_BLENDFACTOR_ONE);
+        pipeline_desc.colors[0].blend.src_factor_alpha = _SBATCH_DEFAULT(desc->colors[0].blend.src_factor_rgb, SG_BLENDFACTOR_ONE);
+        pipeline_desc.colors[0].blend.dst_factor_rgb = _SBATCH_DEFAULT(desc->colors[0].blend.src_factor_rgb, SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA);
+        pipeline_desc.colors[0].blend.dst_factor_alpha = _SBATCH_DEFAULT(desc->colors[0].blend.src_factor_rgb, SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA);
+    }
+
+    pipeline_desc.shader.id = _SBATCH_DEFAULT(desc->shader.id, _sbatch.shader.id);
+    pipeline_desc.index_type = SG_INDEXTYPE_UINT16;
+    pipeline_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3;
+    pipeline_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
+    pipeline_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
+    pipeline_desc.label = _SBATCH_DEFAULT(desc->label, "sokol-spritebatch-pipline");
+    return pipeline_desc;
+}
+
+static void _sbatch_init_pipeline(sbatch_pipeline pip_id, const sg_pipeline_desc* in_desc) {
+    sg_push_debug_group("sokol-spritebatch");
+
+    SOKOL_ASSERT((pip_id.id != SG_INVALID_ID) && in_desc);
+
+    _sbatch_pipeline* pip = _sbatch_lookup_pipeline(pip_id.id);
+    SOKOL_ASSERT(pip);
+
+    pip->desc = _sbatch_pipeline_desc_defaults(in_desc);
+
+    pip->label = _sbatch_make_str(pip->desc.label);
+
+    pip->desc.label = NULL;
+
+    pip->pipeline = sg_make_pipeline(&pip->desc);
+    SOKOL_ASSERT(pip->pipeline.id != SG_INVALID_ID);
+
+    sg_pop_debug_group();
+}
+
+static void _sbatch_destroy_pipeline(sbatch_pipeline pip_id) {
+    _sbatch_pipeline* pip = _sbatch_lookup_pipeline(pip_id.id);
+    if (pip) {
+        sg_push_debug_group("sokol-spritebatch");
+        sg_destroy_pipeline(pip->pipeline);
+        sg_pop_debug_group();
+        memset(pip, 0, sizeof(*pip));
+        _sbatch_pool_free_index(&_sbatch.pipeline_pool.pool, _sbatch_slot_index(pip_id.id));
+    }
+}
+
 static uint32_t _sbatch_pack_color_bytes(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
     return (uint32_t)a << 24 | (uint32_t)b << 16 | (uint32_t)g << 8 | (uint32_t)r;
 }
@@ -774,39 +899,42 @@ SOKOL_API_IMPL void sbatch_setup(const sbatch_desc* desc) {
 
     sbatch_desc batch_desc = *desc;
     batch_desc.context_pool_size = _SBATCH_DEFAULT(batch_desc.context_pool_size, 32);
+    batch_desc.pipeline_pool_size = _SBATCH_DEFAULT(batch_desc.pipeline_pool_size, 32);
     _sbatch_setup_context_pool(&batch_desc);
+    _sbatch_setup_pipeline_pool(&batch_desc);
     _sbatch_setup_sprite_pool();
 
     _sbatch.shader = sg_make_shader(spritebatch_shader_desc(sg_query_backend()));
 
     sg_pipeline_desc pipeline_desc;
     memset(&pipeline_desc, 0, sizeof(sg_pipeline_desc));
-    pipeline_desc.color_count = 1;
-    pipeline_desc.colors[0].blend.enabled = true;
-    pipeline_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_ONE;
-    pipeline_desc.colors[0].blend.src_factor_alpha = SG_BLENDFACTOR_ONE;
-    pipeline_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
-    pipeline_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
     pipeline_desc.shader = _sbatch.shader;
-    pipeline_desc.index_type = SG_INDEXTYPE_UINT16;
-    pipeline_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3;
-    pipeline_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
-    pipeline_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
     pipeline_desc.label = "spritebatch-default-pipeline";
-
-    _sbatch.pipeline = sg_make_pipeline(&pipeline_desc);
+    _sbatch.pipeline = sbatch_make_pipeline(&pipeline_desc);
 
     _sbatch_init_index_buffer();
 }
 
 SOKOL_API_IMPL void sbatch_shutdown(void) {
     sg_destroy_buffer(_sbatch.index_buffer);
-    sg_destroy_pipeline(_sbatch.pipeline);
+    sbatch_destroy_pipeline(_sbatch.pipeline);
     sg_destroy_shader(_sbatch.shader);
     _sbatch_discard_context_pool();
     SOKOL_FREE(_sbatch.sprite_pool.data);
 }
 
+SOKOL_SPRITEBATCH_API_DECL sbatch_pipeline sbatch_make_pipeline(const sg_pipeline_desc* desc) {
+    SOKOL_ASSERT(desc);
+    const sbatch_pipeline result = _sbatch_alloc_pipeline();
+    _sbatch_init_pipeline(result, desc);
+    return result;
+}
+
+SOKOL_SPRITEBATCH_API_DECL void sbatch_destroy_pipeline(sbatch_pipeline pipeline) {
+    SOKOL_ASSERT(pipeline.id != SG_INVALID_ID);
+    _sbatch_destroy_pipeline(pipeline);
+}
+
 SOKOL_API_IMPL sbatch_context sbatch_make_context(const sbatch_context_desc* desc) {
     SOKOL_ASSERT(desc);
     const sbatch_context result = _sbatch_alloc_context();
@@ -1085,7 +1213,8 @@ void sbatch_end(void) {
     int base_element = 0;
     sg_image current_image = ctx->images[0];
 
-    sg_apply_pipeline(ctx->pipeline);
+    const _sbatch_pipeline* pipeline = _sbatch_pipeline_at(ctx->pipeline.id);
+    sg_apply_pipeline(pipeline->pipeline);
 
     _sbatch_mat4x4 matrix =
         _sbatch_orthographic_off_center(0.0f, (float)ctx->desc.canvas_width, (float)ctx->desc.canvas_height, 0.0f, 0.0f, 1000.0f);