aboutsummaryrefslogtreecommitdiff
path: root/util/sokol_gl.h
diff options
context:
space:
mode:
authorAndre Weissflog <floooh@gmail.com>2021-08-20 18:36:59 +0200
committerGitHub <noreply@github.com>2021-08-20 18:36:59 +0200
commit4cf67c1790aee5e1eeb3ddc892fc75380a75d221 (patch)
tree5ed71eb6bb5f5cf5ac0598636600e69b52bb91ef /util/sokol_gl.h
parentabadc11723c860ac17a325b7e272c30696710e38 (diff)
sokol_gl.h: context support (#549)
Diffstat (limited to 'util/sokol_gl.h')
-rw-r--r--util/sokol_gl.h1310
1 files changed, 969 insertions, 341 deletions
diff --git a/util/sokol_gl.h b/util/sokol_gl.h
index dabd70cd..050f6116 100644
--- a/util/sokol_gl.h
+++ b/util/sokol_gl.h
@@ -53,8 +53,7 @@
=================
sokol_gl.h implements a subset of the OpenGLES 1.x feature set useful for
when you just want to quickly render a bunch of colored triangles or
- lines without having to mess with buffers and
- shaders.
+ lines without having to mess with buffers and shaders.
The current feature set is mostly useful for debug visualizations
and simple UI-style 2D rendering:
@@ -104,41 +103,62 @@
(via sg_setup). This is because sgl_setup() needs to create
sokol-gfx resource objects.
- sgl_setup() needs to know the attributes of the sokol-gfx render pass
- where sokol-gl rendering will happen through the passed-in sgl_desc_t
- struct:
+ If you're intending to render to the default pass, and also don't
+ want to tweak memory usage, you can just keep sgl_desc_t zero-initialized:
- sg_pixel_format color_format - color pixel format of render pass
- sg_pixel_format depth_format - depth pixel format of render pass
- int sample_count - MSAA sample count of render pass
+ sgl_setup(&(sgl_desc_t*){ 0 });
- These values have the same defaults as sokol_gfx.h and sokol_app.h,
- to use the default values, leave them zero-initialized.
+ In this case, sokol-gl will create internal sg_pipeline objects that
+ are compatible with the sokol-app default framebuffer. If you want
+ to render into a framebuffer with different pixel-format and MSAA
+ attributes you need to provide the matching attributes in the
+ sgl_setup() call:
- You can adjust the maximum number of vertices and drawing commands
- per frame through the members:
+ sgl_setup(&(sgl_desc_t*){
+ .color_format = SG_PIXELFORMAT_...,
+ .depth_format = SG_PIXELFORMAT_...,
+ .sample_count = ...,
+ });
- int max_vertices - default is 65536
- int max_commands - default is 16384
+ To reduce memory usage, or if you need to create more then the default number of
+ contexts, pipelines, vertices or draw commands, set the following sgl_desc_t
+ members:
- You can adjust the size of the internal pipeline state object pool
- with:
-
- int pipeline_pool_size - default is 64
+ .context_pool_size (default: 4)
+ .pipeline_pool_size (default: 64)
+ .max_vertices (default: 64k)
+ .max_commands (default: 16k)
Finally you can change the face winding for front-facing triangles
and quads:
- sg_face_winding face_winding - default is SG_FACEWINDING_CCW
+ .face_winding - default is SG_FACEWINDING_CCW
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 additional context objects if you want to render into
+ multiple sokol-gfx render passes (or generally if you want to
+ use multiple independent sokol-gl "state buckets")
+
+ sgl_context ctx = sgl_make_context(const sgl_context_desc_t* desc)
+
+ For details on rendering with sokol-gl contexts, search below for
+ WORKING WITH CONTEXTS.
+
--- 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)
+ ...this creates a pipeline object that's compatible with the currently
+ active context, alternatively call:
+
+ sgl_pipeline_pip = sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc)
+
+ ...to create a pipeline object that's compatible with an explicitly
+ provided context.
+
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
@@ -152,6 +172,11 @@
call to sgl_make_pipeline() needs to create several sokol-gfx
pipeline objects (one for each primitive type).
+ 'depth.write_enabled' will be forced to 'false' if the context this
+ pipeline object is intended for has its depth pixel format set to
+ SG_PIXELFORMAT_NONE (which means the framebuffer this context is used
+ with doesn't have a depth-stencil surface).
+
--- if you need to destroy sgl_pipeline objects before sgl_shutdown():
sgl_destroy_pipeline(sgl_pipeline pip)
@@ -314,18 +339,29 @@
list, or it will extend the previous draw command if no relevant
state has changed since the last sgl_begin/end pair.
- --- inside a sokol-gfx rendering pass, call:
+ --- inside a sokol-gfx rendering pass, call the sgl_draw() function
+ to render the currently active context:
sgl_draw()
- This will render everything that has been recorded since the last
- call to sgl_draw() through sokol-gfx, and will 'rewind' the internal
+ ...or alternatively call:
+
+ sgl_context_draw(ctx)
+
+ ...to render an explicitly provided context.
+
+ This will render everything that has been recorded in the context since
+ the last call to sgl_draw() through sokol-gfx, and will 'rewind' the internal
vertex-, uniform- and command-buffers.
- --- sokol-gl tracks a single internal error code which can be
- queried with
+ --- each sokol-gl context tracks an internal error code, to query the
+ current error code for the currently active context call:
+
+ sgl_error_t sgl_error()
- sgl_error_t sgl_error(void)
+ ...alternatively with an explicit context argument:
+
+ sgl_error_t sgl_context_error(ctx);
...which can return the following error codes:
@@ -335,10 +371,82 @@
SGL_ERROR_COMMANDS_FULL - the internal command buffer is full (checked in sgl_end())
SGL_ERROR_STACK_OVERFLOW - matrix- or pipeline-stack overflow
SGL_ERROR_STACK_UNDERFLOW - matrix- or pipeline-stack underflow
+ SGL_ERROR_NO_CONTEXT - the active context no longer exists
...if sokol-gl is in an error-state, sgl_draw() will skip any rendering,
and reset the error code to SGL_NO_ERROR.
+
+ WORKING WITH CONTEXTS:
+ ======================
+ If you want to render to more than one sokol-gfx render pass you need to
+ work with additional sokol-gl context objects (one context object for
+ each offscreen rendering pass, in addition to the implicitly created
+ 'default context'.
+
+ All sokol-gl state is tracked per context, and there is always a "current
+ context" (with the notable exception that the currently set context is
+ destroyed, more on that later).
+
+ Using multiple contexts can also be useful if you only render in
+ a single pass, but want to maintain multiple independent "state buckets".
+
+ To create new context object, call:
+
+ sgl_context ctx = sgl_make_context(&(sgl_context_desc){
+ .max_vertices = ..., // default: 64k
+ .max_commands = ..., // default: 16k
+ .color_format = ...,
+ .depth_format = ...,
+ .sample_count = ...,
+ });
+
+ The color_format, depth_format and sample_count items must be compatible
+ with the render pass the sgl_draw() or sgL_context_draw() function
+ will be called in.
+
+ Creating a context does *not* make the context current. To do this, call:
+
+ sgl_set_context(ctx);
+
+ The currently active context will implicitely be used by most sokol-gl functions
+ which don't take an explicit context handle as argument.
+
+ To switch back to the default context, pass the global constant SGL_DEFAULT_CONTEXT:
+
+ sgl_set_context(SGL_DEFAULT_CONTEXT);
+
+ To get the currently active context, call:
+
+ sgl_context cur_ctx = sgl_get_context();
+
+ The following functions exist in two variants, one which use the currently
+ active context (set with sgl_set_context()), and another version which
+ takes an explicit context handle instead:
+
+ sgl_make_pipeline() vs sgl_context_make_pipeline()
+ sgl_error() vs sgl_context_error();
+ sgl_draw() vs sgl_context_draw();
+
+ Except for using the currently active context versus a provided context
+ handle, the two variants are exactlyidentical, e.g. the following
+ code sequences do the same thing:
+
+ sgl_set_context(ctx);
+ sgl_pipeline pip = sgl_make_pipeline(...);
+ sgl_error_t err = sgl_error();
+ sgl_draw();
+
+ vs
+
+ sgl_pipeline pip = sgl_context_make_pipeline(ctx, ...);
+ sgl_error_t err = sgl_context_error(ctx);
+ sgl_context_draw(ctx);
+
+ Destroying the currently active context is a 'soft error'. All following
+ calls which require a currently active context will silently fail,
+ and sgl_error() will return SGL_ERROR_NO_CONTEXT.
+
UNDER THE HOOD:
===============
sokol_gl.h works by recording vertex data and rendering commands into
@@ -359,11 +467,15 @@
What happens in:
sgl_setup():
- - 3 memory buffers are allocated, one for vertex data,
- one for uniform data, and one for commands
- - sokol-gfx resources are created: a (dynamic) vertex buffer,
- a shader object (using embedded shader source or byte code),
- and an 8x8 all-white default texture
+ Unique resources shared by all contexts are created:
+ - a shader object (using embedded shader source or byte code)
+ - an 8x8 white default texture
+ The default context is created, which involves:
+ - 3 memory buffers are created, one for vertex data,
+ one for uniform data, and one for commands
+ - a dynamic vertex buffer is created
+ - the default sgl_pipeline object is created, which involves
+ creating 5 sg_pipeline objects
One vertex is 24 bytes:
- float3 position
@@ -477,6 +589,9 @@ extern "C" {
/* sokol_gl pipeline handle (created with sgl_make_pipeline()) */
typedef struct sgl_pipeline { uint32_t id; } sgl_pipeline;
+/* a context handle (created with sgl_make_context()) */
+typedef struct sgl_context { uint32_t id; } sgl_context;
+
/*
sgl_error_t
@@ -490,31 +605,59 @@ typedef enum sgl_error_t {
SGL_ERROR_COMMANDS_FULL,
SGL_ERROR_STACK_OVERFLOW,
SGL_ERROR_STACK_UNDERFLOW,
+ SGL_ERROR_NO_CONTEXT,
} sgl_error_t;
+/*
+ sgl_context_desc_t
+
+ Describes the initialization parameters of a rendering context.
+ Creating additional contexts is useful if you want to render
+ in separate sokol-gfx passes.
+*/
+typedef struct sgl_context_desc_t {
+ int max_vertices; // default: 64k
+ int max_commands; // default: 16k
+ sg_pixel_format color_format;
+ sg_pixel_format depth_format;
+ int sample_count;
+} sgl_context_desc_t;
+
typedef struct sgl_desc_t {
- 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 */
+ int max_vertices; // default: 64k
+ int max_commands; // default: 16k
+ int context_pool_size; // max number of contexts (including default context), default: 4
+ int pipeline_pool_size; // size of internal pipeline pool, default: 64
sg_pixel_format color_format;
sg_pixel_format depth_format;
int sample_count;
- sg_face_winding face_winding; /* default front face winding is CCW */
+ sg_face_winding face_winding; // default: SG_FACEWINDING_CCW
} sgl_desc_t;
+/* the default context handle */
+static const sgl_context SGL_DEFAULT_CONTEXT = { 0x00010001 };
+
/* setup/shutdown/misc */
SOKOL_GL_API_DECL void sgl_setup(const sgl_desc_t* desc);
SOKOL_GL_API_DECL void sgl_shutdown(void);
-SOKOL_GL_API_DECL sgl_error_t sgl_error(void);
-SOKOL_GL_API_DECL void sgl_defaults(void);
SOKOL_GL_API_DECL float sgl_rad(float deg);
SOKOL_GL_API_DECL float sgl_deg(float rad);
+SOKOL_GL_API_DECL sgl_error_t sgl_error(void);
+SOKOL_GL_API_DECL sgl_error_t sgl_context_error(sgl_context ctx);
+
+/* context functions */
+SOKOL_GL_API_DECL sgl_context sgl_make_context(const sgl_context_desc_t* desc);
+SOKOL_GL_API_DECL void sgl_destroy_context(sgl_context ctx);
+SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx);
+SOKOL_GL_API_DECL sgl_context sgl_get_context(void);
/* create and destroy pipeline objects */
SOKOL_GL_API_DECL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc);
+SOKOL_GL_API_DECL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc);
SOKOL_GL_API_DECL void sgl_destroy_pipeline(sgl_pipeline pip);
/* render state functions */
+SOKOL_GL_API_DECL void sgl_defaults(void);
SOKOL_GL_API_DECL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left);
SOKOL_GL_API_DECL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left);
SOKOL_GL_API_DECL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left);
@@ -589,15 +732,18 @@ SOKOL_GL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float
SOKOL_GL_API_DECL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba);
SOKOL_GL_API_DECL void sgl_end(void);
-/* render everything */
-SOKOL_GL_API_DECL void sgl_draw(void);
+/* render recorded commands */
+SOKOL_GL_API_DECL void sgl_draw();
+SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx);
#ifdef __cplusplus
} /* extern "C" */
/* reference-based equivalents for C++ */
inline void sgl_setup(const sgl_desc_t& desc) { return sgl_setup(&desc); }
+inline sgl_context sgl_make_context(const sgl_context_desc_t& desc) { return sgl_make_context(&desc); }
inline sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc& desc) { return sgl_make_pipeline(&desc); }
+inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc& desc) { return sgl_context_make_pipeline(ctx, &desc); }
#endif
#endif /* SOKOL_GL_INCLUDED */
@@ -2027,6 +2173,7 @@ typedef struct {
#define _SGL_INVALID_SLOT_INDEX (0)
#define _SGL_MAX_STACK_DEPTH (64)
+#define _SGL_DEFAULT_CONTEXT_POOL_SIZE (4)
#define _SGL_DEFAULT_PIPELINE_POOL_SIZE (64)
#define _SGL_DEFAULT_MAX_VERTICES (1<<16)
#define _SGL_DEFAULT_MAX_COMMANDS (1<<14)
@@ -2035,8 +2182,8 @@ typedef struct {
#define _SGL_SLOT_MASK (_SGL_MAX_POOL_SIZE-1)
typedef struct {
- uint32_t init_cookie;
- sgl_desc_t desc;
+ _sgl_slot_t slot;
+ sgl_context_desc_t desc;
int num_vertices;
int num_uniforms;
@@ -2062,11 +2209,8 @@ typedef struct {
/* sokol-gfx resources */
sg_buffer vbuf;
- sg_image def_img; /* a default white texture */
- sg_shader shd;
- sg_bindings bind;
sgl_pipeline def_pip;
- _sgl_pipeline_pool_t pip_pool;
+ sg_bindings bind;
/* pipeline stack */
int pip_tos;
@@ -2076,6 +2220,23 @@ typedef struct {
_sgl_matrix_mode_t cur_matrix_mode;
int matrix_tos[SGL_NUM_MATRIXMODES];
_sgl_matrix_t matrix_stack[SGL_NUM_MATRIXMODES][_SGL_MAX_STACK_DEPTH];
+} _sgl_context_t;
+
+typedef struct {
+ _sgl_pool_t pool;
+ _sgl_context_t* contexts;
+} _sgl_context_pool_t;
+
+typedef struct {
+ uint32_t init_cookie;
+ sgl_desc_t desc;
+ sg_image def_img; // a default white texture
+ sg_shader shd; // same shader for all contexts
+ sgl_context def_ctx_id;
+ sgl_context cur_ctx_id;
+ _sgl_context_t* cur_ctx; // may be 0!
+ _sgl_pipeline_pool_t pip_pool;
+ _sgl_context_pool_t context_pool;
} _sgl_t;
static _sgl_t _sgl;
@@ -2141,16 +2302,39 @@ static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) {
SOKOL_ASSERT(pool->queue_top <= (pool->size-1));
}
+static void _sgl_reset_context(_sgl_context_t* ctx) {
+ SOKOL_ASSERT(ctx);
+ SOKOL_ASSERT(0 == ctx->vertices);
+ SOKOL_ASSERT(0 == ctx->uniforms);
+ SOKOL_ASSERT(0 == ctx->commands);
+ memset(ctx, 0, sizeof(_sgl_context_t));
+}
+
+static void _sgl_setup_context_pool(int pool_size) {
+ /* note: the pools here will have an additional item, since slot 0 is reserved */
+ SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE));
+ _sgl_init_pool(&_sgl.context_pool.pool, pool_size);
+ size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size;
+ _sgl.context_pool.contexts = (_sgl_context_t*) SOKOL_MALLOC(pool_byte_size);
+ SOKOL_ASSERT(_sgl.context_pool.contexts);
+ memset(_sgl.context_pool.contexts, 0, pool_byte_size);
+}
+
+static void _sgl_discard_context_pool(void) {
+ SOKOL_ASSERT(0 != _sgl.context_pool.contexts);
+ SOKOL_FREE(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0;
+ _sgl_discard_pool(&_sgl.context_pool.pool);
+}
+
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);
+static void _sgl_setup_pipeline_pool(int pool_size) {
/* 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);
+ SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE));
+ _sgl_init_pool(&_sgl.pip_pool.pool, pool_size);
size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size;
_sgl.pip_pool.pips = (_sgl_pipeline_t*) SOKOL_MALLOC(pool_byte_size);
SOKOL_ASSERT(_sgl.pip_pool.pips);
@@ -2158,6 +2342,7 @@ static void _sgl_setup_pipeline_pool(const sgl_desc_t* desc) {
}
static void _sgl_discard_pipeline_pool(void) {
+ SOKOL_ASSERT(0 != _sgl.pip_pool.pips);
SOKOL_FREE(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0;
_sgl_discard_pool(&_sgl.pip_pool.pool);
}
@@ -2211,8 +2396,7 @@ static _sgl_pipeline_t* _sgl_lookup_pipeline(uint32_t pip_id) {
/* make pipeline id from uint32_t id */
static sgl_pipeline _sgl_make_pip_id(uint32_t pip_id) {
- sgl_pipeline pip;
- pip.id = pip_id;
+ sgl_pipeline pip = { pip_id };
return pip;
}
@@ -2229,8 +2413,8 @@ static sgl_pipeline _sgl_alloc_pipeline(void) {
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);
+static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_desc, const sgl_context_desc_t* ctx_desc) {
+ SOKOL_ASSERT((pip_id.id != SG_INVALID_ID) && in_desc && ctx_desc);
/* create a new desc with 'patched' shader and pixel format state */
sg_pipeline_desc desc = *in_desc;
@@ -2254,12 +2438,15 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d
desc.shader = _sgl.shd;
}
desc.index_type = SG_INDEXTYPE_NONE;
- desc.sample_count = _sgl.desc.sample_count;
+ desc.sample_count = ctx_desc->sample_count;
if (desc.face_winding == _SG_FACEWINDING_DEFAULT) {
desc.face_winding = _sgl.desc.face_winding;
}
- desc.depth.pixel_format = _sgl.desc.depth_format;
- desc.colors[0].pixel_format = _sgl.desc.color_format;
+ desc.depth.pixel_format = ctx_desc->depth_format;
+ if (ctx_desc->depth_format == SG_PIXELFORMAT_NONE) {
+ desc.depth.write_enabled = false;
+ }
+ desc.colors[0].pixel_format = ctx_desc->color_format;
if (desc.colors[0].write_mask == _SG_COLORMASK_DEFAULT) {
desc.colors[0].write_mask = SG_COLORMASK_RGB;
}
@@ -2300,11 +2487,11 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d
}
}
-static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc) {
- SOKOL_ASSERT(desc);
+static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc, const sgl_context_desc_t* ctx_desc) {
+ SOKOL_ASSERT(desc && ctx_desc);
sgl_pipeline pip_id = _sgl_alloc_pipeline();
if (pip_id.id != SG_INVALID_ID) {
- _sgl_init_pipeline(pip_id, desc);
+ _sgl_init_pipeline(pip_id, desc, ctx_desc);
}
else {
SOKOL_LOG("sokol_gl.h: pipeline pool exhausted!");
@@ -2315,11 +2502,13 @@ static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc) {
static void _sgl_destroy_pipeline(sgl_pipeline pip_id) {
_sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id);
if (pip) {
+ sg_push_debug_group("sokol-gl");
for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) {
if (i != SGL_PRIMITIVETYPE_QUADS) {
sg_destroy_pipeline(pip->pip[i]);
}
}
+ sg_pop_debug_group();
_sgl_reset_pipeline(pip);
_sgl_pool_free_index(&_sgl.pip_pool.pool, _sgl_slot_index(pip_id.id));
}
@@ -2336,57 +2525,185 @@ static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t
}
}
-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.cur_prim_type = mode;
+// get context pointer without id-check
+static _sgl_context_t* _sgl_context_at(uint32_t ctx_id) {
+ SOKOL_ASSERT(SG_INVALID_ID != ctx_id);
+ int slot_index = _sgl_slot_index(ctx_id);
+ SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.context_pool.pool.size));
+ return &_sgl.context_pool.contexts[slot_index];
+}
+
+// get context pointer with id-check, returns 0 if no match
+static _sgl_context_t* _sgl_lookup_context(uint32_t ctx_id) {
+ if (SG_INVALID_ID != ctx_id) {
+ _sgl_context_t* ctx = _sgl_context_at(ctx_id);
+ if (ctx->slot.id == ctx_id) {
+ return ctx;
+ }
+ }
+ return 0;
+}
+
+// make context id from uint32_t id
+static sgl_context _sgl_make_ctx_id(uint32_t ctx_id) {
+ sgl_context ctx = { ctx_id };
+ return ctx;
+}
+
+static sgl_context _sgl_alloc_context(void) {
+ sgl_context res;
+ int slot_index = _sgl_pool_alloc_index(&_sgl.context_pool.pool);
+ if (_SGL_INVALID_SLOT_INDEX != slot_index) {
+ res = _sgl_make_ctx_id(_sgl_slot_alloc(&_sgl.context_pool.pool, &_sgl.context_pool.contexts[slot_index].slot, slot_index));
+ }
+ else {
+ // pool is exhausted
+ res = _sgl_make_ctx_id(SG_INVALID_ID);
+ }
+ return res;
+}
+
+// return sgl_context_desc_t with patched defaults
+static sgl_context_desc_t _sgl_context_desc_defaults(const sgl_context_desc_t* desc) {
+ sgl_context_desc_t res = *desc;
+ res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES);
+ res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS);
+ return res;
+}
+
+static void _sgl_identity(_sgl_matrix_t*);
+static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_desc) {
+ SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc);
+ _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
+ SOKOL_ASSERT(ctx);
+ ctx->desc = _sgl_context_desc_defaults(in_desc);
+ ctx->cur_img = _sgl.def_img;
+
+ // allocate buffers and pools
+ ctx->num_vertices = ctx->desc.max_vertices;
+ ctx->num_commands = ctx->num_uniforms = ctx->desc.max_commands;
+ ctx->vertices = (_sgl_vertex_t*) SOKOL_MALLOC((size_t)ctx->num_vertices * sizeof(_sgl_vertex_t));
+ SOKOL_ASSERT(ctx->vertices);
+ ctx->uniforms = (_sgl_uniform_t*) SOKOL_MALLOC((size_t)ctx->num_uniforms * sizeof(_sgl_uniform_t));
+ SOKOL_ASSERT(ctx->uniforms);
+ ctx->commands = (_sgl_command_t*) SOKOL_MALLOC((size_t)ctx->num_commands * sizeof(_sgl_command_t));
+ SOKOL_ASSERT(ctx->commands);
+
+ // create sokol-gfx resource objects
+ sg_push_debug_group("sokol-gl");
+
+ sg_buffer_desc vbuf_desc;
+ memset(&vbuf_desc, 0, sizeof(vbuf_desc));
+ vbuf_desc.size = (size_t)ctx->num_vertices * sizeof(_sgl_vertex_t);
+ vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
+ vbuf_desc.usage = SG_USAGE_STREAM;
+ vbuf_desc.label = "sgl-vertex-buffer";
+ ctx->vbuf = sg_make_buffer(&vbuf_desc);
+ SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id);
+
+ sg_pipeline_desc def_pip_desc;
+ memset(&def_pip_desc, 0, sizeof(def_pip_desc));
+ def_pip_desc.depth.write_enabled = true;
+ ctx->def_pip = _sgl_make_pipeline(&def_pip_desc, &ctx->desc);
+ sg_pop_debug_group();
+
+ // default state
+ ctx->rgba = 0xFFFFFFFF;
+ for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) {
+ _sgl_identity(&ctx->matrix_stack[i][0]);
+ }
+ ctx->pip_stack[0] = ctx->def_pip;
+ ctx->matrix_dirty = true;
+}
+
+static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) {
+ SOKOL_ASSERT(desc);
+ sgl_context ctx_id = _sgl_alloc_context();
+ if (ctx_id.id != SG_INVALID_ID) {
+ _sgl_init_context(ctx_id, desc);
+ }
+ else {
+ SOKOL_LOG("sokol_gl.h: context pool exhausted!");
+ }
+ return ctx_id;
+}
+
+static void _sgl_destroy_context(sgl_context ctx_id) {
+ _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
+ if (ctx) {
+ SOKOL_ASSERT(ctx->vertices);
+ SOKOL_ASSERT(ctx->uniforms);
+ SOKOL_ASSERT(ctx->commands);
+
+ SOKOL_FREE(ctx->vertices);
+ SOKOL_FREE(ctx->uniforms);
+ SOKOL_FREE(ctx->commands);
+
+ ctx->vertices = 0;
+ ctx->uniforms = 0;
+ ctx->commands = 0;
+
+ sg_push_debug_group("sokol-gl");
+ sg_destroy_buffer(ctx->vbuf);
+ _sgl_destroy_pipeline(ctx->def_pip);
+ sg_pop_debug_group();
+
+ _sgl_reset_context(ctx);
+ _sgl_pool_free_index(&_sgl.context_pool.pool, _sgl_slot_index(ctx_id.id));
+ }
+}
+
+static inline void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) {
+ ctx->in_begin = true;
+ ctx->base_vertex = ctx->cur_vertex;
+ ctx->vtx_count = 0;
+ ctx->cur_prim_type = mode;
}
-static void _sgl_rewind(void) {
- _sgl.base_vertex = 0;
- _sgl.cur_vertex = 0;
- _sgl.cur_uniform = 0;
- _sgl.cur_command = 0;
- _sgl.error = SGL_NO_ERROR;
- _sgl.matrix_dirty = true;
+static void _sgl_rewind(_sgl_context_t* ctx) {
+ ctx->base_vertex = 0;
+ ctx->cur_vertex = 0;
+ ctx->cur_uniform = 0;
+ ctx->cur_command = 0;
+ ctx->error = SGL_NO_ERROR;
+ ctx->matrix_dirty = true;
}
-static inline _sgl_vertex_t* _sgl_next_vertex(void) {
- if (_sgl.cur_vertex < _sgl.num_vertices) {
- return &_sgl.vertices[_sgl.cur_vertex++];
+static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
+ if (ctx->cur_vertex < ctx->num_vertices) {
+ return &ctx->vertices[ctx->cur_vertex++];
}
else {
- _sgl.error = SGL_ERROR_VERTICES_FULL;
+ ctx->error = SGL_ERROR_VERTICES_FULL;
return 0;
}
}
-static inline _sgl_uniform_t* _sgl_next_uniform(void) {
- if (_sgl.cur_uniform < _sgl.num_uniforms) {
- return &_sgl.uniforms[_sgl.cur_uniform++];
+static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
+ if (ctx->cur_uniform < ctx->num_uniforms) {
+ return &ctx->uniforms[ctx->cur_uniform++];
}
else {
- _sgl.error = SGL_ERROR_UNIFORMS_FULL;
+ ctx->error = SGL_ERROR_UNIFORMS_FULL;
return 0;
}
}
-static inline _sgl_command_t* _sgl_prev_command(void) {
- if (_sgl.cur_command > 0) {
- return &_sgl.commands[_sgl.cur_command - 1];
+static inline _sgl_command_t* _sgl_prev_command(_sgl_context_t* ctx) {
+ if (ctx->cur_command > 0) {
+ return &ctx->commands[ctx->cur_command - 1];
}
else {
return 0;
}
}
-static inline _sgl_command_t* _sgl_next_command(void) {
- if (_sgl.cur_command < _sgl.num_commands) {
- return &_sgl.commands[_sgl.cur_command++];
+static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
+ if (ctx->cur_command < ctx->num_commands) {
+ return &ctx->commands[ctx->cur_command++];
}
else {
- _sgl.error = SGL_ERROR_COMMANDS_FULL;
+ ctx->error = SGL_ERROR_COMMANDS_FULL;
return 0;
}
}
@@ -2409,26 +2726,26 @@ static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8);
}
-static inline void _sgl_vtx(float x, float y, float z, float u, float v, uint32_t rgba) {
- SOKOL_ASSERT(_sgl.in_begin);
+static inline void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) {
+ SOKOL_ASSERT(ctx->in_begin);
_sgl_vertex_t* vtx;
/* handle non-native primitive types */
- if ((_sgl.cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((_sgl.vtx_count & 3) == 3)) {
+ if ((ctx->cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((ctx->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
*/
- vtx = _sgl_next_vertex();
+ vtx = _sgl_next_vertex(ctx);
if (vtx) { *vtx = *(vtx - 3); }
- vtx = _sgl_next_vertex();
+ vtx = _sgl_next_vertex(ctx);
if (vtx) { *vtx = *(vtx - 2); }
}
- vtx = _sgl_next_vertex();
+ vtx = _sgl_next_vertex(ctx);
if (vtx) {
vtx->pos[0] = x; vtx->pos[1] = y; vtx->pos[2] = z;
vtx->uv[0] = u; vtx->uv[1] = v;
vtx->rgba = rgba;
}
- _sgl.vtx_count++;
+ ctx->vtx_count++;
}
static void _sgl_identity(_sgl_matrix_t* m) {
@@ -2622,59 +2939,40 @@ 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.matrix_tos[SGL_MATRIXMODE_PROJECTION]];
+static inline _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) {
+ return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->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.matrix_tos[SGL_MATRIXMODE_MODELVIEW]];
+static inline _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) {
+ return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->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.matrix_tos[SGL_MATRIXMODE_TEXTURE]];
+static inline _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) {
+ return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->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.matrix_tos[_sgl.cur_matrix_mode]];
+static inline _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) {
+ return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]];
+}
+
+// return sg_context_desc_t with patched defaults
+static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) {
+ sgl_desc_t res = *desc;
+ res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES);
+ res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS);
+ res.context_pool_size = _sgl_def(desc->context_pool_size, _SGL_DEFAULT_CONTEXT_POOL_SIZE);
+ res.pipeline_pool_size = _sgl_def(desc->pipeline_pool_size, _SGL_DEFAULT_PIPELINE_POOL_SIZE);
+ res.face_winding = _sgl_def(desc->face_winding, SG_FACEWINDING_CCW);
+ return res;
}
-/*== PUBLIC FUNCTIONS ========================================================*/
-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;
- _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_commands = _sgl.num_uniforms = _sgl.desc.max_commands;
- _sgl.vertices = (_sgl_vertex_t*) SOKOL_MALLOC((size_t)_sgl.num_vertices * sizeof(_sgl_vertex_t));
- SOKOL_ASSERT(_sgl.vertices);
- _sgl.uniforms = (_sgl_uniform_t*) SOKOL_MALLOC((size_t)_sgl.num_uniforms * sizeof(_sgl_uniform_t));
- SOKOL_ASSERT(_sgl.uniforms);
- _sgl.commands = (_sgl_command_t*) SOKOL_MALLOC((size_t)_sgl.num_commands * sizeof(_sgl_command_t));
- SOKOL_ASSERT(_sgl.commands);
- _sgl_setup_pipeline_pool(&_sgl.desc);
-
- /* create sokol-gfx resource objects */
+// create resources which are shared between all contexts
+static void _sgl_setup_common(void) {
sg_push_debug_group("sokol-gl");
- sg_buffer_desc vbuf_desc;
- memset(&vbuf_desc, 0, sizeof(vbuf_desc));
- vbuf_desc.size = (size_t)_sgl.num_vertices * sizeof(_sgl_vertex_t);
- vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
- vbuf_desc.usage = SG_USAGE_STREAM;
- vbuf_desc.label = "sgl-vertex-buffer";
- _sgl.vbuf = sg_make_buffer(&vbuf_desc);
- SOKOL_ASSERT(SG_INVALID_ID != _sgl.vbuf.id);
-
uint32_t pixels[64];
for (int i = 0; i < 64; i++) {
pixels[i] = 0xFFFFFFFF;
@@ -2692,8 +2990,8 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) {
img_desc.label = "sgl-default-texture";
_sgl.def_img = sg_make_image(&img_desc);
SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_img.id);
- _sgl.cur_img = _sgl.def_img;
+ // one shader for all contexts
sg_shader_desc shd_desc;
memset(&shd_desc, 0, sizeof(shd_desc));
shd_desc.attrs[0].name = "position";
@@ -2749,43 +3047,140 @@ SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) {
#endif
_sgl.shd = sg_make_shader(&shd_desc);
SOKOL_ASSERT(SG_INVALID_ID != _sgl.shd.id);
+ sg_pop_debug_group();
+}
- /* create default pipeline object */
- sg_pipeline_desc def_pip_desc;
- memset(&def_pip_desc, 0, sizeof(def_pip_desc));
- def_pip_desc.depth.write_enabled = true;
- _sgl.def_pip = _sgl_make_pipeline(&def_pip_desc);
+// discard resources which are shared between all contexts
+static void _sgl_discard_common(void) {
+ sg_push_debug_group("sokol-gl");
+ sg_destroy_image(_sgl.def_img);
+ sg_destroy_shader(_sgl.shd);
sg_pop_debug_group();
+}
- /* default state */
- _sgl.rgba = 0xFFFFFFFF;
- for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) {
- _sgl_identity(&_sgl.matrix_stack[i][0]);
+static bool _sgl_is_default_context(sgl_context ctx_id) {
+ return ctx_id.id == SGL_DEFAULT_CONTEXT.id;
+}
+
+static void _sgl_draw(_sgl_context_t* ctx) {
+ SOKOL_ASSERT(ctx);
+ if ((ctx->error == SGL_NO_ERROR) && (ctx->cur_vertex > 0) && (ctx->cur_command > 0)) {
+ uint32_t cur_pip_id = SG_INVALID_ID;
+ uint32_t cur_img_id = SG_INVALID_ID;
+ int cur_uniform_index = -1;
+ sg_push_debug_group("sokol-gl");
+ const sg_range range = { ctx->vertices, (size_t)ctx->cur_vertex * sizeof(_sgl_vertex_t) };
+ sg_update_buffer(ctx->vbuf, &range);
+ ctx->bind.vertex_buffers[0] = ctx->vbuf;
+ for (int i = 0; i < ctx->cur_command; i++) {
+ const _sgl_command_t* cmd = &ctx->commands[i];
+ switch (cmd->cmd) {
+ case SGL_COMMAND_VIEWPORT:
+ {
+ const _sgl_viewport_args_t* args = &cmd->args.viewport;
+ sg_apply_viewport(args->x, args->y, args->w, args->h, args->origin_top_left);
+ }
+ break;
+ case SGL_COMMAND_SCISSOR_RECT:
+ {
+ const _sgl_scissor_rect_args_t* args = &cmd->args.scissor_rect;
+ sg_apply_scissor_rect(args->x, args->y, args->w, args->h, args->origin_top_left);
+ }
+ break;
+ case SGL_COMMAND_DRAW:
+ {
+ const _sgl_draw_args_t* args = &cmd->args.draw;
+ 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;
+ }
+ if (cur_img_id != args->img.id) {
+ ctx->bind.fs_images[0] = args->img;
+ sg_apply_bindings(&ctx->bind);
+ cur_img_id = args->img.id;
+ }
+ if (cur_uniform_index != args->uniform_index) {
+ const sg_range ub_range = { &ctx->uniforms[args->uniform_index], sizeof(_sgl_uniform_t) };
+ sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range);
+ cur_uniform_index = args->uniform_index;
+ }
+ /* FIXME: what if number of vertices doesn't match the primitive type? */
+ if (args->num_vertices > 0) {
+ sg_draw(args->base_vertex, args->num_vertices, 1);
+ }
+ }
+ break;
+ }
+ }
+ sg_pop_debug_group();
}
- _sgl.pip_stack[0] = _sgl.def_pip;
- _sgl.matrix_dirty = true;
+ _sgl_rewind(ctx);
+}
+
+static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) {
+ sgl_context_desc_t ctx_desc;
+ memset(&ctx_desc, 0, sizeof(ctx_desc));
+ ctx_desc.max_vertices = desc->max_vertices;
+ ctx_desc.max_commands = desc->max_commands;
+ ctx_desc.color_format = desc->color_format;
+ ctx_desc.depth_format = desc->depth_format;
+ ctx_desc.sample_count = desc->sample_count;
+ return ctx_desc;
+}
+
+/*== PUBLIC FUNCTIONS ========================================================*/
+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;
+ _sgl.desc = _sgl_desc_defaults(desc);
+ _sgl_setup_pipeline_pool(_sgl.desc.pipeline_pool_size);
+ _sgl_setup_context_pool(_sgl.desc.context_pool_size);
+ _sgl_setup_common();
+ const sgl_context_desc_t ctx_desc = _sgl_as_context_desc(&_sgl.desc);
+ _sgl.def_ctx_id = sgl_make_context(&ctx_desc);
+ SOKOL_ASSERT(SGL_DEFAULT_CONTEXT.id == _sgl.def_ctx_id.id);
+ sgl_set_context(_sgl.def_ctx_id);
}
SOKOL_API_IMPL void sgl_shutdown(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_FREE(_sgl.vertices); _sgl.vertices = 0;
- SOKOL_FREE(_sgl.uniforms); _sgl.uniforms = 0;
- SOKOL_FREE(_sgl.commands); _sgl.commands = 0;
- sg_push_debug_group("sokol-gl");
- sg_destroy_buffer(_sgl.vbuf);
- sg_destroy_image(_sgl.def_img);
- sg_destroy_shader(_sgl.shd);
+ // contexts own a pipeline, so destroy contexts before pipelines
+ for (int i = 0; i < _sgl.context_pool.pool.size; i++) {
+ _sgl_context_t* ctx = &_sgl.context_pool.contexts[i];
+ _sgl_destroy_context(_sgl_make_ctx_id(ctx->slot.id));
+ }
for (int i = 0; i < _sgl.pip_pool.pool.size; i++) {
_sgl_pipeline_t* pip = &_sgl.pip_pool.pips[i];
_sgl_destroy_pipeline(_sgl_make_pip_id(pip->slot.id));
}
- sg_pop_debug_group();
+ _sgl_discard_context_pool();
_sgl_discard_pipeline_pool();
+ _sgl_discard_common();
_sgl.init_cookie = 0;
}
SOKOL_API_IMPL sgl_error_t sgl_error(void) {
- return _sgl.error;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ return ctx->error;
+ }
+ else {
+ return SGL_ERROR_NO_CONTEXT;
+ }
+}
+
+SOKOL_API_IMPL sgl_error_t sgl_context_error(sgl_context ctx_id) {
+ const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
+ if (ctx) {
+ return ctx->error;
+ }
+ else {
+ return SGL_ERROR_NO_CONTEXT;
+ }
}
SOKOL_API_IMPL float sgl_rad(float deg) {
@@ -2796,9 +3191,60 @@ SOKOL_API_IMPL float sgl_deg(float rad) {
return (rad * 180.0f) / (float)M_PI;
}
+SOKOL_API_IMPL sgl_context sgl_make_context(const sgl_context_desc_t* desc) {
+ SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
+ return _sgl_make_context(desc);
+}
+
+SOKOL_API_IMPL void sgl_destroy_context(sgl_context ctx_id) {
+ SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
+ if (_sgl_is_default_context(ctx_id)) {
+ SOKOL_LOG("sokol_gl.h: cannot destroy default context");
+ return;
+ }
+ _sgl_destroy_context(ctx_id);
+ // re-validate the current context pointer (this will return a nullptr
+ // if we just destroyed the current context)
+ _sgl.cur_ctx = _sgl_lookup_context(_sgl.cur_ctx_id.id);
+}
+
+SOKOL_API_IMPL void sgl_set_context(sgl_context ctx_id) {
+ SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
+ if (_sgl_is_default_context(ctx_id)) {
+ _sgl.cur_ctx_id = _sgl.def_ctx_id;
+ }
+ else {
+ _sgl.cur_ctx_id = ctx_id;
+ }
+ // this will return null if the handle isn't valid
+ _sgl.cur_ctx = _sgl_lookup_context(_sgl.cur_ctx_id.id);
+}
+
+SOKOL_API_IMPL sgl_context sgl_get_context(void) {
+ SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
+ return _sgl.cur_ctx_id;
+}
+
SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- return _sgl_make_pipeline(desc);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ return _sgl_make_pipeline(desc, &ctx->desc);
+ }
+ else {
+ return _sgl_make_pip_id(SG_INVALID_ID);
+ }
+}
+
+SOKOL_API_IMPL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx_id, const sg_pipeline_desc* desc) {
+ SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
+ const _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
+ if (ctx) {
+ return _sgl_make_pipeline(desc, &ctx->desc);
+ }
+ else {
+ return _sgl_make_pip_id(SG_INVALID_ID);
+ }
}
SOKOL_API_IMPL void sgl_destroy_pipeline(sgl_pipeline pip_id) {
@@ -2808,56 +3254,80 @@ SOKOL_API_IMPL void sgl_destroy_pipeline(sgl_pipeline pip_id) {
SOKOL_API_IMPL void sgl_load_pipeline(sgl_pipeline pip_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT((_sgl.pip_tos >= 0) && (_sgl.pip_tos < _SGL_MAX_STACK_DEPTH));
- _sgl.pip_stack[_sgl.pip_tos] = pip_id;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT((ctx->pip_tos >= 0) && (ctx->pip_tos < _SGL_MAX_STACK_DEPTH));
+ ctx->pip_stack[ctx->pip_tos] = pip_id;
}
SOKOL_API_IMPL void sgl_default_pipeline(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT((_sgl.pip_tos >= 0) && (_sgl.pip_tos < _SGL_MAX_STACK_DEPTH));
- _sgl.pip_stack[_sgl.pip_tos] = _sgl.def_pip;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT((ctx->pip_tos >= 0) && (ctx->pip_tos < _SGL_MAX_STACK_DEPTH));
+ ctx->pip_stack[ctx->pip_tos] = ctx->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];
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ if (ctx->pip_tos < (_SGL_MAX_STACK_DEPTH - 1)) {
+ ctx->pip_tos++;
+ ctx->pip_stack[ctx->pip_tos] = ctx->pip_stack[ctx->pip_tos-1];
}
else {
- _sgl.error = SGL_ERROR_STACK_OVERFLOW;
+ ctx->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--;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ if (ctx->pip_tos > 0) {
+ ctx->pip_tos--;
}
else {
- _sgl.error = SGL_ERROR_STACK_UNDERFLOW;
+ ctx->error = SGL_ERROR_STACK_UNDERFLOW;
}
}
SOKOL_API_IMPL void sgl_defaults(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl.u = 0.0f; _sgl.v = 0.0f;
- _sgl.rgba = 0xFFFFFFFF;
- _sgl.texturing_enabled = false;
- _sgl.cur_img = _sgl.def_img;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ ctx->u = 0.0f; ctx->v = 0.0f;
+ ctx->rgba = 0xFFFFFFFF;
+ ctx->texturing_enabled = false;
+ ctx->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 = SGL_MATRIXMODE_MODELVIEW;
- _sgl.matrix_dirty = true;
+ _sgl_identity(_sgl_matrix_texture(ctx));
+ _sgl_identity(_sgl_matrix_modelview(ctx));
+ _sgl_identity(_sgl_matrix_projection(ctx));
+ ctx->cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW;
+ ctx->matrix_dirty = true;
}
SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_command_t* cmd = _sgl_next_command();
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) {
cmd->cmd = SGL_COMMAND_VIEWPORT;
cmd->args.viewport.x = x;
@@ -2874,8 +3344,12 @@ SOKOL_API_IMPL void sgl_viewportf(float x, float y, float w, float h, bool origi
SOKOL_API_IMPL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_command_t* cmd = _sgl_next_command();
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) {
cmd->cmd = SGL_COMMAND_SCISSOR_RECT;
cmd->args.scissor_rect.x = x;
@@ -2892,86 +3366,126 @@ SOKOL_API_IMPL void sgl_scissor_rectf(float x, float y, float w, float h, bool o
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;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ ctx->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;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ ctx->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);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
if (SG_INVALID_ID != img.id) {
- _sgl.cur_img = img;
+ ctx->cur_img = img;
}
else {
- _sgl.cur_img = _sgl.def_img;
+ ctx->cur_img = _sgl.def_img;
}
}
SOKOL_API_IMPL void sgl_begin_points(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_begin(SGL_PRIMITIVETYPE_POINTS);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_begin(ctx, SGL_PRIMITIVETYPE_POINTS);
}
SOKOL_API_IMPL void sgl_begin_lines(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_begin(SGL_PRIMITIVETYPE_LINES);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_begin(ctx, SGL_PRIMITIVETYPE_LINES);
}
SOKOL_API_IMPL void sgl_begin_line_strip(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_begin(SGL_PRIMITIVETYPE_LINE_STRIP);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_begin(ctx, SGL_PRIMITIVETYPE_LINE_STRIP);
}
SOKOL_API_IMPL void sgl_begin_triangles(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_begin(SGL_PRIMITIVETYPE_TRIANGLES);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_begin(ctx, SGL_PRIMITIVETYPE_TRIANGLES);
}
SOKOL_API_IMPL void sgl_begin_triangle_strip(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_begin(SGL_PRIMITIVETYPE_TRIANGLE_STRIP);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_begin(ctx, SGL_PRIMITIVETYPE_TRIANGLE_STRIP);
}
SOKOL_API_IMPL void sgl_begin_quads(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(!_sgl.in_begin);
- _sgl_begin(SGL_PRIMITIVETYPE_QUADS);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(!ctx->in_begin);
+ _sgl_begin(ctx, SGL_PRIMITIVETYPE_QUADS);
}
SOKOL_API_IMPL void sgl_end(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- SOKOL_ASSERT(_sgl.in_begin);
- SOKOL_ASSERT(_sgl.cur_vertex >= _sgl.base_vertex);
- _sgl.in_begin = false;
- bool matrix_dirty = _sgl.matrix_dirty;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT(ctx->in_begin);
+ SOKOL_ASSERT(ctx->cur_vertex >= ctx->base_vertex);
+ ctx->in_begin = false;
+ bool matrix_dirty = ctx->matrix_dirty;
if (matrix_dirty) {
- _sgl.matrix_dirty = false;
- _sgl_uniform_t* uni = _sgl_next_uniform();
+ ctx->matrix_dirty = false;
+ _sgl_uniform_t* uni = _sgl_next_uniform(ctx);
if (uni) {
- _sgl_matmul4(&uni->mvp, _sgl_matrix_projection(), _sgl_matrix_modelview());
- uni->tm = *_sgl_matrix_texture();
+ _sgl_matmul4(&uni->mvp, _sgl_matrix_projection(ctx), _sgl_matrix_modelview(ctx));
+ uni->tm = *_sgl_matrix_texture(ctx);
}
}
/* check if command can be merged with previous command */
- sg_pipeline pip = _sgl_get_pipeline(_sgl.pip_stack[_sgl.pip_tos], _sgl.cur_prim_type);
- sg_image img = _sgl.texturing_enabled ? _sgl.cur_img : _sgl.def_img;
- _sgl_command_t* prev_cmd = _sgl_prev_command();
+ sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
+ sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img;
+ _sgl_command_t* prev_cmd = _sgl_prev_command(ctx);
bool merge_cmd = false;
if (prev_cmd) {
if ((prev_cmd->cmd == SGL_COMMAND_DRAW) &&
- (_sgl.cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) &&
- (_sgl.cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) &&
+ (ctx->cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) &&
+ (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) &&
!matrix_dirty &&
(prev_cmd->args.draw.img.id == img.id) &&
(prev_cmd->args.draw.pip.id == pip.id))
@@ -2981,316 +3495,430 @@ SOKOL_API_IMPL void sgl_end(void) {
}
if (merge_cmd) {
/* draw command can be merged with the previous command */
- prev_cmd->args.draw.num_vertices += _sgl.cur_vertex - _sgl.base_vertex;
+ prev_cmd->args.draw.num_vertices += ctx->cur_vertex - ctx->base_vertex;
}
else {
/* append a new draw command */
- _sgl_command_t* cmd = _sgl_next_command();
+ _sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) {
- SOKOL_ASSERT(_sgl.cur_uniform > 0);
+ SOKOL_ASSERT(ctx->cur_uniform > 0);
cmd->cmd = SGL_COMMAND_DRAW;
cmd->args.draw.img = img;
- 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;
+ cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
+ cmd->args.draw.base_vertex = ctx->base_vertex;
+ cmd->args.draw.num_vertices = ctx->cur_vertex - ctx->base_vertex;
+ cmd->args.draw.uniform_index = ctx->cur_uniform - 1;
}
}
}
SOKOL_API_IMPL void sgl_t2f(float u, float v) {
- _sgl.u = u; _sgl.v = v;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->u = u;
+ ctx->v = v;
+ }
}
SOKOL_API_IMPL void sgl_c3f(float r, float g, float b) {
- _sgl.rgba = _sgl_pack_rgbaf(r, g, b, 1.0f);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->rgba = _sgl_pack_rgbaf(r, g, b, 1.0f);
+ }
}
SOKOL_API_IMPL void sgl_c4f(float r, float g, float b, float a) {
- _sgl.rgba = _sgl_pack_rgbaf(r, g, b, a);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->rgba = _sgl_pack_rgbaf(r, g, b, a);
+ }
}
SOKOL_API_IMPL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b) {
- _sgl.rgba = _sgl_pack_rgbab(r, g, b, 255);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->rgba = _sgl_pack_rgbab(r, g, b, 255);
+ }
}
SOKOL_API_IMPL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
- _sgl.rgba = _sgl_pack_rgbab(r, g, b, a);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->rgba = _sgl_pack_rgbab(r, g, b, a);
+ }
}
SOKOL_API_IMPL void sgl_c1i(uint32_t rgba) {
- _sgl.rgba = rgba;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->rgba = rgba;
+ }
}
SOKOL_API_IMPL void sgl_v2f(float x, float y) {
- _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl.rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, ctx->rgba);
+ }
}
SOKOL_API_IMPL void sgl_v3f(float x, float y, float z) {
- _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl.rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, ctx->rgba);
+ }
}
SOKOL_API_IMPL void sgl_v2f_t2f(float x, float y, float u, float v) {
- _sgl_vtx(x, y, 0.0f, u, v, _sgl.rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, u, v, ctx->rgba);
+ }
}
SOKOL_API_IMPL void sgl_v3f_t2f(float x, float y, float z, float u, float v) {
- _sgl_vtx(x, y, z, u, v, _sgl.rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, u, v, ctx->rgba);
+ }
}
SOKOL_API_IMPL void sgl_v2f_c3f(float x, float y, float r, float g, float b) {
- _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ }
}
SOKOL_API_IMPL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b) {
- _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, 255));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, 255));
+ }
}
SOKOL_API_IMPL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a) {
- _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
- _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v2f_c1i(float x, float y, uint32_t rgba) {
- _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, ctx->u, ctx->v, rgba);
+ }
}
SOKOL_API_IMPL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b) {
- _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ }
}
SOKOL_API_IMPL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b) {
- _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, 255));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, 255));
+ }
}
SOKOL_API_IMPL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a) {
- _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbaf(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
- _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, _sgl_pack_rgbab(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba) {
- _sgl_vtx(x, y, z, _sgl.u, _sgl.v, rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, ctx->u, ctx->v, rgba);
+ }
}
SOKOL_API_IMPL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b) {
- _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ }
}
SOKOL_API_IMPL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b) {
- _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, 255));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, 255));
+ }
}
SOKOL_API_IMPL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a) {
- _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
- _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba) {
- _sgl_vtx(x, y, 0.0f, u, v, rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, 0.0f, u, v, rgba);
+ }
}
SOKOL_API_IMPL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b) {
- _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f));
+ }
}
SOKOL_API_IMPL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b) {
- _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbab(r, g, b, 255));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbab(r, g, b, 255));
+ }
}
SOKOL_API_IMPL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a) {
- _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
- _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbab(r, g, b, a));
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx, x, y, z, u, v, _sgl_pack_rgbab(r, g, b, a));
+ }
}
SOKOL_API_IMPL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba) {
- _sgl_vtx(x, y, z, u, v, rgba);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_vtx(ctx,x, y, z, u, v, rgba);
+ }
}
SOKOL_API_IMPL void sgl_matrix_mode_modelview(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW;
+ }
}
SOKOL_API_IMPL void sgl_matrix_mode_projection(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.cur_matrix_mode = SGL_MATRIXMODE_PROJECTION;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->cur_matrix_mode = SGL_MATRIXMODE_PROJECTION;
+ }
}
SOKOL_API_IMPL void sgl_matrix_mode_texture(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.cur_matrix_mode = SGL_MATRIXMODE_TEXTURE;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ ctx->cur_matrix_mode = SGL_MATRIXMODE_TEXTURE;
+ }
}
SOKOL_API_IMPL void sgl_load_identity(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_identity(_sgl_matrix());
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_identity(_sgl_matrix(ctx));
}
SOKOL_API_IMPL void sgl_load_matrix(const float m[16]) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- memcpy(&_sgl_matrix()->v[0][0], &m[0], 64);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ memcpy(&_sgl_matrix(ctx)->v[0][0], &m[0], 64);
}
SOKOL_API_IMPL void sgl_load_transpose_matrix(const float m[16]) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_transpose(_sgl_matrix(), (const _sgl_matrix_t*) &m[0]);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_transpose(_sgl_matrix(ctx), (const _sgl_matrix_t*) &m[0]);
}
SOKOL_API_IMPL void sgl_mult_matrix(const float m[16]) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
const _sgl_matrix_t* m0 = (const _sgl_matrix_t*) &m[0];
- _sgl_mul(_sgl_matrix(), m0);
+ _sgl_mul(_sgl_matrix(ctx), m0);
}
SOKOL_API_IMPL void sgl_mult_transpose_matrix(const float m[16]) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
_sgl_matrix_t m0;
_sgl_transpose(&m0, (const _sgl_matrix_t*) &m[0]);
- _sgl_mul(_sgl_matrix(), &m0);
+ _sgl_mul(_sgl_matrix(ctx), &m0);
}
SOKOL_API_IMPL void sgl_rotate(float angle_rad, float x, float y, float z) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_rotate(_sgl_matrix(), angle_rad, x, y, z);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_rotate(_sgl_matrix(ctx), angle_rad, x, y, z);
}
SOKOL_API_IMPL void sgl_scale(float x, float y, float z) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_scale(_sgl_matrix(), x, y, z);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_scale(_sgl_matrix(ctx), x, y, z);
}
SOKOL_API_IMPL void sgl_translate(float x, float y, float z) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_translate(_sgl_matrix(), x, y, z);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_translate(_sgl_matrix(ctx), x, y, z);
}
SOKOL_API_IMPL void sgl_frustum(float l, float r, float b, float t, float n, float f) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_frustum(_sgl_matrix(), l, r, b, t, n, f);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_frustum(_sgl_matrix(ctx), l, r, b, t, n, f);
}
SOKOL_API_IMPL void sgl_ortho(float l, float r, float b, float t, float n, float f) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_ortho(_sgl_matrix(), l, r, b, t, n, f);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_ortho(_sgl_matrix(ctx), l, r, b, t, n, f);
}
SOKOL_API_IMPL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_perspective(_sgl_matrix(), fov_y, aspect, z_near, z_far);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_perspective(_sgl_matrix(ctx), fov_y, aspect, z_near, z_far);
}
SOKOL_API_IMPL 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_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- _sgl.matrix_dirty = true;
- _sgl_lookat(_sgl_matrix(), eye_x, eye_y, eye_z, center_x, center_y, center_z, up_x, up_y, up_z);
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ ctx->matrix_dirty = true;
+ _sgl_lookat(_sgl_matrix(ctx), eye_x, eye_y, eye_z, center_x, center_y, center_z, up_x, up_y, up_z);
}
SOKOL_GL_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.matrix_tos[_sgl.cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) {
- const _sgl_matrix_t* src = _sgl_matrix();
- _sgl.matrix_tos[_sgl.cur_matrix_mode]++;
- _sgl_matrix_t* dst = _sgl_matrix();
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT((ctx->cur_matrix_mode >= 0) && (ctx->cur_matrix_mode < SGL_NUM_MATRIXMODES));
+ ctx->matrix_dirty = true;
+ if (ctx->matrix_tos[ctx->cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) {
+ const _sgl_matrix_t* src = _sgl_matrix(ctx);
+ ctx->matrix_tos[ctx->cur_matrix_mode]++;
+ _sgl_matrix_t* dst = _sgl_matrix(ctx);
*dst = *src;
}
else {
- _sgl.error = SGL_ERROR_STACK_OVERFLOW;
+ ctx->error = SGL_ERROR_STACK_OVERFLOW;
}
}
SOKOL_GL_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.matrix_tos[_sgl.cur_matrix_mode] > 0) {
- _sgl.matrix_tos[_sgl.cur_matrix_mode]--;
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (!ctx) {
+ return;
+ }
+ SOKOL_ASSERT((ctx->cur_matrix_mode >= 0) && (ctx->cur_matrix_mode < SGL_NUM_MATRIXMODES));
+ ctx->matrix_dirty = true;
+ if (ctx->matrix_tos[ctx->cur_matrix_mode] > 0) {
+ ctx->matrix_tos[ctx->cur_matrix_mode]--;
}
else {
- _sgl.error = SGL_ERROR_STACK_UNDERFLOW;
+ ctx->error = SGL_ERROR_STACK_UNDERFLOW;
}
}
-/* this renders the accumulated draw commands via sokol-gfx */
SOKOL_API_IMPL void sgl_draw(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
- if ((_sgl.error == SGL_NO_ERROR) && (_sgl.cur_vertex > 0) && (_sgl.cur_command > 0)) {
- uint32_t cur_pip_id = SG_INVALID_ID;
- uint32_t cur_img_id = SG_INVALID_ID;
- int cur_uniform_index = -1;
- sg_push_debug_group("sokol-gl");
- const sg_range range = { _sgl.vertices, (size_t)_sgl.cur_vertex * sizeof(_sgl_vertex_t) };
- sg_update_buffer(_sgl.vbuf, &range);
- _sgl.bind.vertex_buffers[0] = _sgl.vbuf;
- for (int i = 0; i < _sgl.cur_command; i++) {
- const _sgl_command_t* cmd = &_sgl.commands[i];
- switch (cmd->cmd) {
- case SGL_COMMAND_VIEWPORT:
- {
- const _sgl_viewport_args_t* args = &cmd->args.viewport;
- sg_apply_viewport(args->x, args->y, args->w, args->h, args->origin_top_left);
- }
- break;
- case SGL_COMMAND_SCISSOR_RECT:
- {
- const _sgl_scissor_rect_args_t* args = &cmd->args.scissor_rect;
- sg_apply_scissor_rect(args->x, args->y, args->w, args->h, args->origin_top_left);
- }
- break;
- case SGL_COMMAND_DRAW:
- {
- const _sgl_draw_args_t* args = &cmd->args.draw;
- 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;
- }
- if (cur_img_id != args->img.id) {
- _sgl.bind.fs_images[0] = args->img;
- sg_apply_bindings(&_sgl.bind);
- cur_img_id = args->img.id;
- }
- if (cur_uniform_index != args->uniform_index) {
- const sg_range ub_range = { &_sgl.uniforms[args->uniform_index], sizeof(_sgl_uniform_t) };
- sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range);
- cur_uniform_index = args->uniform_index;
- }
- /* FIXME: what if number of vertices doesn't match the primitive type? */
- if (args->num_vertices > 0) {
- sg_draw(args->base_vertex, args->num_vertices, 1);
- }
- }
- break;
- }
- }
- sg_pop_debug_group();
+ _sgl_context_t* ctx = _sgl.cur_ctx;
+ if (ctx) {
+ _sgl_draw(ctx);
}
- _sgl_rewind();
}
+
+SOKOL_API_IMPL void sgl_context_draw(sgl_context ctx_id) {
+ SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
+ _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
+ if (ctx) {
+ _sgl_draw(ctx);
+ }
+}
+
#endif /* SOKOL_GL_IMPL */