libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
glutils.c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license in the file COPYING
10 * or http://www.opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file COPYING.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2023 Saso Kiselkov. All rights reserved.
24 */
25
26#include <stddef.h>
27#include <string.h>
28
29#include <png.h>
30
31#include <acfutils/avl.h>
32#include <acfutils/assert.h>
33#include <acfutils/glutils.h>
34#include <acfutils/list.h>
35#include <acfutils/safe_alloc.h>
36#include <acfutils/shader.h>
37#include <acfutils/thread.h>
38
39#ifdef _USE_MATH_DEFINES
40#undef _USE_MATH_DEFINES
41#endif
42
43#include <cglm/cglm.h>
44
45TEXSZ_MK_TOKEN(glutils_quads_vbo);
46TEXSZ_MK_TOKEN(glutils_lines_vbo);
47
48typedef struct {
49 GLfloat pos[3];
50 GLfloat tex0[2];
51} vtx_t;
52
53typedef struct {
54 const void *instance;
55 char allocd_at[32];
56 int64_t bytes;
57 avl_node_t node;
59
60typedef struct {
61 const char *token;
62 int64_t bytes;
63 avl_tree_t instances;
64 avl_node_t node;
66
67static struct {
68 bool_t inited;
69 mutex_t lock;
70 int64_t bytes;
71 avl_tree_t allocs;
72} texsz = { .inited = B_FALSE };
73
74typedef enum {
75 CACHE_ENTRY_2D_QUADS,
76 CACHE_ENTRY_3D_QUADS,
77 CACHE_ENTRY_3D_LINES
78} cache_entry_type_t;
79
80typedef struct {
81 cache_entry_type_t type;
82 union {
83 glutils_quads_t quads;
84 glutils_lines_t lines;
85 };
86 void *buf[2];
87 size_t buf_sz[2];
88 avl_node_t tree_node;
89 list_node_t lru_node;
91
93 avl_tree_t tree;
94 list_t lru;
95 size_t sz;
96 size_t cap;
97};
98
99static bool_t inited = B_FALSE;
100static thread_id_t main_thread;
101static bool_t in_zink_mode = B_FALSE;
102
107void
109{
110 if (!inited) {
111 inited = B_TRUE;
112 main_thread = curthread_id;
113 in_zink_mode = (strcmp((char *)glGetString(GL_VENDOR), "Mesa")
114 == 0 &&
115 strncmp((char *)glGetString(GL_RENDERER), "zink", 4) == 0);
116 }
117}
118
127API_EXPORT void
129{
130 static const GLenum disable_caps[] = {
131 GL_COLOR_ARRAY,
132 GL_EDGE_FLAG_ARRAY,
133 GL_FOG_COORD_ARRAY,
134 GL_INDEX_ARRAY,
135 GL_NORMAL_ARRAY,
136 GL_SECONDARY_COLOR_ARRAY,
137 GL_TEXTURE_COORD_ARRAY,
138 GL_VERTEX_ARRAY,
139 0
140 };
141
142 if (GLEW_VERSION_3_1)
143 return;
144
145 for (int i = 0; disable_caps[i] != 0; i++)
146 glDisableClientState(disable_caps[i]);
147}
148
157API_EXPORT void
159{
160 GLint n_attrs;
161
162 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &n_attrs);
163 for (int i = 0; i < MIN(n_attrs, 32); i++)
164 glDisableVertexAttribArray(i);
165}
166
175API_EXPORT GLuint
177{
178 size_t i, n;
179 GLuint buf;
180
181 ASSERT0(num_vtx & 3);
182
183 size_t n_idx = num_vtx + num_vtx / 2;
184 GLuint *idx_data = safe_malloc(n_idx * sizeof (*idx_data));
185 for (i = 0, n = 0; i < num_vtx; i += 4, n += 6) {
186 idx_data[n + 0] = i + 0;
187 idx_data[n + 1] = i + 1;
188 idx_data[n + 2] = i + 2;
189
190 idx_data[n + 3] = i + 0;
191 idx_data[n + 4] = i + 2;
192 idx_data[n + 5] = i + 3;
193 }
194 ASSERT3U(n, ==, n_idx);
195
196 glGenBuffers(1, &buf);
197 VERIFY(buf != 0);
198 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf);
199 glBufferData(GL_ELEMENT_ARRAY_BUFFER, n * sizeof (*idx_data),
200 idx_data, GL_STATIC_DRAW);
201
202 free(idx_data);
203
204 return (buf);
205}
206
208API_EXPORT void
209glutils_init_2D_quads_impl(glutils_quads_t *quads, const char *filename,
210 int line, const vect2_t *p, const vect2_t *t, size_t num_pts)
211{
212 vect3_t *p_3d = safe_malloc(num_pts * sizeof (*p_3d));
213
214 for (size_t i = 0; i < num_pts; i++)
215 p_3d[i] = VECT3(p[i].x, p[i].y, 0);
216
217 glutils_init_3D_quads_impl(quads, filename, line, p_3d, t, num_pts);
218 free(p_3d);
219}
220
224API_EXPORT void
226 int line, const vect2_t *p, const vect2_t *t, size_t num_pts)
227{
228 vect3_t *p_3d = safe_malloc(num_pts * sizeof (*p_3d));
229
230 for (size_t i = 0; i < num_pts; i++)
231 p_3d[i] = VECT3(p[i].x, p[i].y, 0);
232
233 glutils_update_3D_quads_impl(quads, filename, line, p_3d, t, num_pts);
234 free(p_3d);
235}
236
238API_EXPORT void
239glutils_init_3D_quads_impl(glutils_quads_t *quads, const char *filename,
240 int line, const vect3_t *p, const vect2_t *t, size_t num_pts)
241{
242 GLint old_vao = 0;
243
244 ASSERT0(num_pts & 3);
245 ASSERT(p != NULL);
246
247 memset(quads, 0, sizeof (*quads));
248
249 if (GLEW_VERSION_3_0 && curthread_id != main_thread) {
250 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
251
252 glGenVertexArrays(1, &quads->vao);
253 glBindVertexArray(quads->vao);
254 VERIFY(quads->vao != 0);
255 } else {
256 quads->vao = 0;
257 }
258 glGenBuffers(1, &quads->vbo);
259 VERIFY(quads->vbo != 0);
260 glutils_update_3D_quads_impl(quads, filename, line, p, t, num_pts);
261
262 if (GLEW_VERSION_3_0 && curthread_id != main_thread)
263 glBindVertexArray(old_vao);
264}
265
269API_EXPORT void
271 int line, const vect3_t *p, const vect2_t *t, size_t num_pts)
272{
273 vtx_t *vtx_data = safe_calloc(num_pts, sizeof (*vtx_data));
274
275 ASSERT(quads != NULL);
276 ASSERT(p != NULL);
278
279 for (size_t i = 0; i < num_pts; i++) {
280 vtx_data[i].pos[0] = p[i].x;
281 vtx_data[i].pos[1] = p[i].y;
282 vtx_data[i].pos[2] = p[i].z;
283 if (t != NULL) {
284 vtx_data[i].tex0[0] = t[i].x;
285 vtx_data[i].tex0[1] = t[i].y;
286 }
287 }
288 glBindBuffer(GL_ARRAY_BUFFER, quads->vbo);
289 glBufferData(GL_ARRAY_BUFFER, num_pts * sizeof (vtx_t), vtx_data,
290 GL_STATIC_DRAW);
291 if (quads->num_vtx != num_pts || quads->ibo == 0) {
292 if (quads->ibo != 0)
293 glDeleteBuffers(1, &quads->ibo);
294 quads->ibo = glutils_make_quads_IBO(num_pts);
295 }
296 if (quads->num_vtx != num_pts) {
297 IF_TEXSZ(TEXSZ_FREE_BYTES_INSTANCE(glutils_quads_vbo, quads,
298 quads->num_vtx * sizeof (vtx_t)));
299 IF_TEXSZ(TEXSZ_ALLOC_BYTES_INSTANCE(glutils_quads_vbo, quads,
300 filename, line, num_pts * sizeof (vtx_t)));
301 }
302 quads->num_vtx = num_pts;
303
304 free(vtx_data);
305}
306
312API_EXPORT void
314{
315 if (quads->vao != 0)
316 glDeleteVertexArrays(1, &quads->vao);
317 if (quads->vbo != 0) {
318 IF_TEXSZ(TEXSZ_FREE_BYTES_INSTANCE(glutils_quads_vbo, quads,
319 quads->num_vtx * sizeof (vtx_t)));
320 glDeleteBuffers(1, &quads->vbo);
321 }
322 if (quads->ibo != 0)
323 glDeleteBuffers(1, &quads->ibo);
324 memset(quads, 0, sizeof (*quads));
325}
326
327static void
328glutils_draw_common(GLenum mode, GLuint vao, GLuint vbo, GLuint ibo,
329 bool_t *setup, size_t num_vtx, GLint prog)
330{
331 GLint pos_loc = -1, tex0_loc = -1;
332
333 ASSERT(vbo != 0);
334 ASSERT(prog != 0);
335
336 if (vao != 0) {
337 /* Vertex arrays supported */
338 glBindVertexArray(vao);
339 }
340
341 if (vao == 0 || !(*setup)) {
342 glBindBuffer(GL_ARRAY_BUFFER, vbo);
343 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
344
345 pos_loc = glGetAttribLocation(prog, "vtx_pos");
346 tex0_loc = glGetAttribLocation(prog, "vtx_tex0");
347
348 glutils_enable_vtx_attr_ptr(pos_loc, 3, GL_FLOAT, GL_FALSE,
349 sizeof (vtx_t), offsetof(vtx_t, pos));
350 glutils_enable_vtx_attr_ptr(tex0_loc, 2, GL_FLOAT, GL_FALSE,
351 sizeof (vtx_t), offsetof(vtx_t, tex0));
352 *setup = B_TRUE;
353 }
354
355 if (ibo != 0)
356 glDrawElements(mode, num_vtx, GL_UNSIGNED_INT, NULL);
357 else
358 glDrawArrays(mode, 0, num_vtx);
359
360 if (vao != 0) {
361 glBindVertexArray(0);
362 } else {
365 }
366 glBindBuffer(GL_ARRAY_BUFFER, 0);
367 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
368}
369
386API_EXPORT void
388{
389 /*
390 * num_vtx is the number of underlying vertex data array entries,
391 * but glDrawElements needs the number of indices, which is always
392 * exactly 1.5x as many.
393 */
394 glutils_draw_common(GL_TRIANGLES, quads->vao, quads->vbo, quads->ibo,
395 &quads->setup, quads->num_vtx + quads->num_vtx / 2, prog);
396}
397
398static int
399cache_compar(const void *a, const void *b)
400{
401 const cache_entry_t *ca = a, *cb = b;
402
403 if (ca->type < cb->type)
404 return (-1);
405 if (ca->type > cb->type)
406 return (1);
407 for (int i = 0; i < 2; i++) {
408 if (ca->buf_sz[i] < cb->buf_sz[i])
409 return (-1);
410 if (ca->buf_sz[i] > cb->buf_sz[i])
411 return (1);
412 }
413 for (int i = 0; i < 2; i++) {
414 int res = memcmp(ca->buf[i], cb->buf[i], cb->buf_sz[i]);
415
416 if (res < 0)
417 return (-1);
418 if (res > 0)
419 return (1);
420 }
421
422 return (0);
423}
424
439glutils_cache_t *
440glutils_cache_new(size_t cap_bytes)
441{
442 glutils_cache_t *cache = safe_calloc(1, sizeof (*cache));
443
444 ASSERT(cap_bytes != 0);
445
446 avl_create(&cache->tree, cache_compar, sizeof (cache_entry_t),
447 offsetof(cache_entry_t, tree_node));
448 list_create(&cache->lru, sizeof (cache_entry_t),
449 offsetof(cache_entry_t, lru_node));
450 cache->cap = cap_bytes;
451
452 return (cache);
453}
454
455/*
456 * Frees the storage associated with a glutils cache entry.
457 */
458static void
459free_cache_entry(cache_entry_t *ce)
460{
461 ASSERT(ce != NULL);
462
463 switch (ce->type) {
464 case CACHE_ENTRY_2D_QUADS:
465 case CACHE_ENTRY_3D_QUADS:
466 glutils_destroy_quads(&ce->quads);
467 break;
468 case CACHE_ENTRY_3D_LINES:
469 glutils_destroy_lines(&ce->lines);
470 break;
471 default:
472 VERIFY(0);
473 }
474 free(ce->buf[0]);
475 free(ce->buf[1]);
476 free(ce);
477}
478
483void
484glutils_cache_destroy(glutils_cache_t *cache)
485{
486 cache_entry_t *ce;
487 void *cookie;
488
489 if (cache == NULL)
490 return;
491
492 /* The entries are retained in the tree, so we'll just empty the lru */
493 while ((ce = list_remove_head(&cache->lru)) != NULL)
494 ;
495 list_destroy(&cache->lru);
496
497 cookie = NULL;
498 while ((ce = avl_destroy_nodes(&cache->tree, &cookie)) != NULL)
499 free_cache_entry(ce);
500 avl_destroy(&cache->tree);
501
502 free(cache);
503}
504
505/*
506 * Takes a cache and trims its memory usage until it fits within its
507 * capacity limit. `extra_needed' specifies the extra cache storage
508 * space required to insert a new object, and thus adjusts the cache's
509 * storage target.
510 */
511static void
512trim_cache(glutils_cache_t *cache, size_t extra_needed)
513{
514 ASSERT(cache != NULL);
515
516 while (cache->sz + extra_needed > cache->cap) {
517 cache_entry_t *ce = list_remove_tail(&cache->lru);
518
519 if (ce == NULL)
520 break;
521 avl_remove(&cache->tree, ce);
522 ASSERT3U(cache->sz, >=, ce->buf_sz[0] + ce->buf_sz[1]);
523 cache->sz -= (ce->buf_sz[0] + ce->buf_sz[1]);
524 free_cache_entry(ce);
525 }
526}
527
528/*
529 * Adds a new cache entry.
530 */
531static cache_entry_t *
532cache_add_entry(glutils_cache_t *cache, avl_index_t where,
533 cache_entry_type_t type, const void *buf0, size_t buf0_sz,
534 const void *buf1, size_t buf1_sz, size_t num_pts)
535{
536 cache_entry_t *ce = safe_calloc(1, sizeof (*ce));
537
538 ASSERT(cache != NULL);
539 ASSERT(buf0 != NULL);
540
541 ce->type = type;
542
543 ce->buf_sz[0] = buf0_sz;
544 ce->buf[0] = safe_malloc(buf0_sz);
545 memcpy(ce->buf[0], buf0, buf0_sz);
546 if (buf1 != NULL) {
547 ce->buf_sz[1] = buf1_sz;
548 ce->buf[1] = safe_malloc(buf1_sz);
549 memcpy(ce->buf[1], buf1, buf1_sz);
550 }
551
552 switch (type) {
553 case CACHE_ENTRY_2D_QUADS:
554 glutils_init_2D_quads(&ce->quads, buf0, buf1, num_pts);
555 break;
556 case CACHE_ENTRY_3D_QUADS:
557 glutils_init_3D_quads(&ce->quads, buf0, buf1, num_pts);
558 break;
559 case CACHE_ENTRY_3D_LINES:
560 glutils_init_3D_lines(&ce->lines, buf0, num_pts);
561 break;
562 default:
563 VERIFY(0);
564 }
565 avl_insert(&cache->tree, ce, where);
566 list_insert_head(&cache->lru, ce);
567
568 return (ce);
569}
570
571static void *
572glutils_cache_get_common(glutils_cache_t *cache, cache_entry_type_t type,
573 const void *p, const void *t, size_t num_pts)
574{
575 size_t p_sz = (type == CACHE_ENTRY_2D_QUADS ? sizeof (vect2_t) :
576 sizeof (vect3_t));
577 const cache_entry_t srch = {
578 .type = type,
579 .buf = { (void *)p, (void *)t },
580 .buf_sz = {
581 p_sz * num_pts,
582 t != NULL ? sizeof (vect2_t) * num_pts : 0
583 }
584 };
585 cache_entry_t *ce;
586 avl_index_t where;
587 size_t bytes = srch.buf_sz[0] + srch.buf_sz[1];
588
589 ASSERT(cache != NULL);
590 ASSERT(p != NULL);
591 ASSERT(num_pts != 0);
592
593 ce = avl_find(&cache->tree, &srch, &where);
594 if (ce == NULL) {
595 trim_cache(cache, bytes);
596 ce = cache_add_entry(cache, where, type, p, srch.buf_sz[0],
597 t, srch.buf_sz[1], num_pts);
598 } else {
599 list_remove(&cache->lru, ce);
600 list_insert_head(&cache->lru, ce);
601 }
602 if (type <= CACHE_ENTRY_3D_QUADS)
603 return (&ce->quads);
604 else
605 return (&ce->lines);
606}
607
613glutils_cache_get_2D_quads(glutils_cache_t *cache, const vect2_t *p,
614 const vect2_t *t, size_t num_pts)
615{
616 return (glutils_cache_get_common(cache, 0, p, t, num_pts));
617}
618
638glutils_cache_get_3D_quads(glutils_cache_t *cache, const vect3_t *p,
639 const vect2_t *t, size_t num_pts)
640{
641 return (glutils_cache_get_common(cache, 1, p, t, num_pts));
642}
643
652glutils_cache_get_3D_lines(glutils_cache_t *cache, const vect3_t *p,
653 size_t num_pts)
654{
655 return (glutils_cache_get_common(cache, 2, p, NULL, num_pts));
656}
657
665API_EXPORT void
666glutils_init_3D_lines_impl(glutils_lines_t *lines, const char *filename,
667 int line, const vect3_t *p, size_t num_pts)
668{
669 vtx_t *vtx_data = safe_calloc(num_pts, sizeof (*vtx_data));
670 GLint old_vao = 0;
671
672 ASSERT(p != NULL);
673
674 memset(lines, 0, sizeof (*lines));
675 for (size_t i = 0; i < num_pts; i++) {
676 vtx_data[i].pos[0] = p[i].x;
677 vtx_data[i].pos[1] = p[i].y;
678 vtx_data[i].pos[2] = p[i].z;
679 }
680
681 if (GLEW_VERSION_3_0 && curthread_id != main_thread) {
682 glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vao);
683
684 glGenVertexArrays(1, &lines->vao);
685 glBindVertexArray(lines->vao);
686 VERIFY(lines->vao != 0);
687 } else {
688 lines->vao = 0;
689 }
690
691 glGenBuffers(1, &lines->vbo);
692 VERIFY(lines->vbo != 0);
693 lines->num_vtx = num_pts;
694
695 glBindBuffer(GL_ARRAY_BUFFER, lines->vbo);
696 glBufferData(GL_ARRAY_BUFFER, lines->num_vtx * sizeof (vtx_t),
697 vtx_data, GL_STATIC_DRAW);
698
699 if (glutils_texsz_inited()) {
700 TEXSZ_ALLOC_BYTES_INSTANCE(glutils_lines_vbo, lines,
701 filename, line, lines->num_vtx * sizeof (vtx_t));
702 }
703
704 if (GLEW_VERSION_3_0 && curthread_id != main_thread)
705 glBindVertexArray(old_vao);
706 else
707 glBindBuffer(GL_ARRAY_BUFFER, 0);
708
709 free(vtx_data);
710}
711
726API_EXPORT void
728{
729 glutils_draw_common(GL_LINE_STRIP, lines->vao, lines->vbo, 0,
730 &lines->setup, lines->num_vtx, prog);
731}
732
741API_EXPORT void
743{
744 if (lines->vbo != 0) {
745 if (glutils_texsz_inited()) {
746 TEXSZ_FREE_BYTES_INSTANCE(glutils_lines_vbo, lines,
747 lines->num_vtx * sizeof (vtx_t));
748 }
749 glDeleteBuffers(1, &lines->vbo);
750 memset(lines, 0, sizeof (*lines));
751 }
752}
753
761API_EXPORT void
762glutils_vp2pvm(GLfloat pvm[16])
763{
764 int vp[4];
765 mat4 pvm_mat;
766
767 glGetIntegerv(GL_VIEWPORT, vp);
768 glm_ortho(vp[0], vp[0] + vp[2], vp[1], vp[1] + vp[3], 0, 1, pvm_mat);
769 memcpy(pvm, pvm_mat, sizeof (GLfloat) * 16);
770}
771
772static int
773texsz_alloc_compar(const void *a, const void *b)
774{
775 const texsz_alloc_t *ta = a, *tb = b;
776
777 if (ta->token < tb->token)
778 return (-1);
779 if (ta->token > tb->token)
780 return (1);
781
782 return (0);
783}
784
785static int
786texsz_instance_compar(const void *a, const void *b)
787{
788 const texsz_instance_t *ta = a, *tb = b;
789
790 if (ta->instance < tb->instance)
791 return (-1);
792 if (ta->instance > tb->instance)
793 return (1);
794
795 return (0);
796}
797
813API_EXPORT void
815{
816 texsz.inited = B_TRUE;
817 mutex_init(&texsz.lock);
818 texsz.bytes = 0;
819 avl_create(&texsz.allocs, texsz_alloc_compar,
820 sizeof (texsz_alloc_t), offsetof(texsz_alloc_t, node));
821}
822
832API_EXPORT void
834{
835 void *cookie;
836 texsz_alloc_t *ta;
837
838 if (!texsz.inited)
839 return;
840 mutex_destroy(&texsz.lock);
841 cookie = NULL;
842 while ((ta = avl_destroy_nodes(&texsz.allocs, &cookie)) != NULL) {
843 void *cookie2 = NULL;
845
846 ASSERT(ta->token != NULL);
847 if (ta->bytes != 0) {
848 for (ti = avl_first(&ta->instances); ti != NULL;
849 ti = AVL_NEXT(&ta->instances, ti)) {
850 logMsg("%s: %p %ld (at: %s)\n", ta->token,
851 ti->instance, (long)ti->bytes,
852 ti->allocd_at);
853 }
854 VERIFY_MSG(0, "Texture allocation leak: "
855 "%s leaked %ld bytes", ta->token, (long)ta->bytes);
856 }
857 cookie2 = NULL;
858 while ((ti = avl_destroy_nodes(&ta->instances, &cookie2)) !=
859 NULL)
860 free(ti);
861 avl_destroy(&ta->instances);
862 free(ta);
863 }
864 avl_destroy(&texsz.allocs);
865}
866
867static inline void
868texsz_incr(const char *token, const void *instance, const char *filename,
869 int line, int64_t bytes)
870{
871 texsz_alloc_t srch = { .token = token };
872 texsz_alloc_t *ta;
873 avl_index_t where;
874
875 mutex_enter(&texsz.lock);
876
877 ASSERT_MSG(texsz.bytes + bytes >= 0, "Texture size accounting error "
878 "(incr %ld bytes)", (long)bytes);
879 texsz.bytes += bytes;
880 ta = avl_find(&texsz.allocs, &srch, &where);
881 if (ta == NULL) {
882 ta = safe_calloc(1, sizeof (*ta));
883 ta->token = token;
884 avl_create(&ta->instances, texsz_instance_compar,
885 sizeof (texsz_instance_t),
886 offsetof(texsz_instance_t, node));
887 avl_insert(&texsz.allocs, ta, where);
888 }
889 ASSERT_MSG(ta->bytes + bytes >= 0, "Texture size accounting zone "
890 "underflow error (incr %ld bytes in zone %s instance %p)",
891 (long)bytes, ta->token, instance);
892 ta->bytes += bytes;
893
894 if (instance != NULL) {
895 texsz_instance_t srch_ti = { .instance = instance };
897 avl_index_t where_ti;
898
899 ti = avl_find(&ta->instances, &srch_ti, &where_ti);
900 if (ti == NULL) {
901 ASSERT_MSG(bytes >= 0, "Texture size accounting error "
902 "(incr %ld bytes in zone %s instance %p, but "
903 "instance is empty).", (long)bytes, ta->token,
904 instance);
905 ti = safe_calloc(1, sizeof (*ti));
906 ti->instance = instance;
907 avl_insert(&ta->instances, ti, where_ti);
908 }
909 ASSERT_MSG(ti->bytes + bytes >= 0, "Texture size accounting "
910 "instance underflow error (incr %ld bytes in zone %s "
911 "instance %p)", (long)bytes, ta->token, instance);
912 ti->bytes += bytes;
913 if (filename != NULL && snprintf(ti->allocd_at,
914 sizeof (ti->allocd_at), "%s:%d", filename, line) >=
915 (int)sizeof (ti->allocd_at)) {
916 int l = strlen(filename);
917 int off = MAX(l - 26, 0);
918 snprintf(ti->allocd_at, sizeof (ti->allocd_at),
919 "%s:%d", &filename[off], line);
920 }
921 if (ti->bytes == 0) {
922 avl_remove(&ta->instances, ti);
923 free(ti);
924 }
925 }
926
927 mutex_exit(&texsz.lock);
928}
929
930static inline int64_t
931texsz_bytes(GLenum format, GLenum type, unsigned w, unsigned h)
932{
933 int channels, bpc;
934
935 switch (format) {
936 case GL_RG:
937 case GL_RG_INTEGER:
938 channels = 2;
939 break;
940 case GL_RGB:
941 case GL_BGR:
942 case GL_RGB_INTEGER:
943 case GL_BGR_INTEGER:
944 channels = 3;
945 break;
946 case GL_RGBA:
947 case GL_BGRA:
948 case GL_RGBA_INTEGER:
949 case GL_BGRA_INTEGER:
950 channels = 4;
951 break;
952 default:
953 channels = 1;
954 break;
955 }
956
957 switch (type) {
958 case GL_UNSIGNED_BYTE:
959 case GL_BYTE:
960 case GL_UNSIGNED_SHORT:
961 case GL_SHORT:
962 bpc = 2;
963 break;
964 case GL_UNSIGNED_INT:
965 case GL_INT:
966 case GL_FLOAT:
967 bpc = 4;
968 break;
969 default:
970 bpc = 1;
971 break;
972 }
973
974 return (channels * bpc * w * h);
975}
976
983API_EXPORT void
984glutils_texsz_alloc(const char *token, const void *instance,
985 const char *filename, int line, GLenum format, GLenum type,
986 unsigned w, unsigned h)
987{
988 ASSERT(texsz.inited);
989 texsz_incr(token, instance, filename, line,
990 texsz_bytes(format, type, w, h));
991}
992
999API_EXPORT void
1000glutils_texsz_free(const char *token, const void *instance,
1001 GLenum format, GLenum type, unsigned w, unsigned h)
1002{
1003 ASSERT(texsz.inited);
1004 texsz_incr(token, instance, NULL, -1, -texsz_bytes(format, type, w, h));
1005}
1006
1013API_EXPORT void
1014glutils_texsz_alloc_bytes(const char *token, const void *instance,
1015 const char *filename, int line, int64_t bytes)
1016{
1017 ASSERT(texsz.inited);
1018 texsz_incr(token, instance, filename, line, bytes);
1019}
1020
1027API_EXPORT void
1028glutils_texsz_free_bytes(const char *token, const void *instance, int64_t bytes)
1029{
1030 ASSERT(texsz.inited);
1031 texsz_incr(token, instance, NULL, -1, -bytes);
1032}
1033
1038API_EXPORT uint64_t
1040{
1041 ASSERT(texsz.inited);
1042 return (texsz.bytes);
1043}
1044
1051API_EXPORT void
1053{
1054 ASSERT(cb != NULL);
1055
1056 for (texsz_alloc_t *ta = avl_first(&texsz.allocs); ta != NULL;
1057 ta = AVL_NEXT(&texsz.allocs, ta)) {
1058 cb(ta->token, ta->bytes, userinfo);
1059 }
1060}
1061
1065API_EXPORT bool_t
1067{
1068 return (texsz.inited);
1069}
1070
1075bool_t
1077{
1078#if LIN
1079 for (int i = 0; environ[i] != NULL; i++) {
1080 if (strncmp(environ[i], "NSIGHT", 6) == 0 ||
1081 strncmp(environ[i], "NVIDIA_PROCESS", 14) == 0)
1082 return (B_TRUE);
1083 }
1084#endif /* LIN */
1085 /* NSight doesn't exist for MacOS */
1086 return (B_FALSE);
1087}
1088
1114 size_t num_pts;
1115 GLuint vao;
1116 GLuint vbo;
1117 GLuint ibo;
1118
1119 GLuint last_prog;
1120 struct {
1121 /* program uniforms locations */
1122 GLint vp;
1123 GLint semi_width;
1124 /* program attribute locations */
1125 GLint seg_here;
1126 GLint seg_start;
1127 GLint seg_end;
1128 } loc;
1129};
1130
1131typedef struct {
1132 vec3 seg_here;
1133 vec3 seg_start;
1134 vec3 seg_end;
1136
1144glutils_nl_alloc_2D(const vec2 *pts, size_t num_pts)
1145{
1146 vec3 *pts_3d = safe_calloc(num_pts, sizeof (*pts_3d));
1147 glutils_nl_t *nl;
1148
1149 ASSERT(pts != NULL);
1150 ASSERT3U((num_pts & 1), ==, 0);
1151
1152 for (size_t i = 0; i < num_pts; i++) {
1153 pts_3d[i][0] = pts[i][0];
1154 pts_3d[i][1] = pts[i][1];
1155 pts_3d[i][2] = 0.0;
1156 }
1157 nl = glutils_nl_alloc_3D((void *)pts_3d, num_pts);
1158 free(pts_3d);
1159
1160 return (nl);
1161}
1162
1179glutils_nl_alloc_3D(const vec3 *pts, size_t num_pts)
1180{
1181 size_t data_bytes;
1182 nl_vtx_data_t *data;
1183 glutils_nl_t *nl = safe_calloc(1, sizeof (*nl));
1184
1185 ASSERT(pts != NULL);
1186 ASSERT3U((num_pts & 1), ==, 0);
1187
1188 data_bytes = 2 * num_pts * sizeof (*data);
1189 data = safe_malloc(data_bytes);
1190
1191 for (size_t i = 0; i < num_pts; i += 2) {
1192 size_t off = i * 2;
1193 memcpy(&data[off + 0].seg_here, pts[i], sizeof (vec3));
1194 memcpy(&data[off + 1].seg_here, pts[i], sizeof (vec3));
1195 memcpy(&data[off + 2].seg_here, pts[i + 1], sizeof (vec3));
1196 memcpy(&data[off + 3].seg_here, pts[i + 1], sizeof (vec3));
1197
1198 for (size_t j = 0; j < 4; j++) {
1199 memcpy(&data[off + j].seg_start, pts[i],
1200 sizeof (vec3));
1201 memcpy(&data[off + j].seg_end, pts[i + 1],
1202 sizeof (vec3));
1203 }
1204 }
1205
1206 nl->num_pts = num_pts;
1207 if (GLEW_VERSION_3_0 && curthread_id != main_thread) {
1208 glGenVertexArrays(1, &nl->vao);
1209 VERIFY(nl->vao != 0);
1210 glBindVertexArray(nl->vao);
1211 }
1212 glGenBuffers(1, &nl->vbo);
1213 VERIFY(nl->vbo != 0);
1214 glBindBuffer(GL_ARRAY_BUFFER, nl->vbo);
1215 glBufferData(GL_ARRAY_BUFFER, data_bytes, data, GL_STATIC_DRAW);
1216
1217 nl->ibo = glutils_make_quads_IBO(num_pts * 2);
1218 /* glutils_make_quads_IBO binds the generated element buffer */
1219
1220 nl->loc.vp = -1;
1221 nl->loc.semi_width = -1;
1222 nl->loc.seg_here = -1;
1223 nl->loc.seg_start = -1;
1224 nl->loc.seg_end = -1;
1225
1226 if (GLEW_VERSION_3_0 && curthread_id != main_thread)
1227 glBindVertexArray(0);
1228 glBindBuffer(GL_ARRAY_BUFFER, 0);
1229 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1230
1231 free(data);
1232
1233 return (nl);
1234}
1235
1240void
1242{
1243 if (nl == NULL)
1244 return;
1245 if (nl->vao != 0)
1246 glDeleteVertexArrays(1, &nl->vao);
1247 if (nl->vbo != 0)
1248 glDeleteBuffers(1, &nl->vbo);
1249 if (nl->ibo != 0)
1250 glDeleteBuffers(1, &nl->ibo);
1251 free(nl);
1252}
1253
1254static void
1255nl_setup_vertex_attribs(glutils_nl_t *nl, GLuint prog)
1256{
1257 if (nl->vao != 0 && nl->last_prog == prog)
1258 return;
1259
1260 if (nl->vao != 0) {
1261 /*
1262 * Disable our previously-used attribute pointers.
1263 */
1264 glutils_disable_vtx_attr_ptr(nl->loc.seg_here);
1265 glutils_disable_vtx_attr_ptr(nl->loc.seg_start);
1266 glutils_disable_vtx_attr_ptr(nl->loc.seg_end);
1267 }
1268
1269 /* Uniforms */
1270 nl->loc.vp = glGetUniformLocation(prog, "_nl_vp");
1271 nl->loc.semi_width = glGetUniformLocation(prog, "_nl_semi_width");
1272
1273 /* Attributes */
1274 nl->loc.seg_here = glGetAttribLocation(prog, "_nl_seg_here");
1275 nl->loc.seg_start = glGetAttribLocation(prog, "_nl_seg_start");
1276 nl->loc.seg_end = glGetAttribLocation(prog, "_nl_seg_end");
1277
1278 glBindBuffer(GL_ARRAY_BUFFER, nl->vbo);
1279 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, nl->ibo);
1280 glutils_enable_vtx_attr_ptr(nl->loc.seg_here, 3, GL_FLOAT, GL_FALSE,
1281 sizeof (nl_vtx_data_t), offsetof(nl_vtx_data_t, seg_here));
1282 glutils_enable_vtx_attr_ptr(nl->loc.seg_start, 3, GL_FLOAT, GL_FALSE,
1283 sizeof (nl_vtx_data_t), offsetof(nl_vtx_data_t, seg_start));
1284 glutils_enable_vtx_attr_ptr(nl->loc.seg_end, 3, GL_FLOAT, GL_FALSE,
1285 sizeof (nl_vtx_data_t), offsetof(nl_vtx_data_t, seg_end));
1286
1287 nl->last_prog = prog;
1288}
1289
1332void
1333glutils_nl_draw(glutils_nl_t *nl, float width, GLuint prog)
1334{
1335 int vp[4];
1336#if APL
1337 GLenum winding;
1338#endif
1339
1340 ASSERT(nl != NULL);
1341 ASSERT3F(width, >=, 0);
1342 ASSERT(prog != 0);
1343
1344 glGetIntegerv(GL_VIEWPORT, vp);
1345
1346 if (nl->vao != 0) {
1347 glBindVertexArray(nl->vao);
1348 } else {
1349 glBindBuffer(GL_ARRAY_BUFFER, nl->vbo);
1350 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, nl->ibo);
1351 }
1352
1353 nl_setup_vertex_attribs(nl, prog);
1354
1355 if (nl->loc.vp != -1)
1356 glUniform2f(nl->loc.vp, vp[2], vp[3]);
1357 if (nl->loc.semi_width != -1)
1358 glUniform1f(nl->loc.semi_width, width / 2);
1359
1360 /*
1361 * We need to disable backface culling, because sometimes we end up
1362 * drawing vertices in the opposite winding sense when the line is
1363 * going right-to-left on the screen.
1364 */
1365#if APL
1366 /*
1367 * Mac OpenGL resets its winding rules after glEnable(GL_CULL_FACE),
1368 * so save & restore those too.
1369 */
1370 glGetIntegerv(GL_FRONT_FACE, (GLint *)&winding);
1371#endif /* APL */
1372 glDisable(GL_CULL_FACE);
1373 glDrawElements(GL_TRIANGLES, nl->num_pts * 3, GL_UNSIGNED_INT, NULL);
1374 glEnable(GL_CULL_FACE);
1375#if APL
1376 glFrontFace(winding);
1377#endif
1378
1379 if (nl->vao != 0) {
1380 glBindVertexArray(0);
1381 } else {
1382 glutils_disable_vtx_attr_ptr(nl->loc.seg_here);
1383 glutils_disable_vtx_attr_ptr(nl->loc.seg_start);
1384 glutils_disable_vtx_attr_ptr(nl->loc.seg_end);
1385 }
1386 glBindBuffer(GL_ARRAY_BUFFER, 0);
1387 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1388}
1389
1415bool_t
1416glutils_png2gltexfmt(int png_color_type, int png_bit_depth,
1417 GLint *int_fmt, GLint *fmt, GLint *type)
1418{
1419 ASSERT(int_fmt != NULL);
1420 ASSERT(fmt != NULL);
1421 ASSERT(type != NULL);
1422
1423 if (png_bit_depth != 8)
1424 return (B_FALSE);
1425 switch (png_color_type) {
1426 case PNG_COLOR_TYPE_RGB:
1427 *int_fmt = GL_RGB;
1428 *fmt = GL_RGB;
1429 *type = GL_UNSIGNED_BYTE;
1430 return (B_TRUE);
1431 case PNG_COLOR_TYPE_RGB_ALPHA:
1432 *int_fmt = GL_RGBA;
1433 *fmt = GL_RGBA;
1434 *type = GL_UNSIGNED_BYTE;
1435 return (B_TRUE);
1436 default:
1437 /* Other formats are really not representable trivially */
1438 return (B_FALSE);
1439 }
1440}
1441
1445bool_t
1447{
1449 return (in_zink_mode);
1450}
#define ASSERT_MSG(x, fmt,...)
Definition assert.h:214
#define VERIFY(x)
Definition assert.h:78
#define ASSERT3U(x, op, y)
Definition assert.h:210
#define ASSERT(x)
Definition assert.h:208
#define ASSERT3F(x, op, y)
Definition assert.h:211
#define ASSERT0(x)
Definition assert.h:213
#define VERIFY_MSG(x, fmt,...)
Definition assert.h:91
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
Definition avl.c:938
void * avl_first(const avl_tree_t *tree)
Definition avl.c:172
uintptr_t avl_index_t
Definition avl.h:119
void avl_remove(avl_tree_t *tree, void *node)
Definition avl.c:660
void avl_insert(avl_tree_t *tree, void *node, avl_index_t where)
Definition avl.c:471
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
Definition avl.c:867
#define AVL_NEXT(tree, node)
Definition avl.h:212
void avl_destroy(avl_tree_t *tree)
Definition avl.c:890
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
Definition avl.c:244
#define VECT3(x, y, z)
Definition geom.h:192
#define TEXSZ_FREE_BYTES_INSTANCE(__token_id, __instance, __bytes)
Definition glutils.h:408
void glutils_nl_draw(glutils_nl_t *nl, float width, GLuint prog)
Definition glutils.c:1333
void glutils_texsz_init(void)
Definition glutils.c:814
static void glutils_disable_vtx_attr_ptr(GLint index)
Definition glutils.h:649
uint64_t glutils_texsz_get(void)
Definition glutils.c:1039
void glutils_draw_lines(glutils_lines_t *lines, GLint prog)
Definition glutils.c:727
#define TEXSZ_ALLOC_BYTES_INSTANCE(__token_id, __instance, __filename, __line, __bytes)
Definition glutils.h:398
glutils_nl_t * glutils_nl_alloc_3D(const vec3 *pts, size_t num_pts)
Definition glutils.c:1179
void glutils_texsz_alloc(const char *token, const void *instance, const char *filename, int line, GLenum format, GLenum type, unsigned w, unsigned h)
Definition glutils.c:984
bool_t glutils_nsight_debugger_present(void)
Definition glutils.c:1076
void glutils_sys_init(void)
Definition glutils.c:108
glutils_nl_t * glutils_nl_alloc_2D(const vec2 *pts, size_t num_pts)
Definition glutils.c:1144
void glutils_update_3D_quads_impl(glutils_quads_t *quads, const char *filename, int line, const vect3_t *p, const vect2_t *t, size_t num_pts)
Definition glutils.c:270
#define glutils_init_2D_quads(__quads, __p, __t, __num_pts)
Definition glutils.h:144
void glutils_init_3D_quads_impl(glutils_quads_t *quads, const char *filename, int line, const vect3_t *p, const vect2_t *t, size_t num_pts)
Definition glutils.c:239
bool_t glutils_texsz_inited(void)
Definition glutils.c:1066
#define TEXSZ_MK_TOKEN(name)
Definition glutils.h:296
void glutils_init_2D_quads_impl(glutils_quads_t *quads, const char *filename, int line, const vect2_t *p, const vect2_t *t, size_t num_pts)
Definition glutils.c:209
void glutils_destroy_lines(glutils_lines_t *lines)
Definition glutils.c:742
void glutils_cache_destroy(glutils_cache_t *cache)
Definition glutils.c:484
#define glutils_init_3D_lines(__lines, __p, __num_pts)
Definition glutils.h:209
void glutils_init_3D_lines_impl(glutils_lines_t *lines, const char *filename, int line, const vect3_t *p, size_t num_pts)
Definition glutils.c:666
void glutils_disable_all_vtx_attrs(void)
Definition glutils.c:158
void glutils_destroy_quads(glutils_quads_t *quads)
Definition glutils.c:313
glutils_quads_t * glutils_cache_get_2D_quads(glutils_cache_t *cache, const vect2_t *p, const vect2_t *t, size_t num_pts)
Definition glutils.c:613
static bool_t glutils_quads_inited(const glutils_quads_t *quads)
Definition glutils.h:133
glutils_lines_t * glutils_cache_get_3D_lines(glutils_cache_t *cache, const vect3_t *p, size_t num_pts)
Definition glutils.c:652
void(* glutils_texsz_enum_cb_t)(const char *token, int64_t bytes, void *userinfo)
Definition glutils.h:115
void glutils_update_2D_quads_impl(glutils_quads_t *quads, const char *filename, int line, const vect2_t *p, const vect2_t *t, size_t num_pts)
Definition glutils.c:225
#define IF_TEXSZ(__xxx)
Definition glutils.h:434
glutils_quads_t * glutils_cache_get_3D_quads(glutils_cache_t *cache, const vect3_t *p, const vect2_t *t, size_t num_pts)
Definition glutils.c:638
GLuint glutils_make_quads_IBO(size_t num_vtx)
Definition glutils.c:176
#define glutils_init_3D_quads(__quads, __p, __t, __num_pts)
Definition glutils.h:170
static void glutils_enable_vtx_attr_ptr(GLint index, GLint size, GLenum type, GLboolean normalized, size_t stride, size_t offset)
Definition glutils.h:631
void glutils_draw_quads(glutils_quads_t *quads, GLint prog)
Definition glutils.c:387
void glutils_disable_all_client_state(void)
Definition glutils.c:128
bool_t glutils_png2gltexfmt(int png_color_type, int png_bit_depth, GLint *int_fmt, GLint *fmt, GLint *type)
Definition glutils.c:1416
void glutils_texsz_fini(void)
Definition glutils.c:833
void glutils_texsz_enum(glutils_texsz_enum_cb_t cb, void *userinfo)
Definition glutils.c:1052
void glutils_texsz_alloc_bytes(const char *token, const void *instance, const char *filename, int line, int64_t bytes)
Definition glutils.c:1014
glutils_cache_t * glutils_cache_new(size_t cap_bytes)
Definition glutils.c:440
void glutils_texsz_free_bytes(const char *token, const void *instance, int64_t bytes)
Definition glutils.c:1028
void glutils_texsz_free(const char *token, const void *instance, GLenum format, GLenum type, unsigned w, unsigned h)
Definition glutils.c:1000
void glutils_vp2pvm(GLfloat pvm[16])
Definition glutils.c:762
void glutils_nl_free(glutils_nl_t *nl)
Definition glutils.c:1241
bool_t glutils_in_zink_mode(void)
Definition glutils.c:1446
void list_destroy(list_t *)
Definition list.c:136
void list_create(list_t *, size_t, size_t)
Definition list.c:113
void * list_remove_tail(list_t *)
Definition list.c:277
void list_insert_head(list_t *, void *)
Definition list.c:199
void list_remove(list_t *, void *)
Definition list.c:226
void * list_remove_head(list_t *)
Definition list.c:251
#define logMsg(...)
Definition log.h:112
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56
Definition glutils.c:80
Definition geom.h:89
static void mutex_destroy(mutex_t *mtx)
Definition thread.h:499
static void mutex_enter(mutex_t *mtx)
Definition thread.h:530
#define curthread_id
Definition thread.h:467
static void mutex_exit(mutex_t *mtx)
Definition thread.h:556
DWORD thread_id_t
Definition thread.h:401
static void mutex_init(mutex_t *mtx)
Definition thread.h:488