diff options
| author | Andre Weissflog <floooh@gmail.com> | 2019-04-19 18:44:10 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-04-19 18:44:10 +0200 |
| commit | 96ddd89c8a0743860487a113eba71ecaecd0eab6 (patch) | |
| tree | b452f6ca02685b012f08f8e9e8bcbfa0886c26e6 | |
| parent | 82346ae0365a3b1157e2ddce591a2d01087316a1 (diff) | |
Pipeline stack for sokol_gl.h (#144)
This replaces the old render state handling in sokol_gl.h with a 'pipeline stack' (like the GL matrix stack, but for pipeline-state-objects).
| -rw-r--r-- | README.md | 63 | ||||
| -rw-r--r-- | util/sokol_gl.h | 709 |
2 files changed, 558 insertions, 214 deletions
@@ -409,6 +409,69 @@ Mainly some "missing features" for desktop apps: # Updates +- **19-Apr-2019** I have replaced the rather inflexible render-state handling +in **sokol_gl.h** with a *pipeline stack* (like the GL matrix stack, but with +pipeline-state-objects), along with a couple of other minor API tweaks. + + These are the new pipeline-stack functions: + ```c + sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc); + void sgl_destroy_pipeline(sgl_pipeline pip); + void sgl_default_pipeline(void); + void sgl_load_pipeline(sgl_pipeline pip); + void sgl_push_pipeline(void); + void sgl_pop_pipeline(void); + ``` + + A pipeline object is created just like in sokol_gfx.h, but without shaders, + vertex layout, pixel formats, primitive-type and sample count (these details + are filles in by the ```sgl_make_pipeline()``` wrapper functions. For instance + to create a pipeline object for additive transparency: + + ```c + sgl_pipeline additive_pip = sgl_make_pipeline(&(sg_pipeline){ + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_ONE, + .dst_factor_rgb = SG_BLENDFACTOR_ONE + } + }); + ``` + + And to render with this, simply call ```sgl_load_pipeline()```: + + ```c + sgl_load_pipeline(additive_pip); + sgl_begin_triangles(); + ... + sgl_end(); + ``` + + Or to preserve and restore the previously active pipeline state: + + ```c + sgl_push_pipeline(); + sgl_load_pipeline(additive_pip); + sgl_begin_quads(); + ... + sgl_end(); + sgl_pop_pipeline(); + ``` + + You can also load the 'default pipeline' explicitely on the top of the + pipeline stack with ```sgl_default_pipeline()```. + + The other API change is: + + - ```sgl_state_texture(bool b)``` has been replaced with ```sgl_enable_texture()``` + and ```sgl_disable_texture()``` + + The code samples have been updated accordingly: + + - [sgl-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/sgl-sapp.c) + - [sgl-lines-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/sgl-lines-sapp.c) + - [sgl-microui-sapp.c](https://github.com/floooh/sokol-samples/blob/master/sapp/sgl-microui-sapp.c) + - **01-Apr-2019** (not an April Fool's joke): There's a new **sokol_gl.h** util header which implements an 'OpenGL-1.x-in-spirit' rendering API on top of sokol_gfx.h (vertex specification via begin/end, and a matrix stack). This is diff --git a/util/sokol_gl.h b/util/sokol_gl.h index 12d20316..310d9dd4 100644 --- a/util/sokol_gl.h +++ b/util/sokol_gl.h @@ -24,6 +24,7 @@ SOKOL_FREE(p) - your own free function (default: free(p)) SOKOL_API_DECL - public function declaration prefix (default: extern) SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) Include the following headers before including sokol_gl.h: @@ -43,7 +44,6 @@ and simple UI-style 2D rendering: What's implemented: - - vertex components: - position (x, y, z) - 2D texture coords (u, v) @@ -53,38 +53,30 @@ - line list and strip - quad list (TODO: quad strips) - point list (TODO: point size) - - render state: - - depth test (always less-equal when enabled) - - blending (always src_alpha / inv_src_alpha when enabled) - - cull face (front face winding selectable at start, default is CCW) - - texturing (enabled/disabled) - one texture layer (no multi-texturing) - viewport and scissor-rect with selectable origin (top-left or bottom-left) - all GL 1.x matrix stack functions, and additionally equivalent functions for gluPerspective and gluLookat - - Notable GLES 1.x features that are *NOT* implemented: + Notable GLES 1.x features that are *NOT* implemented: - vertex lighting (this is the most likely GL feature that might be added later) - vertex arrays (although providing whole chunks of vertex data at once might be a useful feature for a later version) - - stencil operations - texture coordinate generation - - depth comparison functions other than less-equal - point size and line width - all pixel store functions - no ALPHA_TEST - - no color mask or clear functions (clear is handled by the - sokol-gfx render pass) + - no clear functions (clearing is handled by the sokol-gfx render pass) - fog Notable differences to GL: - - - no "enum soup" for render states etc, instead there are - explicitely named state functions - - all angles are in radians, not degrees (note the sgl_rad() and + - No "enum soup" for render states etc, instead there's a + 'pipeline stack', this is similar to GL's matrix stack, + for for pipeline-state-objects. The pipeline object at + the top of the pipeline stack defines the active set of render states + - All angles are in radians, not degrees (note the sgl_rad() and sgl_deg() conversion functions) - - no enable/disable state for scissor test, this is always enabled + - No enable/disable state for scissor test, this is always enabled STEP BY STEP: ============= @@ -113,6 +105,11 @@ int max_vertices - default is 65536 int max_commands - default is 16384 + You can adjust the size of the internal pipeline state object pool + with: + + int pipeline_pool_size - default is 64 + Finally you can change the face winding for front-facing triangles and quads: @@ -121,6 +118,27 @@ The default winding for front faces is counter-clock-wise. This is the same as OpenGL's default, but different from sokol-gfx. + --- Optionally create pipeline-state-objects if you need render state + that differs from sokol-gl's default state: + + sgl_pipeline pip = sgl_make_pipeline(const sg_pipeline_desc* desc) + + The similarity with sokol_gfx.h's sg_pipeline type and sg_make_pipeline() + function is intended. sgl_make_pipeline() also takes a standard + sokol-gfx sg_pipeline_desc object to describe the render state, but + without: + - shader + - vertex layout + - color- and depth-pixel-formats + - sample count + Those will be filled in by sgl_make_pipeline(). Note that each + call to sgl_make_pipeline() needs to create several sokol-gfx + pipeline objects (one for each primitive type). + + --- if you need to destroy sgl_pipeline objects before sgl_shutdown(): + + sgl_destroy_pipeline(sgl_pipeline pip) + --- After sgl_setup() you can call any of the sokol-gl functions anywhere in a frame, *except* sgl_draw(). The 'vanilla' functions will only change internal sokol-gl state, and not call any sokol-gfx @@ -134,25 +152,36 @@ This will set the following default state: - - depth-test, blending, cull-face, texturing disabled - current texture coordinate to u=0.0f, v=0.0f - current color to white (rgba all 1.0f) - - unbind the current texture + - unbind the current texture and texturing will be disabled - *all* matrices will be set to identity (also the projection matrix) + - the default render state will be set by loading the 'default pipeline' + into the top of the pipeline stack - The current matrix stack depths will not be changed by sgl_defaults(). + The current matrix- and pipeline-stack-depths will not be changed by + sgl_defaults(). + + --- change the currently active renderstate through the + pipeline-stack functions, this works similar to the + traditional GL matrix stack: + + ...load the default pipeline state on the top of the pipeline stack: + + sgl_default_pipeline() - --- adjust render state with: + ...load a specific pipeline on the top of the pipeline stack: - sgl_state_depth_test(bool enabled) - sgl_state_blend(bool enabled) - sgl_state_cull_face(bool enabled) - sgl_state_texture(bool enabled) + sgl_load_pipeline(sgl_pipeline pip) - --- optionally set an sg_image as current texture, this - will only be used when texturing has been enabled - with sgl_state_texture(true): + ...push and pop the pipeline stack: + sgl_push_pipeline() + sgl_pop_pipeline() + --- control texturing with: + + sgl_enable_texture() + sgl_disable_texture() sgl_texture(sg_image img) --- set the current viewport and scissor rect with: @@ -279,8 +308,8 @@ SGL_ERROR_VERTICES_FULL - internal vertex buffer is full (checked in sgl_end()) SGL_ERROR_UNIFORMS_FULL - the internal uniforms buffer is full (checked in sgl_end()) SGL_ERROR_COMMANDS_FULL - the internal command buffer is full (checked in sgl_end()) - SGL_ERROR_STACK_OVERFLOW - current matrix stack overflow (checked in sgl_push_matrix()) - SGL_ERROR_STACK_UNDERFLOW - current matrix stack underflow (checked in sgl_pop_matrix()) + SGL_ERROR_STACK_OVERFLOW - matrix- or pipeline-stack overflow + SGL_ERROR_STACK_UNDERFLOW - matrix- or pipeline-stack underflow ...if sokol-gl is in an error-state, sgl_draw() will skip any rendering, and reset the error code to SGL_NO_ERROR. @@ -341,9 +370,6 @@ - if it's a viewport command, call sg_apply_viewport() - if it's a scissor-rect command, call sg_apply_scissor_rect() - if it's a draw command: - - depending on the primitive type and render state - mask of the command, lookup an existing, or create - a new pipeline-state-object - depending on what has changed since the last draw command, call sg_apply_pipeline(), sg_apply_bindings() and sg_apply_uniforms() @@ -390,6 +416,9 @@ #define SOKOL_API_DECL extern #endif +/* sokol_gl pipeline handle (created with sgl_make_pipeline()) */ +typedef struct sgl_pipeline { uint32_t id; } sgl_pipeline; + /* sgl_error_t @@ -406,8 +435,9 @@ typedef enum sgl_error_t { } sgl_error_t; typedef struct sgl_desc_t { - int max_vertices; /* size for vertex buffer */ - int max_commands; /* size of uniform- and command-buffers */ + int max_vertices; /* size for vertex buffer */ + int max_commands; /* size of uniform- and command-buffers */ + int pipeline_pool_size; /* size of the internal pipeline pool, default is 64 */ sg_pixel_format color_format; sg_pixel_format depth_format; int sample_count; @@ -422,15 +452,42 @@ SOKOL_API_DECL void sgl_defaults(void); SOKOL_API_DECL float sgl_rad(float deg); SOKOL_API_DECL float sgl_deg(float rad); -/* render state functions (only valid outside begin/end) */ -SOKOL_API_DECL void sgl_state_depth_test(bool enabled); -SOKOL_API_DECL void sgl_state_blend(bool enabled); -SOKOL_API_DECL void sgl_state_cull_face(bool enabled); -SOKOL_API_DECL void sgl_state_texture(bool enabled); +/* create and destroy pipeline objects */ +SOKOL_API_DECL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_API_DECL void sgl_destroy_pipeline(sgl_pipeline pip); + +/* render state functions */ SOKOL_API_DECL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left); SOKOL_API_DECL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left); +SOKOL_API_DECL void sgl_enable_texture(void); +SOKOL_API_DECL void sgl_disable_texture(void); SOKOL_API_DECL void sgl_texture(sg_image img); +/* pipeline stack functions */ +SOKOL_API_DECL void sgl_default_pipeline(void); +SOKOL_API_DECL void sgl_load_pipeline(sgl_pipeline pip); +SOKOL_API_DECL void sgl_push_pipeline(void); +SOKOL_API_DECL void sgl_pop_pipeline(void); + +/* matrix stack functions */ +SOKOL_API_DECL void sgl_matrix_mode_modelview(void); +SOKOL_API_DECL void sgl_matrix_mode_projection(void); +SOKOL_API_DECL void sgl_matrix_mode_texture(void); +SOKOL_API_DECL void sgl_load_identity(void); +SOKOL_API_DECL void sgl_load_matrix(const float m[16]); +SOKOL_API_DECL void sgl_load_transpose_matrix(const float m[16]); +SOKOL_API_DECL void sgl_mult_matrix(const float m[16]); +SOKOL_API_DECL void sgl_mult_transpose_matrix(const float m[16]); +SOKOL_API_DECL void sgl_rotate(float angle_rad, float x, float y, float z); +SOKOL_API_DECL void sgl_scale(float x, float y, float z); +SOKOL_API_DECL void sgl_translate(float x, float y, float z); +SOKOL_API_DECL void sgl_frustum(float l, float r, float b, float t, float n, float f); +SOKOL_API_DECL void sgl_ortho(float l, float r, float b, float t, float n, float f); +SOKOL_API_DECL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far); +SOKOL_API_DECL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z); +SOKOL_API_DECL void sgl_push_matrix(void); +SOKOL_API_DECL void sgl_pop_matrix(void); + /* these functions only set the internal 'current texcoord / color' (valid inside or outside begin/end) */ SOKOL_API_DECL void sgl_t2f(float u, float v); SOKOL_API_DECL void sgl_c3f(float r, float g, float b); @@ -472,25 +529,6 @@ SOKOL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, SOKOL_API_DECL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba); SOKOL_API_DECL void sgl_end(void); -/* matrix stack functions (only valid outside begin end) */ -SOKOL_API_DECL void sgl_matrix_mode_modelview(void); -SOKOL_API_DECL void sgl_matrix_mode_projection(void); -SOKOL_API_DECL void sgl_matrix_mode_texture(void); -SOKOL_API_DECL void sgl_load_identity(void); -SOKOL_API_DECL void sgl_load_matrix(const float m[16]); -SOKOL_API_DECL void sgl_load_transpose_matrix(const float m[16]); -SOKOL_API_DECL void sgl_mult_matrix(const float m[16]); -SOKOL_API_DECL void sgl_mult_transpose_matrix(const float m[16]); -SOKOL_API_DECL void sgl_rotate(float angle_rad, float x, float y, float z); -SOKOL_API_DECL void sgl_scale(float x, float y, float z); -SOKOL_API_DECL void sgl_translate(float x, float y, float z); -SOKOL_API_DECL void sgl_frustum(float l, float r, float b, float t, float n, float f); -SOKOL_API_DECL void sgl_ortho(float l, float r, float b, float t, float n, float f); -SOKOL_API_DECL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far); -SOKOL_API_DECL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z); -SOKOL_API_DECL void sgl_push_matrix(void); -SOKOL_API_DECL void sgl_pop_matrix(void); - /* render everything */ SOKOL_API_DECL void sgl_draw(void); @@ -531,6 +569,14 @@ extern "C" { #define SOKOL_MALLOC(s) malloc(s) #define SOKOL_FREE(p) free(p) #endif +#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 #ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #endif @@ -983,13 +1029,27 @@ typedef enum { SGL_NUM_PRIMITIVE_TYPES, } _sgl_primitive_type_t; -typedef enum { - SGL_STATE_DEPTHTEST, - SGL_STATE_BLEND, - SGL_STATE_CULLFACE, - SGL_STATE_TEXTURE, - SGL_NUM_STATES -} _sgl_state_t; +typedef struct { + uint32_t id; + sg_resource_state state; +} _sgl_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sgl_pool_t; + +typedef struct { + _sgl_slot_t slot; + sg_pipeline pip[SGL_NUM_PRIMITIVE_TYPES]; +} _sgl_pipeline_t; + +typedef struct { + _sgl_pool_t pool; + _sgl_pipeline_t* pips; +} _sgl_pipeline_pool_t; typedef enum { SGL_MATRIXMODE_MODELVIEW, @@ -1020,11 +1080,11 @@ typedef enum { } _sgl_command_type_t; typedef struct { + sg_pipeline pip; sg_image img; int base_vertex; int num_vertices; int uniform_index; - uint16_t state_bits; /* bit mask with primitive type and render states */ } _sgl_draw_args_t; typedef struct { @@ -1048,13 +1108,18 @@ typedef struct { _sgl_args_t args; } _sgl_command_t; -/* number of pipelines: 3 bits for primitive type, 3 relevant render state bits */ -#define _SGL_MAX_PIPELINES (64) -/* matrix stack depth */ +#define _SGL_INVALID_SLOT_INDEX (0) #define _SGL_MAX_STACK_DEPTH (64) +#define _SGL_DEFAULT_PIPELINE_POOL_SIZE (64) +#define _SGL_DEFAULT_MAX_VERTICES (1<<16) +#define _SGL_DEFAULT_MAX_COMMANDS (1<<14) +#define _SGL_SLOT_SHIFT (16) +#define _SGL_MAX_POOL_SIZE (1<<_SGL_SLOT_SHIFT) +#define _SGL_SLOT_MASK (_SGL_MAX_POOL_SIZE-1) typedef struct { uint32_t init_cookie; + sgl_desc_t desc; int num_vertices; int num_uniforms; @@ -1071,10 +1136,11 @@ typedef struct { int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ sgl_error_t error; bool in_begin; - uint16_t state_bits; /* bitmask with primitive type and render states */ float u, v; uint32_t rgba; + _sgl_primitive_type_t cur_prim_type; sg_image cur_img; + bool texturing_enabled; bool matrix_dirty; /* reset in sgl_end(), set in any of the matrix stack functions */ /* sokol-gfx resources */ @@ -1082,33 +1148,278 @@ typedef struct { sg_image def_img; /* a default white texture */ sg_shader shd; sg_bindings bind; - sg_pipeline pip[_SGL_MAX_PIPELINES]; - sg_pipeline_desc pip_desc; /* template for lazy pipeline creation */ + sgl_pipeline def_pip; + _sgl_pipeline_pool_t pip_pool; + + /* pipeline stack */ + int pip_tos; + sgl_pipeline pip_stack[_SGL_MAX_STACK_DEPTH]; /* matrix stacks */ _sgl_matrix_mode_t cur_matrix_mode; - int top_of_stack[SGL_NUM_MATRIXMODES]; + int matrix_tos[SGL_NUM_MATRIXMODES]; _sgl_matrix_t matrix_stack[SGL_NUM_MATRIXMODES][_SGL_MAX_STACK_DEPTH]; } _sgl_t; static _sgl_t _sgl; /*== PRIVATE FUNCTIONS =======================================================*/ -/* set primitive type in 16-bit merged state */ -static inline uint16_t _sgl_set_prim_type(_sgl_primitive_type_t type, uint16_t bits) { - SOKOL_ASSERT(((int)type) < 8); - return (bits & ~7) | (type & 7); + +static void _sgl_init_pool(_sgl_pool_t* 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 */ + size_t gen_ctrs_size = sizeof(uint32_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)*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; + } +} + +static void _sgl_discard_pool(_sgl_pool_t* 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; +} + +static int _sgl_pool_alloc_index(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } + else { + /* pool exhausted */ + return _SGL_INVALID_SLOT_INDEX; + } +} + +static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SGL_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)); +} + +static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { + SOKOL_ASSERT(pip); + memset(pip, 0, sizeof(_sgl_pipeline_t)); +} + +static void _sgl_setup_pipeline_pool(const sgl_desc_t* desc) { + SOKOL_ASSERT(desc); + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.pip_pool.pool, desc->pipeline_pool_size); + size_t pool_byte_size = sizeof(_sgl_pipeline_t) * _sgl.pip_pool.pool.size; + _sgl.pip_pool.pips = (_sgl_pipeline_t*) SOKOL_MALLOC(pool_byte_size); + SOKOL_ASSERT(_sgl.pip_pool.pips); + memset(_sgl.pip_pool.pips, 0, pool_byte_size); +} + +static void _sgl_discard_pipeline_pool(void) { + SOKOL_FREE(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; + _sgl_discard_pool(&_sgl.pip_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 + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +static uint32_t _sgl_slot_alloc(_sgl_pool_t* pool, _sgl_slot_t* 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 > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SGL_SLOT_SHIFT)|(slot_index & _SGL_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +static int _sgl_slot_index(uint32_t id) { + int slot_index = (int) (id & _SGL_SLOT_MASK); + SOKOL_ASSERT(_SGL_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +/* get pipeline pointer without id-check */ +static _sgl_pipeline_t* _sgl_pipeline_at(uint32_t pip_id) { + SOKOL_ASSERT(SG_INVALID_ID != pip_id); + int slot_index = _sgl_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.pip_pool.pool.size)); + return &_sgl.pip_pool.pips[slot_index]; +} + +/* get pipeline pointer with id-check, returns 0 if no match */ +static _sgl_pipeline_t* _sgl_lookup_pipeline(uint32_t pip_id) { + if (SG_INVALID_ID != pip_id) { + _sgl_pipeline_t* pip = _sgl_pipeline_at(pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; } -/* extract primitive type from 16-bit merged state */ -static inline _sgl_primitive_type_t _sgl_prim_type(uint16_t bits) { - return (_sgl_primitive_type_t) (bits & 7); +static sgl_pipeline _sgl_alloc_pipeline(void) { + sgl_pipeline res; + int slot_index = _sgl_pool_alloc_index(&_sgl.pip_pool.pool); + if (_SGL_INVALID_SLOT_INDEX != slot_index) { + res.id =_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_desc) { + SOKOL_ASSERT((pip_id.id != SG_INVALID_ID) && in_desc); + + /* create a new desc with 'patched' shader and pixel format state */ + sg_pipeline_desc desc = *in_desc; + desc.layout.buffers[0].stride = sizeof(_sgl_vertex_t); + { + sg_vertex_attr_desc* pos = &desc.layout.attrs[0]; + pos->name = "position"; + pos->sem_name = "POSITION"; + pos->offset = offsetof(_sgl_vertex_t, pos); + pos->format = SG_VERTEXFORMAT_FLOAT3; + } + { + sg_vertex_attr_desc* uv = &desc.layout.attrs[1]; + uv->name = "texcoord0"; + uv->sem_name = "TEXCOORD"; + uv->offset = offsetof(_sgl_vertex_t, uv); + uv->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_desc* rgba = &desc.layout.attrs[2]; + rgba->name = "color0"; + rgba->sem_name = "COLOR"; + rgba->offset = offsetof(_sgl_vertex_t, rgba); + rgba->format = SG_VERTEXFORMAT_UBYTE4N; + } + desc.shader = _sgl.shd; + desc.index_type = SG_INDEXTYPE_NONE; + desc.blend.color_format = _sgl.desc.color_format; + desc.blend.depth_format = _sgl.desc.depth_format; + desc.rasterizer.sample_count = _sgl.desc.sample_count; + if (desc.rasterizer.face_winding == _SG_FACEWINDING_DEFAULT) { + desc.rasterizer.face_winding = _sgl.desc.face_winding; + } + if (desc.blend.color_write_mask == _SG_COLORMASK_DEFAULT) { + desc.blend.color_write_mask = SG_COLORMASK_RGB; + } + + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + pip->slot.state = SG_RESOURCESTATE_VALID; + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + switch (i) { + case SGL_PRIMITIVETYPE_POINTS: + desc.primitive_type = SG_PRIMITIVETYPE_POINTS; + break; + case SGL_PRIMITIVETYPE_LINES: + desc.primitive_type = SG_PRIMITIVETYPE_LINES; + break; + case SGL_PRIMITIVETYPE_LINE_STRIP: + desc.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP; + break; + case SGL_PRIMITIVETYPE_TRIANGLES: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES; + break; + case SGL_PRIMITIVETYPE_TRIANGLE_STRIP: + case SGL_PRIMITIVETYPE_QUADS: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP; + break; + } + if (SGL_PRIMITIVETYPE_QUADS == i) { + /* quads are emulated via triangles, use the same pipeline object */ + pip->pip[i] = pip->pip[SGL_PRIMITIVETYPE_TRIANGLES]; + } + else { + pip->pip[i] = sg_make_pipeline(&desc); + if (pip->pip[i].id == SG_INVALID_ID) { + SOKOL_LOG("sokol_gl.h: failed to create pipeline object"); + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } + } +} + +static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(desc); + sgl_pipeline pip_id = _sgl_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sgl_init_pipeline(pip_id, desc); + } + else { + SOKOL_LOG("sokol_gl.h: pipeline pool exhausted!"); + } + return pip_id; +} + +static void _sgl_destroy_pipeline(sgl_pipeline pip_id) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + if (i != SGL_PRIMITIVETYPE_QUADS) { + sg_destroy_pipeline(pip->pip[i]); + } + } + _sgl_reset_pipeline(pip); + _sgl_pool_free_index(&_sgl.pip_pool.pool, _sgl_slot_index(pip_id.id)); + } +} + +static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t prim_type) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + return pip->pip[prim_type]; + } + else { + return (sg_pipeline) { SG_INVALID_ID }; + } } static inline void _sgl_begin(_sgl_primitive_type_t mode) { _sgl.in_begin = true; _sgl.base_vertex = _sgl.cur_vertex; _sgl.vtx_count = 0; - _sgl.state_bits = _sgl_set_prim_type(mode, _sgl.state_bits); + _sgl.cur_prim_type = mode; } static void _sgl_rewind(void) { @@ -1151,14 +1462,20 @@ static inline _sgl_command_t* _sgl_next_command(void) { } static inline uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - return (uint32_t)((a<<24)|(b<<16)|(g<<8)|r); + return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r); +} + +static inline float _sgl_clamp(float v, float lo, float hi) { + if (v < lo) return lo; + else if (v > hi) return hi; + else return v; } static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) { - uint8_t r_u8 = (uint8_t) (r * 255.0f); - uint8_t g_u8 = (uint8_t) (g * 255.0f); - uint8_t b_u8 = (uint8_t) (b * 255.0f); - uint8_t a_u8 = (uint8_t) (a * 255.0f); + uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f); + uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f); + uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f); + uint8_t a_u8 = (uint8_t) (_sgl_clamp(a, 0.0f, 1.0f) * 255.0f); return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8); } @@ -1166,7 +1483,7 @@ static inline void _sgl_vtx(float x, float y, float z, float u, float v, uint32_ SOKOL_ASSERT(_sgl.in_begin); _sgl_vertex_t* vtx; /* handle non-native primitive types */ - if ((_sgl_prim_type(_sgl.state_bits) == SGL_PRIMITIVETYPE_QUADS) && ((_sgl.vtx_count & 3) == 3)) { + if ((_sgl.cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((_sgl.vtx_count & 3) == 3)) { /* for quads, before writing the last quad vertex, reuse the first and third vertex to start the second triangle in the quad */ @@ -1184,50 +1501,6 @@ static inline void _sgl_vtx(float x, float y, float z, float u, float v, uint32_ _sgl.vtx_count++; } -/* set or clear render state bit in 16-bit merged state */ -static inline uint16_t _sgl_set_state(_sgl_state_t state, bool enabled, uint16_t bits) { - /* first 3 bits are used by the primitive type */ - return (bits & ~(8<<state)) | (enabled ? (8<<state) : 0); -} - -/* get render state from merged state bit mask */ -static inline bool _sgl_state(_sgl_state_t state, uint16_t bits) { - return 0 != (bits & (8<<state)); -} - -/* get pipeline index from merged primitive-type / render state bit mask */ -static inline int _sgl_pipeline_index(uint16_t state) { - /* replace emulated primitive types */ - if (_sgl_prim_type(state) == SGL_PRIMITIVETYPE_QUADS) { - state = _sgl_set_prim_type(SGL_PRIMITIVETYPE_TRIANGLES, state); - } - return (int) (state & (_SGL_MAX_PIPELINES-1)); -} - -/* lookup or lazy-create pipeline object */ -static sg_pipeline _sgl_pipeline(uint16_t state_bits) { - /* NOTE: emulated primitive types like QUADS are 'redirected' to - a native primitive type in _sgl_pipeline_index - */ - int pip_index = _sgl_pipeline_index(state_bits); - if (SG_INVALID_ID == _sgl.pip[pip_index].id) { - _sgl.pip_desc.blend.enabled = _sgl_state(SGL_STATE_BLEND, state_bits); - _sgl.pip_desc.rasterizer.cull_mode = _sgl_state(SGL_STATE_CULLFACE, state_bits) ? SG_CULLMODE_BACK : SG_CULLMODE_NONE; - _sgl.pip_desc.depth_stencil.depth_compare_func = _sgl_state(SGL_STATE_DEPTHTEST, state_bits) ? SG_COMPAREFUNC_LESS_EQUAL : SG_COMPAREFUNC_ALWAYS; - switch (_sgl_prim_type(state_bits)) { - case SGL_PRIMITIVETYPE_POINTS: _sgl.pip_desc.primitive_type = SG_PRIMITIVETYPE_POINTS; break; - case SGL_PRIMITIVETYPE_LINES: _sgl.pip_desc.primitive_type = SG_PRIMITIVETYPE_LINES; break; - case SGL_PRIMITIVETYPE_LINE_STRIP: _sgl.pip_desc.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP; break; - case SGL_PRIMITIVETYPE_TRIANGLES: _sgl.pip_desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES; break; - case SGL_PRIMITIVETYPE_TRIANGLE_STRIP: _sgl.pip_desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP; break; - case SGL_PRIMITIVETYPE_QUADS: _sgl.pip_desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES; break; - default: SOKOL_UNREACHABLE; break; - } - _sgl.pip[pip_index] = sg_make_pipeline(&_sgl.pip_desc); - } - return _sgl.pip[pip_index]; -} - static void _sgl_identity(_sgl_matrix_t* m) { for (int c = 0; c < 4; c++) { for (int r = 0; r < 4; r++) { @@ -1420,22 +1693,22 @@ static void _sgl_lookat(_sgl_matrix_t* dst, /* current top-of-stack projection matrix */ static inline _sgl_matrix_t* _sgl_matrix_projection(void) { - return &_sgl.matrix_stack[SGL_MATRIXMODE_PROJECTION][_sgl.top_of_stack[SGL_MATRIXMODE_PROJECTION]]; + return &_sgl.matrix_stack[SGL_MATRIXMODE_PROJECTION][_sgl.matrix_tos[SGL_MATRIXMODE_PROJECTION]]; } /* get top-of-stack modelview matrix */ static inline _sgl_matrix_t* _sgl_matrix_modelview(void) { - return &_sgl.matrix_stack[SGL_MATRIXMODE_MODELVIEW][_sgl.top_of_stack[SGL_MATRIXMODE_MODELVIEW]]; + return &_sgl.matrix_stack[SGL_MATRIXMODE_MODELVIEW][_sgl.matrix_tos[SGL_MATRIXMODE_MODELVIEW]]; } /* get top-of-stack texture matrix */ static inline _sgl_matrix_t* _sgl_matrix_texture(void) { - return &_sgl.matrix_stack[SGL_MATRIXMODE_TEXTURE][_sgl.top_of_stack[SGL_MATRIXMODE_TEXTURE]]; + return &_sgl.matrix_stack[SGL_MATRIXMODE_TEXTURE][_sgl.matrix_tos[SGL_MATRIXMODE_TEXTURE]]; } /* get pointer to current top-of-stack of current matrix mode */ static inline _sgl_matrix_t* _sgl_matrix(void) { - return &_sgl.matrix_stack[_sgl.cur_matrix_mode][_sgl.top_of_stack[_sgl.cur_matrix_mode]]; + return &_sgl.matrix_stack[_sgl.cur_matrix_mode][_sgl.matrix_tos[_sgl.cur_matrix_mode]]; } /*== PUBLIC FUNCTIONS ========================================================*/ @@ -1443,10 +1716,15 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { SOKOL_ASSERT(desc); memset(&_sgl, 0, sizeof(_sgl)); _sgl.init_cookie = _SGL_INIT_COOKIE; - - /* allocate buffers */ - _sgl.num_vertices = _sgl_def(desc->max_vertices, (1<<16)); - _sgl.num_uniforms = _sgl_def(desc->max_commands, (1<<14)); + _sgl.desc = *desc; + _sgl.desc.pipeline_pool_size = _sgl_def(_sgl.desc.pipeline_pool_size, _SGL_DEFAULT_PIPELINE_POOL_SIZE); + _sgl.desc.max_vertices = _sgl_def(_sgl.desc.max_vertices, _SGL_DEFAULT_MAX_VERTICES); + _sgl.desc.max_commands = _sgl_def(_sgl.desc.max_commands, _SGL_DEFAULT_MAX_COMMANDS); + _sgl.desc.face_winding = _sgl_def(_sgl.desc.face_winding, SG_FACEWINDING_CCW); + + /* allocate buffers and pools */ + _sgl.num_vertices = _sgl.desc.max_vertices; + _sgl.num_uniforms = _sgl.desc.max_commands; _sgl.num_commands = _sgl.num_uniforms; _sgl.vertices = (_sgl_vertex_t*) SOKOL_MALLOC(_sgl.num_vertices * sizeof(_sgl_vertex_t)); SOKOL_ASSERT(_sgl.vertices); @@ -1454,13 +1732,7 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { SOKOL_ASSERT(_sgl.uniforms); _sgl.commands = (_sgl_command_t*) SOKOL_MALLOC(_sgl.num_commands * sizeof(_sgl_command_t)); SOKOL_ASSERT(_sgl.commands); - - /* default state */ - _sgl.rgba = 0xFFFFFFFF; - for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) { - _sgl_identity(&_sgl.matrix_stack[i][0]); - } - _sgl.matrix_dirty = true; + _sgl_setup_pipeline_pool(&_sgl.desc); /* create sokol-gfx resource objects */ sg_push_debug_group("sokol-gl"); @@ -1515,41 +1787,21 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { #endif shd_desc.label = "sgl-shader"; _sgl.shd = sg_make_shader(&shd_desc); + + /* create default pipeline object */ + sg_pipeline_desc def_pip_desc; + memset(&def_pip_desc, 0, sizeof(def_pip_desc)); + def_pip_desc.depth_stencil.depth_write_enabled = true; + _sgl.def_pip = _sgl_make_pipeline(&def_pip_desc); sg_pop_debug_group(); - /* template desc for lazy pipeline creation */ - _sgl.pip_desc.layout.buffers[0].stride = sizeof(_sgl_vertex_t); - { - sg_vertex_attr_desc* pos = &_sgl.pip_desc.layout.attrs[0]; - pos->name = "position"; - pos->sem_name = "POSITION"; - pos->offset = offsetof(_sgl_vertex_t, pos); - pos->format = SG_VERTEXFORMAT_FLOAT3; - } - { - sg_vertex_attr_desc* uv = &_sgl.pip_desc.layout.attrs[1]; - uv->name = "texcoord0"; - uv->sem_name = "TEXCOORD"; - uv->offset = offsetof(_sgl_vertex_t, uv); - uv->format = SG_VERTEXFORMAT_FLOAT2; - } - { - sg_vertex_attr_desc* rgba = &_sgl.pip_desc.layout.attrs[2]; - rgba->name = "color0"; - rgba->sem_name = "COLOR"; - rgba->offset = offsetof(_sgl_vertex_t, rgba); - rgba->format = SG_VERTEXFORMAT_UBYTE4N; + /* default state */ + _sgl.rgba = 0xFFFFFFFF; + for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) { + _sgl_identity(&_sgl.matrix_stack[i][0]); } - _sgl.pip_desc.shader = _sgl.shd; - _sgl.pip_desc.index_type = SG_INDEXTYPE_NONE; - _sgl.pip_desc.depth_stencil.depth_write_enabled = true; - _sgl.pip_desc.blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; - _sgl.pip_desc.blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; - _sgl.pip_desc.blend.color_write_mask = SG_COLORMASK_RGB; - _sgl.pip_desc.blend.color_format = desc->color_format; - _sgl.pip_desc.blend.depth_format = desc->depth_format; - _sgl.pip_desc.rasterizer.sample_count = desc->sample_count; - _sgl.pip_desc.rasterizer.face_winding = _sgl_def(desc->face_winding, SG_FACEWINDING_CCW); + _sgl.pip_stack[0] = _sgl.def_pip; + _sgl.matrix_dirty = true; } SOKOL_API_IMPL void sgl_shutdown(void) { @@ -1560,10 +1812,8 @@ SOKOL_API_IMPL void sgl_shutdown(void) { sg_destroy_buffer(_sgl.vbuf); sg_destroy_image(_sgl.def_img); sg_destroy_shader(_sgl.shd); - /* NOTE: calling sg_destroy_*() with an invalid id is valid */ - for (int i = 0; i < _SGL_MAX_PIPELINES; i++) { - sg_destroy_pipeline(_sgl.pip[i]); - } + _sgl_destroy_pipeline(_sgl.def_pip); + _sgl_discard_pipeline_pool(); _sgl.init_cookie = 0; } @@ -1579,42 +1829,62 @@ SOKOL_API_IMPL float sgl_deg(float rad) { return (rad * 180.0f) / (float)M_PI; } -SOKOL_API_IMPL void sgl_defaults(void) { +SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); - SOKOL_ASSERT(!_sgl.in_begin); - _sgl.state_bits = 0; - _sgl.u = 0.0f; _sgl.v = 0.0f; - _sgl.rgba = 0xFFFFFFFF; - _sgl.cur_img = _sgl.def_img; - _sgl_identity(_sgl_matrix_texture()); - _sgl_identity(_sgl_matrix_modelview()); - _sgl_identity(_sgl_matrix_projection()); - _sgl.cur_matrix_mode = 0; - _sgl.matrix_dirty = true; + return _sgl_make_pipeline(desc); } -SOKOL_API_IMPL void sgl_state_depth_test(bool enabled) { +SOKOL_API_IMPL void sgl_destroy_pipeline(sgl_pipeline pip_id) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); - SOKOL_ASSERT(!_sgl.in_begin); - _sgl.state_bits = _sgl_set_state(SGL_STATE_DEPTHTEST, enabled, _sgl.state_bits); + _sgl_destroy_pipeline(pip_id); } -SOKOL_API_IMPL void sgl_state_blend(bool enabled) { +SOKOL_API_IMPL void sgl_load_pipeline(sgl_pipeline pip_id) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); - SOKOL_ASSERT(!_sgl.in_begin); - _sgl.state_bits = _sgl_set_state(SGL_STATE_BLEND, enabled, _sgl.state_bits); + SOKOL_ASSERT((_sgl.pip_tos >= 0) && (_sgl.pip_tos < _SGL_MAX_STACK_DEPTH)); + _sgl.pip_stack[_sgl.pip_tos] = pip_id; } -SOKOL_API_IMPL void sgl_state_cull_face(bool enabled) { +SOKOL_API_IMPL void sgl_default_pipeline(void) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); - SOKOL_ASSERT(!_sgl.in_begin); - _sgl.state_bits = _sgl_set_state(SGL_STATE_CULLFACE, enabled,_sgl.state_bits); + SOKOL_ASSERT((_sgl.pip_tos >= 0) && (_sgl.pip_tos < _SGL_MAX_STACK_DEPTH)); + _sgl.pip_stack[_sgl.pip_tos] = _sgl.def_pip; +} + +SOKOL_API_IMPL void sgl_push_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl.pip_tos < (_SGL_MAX_STACK_DEPTH - 1)) { + _sgl.pip_tos++; + _sgl.pip_stack[_sgl.pip_tos] = _sgl.pip_stack[_sgl.pip_tos-1]; + } + else { + _sgl.error = SGL_ERROR_STACK_OVERFLOW; + } +} + +SOKOL_API_IMPL void sgl_pop_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl.pip_tos > 0) { + _sgl.pip_tos--; + } + else { + _sgl.error = SGL_ERROR_STACK_UNDERFLOW; + } } -SOKOL_API_IMPL void sgl_state_texture(bool enabled) { +SOKOL_API_IMPL void sgl_defaults(void) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_ASSERT(!_sgl.in_begin); - _sgl.state_bits = _sgl_set_state(SGL_STATE_TEXTURE, enabled, _sgl.state_bits); + _sgl.u = 0.0f; _sgl.v = 0.0f; + _sgl.rgba = 0xFFFFFFFF; + _sgl.texturing_enabled = false; + _sgl.cur_img = _sgl.def_img; + sgl_default_pipeline(); + _sgl_identity(_sgl_matrix_texture()); + _sgl_identity(_sgl_matrix_modelview()); + _sgl_identity(_sgl_matrix_projection()); + _sgl.cur_matrix_mode = 0; + _sgl.matrix_dirty = true; } SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left) { @@ -1645,6 +1915,18 @@ SOKOL_API_IMPL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top } } +SOKOL_API_IMPL void sgl_enable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl.texturing_enabled = true; +} + +SOKOL_API_IMPL void sgl_disable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl.texturing_enabled = false; +} + SOKOL_API_IMPL void sgl_texture(sg_image img) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_ASSERT(!_sgl.in_begin); @@ -1712,7 +1994,7 @@ SOKOL_API_IMPL void sgl_end(void) { SOKOL_ASSERT(_sgl.cur_uniform > 0); cmd->cmd = SGL_COMMAND_DRAW; cmd->args.draw.img = _sgl.cur_img; - cmd->args.draw.state_bits = _sgl.state_bits; + cmd->args.draw.pip = _sgl_get_pipeline(_sgl.pip_stack[_sgl.pip_tos], _sgl.cur_prim_type); cmd->args.draw.base_vertex = _sgl.base_vertex; cmd->args.draw.num_vertices = _sgl.cur_vertex - _sgl.base_vertex; cmd->args.draw.uniform_index = _sgl.cur_uniform - 1; @@ -1933,9 +2215,9 @@ SOKOL_API_DECL void sgl_push_matrix(void) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_ASSERT((_sgl.cur_matrix_mode >= 0) && (_sgl.cur_matrix_mode < SGL_NUM_MATRIXMODES)); _sgl.matrix_dirty = true; - if (_sgl.top_of_stack[_sgl.cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) { + if (_sgl.matrix_tos[_sgl.cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) { const _sgl_matrix_t* src = _sgl_matrix(); - _sgl.top_of_stack[_sgl.cur_matrix_mode]++; + _sgl.matrix_tos[_sgl.cur_matrix_mode]++; _sgl_matrix_t* dst = _sgl_matrix(); *dst = *src; } @@ -1948,8 +2230,8 @@ SOKOL_API_DECL void sgl_pop_matrix(void) { SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_ASSERT((_sgl.cur_matrix_mode >= 0) && (_sgl.cur_matrix_mode < SGL_NUM_MATRIXMODES)); _sgl.matrix_dirty = true; - if (_sgl.top_of_stack[_sgl.cur_matrix_mode] > 0) { - _sgl.top_of_stack[_sgl.cur_matrix_mode]--; + if (_sgl.matrix_tos[_sgl.cur_matrix_mode] > 0) { + _sgl.matrix_tos[_sgl.cur_matrix_mode]--; } else { _sgl.error = SGL_ERROR_STACK_UNDERFLOW; @@ -1984,10 +2266,9 @@ SOKOL_API_IMPL void sgl_draw(void) { case SGL_COMMAND_DRAW: { const _sgl_draw_args_t* args = &cmd->args.draw; - sg_pipeline pip = _sgl_pipeline(args->state_bits); - if (pip.id != cur_pip_id) { - sg_apply_pipeline(pip); - cur_pip_id = pip.id; + if (args->pip.id != cur_pip_id) { + sg_apply_pipeline(args->pip); + cur_pip_id = args->pip.id; /* when pipeline changes, also need to re-apply uniforms and bindings */ cur_img_id = SG_INVALID_ID; cur_uniform_index = -1; |