|
@@ -1,4 +1,4 @@
|
|
|
-#if defined(SOKOL_IMPL) && !defined(SOKOL_SPRITEBATCH_IMPL)
|
|
|
+#if defined(SOKOL_IMPL) && !defined(SOKOL_SPRITEBATCH_IMPL)
|
|
|
#define SOKOL_SPRITEBATCH_IMPL
|
|
|
#endif
|
|
|
|
|
@@ -22,70 +22,92 @@
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
-typedef enum sb_sprite_flags {
|
|
|
- SB_FLIP_NONE = 0,
|
|
|
- SB_FLIP_X = 1 << 0,
|
|
|
- SB_FLIP_Y = 1 << 1,
|
|
|
- SB_FLIP_BOTH = SB_FLIP_Y | SB_FLIP_X,
|
|
|
- SB_Z_TILT = 1<< 2
|
|
|
-} sb_sprite_flags;
|
|
|
-
|
|
|
-typedef enum sb_sort_mode {
|
|
|
- SB_SORT_MODE_DEFERRED,
|
|
|
- SB_SORT_MODE_TEXTURE,
|
|
|
- SB_SORT_MODE_BACK_TO_FRONT,
|
|
|
- SB_SORT_MODE_FRONT_TO_BACK
|
|
|
-} sb_sort_mode;
|
|
|
-
|
|
|
-typedef struct sb_float2 {
|
|
|
- float x;
|
|
|
- float y;
|
|
|
-} sb_float2;
|
|
|
-
|
|
|
-typedef struct sb_sprite_info {
|
|
|
- sg_image image;
|
|
|
- float width;
|
|
|
- float height;
|
|
|
- sb_float2 position;
|
|
|
- float depth;
|
|
|
- float rotation;
|
|
|
- sb_float2 origin;
|
|
|
- sb_float2 scale;
|
|
|
- uint32_t flags;
|
|
|
- sb_float2 source;
|
|
|
- sg_color color;
|
|
|
-} sb_sprite_info;
|
|
|
-
|
|
|
-typedef struct sb_desc {
|
|
|
- int max_quads;
|
|
|
-} sb_desc;
|
|
|
-
|
|
|
-typedef struct sb_matrix {
|
|
|
- float m[4][4];
|
|
|
-} sb_matrix;
|
|
|
-
|
|
|
-typedef struct sb_viewport {
|
|
|
- int x, y, width, height;
|
|
|
- bool origin_top_left;
|
|
|
-} sb_viewport;
|
|
|
-
|
|
|
-typedef struct sb_render_state {
|
|
|
- sb_sort_mode sort_mode;
|
|
|
- sg_pipeline pipeline;
|
|
|
- sb_matrix transform_matrix;
|
|
|
- sb_viewport viewport;
|
|
|
-} sb_render_state;
|
|
|
-
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_setup(const sb_desc* desc);
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_shutdown(void);
|
|
|
-
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_begin(const sb_render_state* render_state);
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_sprite(const sb_sprite_info* sprite);
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_end(void);
|
|
|
-
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_draw(void);
|
|
|
-
|
|
|
-SOKOL_SPRITEBATCH_API_DECL void sb_premultiply_alpha(uint8_t* pixels, int pixel_count);
|
|
|
+#ifdef __cplusplus
|
|
|
+extern "C" {
|
|
|
+#endif
|
|
|
+
|
|
|
+ typedef enum sbatch_sprite_flags {
|
|
|
+ SBATCH_FLIP_NONE = 0,
|
|
|
+ SBATCH_FLIP_X = 1 << 0,
|
|
|
+ SBATCH_FLIP_Y = 1 << 1,
|
|
|
+ SBATCH_FLIP_BOTH = SBATCH_FLIP_Y | SBATCH_FLIP_X
|
|
|
+ } sbatch_sprite_flags;
|
|
|
+
|
|
|
+ typedef struct sbatch_float2 {
|
|
|
+ float x;
|
|
|
+ float y;
|
|
|
+ } sbatch_float2;
|
|
|
+
|
|
|
+ typedef struct sbatch_rect {
|
|
|
+ float x;
|
|
|
+ float y;
|
|
|
+ float width;
|
|
|
+ float height;
|
|
|
+ } sbatch_rect;
|
|
|
+
|
|
|
+ typedef struct sbatch_sprite {
|
|
|
+ sg_image image;
|
|
|
+ sbatch_float2 position;
|
|
|
+ sbatch_rect source;
|
|
|
+ sg_color color;
|
|
|
+ float rotation;
|
|
|
+ sbatch_float2 origin;
|
|
|
+ sbatch_float2 scale;
|
|
|
+ uint32_t flags;
|
|
|
+ float depth;
|
|
|
+ } sbatch_sprite;
|
|
|
+
|
|
|
+ typedef struct sbatch_sprite_rect {
|
|
|
+ sg_image image;
|
|
|
+ sbatch_rect destination;
|
|
|
+ sbatch_rect source;
|
|
|
+ sg_color color;
|
|
|
+ float rotation;
|
|
|
+ sbatch_float2 origin;
|
|
|
+ uint32_t flags;
|
|
|
+ float depth;
|
|
|
+ } sbatch_sprite_rect;
|
|
|
+
|
|
|
+ typedef struct sbatch_desc {
|
|
|
+ int context_pool_size;
|
|
|
+ sg_pixel_format color_format;
|
|
|
+ sg_pixel_format depth_format;
|
|
|
+ int sample_count;
|
|
|
+ } sbatch_desc;
|
|
|
+
|
|
|
+ typedef struct sbatch_context {
|
|
|
+ uint32_t id;
|
|
|
+ } sbatch_context;
|
|
|
+
|
|
|
+ enum
|
|
|
+ {
|
|
|
+ SBATCH_MAX_SPRITES = (1 << 16) / 4,
|
|
|
+ SBATCH_DEFAULT_SPRITES = SBATCH_MAX_SPRITES / 4
|
|
|
+ };
|
|
|
+
|
|
|
+ typedef struct sbatch_context_desc {
|
|
|
+ int canvas_width;
|
|
|
+ int canvas_height;
|
|
|
+ int max_sprites;
|
|
|
+ sg_pipeline pipeline;
|
|
|
+ const char* label;
|
|
|
+ } sbatch_context_desc;
|
|
|
+
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_setup(const sbatch_desc* desc);
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_shutdown(void);
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL int sbatch_frame();
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_begin(sbatch_context context);
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_push_sprite(const sbatch_sprite* sprite);
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_push_sprite_rect(const sbatch_sprite_rect* sprite);
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_end(void);
|
|
|
+
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_apply_fs_uniforms(int ub_index, const sg_range* data);
|
|
|
+
|
|
|
+ SOKOL_SPRITEBATCH_API_DECL void sbatch_premultiply_alpha_rgba8(uint8_t* pixels, int pixel_count);
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
} /* extern "C" */
|
|
@@ -101,36 +123,50 @@ SOKOL_SPRITEBATCH_API_DECL void sb_premultiply_alpha(uint8_t* pixels, int pixel_
|
|
|
#include <math.h> /* sinf, cosf */
|
|
|
|
|
|
#ifndef SOKOL_API_IMPL
|
|
|
- #define SOKOL_API_IMPL
|
|
|
+#define SOKOL_API_IMPL
|
|
|
#endif
|
|
|
|
|
|
#ifndef SOKOL_ASSERT
|
|
|
- #include <assert.h>
|
|
|
- #define SOKOL_ASSERT(c) assert(c)
|
|
|
+#include <assert.h>
|
|
|
+#define SOKOL_ASSERT(c) assert(c)
|
|
|
#endif
|
|
|
|
|
|
#ifndef SOKOL_MALLOC
|
|
|
- #include <stdlib.h>
|
|
|
- #define SOKOL_MALLOC(s) malloc(s)
|
|
|
- #define SOKOL_FREE(result) free(result)
|
|
|
+#include <stdlib.h>
|
|
|
+#define SOKOL_MALLOC(s) malloc(s)
|
|
|
+#define SOKOL_FREE(result) free(result)
|
|
|
#endif
|
|
|
|
|
|
-#define _SB_IMAGE_SLOT_MASK (0xFFFF)
|
|
|
-#define _SB_DEFAULT(val, def) (((val) == 0) ? (def) : (val))
|
|
|
-#define _SB_MAX_VERTICES (1 << 16)
|
|
|
-#define _SB_MAX_QUADS (_SB_MAX_VERTICES / 4)
|
|
|
-#define _SB_MAX_INDICES (_SB_MAX_QUADS * 6)
|
|
|
-#define _SB_INITIAL_BATCH_CAPACITY 32
|
|
|
+#ifndef SOKOL_DEBUG
|
|
|
+#ifndef NDEBUG
|
|
|
+#define SOKOL_DEBUG (1)
|
|
|
+#endif
|
|
|
+#endif
|
|
|
|
|
|
-#ifndef SB_MAX_DEPTH
|
|
|
-#define SB_MAX_DEPTH 1000.0f
|
|
|
+#ifndef SOKOL_LOG
|
|
|
+#ifdef SOKOL_DEBUG
|
|
|
+#include <stdio.h>
|
|
|
+#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
|
|
|
+#else
|
|
|
+#define SOKOL_LOG(s)
|
|
|
+#endif
|
|
|
#endif
|
|
|
|
|
|
-typedef struct {
|
|
|
- sg_image image;
|
|
|
- int width;
|
|
|
- int height;
|
|
|
-} _sb_sprite_data;
|
|
|
+#define _SBATCH_IMAGE_SLOT_MASK (0xFFFF)
|
|
|
+
|
|
|
+#define _SBATCH_DEFAULT(val, def) (((val) == 0) ? (def) : (val))
|
|
|
+
|
|
|
+#define _SBATCH_MAX_VERTICES (1 << 16)
|
|
|
+#define _SBATCH_MAX_QUADS (_SBATCH_MAX_VERTICES / 4)
|
|
|
+#define _SBATCH_MAX_INDICES (_SBATCH_MAX_QUADS * 6)
|
|
|
+
|
|
|
+#define _SBATCH_INITIAL_BATCH_CAPACITY (32)
|
|
|
+
|
|
|
+#define _SBATCH_INVALID_SLOT_INDEX (0)
|
|
|
+#define _SBATCH_SLOT_SHIFT (16)
|
|
|
+#define _SBATCH_MAX_POOL_SIZE (1<<_SBATCH_SLOT_SHIFT)
|
|
|
+#define _SBATCH_SLOT_MASK (_SBATCH_MAX_POOL_SIZE-1)
|
|
|
+#define _SBATCH_STRBUF_LEN (96)
|
|
|
|
|
|
typedef struct {
|
|
|
float x;
|
|
@@ -139,55 +175,81 @@ typedef struct {
|
|
|
float u;
|
|
|
float v;
|
|
|
uint32_t rgba;
|
|
|
-} _sb_vertex;
|
|
|
+} _sbatch_vertex;
|
|
|
|
|
|
typedef struct {
|
|
|
- _sb_vertex top_left;
|
|
|
- _sb_vertex top_right;
|
|
|
- _sb_vertex bottom_left;
|
|
|
- _sb_vertex bottom_right;
|
|
|
- sg_image image;
|
|
|
- uint64_t sort_key;
|
|
|
-} _sb_quad;
|
|
|
+ uint32_t id;
|
|
|
+ sg_resource_state state;
|
|
|
+} _sbatch_slot;
|
|
|
|
|
|
typedef struct {
|
|
|
- _sb_sprite_data* data;
|
|
|
- size_t size;
|
|
|
-} _sb_sprite_pool;
|
|
|
+ char buf[_SBATCH_STRBUF_LEN];
|
|
|
+} _sbatch_str;
|
|
|
+
|
|
|
+typedef struct _sbatch_fs_uniform_state {
|
|
|
+ int ub_index;
|
|
|
+ const sg_range* data;
|
|
|
+} _sbatch_fs_uniform_state;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ _sbatch_str label;
|
|
|
+ _sbatch_slot slot;
|
|
|
+ sbatch_context_desc desc;
|
|
|
+ int sprite_count;
|
|
|
+ _sbatch_vertex* vertices;
|
|
|
+ sg_image* images;
|
|
|
+ sg_buffer vertex_buffer;
|
|
|
+ sg_pipeline pipeline;
|
|
|
+ int update_frame_index;
|
|
|
+ _sbatch_fs_uniform_state fs_uniform_state;
|
|
|
+} _sbatch_context;
|
|
|
|
|
|
typedef struct {
|
|
|
- sg_image image;
|
|
|
- int base_element;
|
|
|
- int num_elements;
|
|
|
- sb_matrix matrix;
|
|
|
-} _sb_batch;
|
|
|
+ int size;
|
|
|
+ int queue_top;
|
|
|
+ uint32_t* gen_ctrs;
|
|
|
+ int* free_queue;
|
|
|
+} _sbatch_pool;
|
|
|
|
|
|
typedef struct {
|
|
|
- size_t batch_size;
|
|
|
- size_t batch_capacity;
|
|
|
- _sb_batch* batches;
|
|
|
-} _sb_batch_data;
|
|
|
+ _sbatch_pool pool;
|
|
|
+ _sbatch_context* contexts;
|
|
|
+} _sbatch_context_pool;
|
|
|
|
|
|
typedef struct {
|
|
|
- _sb_sprite_pool sprite_pool;
|
|
|
- _sb_quad* quads;
|
|
|
- size_t quad_count;
|
|
|
- _sb_vertex* vertex_buffer_data;
|
|
|
- sg_buffer vertex_buffer;
|
|
|
- sg_buffer index_buffer;
|
|
|
- _sb_batch_data batch_data;
|
|
|
- sg_bindings bindings;
|
|
|
- bool begin_called;
|
|
|
- sb_render_state render_state;
|
|
|
- sg_shader default_shader;
|
|
|
- sg_pipeline default_pipeline;
|
|
|
- sb_matrix projection_matrix;
|
|
|
-} _sb_t;
|
|
|
-
|
|
|
-static _sb_t _sb;
|
|
|
+ sg_image image;
|
|
|
+ int width;
|
|
|
+ int height;
|
|
|
+ float texel_width;
|
|
|
+ float texel_height;
|
|
|
+} _sbatch_sprite_data;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ _sbatch_sprite_data* data;
|
|
|
+ size_t size;
|
|
|
+} _sbatch_sprite_pool;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ bool begin_called;
|
|
|
+ _sbatch_slot slot;
|
|
|
+ sg_bindings bindings;
|
|
|
+ sg_shader shader;
|
|
|
+ sg_pipeline pipeline;
|
|
|
+ sbatch_context ctx_id;
|
|
|
+ _sbatch_context_pool context_pool;
|
|
|
+ _sbatch_sprite_pool sprite_pool;
|
|
|
+ sg_buffer index_buffer;
|
|
|
+ int frame_index;
|
|
|
+} _sbatch_t;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ float m[4][4];
|
|
|
+} _sbatch_mat4x4;
|
|
|
+
|
|
|
+static _sbatch_t _sbatch;
|
|
|
|
|
|
#if defined(SOKOL_D3D11)
|
|
|
-static const uint8_t vs_bytecode_hlsl4[884] = {
|
|
|
+static const uint8_t vs_bytecode[884] = {
|
|
|
0x44,0x58,0x42,0x43,0x5f,0x8c,0xaf,0xe1,0x5e,0x2d,0xba,0x0e,0x85,0xba,0xeb,0xc5,
|
|
|
0x0c,0x64,0x6d,0x0c,0x01,0x00,0x00,0x00,0x74,0x03,0x00,0x00,0x05,0x00,0x00,0x00,
|
|
|
0x34,0x00,0x00,0x00,0xf4,0x00,0x00,0x00,0x58,0x01,0x00,0x00,0xc8,0x01,0x00,0x00,
|
|
@@ -246,7 +308,7 @@ static const uint8_t vs_bytecode_hlsl4[884] = {
|
|
|
0x00,0x00,0x00,0x00,
|
|
|
};
|
|
|
|
|
|
-static const uint8_t fs_bytecode_hlsl4[620] = {
|
|
|
+static const uint8_t fs_bytecode[620] = {
|
|
|
0x44,0x58,0x42,0x43,0xd1,0x93,0x1f,0x1b,0x9d,0x70,0x90,0xeb,0xc2,0x7c,0x26,0x07,
|
|
|
0xdf,0x52,0xda,0x49,0x01,0x00,0x00,0x00,0x6c,0x02,0x00,0x00,0x05,0x00,0x00,0x00,
|
|
|
0x34,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x54,0x01,0x00,0x00,
|
|
@@ -287,11 +349,8 @@ static const uint8_t fs_bytecode_hlsl4[620] = {
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
|
};
|
|
|
-#if !defined(SOKOL_GFX_INCLUDED)
|
|
|
-#error "Please include sokol_gfx.h before spritebatch-sapp.glsl.h"
|
|
|
-#endif
|
|
|
|
|
|
-static inline const sg_shader_desc* spritebatch_shader_desc(sg_backend backend) {
|
|
|
+static const sg_shader_desc* spritebatch_shader_desc(sg_backend backend) {
|
|
|
if (backend == SG_BACKEND_D3D11) {
|
|
|
static sg_shader_desc desc;
|
|
|
static bool valid;
|
|
@@ -303,11 +362,11 @@ static inline const sg_shader_desc* spritebatch_shader_desc(sg_backend backend)
|
|
|
desc.attrs[1].sem_index = 1;
|
|
|
desc.attrs[2].sem_name = "TEXCOORD";
|
|
|
desc.attrs[2].sem_index = 2;
|
|
|
- desc.vs.bytecode.ptr = vs_bytecode_hlsl4;
|
|
|
+ desc.vs.bytecode.ptr = vs_bytecode;
|
|
|
desc.vs.bytecode.size = 884;
|
|
|
desc.vs.entry = "main";
|
|
|
desc.vs.uniform_blocks[0].size = 64;
|
|
|
- desc.fs.bytecode.ptr = fs_bytecode_hlsl4;
|
|
|
+ desc.fs.bytecode.ptr = fs_bytecode;
|
|
|
desc.fs.bytecode.size = 620;
|
|
|
desc.fs.entry = "main";
|
|
|
desc.fs.images[0].name = "tex";
|
|
@@ -322,531 +381,756 @@ static inline const sg_shader_desc* spritebatch_shader_desc(sg_backend backend)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
-static inline bool _sb_matrix_is_null(const sb_matrix* m) {
|
|
|
- for (int y = 0; y < 4; y++) {
|
|
|
- for (int x = 0; x < 4; x++) {
|
|
|
- if (0.0f != m->m[y][x]) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
+static void _sbatch_strcpy(_sbatch_str* dst, const char* src) {
|
|
|
+ SOKOL_ASSERT(dst);
|
|
|
+ if (src) {
|
|
|
+#if defined(_MSC_VER)
|
|
|
+ strncpy_s(dst->buf, _SBATCH_STRBUF_LEN, src, (_SBATCH_STRBUF_LEN - 1));
|
|
|
+#else
|
|
|
+ strncpy(dst->buf, src, SG_IMGUI_STRBUF_LEN);
|
|
|
+#endif
|
|
|
+ dst->buf[_SBATCH_STRBUF_LEN - 1] = 0;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ memset(dst->buf, 0, _SBATCH_STRBUF_LEN);
|
|
|
}
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
-static inline sb_matrix _sb_matrix_identity(void) {
|
|
|
- sb_matrix m = {
|
|
|
- {
|
|
|
- { 1.0f, 0.0f, 0.0f, 0.0f },
|
|
|
- { 0.0f, 1.0f, 0.0f, 0.0f },
|
|
|
- { 0.0f, 0.0f, 1.0f, 0.0f },
|
|
|
- { 0.0f, 0.0f, 0.0f, 1.0f }
|
|
|
- }
|
|
|
- };
|
|
|
- return m;
|
|
|
+static _sbatch_str _sbatch_make_str(const char* str) {
|
|
|
+ _sbatch_str res;
|
|
|
+ _sbatch_strcpy(&res, str);
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
-static inline sb_matrix _sb_orthographic_off_center(float left, float right, float bottom, float top, float near, float far) {
|
|
|
- sb_matrix result;
|
|
|
+/*=== 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 */
|
|
|
+ pool->size = num + 1;
|
|
|
+ pool->queue_top = 0;
|
|
|
+ /* generation counters indexable by pool slot index, slot 0 is reserved */
|
|
|
+ const size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size;
|
|
|
+ pool->gen_ctrs = (uint32_t*)SOKOL_MALLOC(gen_ctrs_size);
|
|
|
+ SOKOL_ASSERT(pool->gen_ctrs);
|
|
|
+ memset(pool->gen_ctrs, 0, gen_ctrs_size);
|
|
|
+ /* it's not a bug to only reserve 'num' here */
|
|
|
+ pool->free_queue = (int*)SOKOL_MALLOC(sizeof(int) * (size_t)num);
|
|
|
+ SOKOL_ASSERT(pool->free_queue);
|
|
|
+ /* never allocate the zero-th pool item since the invalid id is 0 */
|
|
|
+ for (int i = pool->size - 1; i >= 1; i--) {
|
|
|
+ pool->free_queue[pool->queue_top++] = i;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- result.m[0][0] = 2.0f / (right - left);
|
|
|
- result.m[0][1] = 0.0f;
|
|
|
- result.m[0][2] = 0.0f;
|
|
|
- result.m[0][3] = 0.0f;
|
|
|
+static void _sbatch_discard_pool(_sbatch_pool* pool) {
|
|
|
+ SOKOL_ASSERT(pool);
|
|
|
+ SOKOL_ASSERT(pool->free_queue);
|
|
|
+ SOKOL_FREE(pool->free_queue);
|
|
|
+ pool->free_queue = 0;
|
|
|
+ SOKOL_ASSERT(pool->gen_ctrs);
|
|
|
+ SOKOL_FREE(pool->gen_ctrs);
|
|
|
+ pool->gen_ctrs = 0;
|
|
|
+ pool->size = 0;
|
|
|
+ pool->queue_top = 0;
|
|
|
+}
|
|
|
|
|
|
- result.m[1][0] = 0.0f;
|
|
|
- result.m[1][1] = 2.0f / (top - bottom);
|
|
|
- result.m[1][2] = 0.0f;
|
|
|
- result.m[1][3] = 0.0f;
|
|
|
+static int _sbatch_pool_alloc_index(_sbatch_pool* pool) {
|
|
|
+ SOKOL_ASSERT(pool);
|
|
|
+ SOKOL_ASSERT(pool->free_queue);
|
|
|
+ if (pool->queue_top > 0) {
|
|
|
+ const int slot_index = pool->free_queue[--pool->queue_top];
|
|
|
+ SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size));
|
|
|
+ return slot_index;
|
|
|
+ }
|
|
|
+ /* pool exhausted */
|
|
|
+ return _SBATCH_INVALID_SLOT_INDEX;
|
|
|
+}
|
|
|
|
|
|
- result.m[2][0] = 0.0f;
|
|
|
- result.m[2][1] = 0.0f;
|
|
|
- result.m[2][2] = 1.0f / (near - far);
|
|
|
- result.m[2][3] = 0.0f;
|
|
|
+static void _sbatch_pool_free_index(_sbatch_pool* pool, int slot_index) {
|
|
|
+ SOKOL_ASSERT((slot_index > _SBATCH_INVALID_SLOT_INDEX) && (slot_index < pool->size));
|
|
|
+ SOKOL_ASSERT(pool);
|
|
|
+ SOKOL_ASSERT(pool->free_queue);
|
|
|
+ SOKOL_ASSERT(pool->queue_top < pool->size);
|
|
|
+#ifdef SOKOL_DEBUG
|
|
|
+ /* debug check against double-free */
|
|
|
+ for (int i = 0; i < pool->queue_top; i++) {
|
|
|
+ SOKOL_ASSERT(pool->free_queue[i] != slot_index);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ pool->free_queue[pool->queue_top++] = slot_index;
|
|
|
+ SOKOL_ASSERT(pool->queue_top <= (pool->size - 1));
|
|
|
+}
|
|
|
|
|
|
- result.m[3][0] = (left + right) / (left - right);
|
|
|
- result.m[3][1] = (bottom + top) / (bottom - top);
|
|
|
- result.m[3][2] = near / (near - far);
|
|
|
- result.m[3][3] = 1.0f;
|
|
|
+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);
|
|
|
+}
|
|
|
|
|
|
- return result;
|
|
|
+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);
|
|
|
}
|
|
|
|
|
|
-static inline int _sg_image_slot_index(uint32_t id) {
|
|
|
- int slot_index = (int)(id & _SB_IMAGE_SLOT_MASK);
|
|
|
- SOKOL_ASSERT(0 != slot_index);
|
|
|
+/* allocate the slot at slot_index:
|
|
|
+ - bump the slot's generation counter
|
|
|
+ - create a resource id from the generation counter and slot index
|
|
|
+ - set the slot's id to this id
|
|
|
+ - set the slot's state to ALLOC
|
|
|
+ - return the resource id
|
|
|
+*/
|
|
|
+static uint32_t _sbatch_slot_alloc(_sbatch_pool* pool, _sbatch_slot* slot, int slot_index) {
|
|
|
+ /* FIXME: add handling for an overflowing generation counter,
|
|
|
+ for now, just overflow (another option is to disable
|
|
|
+ the slot)
|
|
|
+ */
|
|
|
+ SOKOL_ASSERT(pool && pool->gen_ctrs);
|
|
|
+ SOKOL_ASSERT((slot_index > _SBATCH_INVALID_SLOT_INDEX) && (slot_index < pool->size));
|
|
|
+ SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID));
|
|
|
+ const uint32_t ctr = ++pool->gen_ctrs[slot_index];
|
|
|
+ slot->id = (ctr << _SBATCH_SLOT_SHIFT) | (slot_index & _SBATCH_SLOT_MASK);
|
|
|
+ slot->state = SG_RESOURCESTATE_ALLOC;
|
|
|
+ return slot->id;
|
|
|
+}
|
|
|
+
|
|
|
+/* extract slot index from id */
|
|
|
+static int _sbatch_slot_index(uint32_t id) {
|
|
|
+ const int slot_index = (int)(id & _SBATCH_SLOT_MASK);
|
|
|
+ SOKOL_ASSERT(_SBATCH_INVALID_SLOT_INDEX != slot_index);
|
|
|
return slot_index;
|
|
|
}
|
|
|
|
|
|
-static inline void _sb_init_sprite_pool(void) {
|
|
|
- sg_desc sg_desc = sg_query_desc();
|
|
|
- int num_slots = sg_desc.image_pool_size;
|
|
|
- _sb.sprite_pool.size = (size_t)num_slots * sizeof(_sb_sprite_data);
|
|
|
- _sb.sprite_pool.data = (_sb_sprite_data*)SOKOL_MALLOC(_sb.sprite_pool.size);
|
|
|
- SOKOL_ASSERT(_sb.sprite_pool.data);
|
|
|
- memset(_sb.sprite_pool.data, 0, _sb.sprite_pool.size);
|
|
|
+/* get context pointer without id-check */
|
|
|
+static _sbatch_context* _sbatch_context_at(uint32_t ctx_id) {
|
|
|
+ SOKOL_ASSERT(SG_INVALID_ID != ctx_id);
|
|
|
+ const int slot_index = _sbatch_slot_index(ctx_id);
|
|
|
+ SOKOL_ASSERT((slot_index > _SBATCH_INVALID_SLOT_INDEX) && (slot_index < _sbatch.context_pool.pool.size));
|
|
|
+ return &_sbatch.context_pool.contexts[slot_index];
|
|
|
}
|
|
|
|
|
|
-static inline void _sb_init_batch_data(void) {
|
|
|
- _sb.batch_data.batch_capacity = _SB_INITIAL_BATCH_CAPACITY;
|
|
|
- _sb.batch_data.batches = (_sb_batch*)SOKOL_MALLOC(_SB_INITIAL_BATCH_CAPACITY * sizeof(_sb_batch));
|
|
|
+/* get context pointer with id-check, returns 0 if no match */
|
|
|
+static _sbatch_context* _sbatch_lookup_context(uint32_t ctx_id) {
|
|
|
+ if (SG_INVALID_ID != ctx_id) {
|
|
|
+ _sbatch_context* ctx = _sbatch_context_at(ctx_id);
|
|
|
+ if (ctx->slot.id == ctx_id) {
|
|
|
+ return ctx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
-static inline void _sb_init_index_buffer(void) {
|
|
|
- uint16_t* index_buffer = (uint16_t*)SOKOL_MALLOC(_SB_MAX_INDICES * sizeof(uint16_t));
|
|
|
- SOKOL_ASSERT(index_buffer);
|
|
|
+/* make context handle from raw uint32_t id */
|
|
|
+static sbatch_context _sbatch_make_ctx_id(uint32_t ctx_id) {
|
|
|
+ sbatch_context ctx;
|
|
|
+ ctx.id = ctx_id;
|
|
|
+ return ctx;
|
|
|
+}
|
|
|
|
|
|
- uint16_t* index_ptr = index_buffer;
|
|
|
- for (uint32_t i = 0; i < _SB_MAX_QUADS; i++, index_ptr += 6) {
|
|
|
- // Triangle 1
|
|
|
- *(index_ptr + 0) = (uint16_t)(i * 4);
|
|
|
- *(index_ptr + 1) = (uint16_t)(i * 4 + 1);
|
|
|
- *(index_ptr + 2) = (uint16_t)(i * 4 + 2);
|
|
|
- // Triangle 2
|
|
|
- *(index_ptr + 3) = (uint16_t)(i * 4 + 1);
|
|
|
- *(index_ptr + 4) = (uint16_t)(i * 4 + 3);
|
|
|
- *(index_ptr + 5) = (uint16_t)(i * 4 + 2);
|
|
|
+static sbatch_context _sbatch_alloc_context(void) {
|
|
|
+ sbatch_context ctx_id;
|
|
|
+ const int slot_index = _sbatch_pool_alloc_index(&_sbatch.context_pool.pool);
|
|
|
+ if (_SBATCH_INVALID_SLOT_INDEX != slot_index) {
|
|
|
+ ctx_id = _sbatch_make_ctx_id(_sbatch_slot_alloc(&_sbatch.context_pool.pool, &_sbatch.context_pool.contexts[slot_index].slot, slot_index));
|
|
|
}
|
|
|
+ else {
|
|
|
+ /* pool is exhausted */
|
|
|
+ ctx_id = _sbatch_make_ctx_id(SG_INVALID_ID);
|
|
|
+ }
|
|
|
+ return ctx_id;
|
|
|
+}
|
|
|
|
|
|
- sg_buffer_desc index_buffer_desc;
|
|
|
- memset(&index_buffer_desc, 0, sizeof(index_buffer_desc));
|
|
|
- index_buffer_desc.size = _SB_MAX_INDICES * sizeof(uint16_t);
|
|
|
- index_buffer_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
|
|
|
- index_buffer_desc.usage = SG_USAGE_IMMUTABLE;
|
|
|
- index_buffer_desc.label = "sokol-spritebatch-indices";
|
|
|
- index_buffer_desc.data.size = _SB_MAX_INDICES * sizeof(uint16_t);
|
|
|
- index_buffer_desc.data.ptr = index_buffer;
|
|
|
- _sb.index_buffer = sg_make_buffer(&index_buffer_desc);
|
|
|
- SOKOL_ASSERT(SG_INVALID_ID != _sb.index_buffer.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);
|
|
|
+ res.canvas_height = _SBATCH_DEFAULT(res.canvas_height, 480);
|
|
|
+ res.canvas_width = _SBATCH_DEFAULT(res.canvas_width, 640);
|
|
|
+ return res;
|
|
|
+}
|
|
|
|
|
|
- SOKOL_FREE(index_buffer);
|
|
|
+static void _sbatch_init_context(sbatch_context ctx_id, const sbatch_context_desc* in_desc) {
|
|
|
+ sg_push_debug_group("sokol-spritebatch");
|
|
|
|
|
|
- _sb.bindings.index_buffer = _sb.index_buffer;
|
|
|
-}
|
|
|
+ SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc);
|
|
|
|
|
|
-static inline void _sb_init_quad_rotated(
|
|
|
- _sb_quad* quad, float x, float y, float dx, float dy,
|
|
|
- float w, float h, float sin, float cos, uint32_t rgba,
|
|
|
- sb_float2 top_left, sb_float2 bottom_right, float depth) {
|
|
|
-
|
|
|
- /* TODO: Z-tilt: what the hell do we do? */
|
|
|
-
|
|
|
- quad->top_left.x = x + dx * cos - dy * sin;
|
|
|
- quad->top_left.y = y + dx * sin + dy * cos;
|
|
|
- quad->top_left.z = depth;
|
|
|
- quad->top_left.rgba = rgba;
|
|
|
- quad->top_left.u = top_left.x;
|
|
|
- quad->top_left.v = top_left.y;
|
|
|
-
|
|
|
- quad->top_right.x = x + (dx + w) * cos - dy * sin;
|
|
|
- quad->top_right.y = y + (dx + w) * sin + dy * cos;
|
|
|
- quad->top_right.z = depth;
|
|
|
- quad->top_right.rgba = rgba;
|
|
|
- quad->top_right.u = bottom_right.x;
|
|
|
- quad->top_right.v = top_left.y;
|
|
|
-
|
|
|
- quad->bottom_left.x = x + dx * cos - (dy + h) * sin;
|
|
|
- quad->bottom_left.y = y + dx * sin + (dy + h) * cos;
|
|
|
- quad->bottom_left.z = depth;
|
|
|
- quad->bottom_left.rgba = rgba;
|
|
|
- quad->bottom_left.u = top_left.x;
|
|
|
- quad->bottom_left.v = bottom_right.y;
|
|
|
-
|
|
|
- quad->bottom_right.x = x + (dx + w) * cos - (dy + h) * sin;
|
|
|
- quad->bottom_right.y = y + (dx + w) * sin + (dy + h) * cos;
|
|
|
- quad->bottom_right.z = depth;
|
|
|
- quad->bottom_right.rgba = rgba;
|
|
|
- quad->bottom_right.u = bottom_right.x;
|
|
|
- quad->bottom_right.v = bottom_right.y;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void _sb_init_quad(
|
|
|
- _sb_quad* quad, uint32_t flags, float x, float y, float w, float h, uint32_t rgba,
|
|
|
- sb_float2 top_left, sb_float2 bottom_right, float depth) {
|
|
|
-
|
|
|
- quad->top_left.x = x;
|
|
|
- quad->top_left.y = y;
|
|
|
- quad->top_left.z = depth;
|
|
|
- quad->top_left.rgba = rgba;
|
|
|
- quad->top_left.u = top_left.x;
|
|
|
- quad->top_left.v = top_left.y;
|
|
|
-
|
|
|
- quad->top_right.x = x + w;
|
|
|
- quad->top_right.y = y;
|
|
|
- quad->top_right.z = depth;
|
|
|
- quad->top_right.rgba = rgba;
|
|
|
- quad->top_right.u = bottom_right.x;
|
|
|
- quad->top_right.v = top_left.y;
|
|
|
-
|
|
|
- if ((flags & SB_Z_TILT) != SB_FLIP_NONE) {
|
|
|
- // move the topmost vertices further out to enable z-tilting
|
|
|
- const float angle = 0.785398f; // 45 degrees
|
|
|
- const float depth = h * tanf(angle);
|
|
|
- quad->top_left.z -= depth;
|
|
|
- quad->top_right.z -= depth;
|
|
|
- }
|
|
|
+ _sbatch_context* ctx = _sbatch_lookup_context(ctx_id.id);
|
|
|
+ SOKOL_ASSERT(ctx);
|
|
|
|
|
|
- quad->bottom_left.x = x;
|
|
|
- quad->bottom_left.y = y + h;
|
|
|
- quad->bottom_left.z = depth;
|
|
|
- quad->bottom_left.rgba = rgba;
|
|
|
- quad->bottom_left.u = top_left.x;
|
|
|
- quad->bottom_left.v = bottom_right.y;
|
|
|
-
|
|
|
- quad->bottom_right.x = x + w;
|
|
|
- quad->bottom_right.y = y + h;
|
|
|
- quad->bottom_right.z = depth;
|
|
|
- quad->bottom_right.rgba = rgba;
|
|
|
- quad->bottom_right.u = bottom_right.x;
|
|
|
- quad->bottom_right.v = bottom_right.y;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void _sb_init_vertex_buffer(void) {
|
|
|
- sg_buffer_desc vertex_buffer_desc;
|
|
|
- memset(&vertex_buffer_desc, 0, sizeof(vertex_buffer_desc));
|
|
|
- vertex_buffer_desc.size = _SB_MAX_VERTICES * sizeof(_sb_vertex);
|
|
|
- vertex_buffer_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
|
|
|
- vertex_buffer_desc.usage = SG_USAGE_STREAM;
|
|
|
- vertex_buffer_desc.label = "sokol-spritebatch-vertices";
|
|
|
- _sb.vertex_buffer = sg_make_buffer(&vertex_buffer_desc);
|
|
|
- SOKOL_ASSERT(SG_INVALID_ID != _sb.vertex_buffer.id);
|
|
|
-
|
|
|
- _sb.vertex_buffer_data = (_sb_vertex*)SOKOL_MALLOC(_SB_MAX_VERTICES * sizeof(_sb_vertex));
|
|
|
- SOKOL_ASSERT(_sb.vertex_buffer_data);
|
|
|
-
|
|
|
- _sb.bindings.vertex_buffers[0] = _sb.vertex_buffer;
|
|
|
-}
|
|
|
-
|
|
|
-static inline uint32_t _sb_float_flip(uint32_t f) {
|
|
|
- uint32_t mask = -(int32_t)(f >> 31) | 0x80000000;
|
|
|
- return f ^ mask;
|
|
|
-}
|
|
|
-
|
|
|
-static inline uint32_t _sb_depth_to_bits(float value) {
|
|
|
- // https://aras-p.info/blog/2014/01/16/rough-sorting-by-depth/
|
|
|
- // Taking highest 10 bits for rough sort of positive floats.
|
|
|
- // Sign is always zero, so only 9 bits in the result are used.
|
|
|
- // 0.01 maps to 240; 0.1 to 247; 1.0 to 254; 10.0 to 260;
|
|
|
- // 100.0 to 267; 1000.0 to 273 etc.
|
|
|
- uint32_t i;
|
|
|
- memcpy(&i, &value, sizeof(value));
|
|
|
- i = _sb_float_flip(i);
|
|
|
- return i >> 22; // take highest 10 bits
|
|
|
-}
|
|
|
-
|
|
|
-static inline uint64_t _sb_make_sort_key(const sb_sprite_info* sprite) {
|
|
|
- switch (_sb.render_state.sort_mode) {
|
|
|
- case SB_SORT_MODE_TEXTURE: {
|
|
|
- return (uint64_t)sprite->image.id;
|
|
|
- }
|
|
|
- case SB_SORT_MODE_BACK_TO_FRONT: {
|
|
|
- return (uint64_t)_sb_depth_to_bits(-sprite->depth) << 32 | sprite->image.id;
|
|
|
- }
|
|
|
- case SB_SORT_MODE_FRONT_TO_BACK: {
|
|
|
- return (uint64_t)_sb_depth_to_bits(sprite->depth) << 32 | sprite->image.id;
|
|
|
- }
|
|
|
+ ctx->update_frame_index = -1;
|
|
|
+
|
|
|
+ ctx->desc = _sbatch_context_desc_defaults(in_desc);
|
|
|
+
|
|
|
+ ctx->label = _sbatch_make_str(ctx->desc.label);
|
|
|
+
|
|
|
+ ctx->desc.label = NULL;
|
|
|
+
|
|
|
+ const int max_vertices = 4 * ctx->desc.max_sprites;
|
|
|
+ const size_t vbuf_size = (size_t)max_vertices * sizeof(_sbatch_vertex);
|
|
|
+
|
|
|
+ ctx->vertices = (_sbatch_vertex*)SOKOL_MALLOC(vbuf_size);
|
|
|
+ SOKOL_ASSERT(ctx->vertices);
|
|
|
+
|
|
|
+ ctx->images = (sg_image*)SOKOL_MALLOC(ctx->desc.max_sprites * sizeof(sg_image));
|
|
|
+ SOKOL_ASSERT(ctx->images);
|
|
|
+
|
|
|
+ ctx->pipeline.id = _SBATCH_DEFAULT(in_desc->pipeline.id, _sbatch.pipeline.id);
|
|
|
+
|
|
|
+ ctx->sprite_count = 0;
|
|
|
+
|
|
|
+ sg_buffer_desc vbuf_desc;
|
|
|
+ memset(&vbuf_desc, 0, sizeof(vbuf_desc));
|
|
|
+ vbuf_desc.size = vbuf_size;
|
|
|
+ vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
|
|
|
+ vbuf_desc.usage = SG_USAGE_STREAM;
|
|
|
+ vbuf_desc.label = "sokol-spritebatch-vertices";
|
|
|
+ ctx->vertex_buffer = sg_make_buffer(&vbuf_desc);
|
|
|
+ SOKOL_ASSERT(SG_INVALID_ID != ctx->vertex_buffer.id);
|
|
|
+
|
|
|
+ sg_pop_debug_group();
|
|
|
+}
|
|
|
+
|
|
|
+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);
|
|
|
+ }
|
|
|
+ if (ctx->images) {
|
|
|
+ SOKOL_FREE(ctx->images);
|
|
|
+ }
|
|
|
+ sg_push_debug_group("sokol-spritebatch");
|
|
|
+ sg_destroy_buffer(ctx->vertex_buffer);
|
|
|
+ sg_pop_debug_group();
|
|
|
+ memset(ctx, 0, sizeof(*ctx));
|
|
|
+ _sbatch_pool_free_index(&_sbatch.context_pool.pool, _sbatch_slot_index(ctx_id.id));
|
|
|
}
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
-static inline uint32_t _sb_pack_color_bytes(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
|
|
- return (uint32_t)(((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)g << 8) | r);
|
|
|
+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;
|
|
|
}
|
|
|
|
|
|
-static inline float _sb_clampf(float v, float low, float high) {
|
|
|
+static float _sbatch_clampf(float v, float low, float high) {
|
|
|
if (v < low) {
|
|
|
return low;
|
|
|
}
|
|
|
- else if (v > high) {
|
|
|
+ if (v > high) {
|
|
|
return high;
|
|
|
}
|
|
|
return v;
|
|
|
}
|
|
|
|
|
|
-static inline uint32_t _sb_pack_color(const sg_color* color) {
|
|
|
- const uint8_t r = (uint8_t)(_sb_clampf(color->r, 0.0f, 1.0f) * 255.0f);
|
|
|
- const uint8_t g = (uint8_t)(_sb_clampf(color->g, 0.0f, 1.0f) * 255.0f);
|
|
|
- const uint8_t b = (uint8_t)(_sb_clampf(color->b, 0.0f, 1.0f) * 255.0f);
|
|
|
- const uint8_t a = (uint8_t)(_sb_clampf(color->a, 0.0f, 1.0f) * 255.0f);
|
|
|
- return _sb_pack_color_bytes(r, g, b, a);
|
|
|
+static uint32_t _sbatch_pack_color(const sg_color* color) {
|
|
|
+ const uint8_t r = (uint8_t)(_sbatch_clampf(color->r, 0.0f, 1.0f) * 255.0f);
|
|
|
+ const uint8_t g = (uint8_t)(_sbatch_clampf(color->g, 0.0f, 1.0f) * 255.0f);
|
|
|
+ const uint8_t b = (uint8_t)(_sbatch_clampf(color->b, 0.0f, 1.0f) * 255.0f);
|
|
|
+ const uint8_t a = (uint8_t)(_sbatch_clampf(color->a, 0.0f, 1.0f) * 255.0f);
|
|
|
+ return _sbatch_pack_color_bytes(r, g, b, a);
|
|
|
}
|
|
|
|
|
|
-static inline int _sb_quad_compare(const void* a, const void* b) {
|
|
|
- uint64_t key_a = ((const _sb_quad*)a)->sort_key;
|
|
|
- uint64_t key_b = ((const _sb_quad*)b)->sort_key;
|
|
|
- if (key_a < key_b) return -1;
|
|
|
- if (key_a > key_b) return 1;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void* _sb_realloc(void* old_ptr, size_t old_size, size_t new_size) {
|
|
|
- SOKOL_ASSERT((new_size > 0) && (new_size > old_size));
|
|
|
- void* new_ptr = SOKOL_MALLOC(new_size);
|
|
|
- SOKOL_ASSERT(new_ptr);
|
|
|
- if (old_ptr) {
|
|
|
- if (old_size > 0) {
|
|
|
- memcpy(new_ptr, old_ptr, old_size);
|
|
|
- }
|
|
|
- SOKOL_FREE(old_ptr);
|
|
|
- }
|
|
|
- return new_ptr;
|
|
|
+static int _sg_image_slot_index(uint32_t id) {
|
|
|
+ int slot_index = (int)(id & _SBATCH_IMAGE_SLOT_MASK);
|
|
|
+ SOKOL_ASSERT(0 != slot_index);
|
|
|
+ return slot_index;
|
|
|
}
|
|
|
|
|
|
-static inline void _sb_grow_batch_buffer(void) {
|
|
|
- const size_t new_capacity = _sb.batch_data.batch_capacity * 2;
|
|
|
- _sb.batch_data.batches = (_sb_batch*)_sb_realloc(_sb.batch_data.batches, _sb.batch_data.batch_capacity, new_capacity);
|
|
|
- _sb.batch_data.batch_capacity = new_capacity;
|
|
|
+static void _sbatch_setup_sprite_pool(void) {
|
|
|
+ sg_desc sg_desc = sg_query_desc();
|
|
|
+ const int num_slots = sg_desc.image_pool_size;
|
|
|
+ _sbatch.sprite_pool.size = (size_t)num_slots * sizeof(_sbatch_sprite_data);
|
|
|
+ _sbatch.sprite_pool.data = (_sbatch_sprite_data*)SOKOL_MALLOC(_sbatch.sprite_pool.size);
|
|
|
+ SOKOL_ASSERT(_sbatch.sprite_pool.data);
|
|
|
+ memset(_sbatch.sprite_pool.data, 0, _sbatch.sprite_pool.size);
|
|
|
}
|
|
|
|
|
|
-static inline _sb_batch* _sb_create_batch(void) {
|
|
|
- if (_sb.batch_data.batch_size >= _sb.batch_data.batch_capacity) {
|
|
|
- _sb_grow_batch_buffer();
|
|
|
- }
|
|
|
- return &_sb.batch_data.batches[_sb.batch_data.batch_size++];
|
|
|
+static void _sbatch_init_quad_rotated(
|
|
|
+ _sbatch_vertex* vertices, float x, float y, float dx, float dy,
|
|
|
+ float w, float h, float sin, float cos, uint32_t rgba,
|
|
|
+ sbatch_float2 top_left, sbatch_float2 bottom_right, float depth) {
|
|
|
+
|
|
|
+ _sbatch_vertex* top_left_vertex = vertices;
|
|
|
+ top_left_vertex->x = x + dx * cos - dy * sin;
|
|
|
+ top_left_vertex->y = y + dx * sin + dy * cos;
|
|
|
+ top_left_vertex->z = depth;
|
|
|
+ top_left_vertex->rgba = rgba;
|
|
|
+ top_left_vertex->u = top_left.x;
|
|
|
+ top_left_vertex->v = top_left.y;
|
|
|
+
|
|
|
+ _sbatch_vertex* top_right_vertex = vertices + 1;
|
|
|
+ top_right_vertex->x = x + (dx + w) * cos - dy * sin;
|
|
|
+ top_right_vertex->y = y + (dx + w) * sin + dy * cos;
|
|
|
+ top_right_vertex->z = depth;
|
|
|
+ top_right_vertex->rgba = rgba;
|
|
|
+ top_right_vertex->u = bottom_right.x;
|
|
|
+ top_right_vertex->v = top_left.y;
|
|
|
+
|
|
|
+ _sbatch_vertex* bottom_left_vertex = vertices + 2;
|
|
|
+ bottom_left_vertex->x = x + dx * cos - (dy + h) * sin;
|
|
|
+ bottom_left_vertex->y = y + dx * sin + (dy + h) * cos;
|
|
|
+ bottom_left_vertex->z = depth;
|
|
|
+ bottom_left_vertex->rgba = rgba;
|
|
|
+ bottom_left_vertex->u = top_left.x;
|
|
|
+ bottom_left_vertex->v = bottom_right.y;
|
|
|
+
|
|
|
+ _sbatch_vertex* bottom_right_vertex = vertices + 3;
|
|
|
+ bottom_right_vertex->x = x + (dx + w) * cos - (dy + h) * sin;
|
|
|
+ bottom_right_vertex->y = y + (dx + w) * sin + (dy + h) * cos;
|
|
|
+ bottom_right_vertex->z = depth;
|
|
|
+ bottom_right_vertex->rgba = rgba;
|
|
|
+ bottom_right_vertex->u = bottom_right.x;
|
|
|
+ bottom_right_vertex->v = bottom_right.y;
|
|
|
}
|
|
|
|
|
|
-static inline void _sb_init_batch(sg_image image, int num_elements, int base_element) {
|
|
|
- _sb_batch* batch = _sb_create_batch();
|
|
|
- batch->image = image;
|
|
|
- batch->num_elements = num_elements;
|
|
|
- batch->base_element = base_element;
|
|
|
- batch->matrix = _sb.projection_matrix;
|
|
|
+static void _sbatch_init_quad(
|
|
|
+ _sbatch_vertex* vertices, float x, float y, float w, float h, uint32_t rgba,
|
|
|
+ sbatch_float2 top_left, sbatch_float2 bottom_right, float depth) {
|
|
|
+
|
|
|
+ _sbatch_vertex* top_left_vertex = vertices;
|
|
|
+ top_left_vertex->x = x;
|
|
|
+ top_left_vertex->y = y;
|
|
|
+ top_left_vertex->z = depth;
|
|
|
+ top_left_vertex->rgba = rgba;
|
|
|
+ top_left_vertex->u = top_left.x;
|
|
|
+ top_left_vertex->v = top_left.y;
|
|
|
+
|
|
|
+ _sbatch_vertex* top_right_vertex = vertices + 1;
|
|
|
+ top_right_vertex->x = x + w;
|
|
|
+ top_right_vertex->y = y;
|
|
|
+ top_right_vertex->z = depth;
|
|
|
+ top_right_vertex->rgba = rgba;
|
|
|
+ top_right_vertex->u = bottom_right.x;
|
|
|
+ top_right_vertex->v = top_left.y;
|
|
|
+
|
|
|
+ _sbatch_vertex* bottom_left_vertex = vertices + 2;
|
|
|
+ bottom_left_vertex->x = x;
|
|
|
+ bottom_left_vertex->y = y + h;
|
|
|
+ bottom_left_vertex->z = depth;
|
|
|
+ bottom_left_vertex->rgba = rgba;
|
|
|
+ bottom_left_vertex->u = top_left.x;
|
|
|
+ bottom_left_vertex->v = bottom_right.y;
|
|
|
+
|
|
|
+ _sbatch_vertex* bottom_right_vertex = vertices + 3;
|
|
|
+ bottom_right_vertex->x = x + w;
|
|
|
+ bottom_right_vertex->y = y + h;
|
|
|
+ bottom_right_vertex->z = depth;
|
|
|
+ bottom_right_vertex->rgba = rgba;
|
|
|
+ bottom_right_vertex->u = bottom_right.x;
|
|
|
+ bottom_right_vertex->v = bottom_right.y;
|
|
|
}
|
|
|
|
|
|
-static inline void _sb_init_batches(void) {
|
|
|
- int batch_size = 0;
|
|
|
- int base_element = 0;
|
|
|
- sg_image current_image = { SG_INVALID_ID };
|
|
|
+static void _sbatch_init_index_buffer(void) {
|
|
|
+ uint16_t* index_buffer = (uint16_t*)SOKOL_MALLOC(_SBATCH_MAX_INDICES * sizeof(uint16_t));
|
|
|
+ SOKOL_ASSERT(index_buffer);
|
|
|
|
|
|
- for (int i = 0; i < _sb.quad_count; ++i, ++batch_size) {
|
|
|
- if (_sb.quads[i].image.id != current_image.id) {
|
|
|
- const int num_elements = batch_size * 6;
|
|
|
- _sb_init_batch(current_image, num_elements, base_element);
|
|
|
- current_image = _sb.quads[i].image;
|
|
|
- batch_size = 0;
|
|
|
- base_element += num_elements;
|
|
|
- }
|
|
|
+ uint16_t* index_ptr = index_buffer;
|
|
|
+ for (uint32_t i = 0; i < _SBATCH_MAX_QUADS; i++, index_ptr += 6) {
|
|
|
+ // Triangle 1
|
|
|
+ *(index_ptr + 0) = (uint16_t)(i * 4);
|
|
|
+ *(index_ptr + 1) = (uint16_t)(i * 4 + 1);
|
|
|
+ *(index_ptr + 2) = (uint16_t)(i * 4 + 2);
|
|
|
+ // Triangle 2
|
|
|
+ *(index_ptr + 3) = (uint16_t)(i * 4 + 1);
|
|
|
+ *(index_ptr + 4) = (uint16_t)(i * 4 + 3);
|
|
|
+ *(index_ptr + 5) = (uint16_t)(i * 4 + 2);
|
|
|
}
|
|
|
|
|
|
- const int num_elements = batch_size * 6;
|
|
|
- _sb_init_batch(current_image, num_elements, base_element);
|
|
|
+ sg_buffer_desc index_buffer_desc;
|
|
|
+ memset(&index_buffer_desc, 0, sizeof(index_buffer_desc));
|
|
|
+ index_buffer_desc.size = _SBATCH_MAX_INDICES * sizeof(uint16_t);
|
|
|
+ index_buffer_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
|
|
|
+ index_buffer_desc.usage = SG_USAGE_IMMUTABLE;
|
|
|
+ index_buffer_desc.label = "sokol-spritebatch-indices";
|
|
|
+ index_buffer_desc.data.size = _SBATCH_MAX_INDICES * sizeof(uint16_t);
|
|
|
+ index_buffer_desc.data.ptr = index_buffer;
|
|
|
+ _sbatch.index_buffer = sg_make_buffer(&index_buffer_desc);
|
|
|
+ SOKOL_ASSERT(SG_INVALID_ID != _sbatch.index_buffer.id);
|
|
|
+
|
|
|
+ SOKOL_FREE(index_buffer);
|
|
|
+
|
|
|
+ _sbatch.bindings.index_buffer = _sbatch.index_buffer;
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_premultiply_alpha(uint8_t* pixels, int pixel_count) {
|
|
|
- /*
|
|
|
- http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/
|
|
|
- https://shawnhargreaves.com/blog/premultiplied-alpha.html
|
|
|
- */
|
|
|
- SOKOL_ASSERT(pixels);
|
|
|
- for (int i = 0; i < pixel_count; ++i) {
|
|
|
- pixels[0] = pixels[0] * pixels[3] / 255;
|
|
|
- pixels[1] = pixels[1] * pixels[3] / 255;
|
|
|
- pixels[2] = pixels[2] * pixels[3] / 255;
|
|
|
- pixels += 4;
|
|
|
+static bool _sbatch_rect_is_valid(const sbatch_rect* rect) {
|
|
|
+ if (rect->width != 0.0f && rect->height != 0.0f) {
|
|
|
+ return true;
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_setup(const sb_desc* desc) {
|
|
|
- SOKOL_ASSERT(desc);
|
|
|
- memset(&_sb, 0, sizeof(_sb_t));
|
|
|
- _sb_init_sprite_pool();
|
|
|
- _sb.quads = (_sb_quad*)SOKOL_MALLOC(_SB_MAX_QUADS * sizeof(_sb_quad));
|
|
|
- SOKOL_ASSERT(_sb.quads);
|
|
|
- _sb_init_vertex_buffer();
|
|
|
- _sb_init_index_buffer();
|
|
|
- _sb_init_batch_data();
|
|
|
+SOKOL_SPRITEBATCH_API_DECL int sbatch_frame() {
|
|
|
+ return ++_sbatch.frame_index;
|
|
|
+}
|
|
|
|
|
|
- _sb.default_shader = sg_make_shader(spritebatch_shader_desc(sg_query_backend()));
|
|
|
+SOKOL_API_IMPL void sbatch_setup(const sbatch_desc* desc) {
|
|
|
+
|
|
|
+ memset(&_sbatch, 0, sizeof _sbatch);
|
|
|
+
|
|
|
+ sbatch_desc batch_desc = *desc;
|
|
|
+ batch_desc.context_pool_size = _SBATCH_DEFAULT(batch_desc.context_pool_size, 32);
|
|
|
+ _sbatch_setup_context_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.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_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
|
|
|
pipeline_desc.colors[0].blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
|
|
|
- pipeline_desc.shader = _sb.default_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";
|
|
|
+ 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);
|
|
|
|
|
|
- _sb.default_pipeline = sg_make_pipeline(&pipeline_desc);
|
|
|
+ _sbatch_init_index_buffer();
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_shutdown(void) {
|
|
|
- sg_destroy_pipeline(_sb.default_pipeline);
|
|
|
- sg_destroy_shader(_sb.default_shader);
|
|
|
- SOKOL_FREE(_sb.batch_data.batches);
|
|
|
- sg_destroy_buffer(_sb.index_buffer);
|
|
|
- sg_destroy_buffer(_sb.vertex_buffer);
|
|
|
- SOKOL_FREE(_sb.vertex_buffer_data);
|
|
|
- SOKOL_FREE(_sb.quads);
|
|
|
- SOKOL_FREE(_sb.sprite_pool.data);
|
|
|
+SOKOL_API_IMPL void sbatch_shutdown(void) {
|
|
|
+ sg_destroy_buffer(_sbatch.index_buffer);
|
|
|
+ sg_destroy_pipeline(_sbatch.pipeline);
|
|
|
+ sg_destroy_shader(_sbatch.shader);
|
|
|
+ _sbatch_discard_context_pool();
|
|
|
+ SOKOL_FREE(_sbatch.sprite_pool.data);
|
|
|
+}
|
|
|
+
|
|
|
+SOKOL_API_IMPL sbatch_context sbatch_make_context(const sbatch_context_desc* desc) {
|
|
|
+ SOKOL_ASSERT(desc);
|
|
|
+ const sbatch_context result = _sbatch_alloc_context();
|
|
|
+ _sbatch_init_context(result, desc);
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_begin(const sb_render_state* render_state) {
|
|
|
- SOKOL_ASSERT(render_state);
|
|
|
- SOKOL_ASSERT(!_sb.begin_called);
|
|
|
- _sb.begin_called = true;
|
|
|
+SOKOL_API_IMPL void sbatch_destroy_context(sbatch_context context) {
|
|
|
+ SOKOL_ASSERT(context.id != SG_INVALID_ID);
|
|
|
+ _sbatch_destroy_context(context);
|
|
|
+}
|
|
|
|
|
|
- _sb.render_state.viewport = render_state->viewport;
|
|
|
- _sb.render_state.sort_mode = render_state->sort_mode;
|
|
|
- _sb.render_state.pipeline.id
|
|
|
- = _SB_DEFAULT(render_state->pipeline.id, _sb.default_pipeline.id);
|
|
|
+SOKOL_API_IMPL void sbatch_begin(sbatch_context context) {
|
|
|
+ SOKOL_ASSERT(context.id != SG_INVALID_ID);
|
|
|
+ _sbatch.ctx_id = context;
|
|
|
+ SOKOL_ASSERT(!_sbatch.begin_called);
|
|
|
+ _sbatch.begin_called = true;
|
|
|
|
|
|
- const float width = (float)_sb.render_state.viewport.width;
|
|
|
- const float height = (float)_sb.render_state.viewport.height;
|
|
|
+ _sbatch_context* ctx = _sbatch_context_at(_sbatch.ctx_id.id);
|
|
|
|
|
|
- _sb.projection_matrix
|
|
|
- = _sb_orthographic_off_center(0.0f, width, height, 0.0f, 0.0f, SB_MAX_DEPTH);
|
|
|
+ // a sbatch_context object can only be used in one sbatch_begin call per frame.
|
|
|
+ SOKOL_ASSERT(_sbatch.frame_index != ctx->update_frame_index);
|
|
|
+ ctx->update_frame_index = _sbatch.frame_index;
|
|
|
|
|
|
- _sb.render_state.transform_matrix
|
|
|
- = _sb_matrix_is_null(&render_state->transform_matrix)
|
|
|
- ? _sb_matrix_identity()
|
|
|
- : render_state->transform_matrix;
|
|
|
+ SOKOL_ASSERT(ctx);
|
|
|
+ _sbatch.bindings.vertex_buffers[0] = ctx->vertex_buffer;
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_sprite(const sb_sprite_info* sprite) {
|
|
|
- /* TODO: maybe take an array of sprite info? */
|
|
|
+SOKOL_API_IMPL void sbatch_push_sprite(const sbatch_sprite* sprite) {
|
|
|
SOKOL_ASSERT(sprite);
|
|
|
SOKOL_ASSERT(sprite->image.id != SG_INVALID_ID);
|
|
|
- SOKOL_ASSERT(_sb.quad_count < _SB_MAX_QUADS);
|
|
|
|
|
|
- _sb_sprite_data* cached_sprite_data = &_sb.sprite_pool.data[_sg_image_slot_index(sprite->image.id)];
|
|
|
- if (cached_sprite_data->image.id != sprite->image.id)
|
|
|
- {
|
|
|
- sg_image_info info = sg_query_image_info(sprite->image);
|
|
|
- cached_sprite_data->height = info.height;
|
|
|
- cached_sprite_data->width = info.width;
|
|
|
- cached_sprite_data->image = sprite->image;
|
|
|
- }
|
|
|
+ _sbatch_context* ctx = _sbatch_context_at(_sbatch.ctx_id.id);
|
|
|
+ SOKOL_ASSERT(ctx);
|
|
|
|
|
|
- const float scale_x = _SB_DEFAULT(sprite->scale.x, 1.0f);
|
|
|
- const float scale_y = _SB_DEFAULT(sprite->scale.y, 1.0f);
|
|
|
+ if (ctx->sprite_count < ctx->desc.max_sprites) {
|
|
|
|
|
|
- const float sprite_width = _SB_DEFAULT(sprite->width, (float)cached_sprite_data->width);
|
|
|
- const float sprite_height = _SB_DEFAULT(sprite->height, (float)cached_sprite_data->height);
|
|
|
+ const int sprite_index = ctx->sprite_count++;
|
|
|
|
|
|
- const float width = sprite_width * scale_x;
|
|
|
- const float height = sprite_height * scale_y;
|
|
|
+ _sbatch_sprite_data* cached_sprite_data = &_sbatch.sprite_pool.data[_sg_image_slot_index(sprite->image.id)];
|
|
|
|
|
|
- const float texel_width = (1.0f / cached_sprite_data->width);
|
|
|
- const float texel_height = (1.0f / cached_sprite_data->height);
|
|
|
+ if (cached_sprite_data->image.id != sprite->image.id) {
|
|
|
+ const sg_image_info info = sg_query_image_info(sprite->image);
|
|
|
+ cached_sprite_data->height = info.height;
|
|
|
+ cached_sprite_data->width = info.width;
|
|
|
+ cached_sprite_data->texel_height = 1.0f / (float)info.height;
|
|
|
+ cached_sprite_data->texel_width = 1.0f / (float)info.width;
|
|
|
+ cached_sprite_data->image = sprite->image;
|
|
|
+ }
|
|
|
|
|
|
- sb_float2 tex_coord_top_left = {
|
|
|
- sprite->source.x * texel_width,
|
|
|
- sprite->source.y * texel_height
|
|
|
- };
|
|
|
+ const sbatch_float2 scale = { _SBATCH_DEFAULT(sprite->scale.x, 1.0f), _SBATCH_DEFAULT(sprite->scale.y, 1.0f) };
|
|
|
+ const sbatch_float2 scaled_origin = { scale.x * sprite->origin.x, scale.y * sprite->origin.y };
|
|
|
+ float width, height;
|
|
|
+ sbatch_float2 tex_coord_top_left;
|
|
|
+ sbatch_float2 tex_coord_bottom_right;
|
|
|
+
|
|
|
+ if (_sbatch_rect_is_valid(&sprite->source)) {
|
|
|
+ width = sprite->source.width * scale.x;
|
|
|
+ height = sprite->source.height * scale.y;
|
|
|
+ tex_coord_top_left.x = sprite->source.x * cached_sprite_data->texel_width;
|
|
|
+ tex_coord_top_left.y = sprite->source.y * cached_sprite_data->texel_height;
|
|
|
+ tex_coord_bottom_right.x = (sprite->source.x + sprite->source.width) * cached_sprite_data->texel_width;
|
|
|
+ tex_coord_bottom_right.y = (sprite->source.y + sprite->source.height) * cached_sprite_data->texel_height;
|
|
|
+ } else {
|
|
|
+ width = (float)cached_sprite_data->width * scale.x;
|
|
|
+ height = (float)cached_sprite_data->height * scale.y;
|
|
|
+ tex_coord_top_left.x = 0.0f;
|
|
|
+ tex_coord_top_left.y = 0.0f;
|
|
|
+ tex_coord_bottom_right.x = 1.0f;
|
|
|
+ tex_coord_bottom_right.y = 1.0f;
|
|
|
+ }
|
|
|
|
|
|
- sb_float2 tex_coord_bottom_right = {
|
|
|
- (sprite->source.x + sprite_width) * texel_width,
|
|
|
- (sprite->source.y + sprite_height) * texel_height
|
|
|
- };
|
|
|
+ if ((sprite->flags & SBATCH_FLIP_Y) != SBATCH_FLIP_NONE) {
|
|
|
+ const float temp = tex_coord_bottom_right.y;
|
|
|
+ tex_coord_bottom_right.y = tex_coord_top_left.y;
|
|
|
+ tex_coord_top_left.y = temp;
|
|
|
+ }
|
|
|
|
|
|
- if ((sprite->flags & SB_FLIP_Y) != SB_FLIP_NONE)
|
|
|
- {
|
|
|
- const float temp = tex_coord_bottom_right.y;
|
|
|
- tex_coord_bottom_right.y = tex_coord_top_left.y;
|
|
|
- tex_coord_top_left.y = temp;
|
|
|
- }
|
|
|
+ if ((sprite->flags & SBATCH_FLIP_X) != SBATCH_FLIP_NONE) {
|
|
|
+ const float temp = tex_coord_bottom_right.x;
|
|
|
+ tex_coord_bottom_right.x = tex_coord_top_left.x;
|
|
|
+ tex_coord_top_left.x = temp;
|
|
|
+ }
|
|
|
|
|
|
- if ((sprite->flags & SB_FLIP_X) != SB_FLIP_NONE)
|
|
|
- {
|
|
|
- const float temp = tex_coord_bottom_right.x;
|
|
|
- tex_coord_bottom_right.x = tex_coord_top_left.x;
|
|
|
- tex_coord_top_left.x = temp;
|
|
|
+ uint32_t packed_color = _sbatch_pack_color(&sprite->color);
|
|
|
+ packed_color = packed_color == 0 ? 0xFFFFFFFF : packed_color;
|
|
|
+
|
|
|
+ ctx->images[sprite_index] = sprite->image;
|
|
|
+
|
|
|
+ const int base_vertex_index = sprite_index * 4;
|
|
|
+ _sbatch_vertex* vertices = ctx->vertices + base_vertex_index;
|
|
|
+
|
|
|
+ if (sprite->rotation == 0.0f) {
|
|
|
+ _sbatch_init_quad(vertices,
|
|
|
+ sprite->position.x - scaled_origin.x,
|
|
|
+ sprite->position.y - scaled_origin.y,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ packed_color,
|
|
|
+ tex_coord_top_left,
|
|
|
+ tex_coord_bottom_right,
|
|
|
+ sprite->depth);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ _sbatch_init_quad_rotated(vertices,
|
|
|
+ sprite->position.x,
|
|
|
+ sprite->position.y,
|
|
|
+ -scaled_origin.x,
|
|
|
+ -scaled_origin.y,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ sinf(sprite->rotation),
|
|
|
+ cosf(sprite->rotation),
|
|
|
+ packed_color,
|
|
|
+ tex_coord_top_left,
|
|
|
+ tex_coord_bottom_right,
|
|
|
+ sprite->depth);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (ctx->label.buf[0] != '\0') {
|
|
|
+ SOKOL_LOG("sokol_spritebatch.h: dropped sprites, increase max_sprites of sbatch_context:");
|
|
|
+ SOKOL_LOG(ctx->label.buf);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ SOKOL_LOG("sokol_spritebatch.h: dropped sprites, increase max_sprites");
|
|
|
+ }
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- const float scaled_origin_x = scale_x * sprite->origin.x;
|
|
|
- const float scaled_origin_y = scale_y * sprite->origin.y;
|
|
|
+SOKOL_API_IMPL void sbatch_push_sprite_rect(const sbatch_sprite_rect* sprite) {
|
|
|
+ SOKOL_ASSERT(sprite);
|
|
|
+ SOKOL_ASSERT(sprite->image.id != SG_INVALID_ID);
|
|
|
|
|
|
- _sb_quad* quad = &_sb.quads[_sb.quad_count++];
|
|
|
- quad->sort_key = _sb_make_sort_key(sprite);
|
|
|
- quad->image = sprite->image;
|
|
|
+ _sbatch_context* ctx = _sbatch_context_at(_sbatch.ctx_id.id);
|
|
|
+ SOKOL_ASSERT(ctx);
|
|
|
|
|
|
- uint32_t packed_color = _sb_pack_color(&sprite->color);
|
|
|
- packed_color = packed_color == 0 ? 0xFFFFFFFF : packed_color;
|
|
|
+ if (ctx->sprite_count < ctx->desc.max_sprites) {
|
|
|
|
|
|
- if (sprite->rotation == 0.0f)
|
|
|
- {
|
|
|
- _sb_init_quad(quad,
|
|
|
- sprite->flags,
|
|
|
- sprite->position.x - scaled_origin_x,
|
|
|
- sprite->position.y - scaled_origin_y,
|
|
|
- width,
|
|
|
- height,
|
|
|
- packed_color,
|
|
|
- tex_coord_top_left,
|
|
|
- tex_coord_bottom_right,
|
|
|
- sprite->depth);
|
|
|
+ const int sprite_index = ctx->sprite_count++;
|
|
|
+
|
|
|
+ _sbatch_sprite_data* cached_sprite_data = &_sbatch.sprite_pool.data[_sg_image_slot_index(sprite->image.id)];
|
|
|
+
|
|
|
+ if (cached_sprite_data->image.id != sprite->image.id) {
|
|
|
+ const sg_image_info info = sg_query_image_info(sprite->image);
|
|
|
+ cached_sprite_data->height = info.height;
|
|
|
+ cached_sprite_data->width = info.width;
|
|
|
+ cached_sprite_data->texel_height = 1.0f / (float)info.height;
|
|
|
+ cached_sprite_data->texel_width = 1.0f / (float)info.width;
|
|
|
+ cached_sprite_data->image = sprite->image;
|
|
|
+ }
|
|
|
+
|
|
|
+ sbatch_float2 scaled_origin;
|
|
|
+ sbatch_float2 tex_coord_top_left;
|
|
|
+ sbatch_float2 tex_coord_bottom_right;
|
|
|
+
|
|
|
+ if (_sbatch_rect_is_valid(&sprite->source)) {
|
|
|
+ tex_coord_top_left.x = sprite->source.x * cached_sprite_data->texel_width;
|
|
|
+ tex_coord_top_left.y = sprite->source.y * cached_sprite_data->texel_height;
|
|
|
+ tex_coord_bottom_right.x = (sprite->source.x + sprite->source.width) * cached_sprite_data->texel_width;
|
|
|
+ tex_coord_bottom_right.y = (sprite->source.y + sprite->source.height) * cached_sprite_data->texel_height;
|
|
|
+ scaled_origin.x = sprite->origin.x * sprite->destination.width / sprite->source.width;
|
|
|
+ scaled_origin.y = sprite->origin.y * sprite->destination.height / sprite->source.height;
|
|
|
+ } else {
|
|
|
+ tex_coord_top_left.x = 0.0f;
|
|
|
+ tex_coord_top_left.y = 0.0f;
|
|
|
+ tex_coord_bottom_right.x = 1.0f;
|
|
|
+ tex_coord_bottom_right.y = 1.0f;
|
|
|
+ scaled_origin.x = sprite->origin.x * sprite->destination.width * cached_sprite_data->texel_width;
|
|
|
+ scaled_origin.y = sprite->origin.y * sprite->destination.height * cached_sprite_data->texel_height;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((sprite->flags & SBATCH_FLIP_Y) != SBATCH_FLIP_NONE) {
|
|
|
+ const float temp = tex_coord_bottom_right.y;
|
|
|
+ tex_coord_bottom_right.y = tex_coord_top_left.y;
|
|
|
+ tex_coord_top_left.y = temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((sprite->flags & SBATCH_FLIP_X) != SBATCH_FLIP_NONE) {
|
|
|
+ const float temp = tex_coord_bottom_right.x;
|
|
|
+ tex_coord_bottom_right.x = tex_coord_top_left.x;
|
|
|
+ tex_coord_top_left.x = temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ uint32_t packed_color = _sbatch_pack_color(&sprite->color);
|
|
|
+ packed_color = packed_color == 0 ? 0xFFFFFFFF : packed_color;
|
|
|
+
|
|
|
+ ctx->images[sprite_index] = sprite->image;
|
|
|
+
|
|
|
+ const int base_vertex_index = sprite_index * 4;
|
|
|
+ _sbatch_vertex* vertices = ctx->vertices + base_vertex_index;
|
|
|
+
|
|
|
+ if (sprite->rotation == 0.0f) {
|
|
|
+ _sbatch_init_quad(vertices,
|
|
|
+ sprite->destination.x - scaled_origin.x,
|
|
|
+ sprite->destination.y - scaled_origin.y,
|
|
|
+ sprite->destination.width,
|
|
|
+ sprite->destination.height,
|
|
|
+ packed_color,
|
|
|
+ tex_coord_top_left,
|
|
|
+ tex_coord_bottom_right,
|
|
|
+ sprite->depth);
|
|
|
+ } else {
|
|
|
+ _sbatch_init_quad_rotated(vertices,
|
|
|
+ sprite->destination.x,
|
|
|
+ sprite->destination.y,
|
|
|
+ -scaled_origin.x,
|
|
|
+ -scaled_origin.y,
|
|
|
+ sprite->destination.width,
|
|
|
+ sprite->destination.height,
|
|
|
+ sinf(sprite->rotation),
|
|
|
+ cosf(sprite->rotation),
|
|
|
+ packed_color,
|
|
|
+ tex_coord_top_left,
|
|
|
+ tex_coord_bottom_right,
|
|
|
+ sprite->depth);
|
|
|
+ }
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- _sb_init_quad_rotated(quad,
|
|
|
- sprite->position.x,
|
|
|
- sprite->position.y,
|
|
|
- -scaled_origin_x,
|
|
|
- -scaled_origin_y,
|
|
|
- width,
|
|
|
- height,
|
|
|
- sinf(sprite->rotation),
|
|
|
- cosf(sprite->rotation),
|
|
|
- packed_color,
|
|
|
- tex_coord_top_left,
|
|
|
- tex_coord_bottom_right,
|
|
|
- sprite->depth);
|
|
|
+ else {
|
|
|
+ if (ctx->label.buf[0] != '\0') {
|
|
|
+ SOKOL_LOG("sokol_spritebatch.h: dropped sprites, increase max_sprites of sbatch_context:");
|
|
|
+ SOKOL_LOG(ctx->label.buf);
|
|
|
+ } else {
|
|
|
+ SOKOL_LOG("sokol_spritebatch.h: dropped sprites, increase max_sprites");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_end(void) {
|
|
|
+static void _sbatch_draw(int base_element, sg_image current_image, int num_elements) {
|
|
|
+ _sbatch.bindings.fs_images[0] = current_image;
|
|
|
+ sg_apply_bindings(&_sbatch.bindings);
|
|
|
+ sg_draw(base_element, num_elements, 1);
|
|
|
+}
|
|
|
|
|
|
- SOKOL_ASSERT(_sb.begin_called);
|
|
|
- _sb.begin_called = false;
|
|
|
+static _sbatch_mat4x4 _sbatch_orthographic_off_center(float left, float right, float bottom, float top, float z_near, float z_far) {
|
|
|
+ _sbatch_mat4x4 result;
|
|
|
|
|
|
- if (_sb.quad_count == 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ result.m[0][0] = 2.0f / (right - left);
|
|
|
+ result.m[0][1] = 0.0f;
|
|
|
+ result.m[0][2] = 0.0f;
|
|
|
+ result.m[0][3] = 0.0f;
|
|
|
|
|
|
- if (_sb.render_state.sort_mode != SB_SORT_MODE_DEFERRED) {
|
|
|
- qsort(_sb.quads, _sb.quad_count, sizeof(_sb_quad), _sb_quad_compare);
|
|
|
- }
|
|
|
+ result.m[1][0] = 0.0f;
|
|
|
+ result.m[1][1] = 2.0f / (top - bottom);
|
|
|
+ result.m[1][2] = 0.0f;
|
|
|
+ result.m[1][3] = 0.0f;
|
|
|
|
|
|
- /*
|
|
|
- If this is moved to storing the quad data SOA, we would be able to use the array that stores all the
|
|
|
- vertex data verbatim without copying vertex data into an auxiliary buffer. This would significantly
|
|
|
- complicate the code though, especially sorting the quads before submission. Can't use a standard sorting
|
|
|
- algorithm to sort SOA data. Would need to implement a bespoke solution and that'd be a bit of a pain to
|
|
|
- profile and maintain. It is something to think about though.
|
|
|
- */
|
|
|
- for (size_t i = 0; i < _sb.quad_count; i++) {
|
|
|
- memcpy(_sb.vertex_buffer_data + (i * 4), &_sb.quads[i], 4 * sizeof(_sb_vertex));
|
|
|
- }
|
|
|
+ result.m[2][0] = 0.0f;
|
|
|
+ result.m[2][1] = 0.0f;
|
|
|
+ result.m[2][2] = 1.0f / (z_near - z_far);
|
|
|
+ result.m[2][3] = 0.0f;
|
|
|
|
|
|
- _sb_init_batches();
|
|
|
+ result.m[3][0] = (left + right) / (left - right);
|
|
|
+ result.m[3][1] = (bottom + top) / (bottom - top);
|
|
|
+ result.m[3][2] = z_near / (z_near - z_far);
|
|
|
+ result.m[3][3] = 1.0f;
|
|
|
+
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
-SOKOL_API_IMPL void sb_draw(void) {
|
|
|
+void sbatch_end(void) {
|
|
|
+ SOKOL_ASSERT(_sbatch.begin_called);
|
|
|
+ _sbatch.begin_called = false;
|
|
|
+
|
|
|
+ _sbatch_context* ctx = _sbatch_context_at(_sbatch.ctx_id.id);
|
|
|
+ SOKOL_ASSERT(ctx);
|
|
|
|
|
|
- if (_sb.batch_data.batch_size == 0) {
|
|
|
+ if (ctx->sprite_count == 0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const sg_range range = { _sb.vertex_buffer_data, _sb.quad_count * 4 * sizeof(_sb_vertex) };
|
|
|
- sg_update_buffer(_sb.vertex_buffer, &range);
|
|
|
+ const int max_vertices = 4 * ctx->sprite_count;
|
|
|
+ const size_t vbuf_size = (size_t)max_vertices * sizeof(_sbatch_vertex);
|
|
|
+ const sg_range range = { ctx->vertices, vbuf_size };
|
|
|
+ sg_update_buffer(ctx->vertex_buffer, &range);
|
|
|
+
|
|
|
+ int batch_size = 0;
|
|
|
+ int base_element = 0;
|
|
|
+ sg_image current_image = ctx->images[0];
|
|
|
+
|
|
|
+ sg_apply_pipeline(ctx->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);
|
|
|
+
|
|
|
+ const sg_range matrix_range = { &matrix, sizeof matrix };
|
|
|
+ sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &matrix_range);
|
|
|
|
|
|
- for (size_t i = 1; i < _sb.batch_data.batch_size; i++) {
|
|
|
- _sb_batch* batch = _sb.batch_data.batches + i;
|
|
|
- sg_apply_pipeline(_sb.default_pipeline);
|
|
|
- sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &SG_RANGE(batch->matrix));
|
|
|
- _sb.bindings.fs_images[0] = batch->image;
|
|
|
- sg_apply_bindings(&_sb.bindings);
|
|
|
- sg_draw(batch->base_element, batch->num_elements, 1);
|
|
|
+ if(ctx->fs_uniform_state.data) {
|
|
|
+ sg_apply_uniforms(SG_SHADERSTAGE_FS, ctx->fs_uniform_state.ub_index, ctx->fs_uniform_state.data);
|
|
|
}
|
|
|
|
|
|
- _sb.quad_count = 0;
|
|
|
- _sb.batch_data.batch_size = 0;
|
|
|
+ for (int i = 0; i < ctx->sprite_count; ++i, ++batch_size) {
|
|
|
+ if (ctx->images[i].id != current_image.id) {
|
|
|
+ const int num_elements = batch_size * 6;
|
|
|
+ _sbatch_draw(base_element, current_image, num_elements);
|
|
|
+ batch_size = 0;
|
|
|
+ base_element += num_elements;
|
|
|
+ current_image = ctx->images[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const int num_elements = batch_size * 6;
|
|
|
+ _sbatch_draw(base_element, current_image, num_elements);
|
|
|
+
|
|
|
+ ctx->sprite_count = 0;
|
|
|
+}
|
|
|
+
|
|
|
+SOKOL_API_IMPL void sbatch_apply_fs_uniforms(int ub_index, const sg_range* data) {
|
|
|
+ SOKOL_ASSERT(data);
|
|
|
+ SOKOL_ASSERT(data->ptr);
|
|
|
+ _sbatch_context* ctx = _sbatch_context_at(_sbatch.ctx_id.id);
|
|
|
+ ctx->fs_uniform_state.ub_index = ub_index;
|
|
|
+ ctx->fs_uniform_state.data = data;
|
|
|
+}
|
|
|
+
|
|
|
+SOKOL_API_IMPL void sbatch_premultiply_alpha_rgba8(uint8_t* pixels, int pixel_count) {
|
|
|
+ SOKOL_ASSERT(pixels);
|
|
|
+ for (int i = 0; i < pixel_count; ++i) {
|
|
|
+ pixels[0] = pixels[0] * pixels[3] / 255;
|
|
|
+ pixels[1] = pixels[1] * pixels[3] / 255;
|
|
|
+ pixels[2] = pixels[2] * pixels[3] / 255;
|
|
|
+ pixels += 4;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#endif /* SOKOL_SPRITEBATCH_IMPL */
|