-
Notifications
You must be signed in to change notification settings - Fork 494
/
sokol_gl.h
4811 lines (4303 loc) · 221 KB
/
sokol_gl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#if defined(SOKOL_IMPL) && !defined(SOKOL_GL_IMPL)
#define SOKOL_GL_IMPL
#endif
#ifndef SOKOL_GL_INCLUDED
/*
sokol_gl.h -- OpenGL 1.x style rendering on top of sokol_gfx.h
Project URL: https://github.com/floooh/sokol
Do this:
#define SOKOL_IMPL or
#define SOKOL_GL_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
The following defines are used by the implementation to select the
platform-specific embedded shader code (these are the same defines as
used by sokol_gfx.h and sokol_app.h):
SOKOL_GLCORE
SOKOL_GLES3
SOKOL_D3D11
SOKOL_METAL
SOKOL_WGPU
...optionally provide the following macros to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_GL_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_GL_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
If sokol_gl.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
On Windows, SOKOL_DLL will define SOKOL_GL_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
Include the following headers before including sokol_gl.h:
sokol_gfx.h
Matrix functions have been taken from MESA and Regal.
FEATURE OVERVIEW:
=================
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 triangles or
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:
What's implemented:
- vertex components:
- position (x, y, z)
- 2D texture coords (u, v)
- color (r, g, b, a)
- primitive types:
- triangle list and strip
- line list and strip
- quad list (TODO: quad strips)
- point list
- 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:
- 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)
- texture coordinate generation
- line width
- all pixel store functions
- no ALPHA_TEST
- 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's a
'pipeline stack', this is similar to GL's matrix stack,
but 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
STEP BY STEP:
=============
--- To initialize sokol-gl, call:
sgl_setup(const sgl_desc_t* desc)
NOTE that sgl_setup() must be called *after* initializing sokol-gfx
(via sg_setup). This is because sgl_setup() needs to create
sokol-gfx resource objects.
If you're intending to render to the default pass, and also don't
want to tweak memory usage, and don't want any logging output you can
just keep sgl_desc_t zero-initialized:
sgl_setup(&(sgl_desc_t*){ 0 });
In this case, sokol-gl will create internal sg_pipeline objects that
are compatible with the sokol-app default framebuffer.
I would recommend to at least install a logging callback so that
you'll see any warnings and errors. The easiest way is through
sokol_log.h:
#include "sokol_log.h"
sgl_setup(&(sgl_desc_t){
.logger.func = slog_func.
});
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:
sgl_setup(&(sgl_desc_t*){
.color_format = SG_PIXELFORMAT_...,
.depth_format = SG_PIXELFORMAT_...,
.sample_count = ...,
});
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:
.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:
.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
without:
- shader
- vertex layout
- color- and depth-pixel-formats
- primitive type (lines, triangles, ...)
- MSAA 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).
'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)
--- 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
functions.
--- Unlike OpenGL, sokol-gl has a function to reset internal state to
a known default. This is useful at the start of a sequence of
rendering operations:
void sgl_defaults(void)
This will set the following default state:
- current texture coordinate to u=0.0f, v=0.0f
- current color to white (rgba all 1.0f)
- current point size to 1.0f
- 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- 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_load_default_pipeline()
...load a specific pipeline on the top of the pipeline stack:
sgl_load_pipeline(sgl_pipeline pip)
...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, sg_sampler smp)
NOTE: the img and smp handles can be invalid (SG_INVALID_ID), in this
case, sokol-gl will fall back to the internal default (white) texture
and sampler.
--- set the current viewport and scissor rect with:
sgl_viewport(int x, int y, int w, int h, bool origin_top_left)
sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left)
...or call these alternatives which take float arguments (this might allow
to avoid casting between float and integer in more strongly typed languages
when floating point pixel coordinates are used):
sgl_viewportf(float x, float y, float w, float h, bool origin_top_left)
sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left)
...these calls add a new command to the internal command queue, so
that the viewport or scissor rect are set at the right time relative
to other sokol-gl calls.
--- adjust the transform matrices, matrix manipulation works just like
the OpenGL matrix stack:
...set the current matrix mode:
sgl_matrix_mode_modelview()
sgl_matrix_mode_projection()
sgl_matrix_mode_texture()
...load the identity matrix into the current matrix:
sgl_load_identity()
...translate, rotate and scale the current matrix:
sgl_translate(float x, float y, float z)
sgl_rotate(float angle_rad, float x, float y, float z)
sgl_scale(float x, float y, float z)
NOTE that all angles in sokol-gl are in radians, not in degree.
Convert between radians and degree with the helper functions:
float sgl_rad(float deg) - degrees to radians
float sgl_deg(float rad) - radians to degrees
...directly load the current matrix from a float[16] array:
sgl_load_matrix(const float m[16])
sgl_load_transpose_matrix(const float m[16])
...directly multiply the current matrix from a float[16] array:
sgl_mult_matrix(const float m[16])
sgl_mult_transpose_matrix(const float m[16])
The memory layout of those float[16] arrays is the same as in OpenGL.
...more matrix functions:
sgl_frustum(float left, float right, float bottom, float top, float near, float far)
sgl_ortho(float left, float right, float bottom, float top, float near, float far)
sgl_perspective(float fov_y, float aspect, float near, float far)
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)
These functions work the same as glFrustum(), glOrtho(), gluPerspective()
and gluLookAt().
...and finally to push / pop the current matrix stack:
sgl_push_matrix(void)
sgl_pop_matrix(void)
Again, these work the same as glPushMatrix() and glPopMatrix().
--- perform primitive rendering:
...set the current texture coordinate and color 'registers' with or
point size with:
sgl_t2f(float u, float v) - set current texture coordinate
sgl_c*(...) - set current color
sgl_point_size(float size) - set current point size
There are several functions for setting the color (as float values,
unsigned byte values, packed as unsigned 32-bit integer, with
and without alpha).
NOTE that these are the only functions that can be called both inside
sgl_begin_*() / sgl_end() and outside.
Also NOTE that point size is currently hardwired to 1.0f if the D3D11
backend is used.
...start a primitive vertex sequence with:
sgl_begin_points()
sgl_begin_lines()
sgl_begin_line_strip()
sgl_begin_triangles()
sgl_begin_triangle_strip()
sgl_begin_quads()
...after sgl_begin_*() specify vertices:
sgl_v*(...)
sgl_v*_t*(...)
sgl_v*_c*(...)
sgl_v*_t*_c*(...)
These functions write a new vertex to sokol-gl's internal vertex buffer,
optionally with texture-coords and color. If the texture coordinate
and/or color is missing, it will be taken from the current texture-coord
and color 'register'.
...finally, after specifying vertices, call:
sgl_end()
This will record a new draw command in sokol-gl's internal command
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 the sgl_draw() function
to render the currently active context:
sgl_draw()
...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.
--- each sokol-gl context tracks internal error states which can
be obtains via:
sgl_error_t sgl_error()
...alternatively with an explicit context argument:
sgl_error_t sgl_context_error(ctx);
...this returns a struct with the following booleans:
.any - true if any of the below errors is true
.vertices_full - internal vertex buffer is full (checked in sgl_end())
.uniforms_full - the internal uniforms buffer is full (checked in sgl_end())
.commands_full - the internal command buffer is full (checked in sgl_end())
.stack_overflow - matrix- or pipeline-stack overflow
.stack_underflow - matrix- or pipeline-stack underflow
.no_context - the active context no longer exists
...depending on the above error state, sgl_draw() may skip rendering
completely, or only draw partial geometry
--- you can get the number of recorded vertices and draw commands in the current
frame and active sokol-gl context via:
int sgl_num_vertices()
int sgl_num_commands()
...this allows you to check whether the vertex or command pools are running
full before the overflow actually happens (in this case you could also
check the error booleans in the result of sgl_error()).
RENDER LAYERS
=============
Render layers allow to split sokol-gl rendering into separate draw-command
groups which can then be rendered separately in a sokol-gfx draw pass. This
allows to mix/interleave sokol-gl rendering with other render operations.
Layered rendering is controlled through two functions:
sgl_layer(int layer_id)
sgl_draw_layer(int layer_id)
(and the context-variant sgl_draw_layer(): sgl_context_draw_layer()
The sgl_layer() function sets the 'current layer', any sokol-gl calls
which internally record draw commands will also store the current layer
in the draw command, and later in a sokol-gfx render pass, a call
to sgl_draw_layer() will only render the draw commands that have
a matching layer.
The default layer is '0', this is active after sokol-gl setup, and
is also restored at the start of a new frame (but *not* by calling
sgl_defaults()).
NOTE that calling sgl_draw() is equivalent with sgl_draw_layer(0)
(in general you should either use either use sgl_draw() or
sgl_draw_layer() in an application, but not both).
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 implicitly 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);
...or alternatively use the function sgl_default_context() instead of the
global constant:
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
memory buffers, and then drawing the recorded commands via sokol_gfx.h
The only functions which call into sokol_gfx.h are:
- sgl_setup()
- sgl_shutdown()
- sgl_draw() (and variants)
sgl_setup() must be called after initializing sokol-gfx.
sgl_shutdown() must be called before shutting down sokol-gfx.
sgl_draw() must be called once per frame inside a sokol-gfx render pass.
All other sokol-gl function can be called anywhere in a frame, since
they just record data into memory buffers owned by sokol-gl.
What happens in:
sgl_setup():
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
- float2 texture coords
- uint32_t color
One uniform block is 128 bytes:
- mat4 model-view-projection matrix
- mat4 texture matrix
One draw command is ca. 24 bytes for the actual
command code plus command arguments.
Each sgl_end() consumes one command, and one uniform block
(only when the matrices have changed).
The required size for one sgl_begin/end pair is (at most):
(152 + 24 * num_verts) bytes
sgl_shutdown():
- all sokol-gfx resources (buffer, shader, default-texture and
all pipeline objects) are destroyed
- the 3 memory buffers are freed
sgl_draw() (and variants)
- copy all recorded vertex data into the dynamic sokol-gfx buffer
via a call to sg_update_buffer()
- for each recorded command:
- if the layer number stored in the command doesn't match
the layer that's to be rendered, skip to the next
command
- 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 what has changed since the last draw command,
call sg_apply_pipeline(), sg_apply_bindings() and
sg_apply_uniforms()
- finally call sg_draw()
All other functions only modify the internally tracked state, add
data to the vertex, uniform and command buffers, or manipulate
the matrix stack.
ON DRAW COMMAND MERGING
=======================
Not every call to sgl_end() will automatically record a new draw command.
If possible, the previous draw command will simply be extended,
resulting in fewer actual draw calls later in sgl_draw().
A draw command will be merged with the previous command if "no relevant
state has changed" since the last sgl_end(), meaning:
- no calls to sgl_viewport() and sgl_scissor_rect()
- the primitive type hasn't changed
- the primitive type isn't a 'strip type' (no line or triangle strip)
- the pipeline state object hasn't changed
- the current layer hasn't changed
- none of the matrices has changed
- none of the texture state has changed
Merging a draw command simply means that the number of vertices
to render in the previous draw command will be incremented by the
number of vertices in the new draw command.
MEMORY ALLOCATION OVERRIDE
==========================
You can override the memory allocation functions at initialization time
like this:
void* my_alloc(size_t size, void* user_data) {
return malloc(size);
}
void my_free(void* ptr, void* user_data) {
free(ptr);
}
...
sgl_setup(&(sgl_desc_t){
// ...
.allocator = {
.alloc_fn = my_alloc,
.free_fn = my_free,
.user_data = ...;
}
});
...
If no overrides are provided, malloc and free will be used.
ERROR REPORTING AND LOGGING
===========================
To get any logging information at all you need to provide a logging callback in the setup call,
the easiest way is to use sokol_log.h:
#include "sokol_log.h"
sgl_setup(&(sgl_desc_t){
// ...
.logger.func = slog_func
});
To override logging with your own callback, first write a logging function like this:
void my_log(const char* tag, // e.g. 'sgl'
uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info
uint32_t log_item_id, // SGL_LOGITEM_*
const char* message_or_null, // a message string, may be nullptr in release mode
uint32_t line_nr, // line number in sokol_gl.h
const char* filename_or_null, // source filename, may be nullptr in release mode
void* user_data)
{
...
}
...and then setup sokol-gl like this:
sgl_setup(&(sgl_desc_t){
.logger = {
.func = my_log,
.user_data = my_user_data,
}
});
The provided logging function must be reentrant (e.g. be callable from
different threads).
If you don't want to provide your own custom logger it is highly recommended to use
the standard logger in sokol_log.h instead, otherwise you won't see any warnings or
errors.
LICENSE
=======
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_GL_INCLUDED (1)
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h> // size_t, offsetof
#if !defined(SOKOL_GFX_INCLUDED)
#error "Please include sokol_gfx.h before sokol_gl.h"
#endif
#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL)
#define SOKOL_GL_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_GL_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GL_IMPL)
#define SOKOL_GL_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_GL_API_DECL __declspec(dllimport)
#else
#define SOKOL_GL_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
sgl_log_item_t
Log items are defined via X-Macros, and expanded to an
enum 'sgl_log_item' - and in debug mode only - corresponding strings.
Used as parameter in the logging callback.
*/
#define _SGL_LOG_ITEMS \
_SGL_LOGITEM_XMACRO(OK, "Ok") \
_SGL_LOGITEM_XMACRO(MALLOC_FAILED, "memory allocation failed") \
_SGL_LOGITEM_XMACRO(MAKE_PIPELINE_FAILED, "sg_make_pipeline() failed") \
_SGL_LOGITEM_XMACRO(PIPELINE_POOL_EXHAUSTED, "pipeline pool exhausted (use sgl_desc_t.pipeline_pool_size to adjust)") \
_SGL_LOGITEM_XMACRO(ADD_COMMIT_LISTENER_FAILED, "sg_add_commit_listener() failed") \
_SGL_LOGITEM_XMACRO(CONTEXT_POOL_EXHAUSTED, "context pool exhausted (use sgl_desc_t.context_pool_size to adjust)") \
_SGL_LOGITEM_XMACRO(CANNOT_DESTROY_DEFAULT_CONTEXT, "cannot destroy default context") \
#define _SGL_LOGITEM_XMACRO(item,msg) SGL_LOGITEM_##item,
typedef enum sgl_log_item_t {
_SGL_LOG_ITEMS
} sgl_log_item_t;
#undef _SGL_LOGITEM_XMACRO
/*
sgl_logger_t
Used in sgl_desc_t to provide a custom logging and error reporting
callback to sokol-gl.
*/
typedef struct sgl_logger_t {
void (*func)(
const char* tag, // always "sgl"
uint32_t log_level, // 0=panic, 1=error, 2=warning, 3=info
uint32_t log_item_id, // SGL_LOGITEM_*
const char* message_or_null, // a message string, may be nullptr in release mode
uint32_t line_nr, // line number in sokol_gl.h
const char* filename_or_null, // source filename, may be nullptr in release mode
void* user_data);
void* user_data;
} sgl_logger_t;
/* 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
Errors are reset each frame after calling sgl_draw(),
get the last error code with sgl_error()
*/
typedef struct sgl_error_t {
bool any;
bool vertices_full;
bool uniforms_full;
bool commands_full;
bool stack_overflow;
bool stack_underflow;
bool 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;
/*
sgl_allocator_t
Used in sgl_desc_t to provide custom memory-alloc and -free functions
to sokol_gl.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct sgl_allocator_t {
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} sgl_allocator_t;
typedef struct sgl_desc_t {
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: SG_FACEWINDING_CCW
sgl_allocator_t allocator; // optional memory allocation overrides (default: malloc/free)
sgl_logger_t logger; // optional log function override (default: NO LOGGING)
} 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 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);
SOKOL_GL_API_DECL sgl_context sgl_default_context(void);
/* get information about recorded vertices and commands in current context */
SOKOL_GL_API_DECL int sgl_num_vertices(void);
SOKOL_GL_API_DECL int sgl_num_commands(void);
/* draw recorded commands (call inside a sokol-gfx render pass) */
SOKOL_GL_API_DECL void sgl_draw(void);
SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx);
SOKOL_GL_API_DECL void sgl_draw_layer(int layer_id);
SOKOL_GL_API_DECL void sgl_context_draw_layer(sgl_context ctx, int layer_id);
/* 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);
SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left);
SOKOL_GL_API_DECL void sgl_enable_texture(void);
SOKOL_GL_API_DECL void sgl_disable_texture(void);
SOKOL_GL_API_DECL void sgl_texture(sg_image img, sg_sampler smp);
SOKOL_GL_API_DECL void sgl_layer(int layer_id);
/* pipeline stack functions */
SOKOL_GL_API_DECL void sgl_load_default_pipeline(void);
SOKOL_GL_API_DECL void sgl_load_pipeline(sgl_pipeline pip);
SOKOL_GL_API_DECL void sgl_push_pipeline(void);
SOKOL_GL_API_DECL void sgl_pop_pipeline(void);
/* matrix stack functions */
SOKOL_GL_API_DECL void sgl_matrix_mode_modelview(void);
SOKOL_GL_API_DECL void sgl_matrix_mode_projection(void);
SOKOL_GL_API_DECL void sgl_matrix_mode_texture(void);
SOKOL_GL_API_DECL void sgl_load_identity(void);
SOKOL_GL_API_DECL void sgl_load_matrix(const float m[16]);
SOKOL_GL_API_DECL void sgl_load_transpose_matrix(const float m[16]);
SOKOL_GL_API_DECL void sgl_mult_matrix(const float m[16]);
SOKOL_GL_API_DECL void sgl_mult_transpose_matrix(const float m[16]);
SOKOL_GL_API_DECL void sgl_rotate(float angle_rad, float x, float y, float z);
SOKOL_GL_API_DECL void sgl_scale(float x, float y, float z);
SOKOL_GL_API_DECL void sgl_translate(float x, float y, float z);
SOKOL_GL_API_DECL void sgl_frustum(float l, float r, float b, float t, float n, float f);
SOKOL_GL_API_DECL void sgl_ortho(float l, float r, float b, float t, float n, float f);
SOKOL_GL_API_DECL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far);
SOKOL_GL_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_GL_API_DECL void sgl_push_matrix(void);
SOKOL_GL_API_DECL void sgl_pop_matrix(void);
/* these functions only set the internal 'current texcoord / color / point size' (valid inside or outside begin/end) */
SOKOL_GL_API_DECL void sgl_t2f(float u, float v);
SOKOL_GL_API_DECL void sgl_c3f(float r, float g, float b);
SOKOL_GL_API_DECL void sgl_c4f(float r, float g, float b, float a);
SOKOL_GL_API_DECL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b);
SOKOL_GL_API_DECL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
SOKOL_GL_API_DECL void sgl_c1i(uint32_t rgba);
SOKOL_GL_API_DECL void sgl_point_size(float s);
/* define primitives, each begin/end is one draw command */
SOKOL_GL_API_DECL void sgl_begin_points(void);
SOKOL_GL_API_DECL void sgl_begin_lines(void);
SOKOL_GL_API_DECL void sgl_begin_line_strip(void);
SOKOL_GL_API_DECL void sgl_begin_triangles(void);
SOKOL_GL_API_DECL void sgl_begin_triangle_strip(void);
SOKOL_GL_API_DECL void sgl_begin_quads(void);
SOKOL_GL_API_DECL void sgl_v2f(float x, float y);
SOKOL_GL_API_DECL void sgl_v3f(float x, float y, float z);
SOKOL_GL_API_DECL void sgl_v2f_t2f(float x, float y, float u, float v);
SOKOL_GL_API_DECL void sgl_v3f_t2f(float x, float y, float z, float u, float v);
SOKOL_GL_API_DECL void sgl_v2f_c3f(float x, float y, float r, float g, float b);
SOKOL_GL_API_DECL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b);
SOKOL_GL_API_DECL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a);
SOKOL_GL_API_DECL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
SOKOL_GL_API_DECL void sgl_v2f_c1i(float x, float y, uint32_t rgba);
SOKOL_GL_API_DECL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b);
SOKOL_GL_API_DECL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b);
SOKOL_GL_API_DECL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a);
SOKOL_GL_API_DECL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
SOKOL_GL_API_DECL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba);
SOKOL_GL_API_DECL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b);
SOKOL_GL_API_DECL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b);
SOKOL_GL_API_DECL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a);
SOKOL_GL_API_DECL 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);
SOKOL_GL_API_DECL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba);
SOKOL_GL_API_DECL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b);
SOKOL_GL_API_DECL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b);
SOKOL_GL_API_DECL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a);
SOKOL_GL_API_DECL 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);
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);
#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 */
// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██
// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████
//
// >>implementation
#ifdef SOKOL_GL_IMPL
#define SOKOL_GL_IMPL_INCLUDED (1)
#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sgl_desc_t.allocator to override memory allocation functions"
#endif
#include <stdlib.h> // malloc/free
#include <string.h> // memset
#include <math.h> // M_PI, sqrtf, sinf, cosf
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#define _sgl_def(val, def) (((val) == 0) ? (def) : (val))
#define _SGL_INIT_COOKIE (0xABCDABCD)
/*
Embedded source code compiled with:
sokol-shdc -i sgl.glsl -o sgl.h -l glsl410:glsl300es:hlsl4:metal_macos:metal_ios:metal_sim:wgsl -b
(not that for Metal and D3D11 byte code, sokol-shdc must be run
on macOS and Windows)
@vs vs
layout(binding=0) uniform vs_params {
mat4 mvp;
mat4 tm;
};
in vec4 position;