浏览代码

Merge branch 'master' into windows-llvm-13.0.0

gingerBill 2 年之前
父节点
当前提交
2a3c6c42bd
共有 100 个文件被更改,包括 16463 次插入3601 次删除
  1. 13 9
      .github/workflows/ci.yml
  2. 1 1
      README.md
  3. 12 0
      build_odin.sh
  4. 7 0
      build_vendor.bat
  5. 1 1
      core/compress/gzip/example.odin
  6. 1 1
      core/compress/zlib/example.odin
  7. 17 17
      core/container/small_array/small_array.odin
  8. 11 14
      core/encoding/json/marshal.odin
  9. 3 6
      core/encoding/json/unmarshal.odin
  10. 34 30
      core/fmt/fmt.odin
  11. 4 2
      core/hash/hash.odin
  12. 1 1
      core/image/png/example.odin
  13. 4 1
      core/intrinsics/intrinsics.odin
  14. 7 0
      core/log/log_allocator.odin
  15. 0 2
      core/math/big/internal.odin
  16. 1 1
      core/math/big/tune.odin
  17. 2 0
      core/math/linalg/doc.odin
  18. 212 10
      core/math/math.odin
  19. 25 17
      core/mem/alloc.odin
  20. 60 34
      core/mem/allocators.odin
  21. 2 6
      core/mem/raw.odin
  22. 2 2
      core/mem/virtual/arena.odin
  23. 4 2
      core/os/file_windows.odin
  24. 5 5
      core/os/os.odin
  25. 12 3
      core/os/os_darwin.odin
  26. 2 2
      core/os/os_essence.odin
  27. 9 3
      core/os/os_freebsd.odin
  28. 9 3
      core/os/os_linux.odin
  29. 9 3
      core/os/os_openbsd.odin
  30. 33 4
      core/os/os_wasi.odin
  31. 2 2
      core/os/os_windows.odin
  32. 76 0
      core/reflect/iterator.odin
  33. 0 42
      core/reflect/map.odin
  34. 2 2
      core/reflect/reflect.odin
  35. 29 7
      core/runtime/core.odin
  36. 18 92
      core/runtime/core_builtin.odin
  37. 1 1
      core/runtime/default_allocators_nil.odin
  38. 3 3
      core/runtime/default_allocators_windows.odin
  39. 2 2
      core/runtime/default_temporary_allocator.odin
  40. 681 314
      core/runtime/dynamic_map_internal.odin
  41. 7 0
      core/runtime/internal.odin
  42. 6 6
      core/runtime/os_specific_windows.odin
  43. 22 26
      core/slice/map.odin
  44. 83 0
      core/strconv/decimal/decimal.odin
  45. 86 0
      core/strconv/generic_float.odin
  46. 252 98
      core/strconv/strconv.odin
  47. 44 0
      core/strings/builder.odin
  48. 1 1
      core/sys/info/doc.odin
  49. 1 0
      core/sys/windows/shell32.odin
  50. 52 0
      core/sys/windows/types.odin
  51. 413 0
      core/text/edit/text_edit.odin
  52. 1 1
      core/text/i18n/doc.odin
  53. 2 0
      core/time/time_wasi.odin
  54. 2 0
      examples/all/all_vendor.odin
  55. 14 7
      src/build_settings.cpp
  56. 97 0
      src/check_builtin.cpp
  57. 88 35
      src/check_expr.cpp
  58. 35 74
      src/check_type.cpp
  59. 31 10
      src/checker.cpp
  60. 10 2
      src/checker_builtin_procs.hpp
  61. 7 0
      src/exact_value.cpp
  62. 26 1
      src/llvm_abi.cpp
  63. 434 84
      src/llvm_backend.cpp
  64. 12 8
      src/llvm_backend.hpp
  65. 29 3
      src/llvm_backend_const.cpp
  66. 116 1
      src/llvm_backend_debug.cpp
  67. 32 37
      src/llvm_backend_expr.cpp
  68. 38 42
      src/llvm_backend_general.cpp
  69. 67 17
      src/llvm_backend_proc.cpp
  70. 124 13
      src/llvm_backend_stmt.cpp
  71. 5 9
      src/llvm_backend_type.cpp
  72. 71 51
      src/llvm_backend_utility.cpp
  73. 43 48
      src/main.cpp
  74. 1 1
      src/microsoft_craziness.h
  75. 17 4
      src/parser.cpp
  76. 59 22
      src/types.cpp
  77. 0 1
      tests/core/math/big/test.odin
  78. 6 0
      tests/internal/Makefile
  79. 4 0
      tests/internal/build.bat
  80. 382 0
      tests/internal/test_map.odin
  81. 958 542
      vendor/OpenGL/constants.odin
  82. 1831 1404
      vendor/OpenGL/enums.odin
  83. 11 1
      vendor/README.md
  84. 7 0
      vendor/cgltf/LICENSE
  85. 146 0
      vendor/cgltf/README.md
  86. 689 0
      vendor/cgltf/cgltf.odin
  87. 8 0
      vendor/cgltf/src/build.bat
  88. 15 0
      vendor/cgltf/src/cgltf.c
  89. 6818 0
      vendor/cgltf/src/cgltf.h
  90. 1488 0
      vendor/cgltf/src/cgltf_write.h
  91. 6 3
      vendor/commonmark/cmark.odin
  92. 1 1
      vendor/commonmark/doc.odin
  93. 12 11
      vendor/directx/d3d11/d3d11.odin
  94. 339 311
      vendor/directx/d3d12/d3d12.odin
  95. 53 46
      vendor/directx/d3d_compiler/d3d_compiler.odin
  96. 40 31
      vendor/directx/dxgi/dxgi.odin
  97. 1 1
      vendor/raylib/raylib.odin
  98. 1 1
      vendor/stb/rect_pack/stb_rect_pack.odin
  99. 1 1
      vendor/stb/truetype/stb_truetype.odin
  100. 1 1
      vendor/stb/vorbis/stb_vorbis.odin

+ 13 - 9
.github/workflows/ci.yml

@@ -38,10 +38,11 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
-      - name: Odin issues tests
+      - name: Odin internals tests
         run: |
-          cd tests/issues
-          ./run.sh
+          cd tests/internal
+          make
+        timeout-minutes: 10
       - name: Odin check examples/all for Linux i386
         run: ./odin check examples/all -vet -strict-style -target:linux_i386
         timeout-minutes: 10
@@ -91,10 +92,10 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
-      - name: Odin issues tests
+      - name: Odin internals tests
         run: |
-          cd tests/issues
-          ./run.sh
+          cd tests/internal
+          make
         timeout-minutes: 10
       - name: Odin check examples/all for Darwin arm64
         run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
@@ -155,10 +156,13 @@ jobs:
           cd tests\vendor
           call build.bat
         timeout-minutes: 10
-      - name: Odin issues tests
+      - name: Odin internals tests
+        shell: cmd
         run: |
-          cd tests/issues
-          ./run.bat
+          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+          cd tests\internal
+          call build.bat
+        timeout-minutes: 10
       - name: core:math/big tests
         shell: cmd
         run: |

+ 1 - 1
README.md

@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
 
 #### [Odin Discord](https://discord.gg/sVBPHEv)
 
-Get live support and talk with other odiners on the Odin Discord.
+Get live support and talk with other Odin programmers on the Odin Discord.
 
 ### Articles
 

+ 12 - 0
build_odin.sh

@@ -44,6 +44,12 @@ config_darwin() {
 		fi
 	fi
 
+	MAX_LLVM_VERSION=("14.999.999")
+	if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
+		echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
+		panic "Requirement: llvm-config must be base version smaller than 15"
+	fi
+
 	LDFLAGS="$LDFLAGS -liconv -ldl"
 	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
 	LDFLAGS="$LDFLAGS -lLLVM-C"
@@ -97,6 +103,12 @@ config_linux() {
 		panic "Requirement: llvm-config must be base version greater than 11"
 	fi
 
+	MAX_LLVM_VERSION=("14.999.999")
+	if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
+		echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
+		panic "Requirement: llvm-config must be base version smaller than 15"
+	fi
+
 	LDFLAGS="$LDFLAGS -ldl"
 	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
 	LDFLAGS="$LDFLAGS $($LLVM_CONFIG  --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"

+ 7 - 0
build_vendor.bat

@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
 		call build.bat
 	popd
 )
+
+
+if not exist "vendor\cgltf\lib\*.lib" (
+	pushd vendor\cgltf\src
+		call build.bat
+	popd
+)

+ 1 - 1
core/compress/gzip/example.odin

@@ -1,4 +1,4 @@
-//+ignore
+//+build ignore
 package gzip
 
 /*

+ 1 - 1
core/compress/zlib/example.odin

@@ -1,4 +1,4 @@
-//+ignore
+//+build ignore
 package zlib
 
 /*

+ 17 - 17
core/container/small_array/small_array.odin

@@ -8,40 +8,40 @@ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
 }
 
 
-len :: proc(a: $A/Small_Array) -> int {
+len :: proc "contextless" (a: $A/Small_Array) -> int {
 	return a.len
 }
 
-cap :: proc(a: $A/Small_Array) -> int {
+cap :: proc "contextless" (a: $A/Small_Array) -> int {
 	return builtin.len(a.data)
 }
 
-space :: proc(a: $A/Small_Array) -> int {
+space :: proc "contextless" (a: $A/Small_Array) -> int {
 	return builtin.len(a.data) - a.len
 }
 
-slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
+slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
 	return a.data[:a.len]
 }
 
 
-get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
+get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
 	return a.data[index]
 }
-get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
+get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
 	return &a.data[index]
 }
 
-set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
+set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
 	a.data[index] = item
 }
 
-resize :: proc(a: ^$A/Small_Array, length: int) {
+resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
 	a.len = min(length, builtin.len(a.data))
 }
 
 
-push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
+push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
 	if a.len < cap(a^) {
 		a.data[a.len] = item
 		a.len += 1
@@ -50,7 +50,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
 	return false
 }
 
-push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
+push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
 	if a.len < cap(a^) {
 		a.len += 1
 		data := slice(a)
@@ -61,14 +61,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
 	return false
 }
 
-pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
+pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
 	assert(condition=(N > 0 && a.len > 0), loc=loc)
 	item := a.data[a.len-1]
 	a.len -= 1
 	return item
 }
 
-pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
+pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
 	assert(condition=(N > 0 && a.len > 0), loc=loc)
 	item := a.data[0]
 	s := slice(a)
@@ -77,7 +77,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
 	return item
 }
 
-pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
+pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
 	if N > 0 && a.len > 0 {
 		item = a.data[a.len-1]
 		a.len -= 1
@@ -86,7 +86,7 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
 	return
 }
 
-pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
+pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
 	if N > 0 && a.len > 0 {
 		item = a.data[0]
 		s := slice(a)
@@ -97,16 +97,16 @@ pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
 	return
 }
 
-consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
+consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
 	assert(condition=a.len >= count, loc=loc)
 	a.len -= count
 }
 
-clear :: proc(a: ^$A/Small_Array($N, $T)) {
+clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
 	resize(a, 0)
 }
 
-push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
+push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
 	n := copy(a.data[a.len:], items[:])
 	a.len += n
 }

+ 11 - 14
core/encoding/json/marshal.odin

@@ -257,21 +257,18 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 		opt_write_start(w, opt, '{') or_return
 
 		if m != nil {
-			if info.generated_struct == nil {
+			if info.map_info == nil {
 				return .Unsupported_Type
 			}
-			entries    := &m.entries
-			gs         := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
-			ed         := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
-			entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
-			entry_size := ed.elem_size
-
-			for i in 0..<entries.len {
-				opt_write_iteration(w, opt, i) or_return
+			map_cap := uintptr(runtime.map_cap(m^))
+			ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
+			for bucket_index in 0..<map_cap {
+				if !runtime.map_hash_is_valid(hs[bucket_index]) {
+					continue
+				}
 
-				data := uintptr(entries.data) + uintptr(i*entry_size)
-				key   := rawptr(data + entry_type.offsets[2])
-				value := rawptr(data + entry_type.offsets[3])
+				key   := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
+				value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
 
 				// check for string type
 				{
@@ -281,13 +278,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 					name: string
 
 					#partial switch info in ti.variant {
-					case runtime.Type_Info_String: 
+					case runtime.Type_Info_String:
 						switch s in a {
 							case string: name = s
 							case cstring: name = string(s)
 						}
 						opt_write_key(w, opt, name) or_return
-	
+
 					case: return .Unsupported_Type
 					}
 				}

+ 3 - 6
core/encoding/json/unmarshal.odin

@@ -399,12 +399,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 			return UNSUPPORTED_TYPE
 		}
 		raw_map := (^mem.Raw_Map)(v.data)
-		if raw_map.entries.allocator.procedure == nil {
-			raw_map.entries.allocator = p.allocator
+		if raw_map.allocator.procedure == nil {
+			raw_map.allocator = p.allocator
 		}
 		
-		header := runtime.__get_map_header_table_runtime(t)
-		
 		elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
 		defer delete(elem_backing, p.allocator)
 		
@@ -421,7 +419,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 				return err
 			}
 
-			key_hash := runtime.default_hasher_string(&key, 0)
 			key_ptr := rawptr(&key)
 
 			key_cstr: cstring
@@ -430,7 +427,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
 				key_ptr = &key_cstr
 			}
 			
-			set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
+			set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
 			if set_ptr == nil {
 				delete(key, p.allocator)
 			} 

+ 34 - 30
core/fmt/fmt.odin

@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
 	p("Panic", message, loc)
 }
 
+// formatted printing for cstrings
+caprintf :: proc(format: string, args: ..any) -> cstring {
+	str: strings.Builder
+	strings.builder_init(&str)
+	sbprintf(&str, format, ..args)
+	strings.write_byte(&str, 0)
+	s := strings.to_string(str)
+	return cstring(raw_data(s))
+}
 
+// c string with temp allocator
+ctprintf :: proc(format: string, args: ..any) -> cstring {
+	str: strings.Builder
+	strings.builder_init(&str, context.temp_allocator)
+	sbprintf(&str, format, ..args)
+	strings.write_byte(&str, 0)
+	s := strings.to_string(str)
+	return cstring(raw_data(s))
+}
 
 // sbprint formats using the default print settings and writes to buf
 sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
@@ -240,7 +258,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
 	was_prev_index := false
 
 	loop: for i := 0; i < end; /**/ {
-		fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
+		fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
 
 		prev_i := i
 		for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
@@ -975,7 +993,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
 				}
 			}
 			else {
-				io.write_string(fi.writer, s[:fi.width], &fi.n)
+				io.write_string(fi.writer, s, &fi.n)
 			}
 		}
 		else
@@ -2051,41 +2069,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 
 		m := (^mem.Raw_Map)(v.data)
 		if m != nil {
-			if info.generated_struct == nil {
+			if info.map_info == nil {
 				return
 			}
-			entries    := &m.entries
-			gs         := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
-			ed         := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
-			entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
-			entry_size := ed.elem_size
-			/*
-				NOTE: The layout of a `map` is as follows:
-
-					map[Key]Value
-
-				## Internal Layout
-				struct {
-					hashes: []int,
-					entries: [dynamic]struct{
-						hash:  uintptr,
-						next:  int,
-						key:   Key,
-						value: Value,
-					},
+			map_cap := uintptr(runtime.map_cap(m^))
+			ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
+			j := 0
+			for bucket_index in 0..<map_cap {
+				if !runtime.map_hash_is_valid(hs[bucket_index]) {
+					continue
 				}
-			*/
-			for i in 0..<entries.len {
-				if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
 
-				data := uintptr(entries.data) + uintptr(i*entry_size)
+				if j > 0 {
+					io.write_string(fi.writer, ", ", &fi.n)
+				}
+				j += 1
 
-				key := data + entry_type.offsets[2] // key: Key
-				fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
+				key   := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
+				value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
 
+				fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
 				io.write_string(fi.writer, "=", &fi.n)
-
-				value := data + entry_type.offsets[3] // value: Value
 				fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
 			}
 		}

+ 4 - 2
core/hash/hash.odin

@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
 	return
 }
 
+// If you have a choice, prefer fnv32a
 @(optimization_mode="speed")
-fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
+fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
 	h: u32 = seed
 	for b in data {
 		h = (h * 0x01000193) ~ u32(b)
@@ -81,8 +82,9 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
 	return h
 }
 
+// If you have a choice, prefer fnv64a
 @(optimization_mode="speed")
-fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
+fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
 	h: u64 = seed
 	for b in data {
 		h = (h * 0x100000001b3) ~ u64(b)

+ 1 - 1
core/image/png/example.odin

@@ -8,7 +8,7 @@
 
 	An example of how to use `load`.
 */
-//+ignore
+//+build ignore
 package png
 
 import "core:image"

+ 4 - 1
core/intrinsics/intrinsics.odin

@@ -1,5 +1,5 @@
 // This is purely for documentation
-//+ignore
+//+build ignore
 package intrinsics
 
 // Package-Related
@@ -188,6 +188,9 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
 type_equal_proc  :: proc($T: typeid) -> (equal:  proc "contextless" (rawptr, rawptr) -> bool)                 where type_is_comparable(T) ---
 type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
 
+type_map_info      :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
+type_map_cell_info :: proc($T: typeid)           -> ^runtime.Map_Cell_Info ---
+
 type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
 
 constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---

+ 7 - 0
core/log/log_allocator.odin

@@ -43,6 +43,13 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
 				args = {la.prefix, padding, size, alignment},
 				location = location,
 			)
+		case .Alloc_Non_Zeroed:
+			logf(
+				level=la.level,
+				fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
+				args = {la.prefix, padding, size, alignment},
+				location = location,
+			)
 		case .Free:
 			if old_size != 0 {
 				logf(

+ 0 - 2
core/math/big/internal.odin

@@ -25,8 +25,6 @@
 	TODO: Handle +/- Infinity and NaN.
 */
 
-
-//+ignore
 package math_big
 
 import "core:mem"

+ 1 - 1
core/math/big/tune.odin

@@ -7,7 +7,7 @@
 	The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
 */
 
-//+ignore
+//+build ignore
 package math_big
 
 import "core:time"

+ 2 - 0
core/math/linalg/doc.odin

@@ -0,0 +1,2 @@
+// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
+package linalg

+ 212 - 10
core/math/math.odin

@@ -1379,21 +1379,223 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
 }
 
 atan2 :: proc{
-	atan2_f16, atan2_f16le, atan2_f16be,
-	atan2_f32, atan2_f32le, atan2_f32be,
-	atan2_f64, atan2_f64le, atan2_f64be,
+	atan2_f64, atan2_f32, atan2_f16,
+	atan2_f64le, atan2_f64be,
+	atan2_f32le, atan2_f32be,
+	atan2_f16le, atan2_f16be,
 }
 
 atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return atan2(x, 1)
+	return atan2(1, x)
+}
+
+
+
+asin_f64 :: proc "contextless" (x: f64) -> f64 {
+	/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
+	/*
+	 * ====================================================
+	 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+	 *
+	 * Developed at SunSoft, a Sun Microsystems, Inc. business.
+	 * Permission to use, copy, modify, and distribute this
+	 * software is freely granted, provided that this notice
+	 * is preserved.
+	 * ====================================================
+	 */
+
+	pio2_hi :: 0h3FF921FB54442D18
+	pio2_lo :: 0h3C91A62633145C07
+	pS0     :: 0h3FC5555555555555
+	pS1     :: 0hBFD4D61203EB6F7D
+	pS2     :: 0h3FC9C1550E884455
+	pS3     :: 0hBFA48228B5688F3B
+	pS4     :: 0h3F49EFE07501B288
+	pS5     :: 0h3F023DE10DFDF709
+	qS1     :: 0hC0033A271C8A2D4B
+	qS2     :: 0h40002AE59C598AC8
+	qS3     :: 0hBFE6066C1B8D0159
+	qS4     :: 0h3FB3B8C5B12E9282
+
+	R :: #force_inline proc "contextless" (z: f64) -> f64 {
+		p, q: f64
+		p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
+		q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
+		return p/q
+	}
+
+	x := x
+	z, r, s: f64
+	dwords := transmute([2]u32)x
+	hx := dwords[1]
+	ix := hx & 0x7fffffff
+	/* |x| >= 1 or nan */
+	if ix >= 0x3ff00000 {
+		lx := dwords[0]
+		if (ix-0x3ff00000 | lx) == 0 {
+			/* asin(1) = +-pi/2 with inexact */
+			return x*pio2_hi + 1e-120
+		}
+		return 0/(x-x)
+	}
+	/* |x| < 0.5 */
+	if ix < 0x3fe00000 {
+		/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
+		if ix < 0x3e500000 && ix >= 0x00100000 {
+			return x
+		}
+		return x + x*R(x*x)
+	}
+	/* 1 > |x| >= 0.5 */
+	z = (1 - abs(x))*0.5
+	s = sqrt(z)
+	r = R(z)
+	if ix >= 0x3fef3333 {  /* if |x| > 0.975 */
+		x = pio2_hi-(2*(s+s*r)-pio2_lo)
+	} else {
+		f, c: f64
+		/* f+c = sqrt(z) */
+		f = s
+		(^u64)(&f)^ &= 0xffffffff_00000000
+		c = (z-f*f)/(s+f)
+		x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f))
+	}
+	return -x if hx >> 31 != 0 else x
+}
+asin_f64le :: proc "contextless" (x: f64le) -> f64le {
+	return f64le(asin_f64(f64(x)))
+}
+asin_f64be :: proc "contextless" (x: f64be) -> f64be {
+	return f64be(asin_f64(f64(x)))
+}
+asin_f32 :: proc "contextless" (x: f32) -> f32 {
+	return f32(asin_f64(f64(x)))
+}
+asin_f32le :: proc "contextless" (x: f32le) -> f32le {
+	return f32le(asin_f64(f64(x)))
+}
+asin_f32be :: proc "contextless" (x: f32be) -> f32be {
+	return f32be(asin_f64(f64(x)))
+}
+asin_f16 :: proc "contextless" (x: f16) -> f16 {
+	return f16(asin_f64(f64(x)))
+}
+asin_f16le :: proc "contextless" (x: f16le) -> f16le {
+	return f16le(asin_f64(f64(x)))
+}
+asin_f16be :: proc "contextless" (x: f16be) -> f16be {
+	return f16be(asin_f64(f64(x)))
+}
+asin :: proc{
+	asin_f64, asin_f32, asin_f16,
+	asin_f64le, asin_f64be,
+	asin_f32le, asin_f32be,
+	asin_f16le, asin_f16be,
+}
+
+
+acos_f64 :: proc "contextless" (x: f64) -> f64 {
+	/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
+	/*
+	 * ====================================================
+	 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+	 *
+	 * Developed at SunSoft, a Sun Microsystems, Inc. business.
+	 * Permission to use, copy, modify, and distribute this
+	 * software is freely granted, provided that this notice
+	 * is preserved.
+	 * ====================================================
+	 */
+
+	pio2_hi :: 0h3FF921FB54442D18
+	pio2_lo :: 0h3C91A62633145C07
+	pS0     :: 0h3FC5555555555555
+	pS1     :: 0hBFD4D61203EB6F7D
+	pS2     :: 0h3FC9C1550E884455
+	pS3     :: 0hBFA48228B5688F3B
+	pS4     :: 0h3F49EFE07501B288
+	pS5     :: 0h3F023DE10DFDF709
+	qS1     :: 0hC0033A271C8A2D4B
+	qS2     :: 0h40002AE59C598AC8
+	qS3     :: 0hBFE6066C1B8D0159
+	qS4     :: 0h3FB3B8C5B12E9282
+
+	R :: #force_inline proc "contextless" (z: f64) -> f64 {
+		p, q: f64
+		p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
+		q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
+		return p/q
+	}
+
+	z, w, s, c, df: f64
+	dwords := transmute([2]u32)x
+	hx := dwords[1]
+	ix := hx & 0x7fffffff
+	/* |x| >= 1 or nan */
+	if ix >= 0x3ff00000 {
+		lx := dwords[0]
+
+		if (ix-0x3ff00000 | lx) == 0 {
+			/* acos(1)=0, acos(-1)=pi */
+			if hx >> 31 != 0 {
+				return 2*pio2_hi + 1e-120
+			}
+			return 0
+		}
+		return 0/(x-x)
+	}
+	/* |x| < 0.5 */
+	if ix < 0x3fe00000 {
+		if ix <= 0x3c600000 { /* |x| < 2**-57 */
+			return pio2_hi + 1e-120
+		}
+		return pio2_hi - (x - (pio2_lo-x*R(x*x)))
+	}
+	/* x < -0.5 */
+	if hx >> 31 != 0 {
+		z = (1.0+x)*0.5
+		s = sqrt(z)
+		w = R(z)*s-pio2_lo
+		return 2*(pio2_hi - (s+w))
+	}
+	/* x > 0.5 */
+	z = (1.0-x)*0.5
+	s = sqrt(z)
+	df = s
+	(^u64)(&df)^ &= 0xffffffff_00000000
+	c = (z-df*df)/(s+df)
+	w = R(z)*s+c
+	return 2*(df+w)
 }
-
-asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return atan2(x, sqrt(1 - x*x))
+acos_f64le :: proc "contextless" (x: f64le) -> f64le {
+	return f64le(acos_f64(f64(x)))
 }
-
-acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return 2 * atan2(sqrt(1 - x), sqrt(1 + x))
+acos_f64be :: proc "contextless" (x: f64be) -> f64be {
+	return f64be(acos_f64(f64(x)))
+}
+acos_f32 :: proc "contextless" (x: f32) -> f32 {
+	return f32(acos_f64(f64(x)))
+}
+acos_f32le :: proc "contextless" (x: f32le) -> f32le {
+	return f32le(acos_f64(f64(x)))
+}
+acos_f32be :: proc "contextless" (x: f32be) -> f32be {
+	return f32be(acos_f64(f64(x)))
+}
+acos_f16 :: proc "contextless" (x: f16) -> f16 {
+	return f16(acos_f64(f64(x)))
+}
+acos_f16le :: proc "contextless" (x: f16le) -> f16le {
+	return f16le(acos_f64(f64(x)))
+}
+acos_f16be :: proc "contextless" (x: f16be) -> f16be {
+	return f16be(acos_f64(f64(x)))
+}
+acos :: proc{
+	acos_f64, acos_f32, acos_f16,
+	acos_f64le, acos_f64be,
+	acos_f32le, acos_f32be,
+	acos_f16le, acos_f16be,
 }
 
 sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {

+ 25 - 17
core/mem/alloc.odin

@@ -69,10 +69,22 @@ alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator :=
 	return runtime.mem_alloc(size, alignment, allocator, loc)
 }
 
+alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
+}
+
 free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
 	return runtime.mem_free(ptr, allocator, loc)
 }
 
+free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	if ptr == nil || allocator.procedure == nil {
+		return nil
+	}
+	_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
+	return err
+}
+
 free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
 	return runtime.mem_free_bytes(bytes, allocator, loc)
 }
@@ -108,22 +120,20 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
 
 
 
-delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
-	free(raw_data(str), allocator, loc)
+delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	return free_with_size(raw_data(str), len(str), allocator, loc)
 }
-delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
-	free((^byte)(str), allocator, loc)
+delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	return free((^byte)(str), allocator, loc)
 }
-delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
-	free(raw_data(array), array.allocator, loc)
+delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
+	return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
 }
-delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
-	free(raw_data(array), allocator, loc)
+delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
+	return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
 }
-delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
-	raw := transmute(Raw_Map)m
-	delete_slice(raw.hashes, raw.entries.allocator, loc)
-	free(raw.entries.data, raw.entries.allocator, loc)
+delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
+	return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
 }
 
 
@@ -154,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
 	return nil, .Out_Of_Memory
 }
 
-DEFAULT_RESERVE_CAPACITY :: 16
-
 make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
 	runtime.make_slice_error_loc(loc, len)
 	data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
@@ -169,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
 	return make_aligned(T, len, align_of(E), allocator, loc)
 }
 make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
-	return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
+	return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
 }
 make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
 	return make_dynamic_array_len_cap(T, len, len, allocator, loc)
@@ -184,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
 	array = transmute(T)s
 	return
 }
-make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
+make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
 	runtime.make_map_expr_error_loc(loc, cap)
 	context.allocator = allocator
 
 	m: T
-	reserve_map(&m, cap)
+	reserve_map(&m, cap, loc)
 	return m
 }
 make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {

+ 60 - 34
core/mem/allocators.odin

@@ -59,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	arena := cast(^Arena)allocator_data
 
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		#no_bounds_check end := &arena.data[arena.offset]
 
 		ptr := align_forward(end, uintptr(alignment))
@@ -72,7 +72,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		arena.offset += total_size
 		arena.peak_used = max(arena.peak_used, arena.offset)
-		zero(ptr, size)
+		if mode != .Alloc_Non_Zeroed {
+			zero(ptr, size)
+		}
 		return byte_slice(ptr, size), nil
 
 	case .Free:
@@ -87,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
 		}
 		return nil, nil
 
@@ -162,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	size := size
 
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		size = align_forward_int(size, alignment)
 
 		switch {
@@ -170,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			start := uintptr(raw_data(s.data))
 			ptr := start + uintptr(s.curr_offset)
 			ptr = align_forward_uintptr(ptr, uintptr(alignment))
-			zero(rawptr(ptr), size)
+			if mode != .Alloc_Non_Zeroed {
+				zero(rawptr(ptr), size)
+			}
 
 			s.prev_allocation = rawptr(ptr)
 			offset := int(ptr - start)
@@ -180,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		case size <= len(s.data):
 			start := uintptr(raw_data(s.data))
 			ptr := align_forward_uintptr(start, uintptr(alignment))
-			zero(rawptr(ptr), size)
+			if mode != .Alloc_Non_Zeroed {
+				zero(rawptr(ptr), size)
+			}
 
 			s.prev_allocation = rawptr(ptr)
 			offset := int(ptr - start)
@@ -211,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		return ptr, err
 
 	case .Free:
+		if old_memory == nil {
+			return nil, nil
+		}
 		start := uintptr(raw_data(s.data))
 		end := start + uintptr(len(s.data))
 		old_ptr := uintptr(old_memory)
@@ -266,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
 		}
 		return nil, nil
 
@@ -333,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		return nil, .Invalid_Argument
 	}
 
-	raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
+	raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
 		curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset)
 		padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
 		if s.curr_offset + padding + size > len(s.data) {
@@ -351,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		s.peak_used = max(s.peak_used, s.curr_offset)
 
-		zero(rawptr(next_addr), size)
+		if zero_memory {
+			zero(rawptr(next_addr), size)
+		}
 		return byte_slice(rawptr(next_addr), size), nil
 	}
 
 	switch mode {
-	case .Alloc:
-		return raw_alloc(s, size, alignment)
+	case .Alloc, .Alloc_Non_Zeroed:
+		return raw_alloc(s, size, alignment, mode == .Alloc)
 	case .Free:
 		if old_memory == nil {
 			return nil, nil
@@ -392,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 	case .Resize:
 		if old_memory == nil {
-			return raw_alloc(s, size, alignment)
+			return raw_alloc(s, size, alignment, true)
 		}
 		if size == 0 {
 			return nil, nil
@@ -418,7 +429,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
 
 		if old_offset != header.prev_offset {
-			data, err := raw_alloc(s, size, alignment)
+			data, err := raw_alloc(s, size, alignment, true)
 			if err == nil {
 				runtime.copy(data, byte_slice(old_memory, old_size))
 			}
@@ -439,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
 		}
 		return nil, nil
 	case .Query_Info:
@@ -497,7 +508,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 	align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
 
-	raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
+	raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
 		curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset)
 		padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
 		if s.offset + padding + size > len(s.data) {
@@ -513,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 		s.peak_used = max(s.peak_used, s.offset)
 
-		zero(rawptr(next_addr), size)
+		if zero_memory {
+			zero(rawptr(next_addr), size)
+		}
 		return byte_slice(rawptr(next_addr), size), nil
 	}
 
 	switch mode {
-	case .Alloc:
-		return raw_alloc(s, size, align)
+	case .Alloc, .Alloc_Non_Zeroed:
+		return raw_alloc(s, size, align, mode == .Alloc)
 	case .Free:
 		if old_memory == nil {
 			return nil, nil
@@ -548,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 	case .Resize:
 		if old_memory == nil {
-			return raw_alloc(s, size, align)
+			return raw_alloc(s, size, align, true)
 		}
 		if size == 0 {
 			return nil, nil
@@ -571,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			return byte_slice(old_memory, size), nil
 		}
 
-		data, err := raw_alloc(s, size, align)
+		data, err := raw_alloc(s, size, align, true)
 		if err == nil {
 			runtime.copy(data, byte_slice(old_memory, old_size))
 		}
@@ -580,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
 		}
 		return nil, nil
 
@@ -623,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 	pool := (^Dynamic_Pool)(allocator_data)
 
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		return dynamic_pool_alloc_bytes(pool, size)
 	case .Free:
 		return nil, .Mode_Not_Implemented
@@ -643,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
 		}
 		return nil, nil
 
@@ -794,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		if size > 0 {
 			panic("mem: panic allocator, .Alloc called")
 		}
+	case .Alloc_Non_Zeroed:
+		if size > 0 {
+			panic("mem: panic allocator, .Alloc_Non_Zeroed called")
+		}
 	case .Resize:
 		if size > 0 {
 			panic("mem: panic allocator, .Resize called")
@@ -831,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
 	memory:    rawptr,
 	size:      int,
 	alignment: int,
+	mode:      Allocator_Mode,
 	err:       Allocator_Error,
 	location:  runtime.Source_Code_Location,
 }
@@ -849,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
 	t.backing = backing_allocator
 	t.allocation_map.allocator = internals_allocator
 	t.bad_free_array.allocator = internals_allocator
+
+	if .Free_All in query_features(t.backing) {
+		t.clear_on_free_all = true
+	}
 }
 
 tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
@@ -856,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
 	delete(t.bad_free_array)
 }
 
+
+tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
+	clear(&t.allocation_map)
+	clear(&t.bad_free_array)
+}
+
+
 tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
 	return Allocator{
 		data = data,
@@ -865,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
 
 tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                 size, alignment: int,
-                                old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+                                old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
 	data := (^Tracking_Allocator)(allocator_data)
 	if mode == .Query_Info {
 		info := (^Allocator_Query_Info)(old_memory)
@@ -877,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 			info.pointer = nil
 		}
 
-		return nil, nil
+		return
 	}
 
-	result: []byte
-	err: Allocator_Error
 	if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
 		append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
 			memory = old_memory,
 			location = loc,
 		})
 	} else {
-		result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
-		if err != nil {
-			return result, err
-		}
+		result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
 	}
 	result_ptr := raw_data(result)
 
@@ -900,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	}
 
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
 			memory = result_ptr,
 			size = size,
+			mode = mode,
 			alignment = alignment,
 			err = err,
 			location = loc,
@@ -921,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
 			memory = result_ptr,
 			size = size,
+			mode = mode,
 			alignment = alignment,
 			err = err,
 			location = loc,
@@ -929,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
 		}
 		return nil, nil
 
@@ -937,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		unreachable()
 	}
 
-	return result, err
+	return
 }
 

+ 2 - 6
core/mem/raw.odin

@@ -1,5 +1,6 @@
 package mem
 
+import "core:builtin"
 import "core:runtime"
 
 Raw_Any           :: runtime.Raw_Any
@@ -21,12 +22,7 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
 	return transmute(any)Raw_Any{data, id}
 }
 
-raw_array_data         :: runtime.raw_array_data
-raw_simd_data          :: runtime.raw_simd_data
-raw_string_data        :: runtime.raw_string_data
-raw_slice_data         :: runtime.raw_slice_data
-raw_dynamic_array_data :: runtime.raw_dynamic_array_data
-raw_data               :: runtime.raw_data
+raw_data :: builtin.raw_data
 
 
 Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {

+ 2 - 2
core/mem/virtual/arena.odin

@@ -232,7 +232,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	old_size := uint(old_size)
 
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		return arena_alloc(arena, size, alignment)
 	case .Free:
 		err = .Mode_Not_Implemented
@@ -266,7 +266,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	case .Query_Features:
 		set := (^mem.Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
 		}
 	case .Query_Info:
 		err = .Mode_Not_Implemented

+ 4 - 2
core/os/file_windows.odin

@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	total_read: int
 	length := len(data)
 
-	to_read := min(win32.DWORD(length), MAX_RW)
+	// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
+	to_read := min(i64(length), MAX_RW)
 
 	e: win32.BOOL
 	if is_console {
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 			return int(total_read), err
 		}
 	} else {
-		e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
+		// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
+		e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
 	}
 	if single_read_length <= 0 || !e {
 		err := Errno(win32.GetLastError())

+ 5 - 5
core/os/os.odin

@@ -178,7 +178,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	// the pointer we return to the user.
 	//
 
-	aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
+	aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, mem.Allocator_Error) {
 		a := max(alignment, align_of(rawptr))
 		space := size + a - 1
 
@@ -187,7 +187,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 			original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
 			allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
 		} else {
-			allocated_mem = heap_alloc(space+size_of(rawptr))
+			allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
 		}
 		aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
 
@@ -226,8 +226,8 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	}
 
 	switch mode {
-	case .Alloc:
-		return aligned_alloc(size, alignment)
+	case .Alloc, .Alloc_Non_Zeroed:
+		return aligned_alloc(size, alignment, nil, mode == .Alloc)
 
 	case .Free:
 		aligned_free(old_memory)
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 	case .Query_Features:
 		set := (^mem.Allocator_Mode_Set)(old_memory)
 		if set != nil {
-			set^ = {.Alloc, .Free, .Resize, .Query_Features}
+			set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
 		}
 		return nil, nil
 

+ 12 - 3
core/os/os_darwin.odin

@@ -404,6 +404,9 @@ read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
 		if bytes_read == -1 {
 			return bytes_read_total, 1
 		}
+		if bytes_read == 0 {
+			break
+		}
 		bytes_read_total += bytes_read
 	}
 
@@ -643,9 +646,15 @@ access :: proc(path: string, mask: int) -> bool {
 	return _unix_access(cstr, mask) == 0
 }
 
-heap_alloc :: proc(size: int) -> rawptr {
-	assert(size > 0)
-	return _unix_calloc(1, size)
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+	if size <= 0 {
+		return nil
+	}
+	if zero_memory {
+		return _unix_calloc(1, size)
+	} else {
+		return _unix_malloc(size)
+	}
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on

+ 2 - 2
core/os/os_essence.odin

@@ -18,8 +18,8 @@ current_thread_id :: proc "contextless" () -> int {
 	return (int) (es.ThreadGetID(es.CURRENT_THREAD));
 }
 
-heap_alloc :: proc(size: int) -> rawptr {
-	return es.HeapAllocate(size, false);
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+	return es.HeapAllocate(size, zero_memory);
 }
 
 heap_free :: proc(ptr: rawptr) {

+ 9 - 3
core/os/os_freebsd.odin

@@ -603,9 +603,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
 	return true, ERROR_NONE
 }
 
-heap_alloc :: proc(size: int) -> rawptr {
-	assert(size >= 0)
-	return _unix_calloc(1, c.size_t(size))
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+	if size <= 0 {
+		return nil
+	}
+	if zero_memory {
+		return _unix_calloc(1, c.size_t(size))
+	} else {
+		return _unix_malloc(c.size_t(size))
+	}
 }
 
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {

+ 9 - 3
core/os/os_linux.odin

@@ -755,9 +755,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
 	return true, ERROR_NONE
 }
 
-heap_alloc :: proc(size: int) -> rawptr {
-	assert(size >= 0)
-	return _unix_calloc(1, c.size_t(size))
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+	if size <= 0 {
+		return nil
+	}
+	if zero_memory {
+		return _unix_calloc(1, c.size_t(size))
+	} else {
+		return _unix_malloc(c.size_t(size))
+	}
 }
 
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {

+ 9 - 3
core/os/os_openbsd.odin

@@ -605,9 +605,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
 	return true, ERROR_NONE
 }
 
-heap_alloc :: proc(size: int) -> rawptr {
-	assert(size >= 0)
-	return _unix_calloc(1, c.size_t(size))
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+	if size <= 0 {
+		return nil
+	}
+	if zero_memory {
+		return _unix_calloc(1, c.size_t(size))
+	} else {
+		return _unix_malloc(c.size_t(size))
+	}
 }
 
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {

+ 33 - 4
core/os/os_wasi.odin

@@ -24,7 +24,7 @@ O_CLOEXEC  :: 0x80000
 stdin:  Handle = 0
 stdout: Handle = 1
 stderr: Handle = 2
-
+current_dir: Handle = 3
 
 write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	iovs := wasi.ciovec_t(data)
@@ -47,7 +47,36 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
 	return int(n), Errno(err)
 }
 open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
-	return 0, -1
+	oflags: wasi.oflags_t
+	if mode & O_CREATE == O_CREATE {
+		oflags += {.CREATE}
+	}
+	if mode & O_EXCL == O_EXCL {
+		oflags += {.EXCL}
+	}
+	if mode & O_TRUNC == O_TRUNC {
+		oflags += {.TRUNC}
+	}
+
+	rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET}
+	switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
+	case O_RDONLY: rights += {.FD_READ}
+	case O_WRONLY: rights += {.FD_WRITE}
+	case O_RDWR:   rights += {.FD_READ, .FD_WRITE}
+	}
+
+	fdflags: wasi.fdflags_t
+	if mode & O_APPEND == O_APPEND {
+		fdflags += {.APPEND}
+	}
+	if mode & O_NONBLOCK == O_NONBLOCK {
+		fdflags += {.NONBLOCK}
+	}
+	if mode & O_SYNC == O_SYNC {
+		fdflags += {.SYNC}
+	}
+	fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags)
+	return Handle(fd), Errno(err)
 }
 close :: proc(fd: Handle) -> Errno {
 	err := wasi.fd_close(wasi.fd_t(fd))
@@ -72,7 +101,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
 
 
 
-heap_alloc :: proc(size: int) -> rawptr {
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
 	return nil
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -96,4 +125,4 @@ heap_free :: proc(ptr: rawptr) {
 exit :: proc "contextless" (code: int) -> ! {
 	runtime._cleanup_runtime_contextless()
 	wasi.proc_exit(wasi.exitcode_t(code))
-}
+}

+ 2 - 2
core/os/os_windows.odin

@@ -91,8 +91,8 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
 
 
 
-heap_alloc :: proc(size: int) -> rawptr {
-	return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+	return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
 }
 heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
 	if new_size == 0 {

+ 76 - 0
core/reflect/iterator.odin

@@ -0,0 +1,76 @@
+package reflect
+
+import "core:runtime"
+
+iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
+	if val == nil || it == nil {
+		return
+	}
+
+	ti := type_info_base(type_info_of(val.id))
+	#partial switch info in ti.variant {
+	case Type_Info_Pointer:
+		if ptr := (^rawptr)(val.data)^; ptr != nil {
+			return iterate_array(any{ptr, info.elem.id}, it)
+		}
+	case Type_Info_Array:
+		if it^ < info.count {
+			elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
+			elem.id = info.elem.id
+			ok = true
+			it^ += 1
+		}
+	case Type_Info_Slice:
+		array := (^runtime.Raw_Slice)(val.data)
+		if it^ < array.len {
+			elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
+			elem.id = info.elem.id
+			ok = true
+			it^ += 1
+		}
+	case Type_Info_Dynamic_Array:
+		array := (^runtime.Raw_Dynamic_Array)(val.data)
+		if it^ < array.len {
+			elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
+			elem.id = info.elem.id
+			ok = true
+			it^ += 1
+		}
+	}
+
+	return
+}
+
+iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
+	if val == nil || it == nil {
+		return
+	}
+	ti := type_info_base(type_info_of(val.id))
+	#partial switch info in ti.variant {
+	case Type_Info_Pointer:
+		if ptr := (^rawptr)(val.data)^; ptr != nil {
+			return iterate_map(any{ptr, info.elem.id}, it)
+		}
+	case Type_Info_Map:
+		if info.map_info == nil {
+			break
+		}
+		rm := (^runtime.Raw_Map)(val.data)
+		ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
+		for /**/ ; it^ < int(runtime.map_cap(rm^)); it^ += 1 {
+			if hash := hs[it^]; runtime.map_hash_is_valid(hash) {
+				key_ptr   := runtime.map_cell_index_dynamic(ks, info.map_info.ks, uintptr(it^))
+				value_ptr := runtime.map_cell_index_dynamic(vs, info.map_info.vs, uintptr(it^))
+
+				key.data   = rawptr(key_ptr)
+				value.data = rawptr(value_ptr)
+				key.id     = info.key.id
+				value.id   = info.value.id
+				ok = true
+				break
+			}
+
+		}
+	}
+	return
+}

+ 0 - 42
core/reflect/map.odin

@@ -1,42 +0,0 @@
-package reflect
-
-import "core:runtime"
-import "core:mem"
-_ :: runtime
-_ :: mem
-
-Map_Entry_Info :: struct($Key, $Value: typeid) {
-	hash:  uintptr,
-	key:   Key,
-	value: Value,
-}
-
-map_entry_info_slice :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
-	m := m
-	rm := (^mem.Raw_Map)(&m)
-
-	info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
-	gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct)
-	ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array)
-	entry_type := ed.elem.variant.(Type_Info_Struct)
-	key_offset :=  entry_type.offsets[2]
-	value_offset :=  entry_type.offsets[3]
-	entry_size := uintptr(ed.elem_size)
-
-	entries = make(type_of(entries), rm.entries.len)
-
-	data := uintptr(rm.entries.data)
-	for i in 0..<rm.entries.len {
-		header := (^runtime.Map_Entry_Header)(data)
-
-		hash  := header.hash
-		key   := (^K)(data + key_offset)^
-		value := (^V)(data + value_offset)^
-
-		entries[i] = {hash, key, value}
-
-		data += entry_size
-	}
-
-	return entries
-}

+ 2 - 2
core/reflect/reflect.odin

@@ -273,7 +273,7 @@ length :: proc(val: any) -> int {
 		return (^runtime.Raw_Dynamic_Array)(val.data).len
 
 	case Type_Info_Map:
-		return (^runtime.Raw_Map)(val.data).entries.len
+		return runtime.map_len((^runtime.Raw_Map)(val.data)^)
 
 	case Type_Info_String:
 		if a.is_cstring {
@@ -305,7 +305,7 @@ capacity :: proc(val: any) -> int {
 		return (^runtime.Raw_Dynamic_Array)(val.data).cap
 
 	case Type_Info_Map:
-		return (^runtime.Raw_Map)(val.data).entries.cap
+		return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
 	}
 	return 0
 }

+ 29 - 7
core/runtime/core.odin

@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
 	values:    []Type_Info_Enum_Value,
 }
 Type_Info_Map :: struct {
-	key:              ^Type_Info,
-	value:            ^Type_Info,
-	generated_struct: ^Type_Info,
-	key_equal:        Equal_Proc,
-	key_hasher:       Hasher_Proc,
+	key:      ^Type_Info,
+	value:    ^Type_Info,
+	map_info: ^Map_Info,
 }
 Type_Info_Bit_Set :: struct {
 	elem:       ^Type_Info,
@@ -303,6 +301,7 @@ Allocator_Mode :: enum byte {
 	Resize,
 	Query_Features,
 	Query_Info,
+	Alloc_Non_Zeroed,
 }
 
 Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
@@ -393,9 +392,32 @@ Raw_Dynamic_Array :: struct {
 	allocator: Allocator,
 }
 
+// The raw, type-erased representation of a map.
+//
+// 32-bytes on 64-bit
+// 16-bytes on 32-bit
 Raw_Map :: struct {
-	hashes:  []Map_Index,
-	entries: Raw_Dynamic_Array,
+	// A single allocation spanning all keys, values, and hashes.
+	// {
+	//   k: Map_Cell(K) * (capacity / ks_per_cell)
+	//   v: Map_Cell(V) * (capacity / vs_per_cell)
+	//   h: Map_Cell(H) * (capacity / hs_per_cell)
+	// }
+	//
+	// The data is allocated assuming 64-byte alignment, meaning the address is
+	// always a multiple of 64. This means we have 6 bits of zeros in the pointer
+	// to store the capacity. We can store a value as large as 2^6-1 or 63 in
+	// there. This conveniently is the maximum log2 capacity we can have for a map
+	// as Odin uses signed integers to represent capacity.
+	//
+	// Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned
+	// integer, the cell structure for hashes is unnecessary because 64/8 is 8 and
+	// requires no padding, meaning it can be indexed as a regular array of
+	// Map_Hash directly, though for consistency sake it's written as if it were
+	// an array of Map_Cell(Map_Hash).
+	data:      uintptr,   // 8-bytes on 64-bits, 4-bytes on 32-bits
+	len:       int,       // 8-bytes on 64-bits, 4-bytes on 32-bits
+	allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
 }
 
 Raw_Any :: struct {

+ 18 - 92
core/runtime/core_builtin.odin

@@ -159,20 +159,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
 }
 @builtin
 delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
-	Entry :: struct {
-		hash:  uintptr,
-		next:  int,
-		key:   K,
-		value: V,
-	}
-
-	raw := transmute(Raw_Map)m
-	err := delete_slice(raw.hashes, raw.entries.allocator, loc)
-	err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
-	if err == nil {
-		err = err1
-	}
-	return err
+	return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
 }
 
 
@@ -244,12 +231,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
 	return
 }
 @(builtin)
-make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
-	make_map_expr_error_loc(loc, cap)
+make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
+	make_map_expr_error_loc(loc, capacity)
 	context.allocator = allocator
 
 	m: T
-	reserve_map(&m, cap)
+	reserve_map(&m, capacity, loc)
 	return m
 }
 @(builtin)
@@ -285,36 +272,24 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
 	if m == nil {
 		return
 	}
-	raw_map := (^Raw_Map)(m)
-	entries := (^Raw_Dynamic_Array)(&raw_map.entries)
-	entries.len = 0
-	for _, i in raw_map.hashes {
-		raw_map.hashes[i] = MAP_SENTINEL
-	}
+	map_clear_dynamic((^Raw_Map)(m), map_info(T))
 }
 
 @builtin
 reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
 	if m != nil {
-		h := __get_map_header_table(T)
-		__dynamic_map_reserve(m, h, uint(capacity), loc)
+		__dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
 	}
 }
 
 /*
-	Shrinks the capacity of a map down to the current length, or the given capacity.
-
-	If `new_cap` is negative, then `len(m)` is used.
-
-	Returns false if `cap(m) < new_cap`, or the allocator report failure.
-
-	If `len(m) < new_cap`, then `len(m)` will be left unchanged.
+	Shrinks the capacity of a map down to the current length.
 */
 @builtin
-shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
+shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
 	if m != nil {
-		new_cap := new_cap if new_cap >= 0 else len(m)
-		return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
+		err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
+		did_shrink = err == nil
 	}
 	return
 }
@@ -325,14 +300,10 @@ shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) ->
 delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
 	if m != nil {
 		key := key
-		h := __get_map_header(m)
-		fr := __map_find(h, &key)
-		if fr.entry_index != MAP_SENTINEL {
-			entry := __dynamic_map_get_entry(h, fr.entry_index)
-			deleted_key   = (^K)(uintptr(entry)+h.key_offset)^
-			deleted_value = (^V)(uintptr(entry)+h.value_offset)^
-
-			__dynamic_map_erase(h, fr)
+		old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
+		if ok {
+			deleted_key   = (^K)(old_k)^
+			deleted_value = (^V)(old_v)^
 		}
 	}
 	return
@@ -573,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
 	new_size  := capacity * size_of(E)
 	allocator := a.allocator
 
-	new_data, err := allocator.procedure(
-		allocator.data, .Resize, new_size, align_of(E),
-		a.data, old_size, loc,
-	)
+	new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
 	if new_data == nil || err != nil {
 		return false
 	}
@@ -607,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
 	new_size  := length * size_of(E)
 	allocator := a.allocator
 
-	new_data, err := allocator.procedure(
-		allocator.data, .Resize, new_size, align_of(E),
-		a.data, old_size, loc,
-	)
+	new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
 	if new_data == nil || err != nil {
 		return false
 	}
@@ -650,15 +615,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
 	old_size := a.cap * size_of(E)
 	new_size := new_cap * size_of(E)
 
-	new_data, err := a.allocator.procedure(
-		a.allocator.data,
-		.Resize,
-		new_size,
-		align_of(E),
-		a.data,
-		old_size,
-		loc,
-	)
+	new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
 	if err != nil {
 		return
 	}
@@ -672,10 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
 @builtin
 map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
 	key, value := key, value
-	h := __get_map_header_table(T)
-
-	e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
-	return (^V)(uintptr(e) + h.value_offset)
+	return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
 }
 
 
@@ -731,34 +685,6 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
 
 
 
-@builtin
-raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
-	return ([^]E)(a)
-}
-@builtin
-raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
-	return ([^]E)(a)
-}
-@builtin
-raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
-	ptr := (transmute(Raw_Slice)s).data
-	return ([^]E)(ptr)
-}
-@builtin
-raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
-	ptr := (transmute(Raw_Dynamic_Array)s).data
-	return ([^]E)(ptr)
-}
-@builtin
-raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
-	return (transmute(Raw_String)s).data
-}
-
-@builtin
-raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
-
-
-
 @builtin
 @(disabled=ODIN_DISABLE_ASSERT)
 assert :: proc(condition: bool, message := "", loc := #caller_location) {

+ 1 - 1
core/runtime/default_allocators_nil.odin

@@ -4,7 +4,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                                size, alignment: int,
                                old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		return nil, .Out_Of_Memory
 	case .Free:
 		return nil, .None

+ 3 - 3
core/runtime/default_allocators_windows.odin

@@ -10,8 +10,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
 	                                size, alignment: int,
 	                                old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
 		switch mode {
-		case .Alloc:
-			data, err = _windows_default_alloc(size, alignment)
+		case .Alloc, .Alloc_Non_Zeroed:
+			data, err = _windows_default_alloc(size, alignment, mode == .Alloc)
 
 		case .Free:
 			_windows_default_free(old_memory)
@@ -25,7 +25,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
 		case .Query_Features:
 			set := (^Allocator_Mode_Set)(old_memory)
 			if set != nil {
-				set^ = {.Alloc, .Free, .Resize, .Query_Features}
+				set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
 			}
 
 		case .Query_Info:

+ 2 - 2
core/runtime/default_temporary_allocator.odin

@@ -167,7 +167,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
 		}
 
 		switch mode {
-		case .Alloc:
+		case .Alloc, .Alloc_Non_Zeroed:
 			data, err = default_temp_allocator_alloc(s, size, alignment, loc)
 		case .Free:
 			err = default_temp_allocator_free(s, old_memory, loc)
@@ -181,7 +181,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
 		case .Query_Features:
 			set := (^Allocator_Mode_Set)(old_memory)
 			if set != nil {
-				set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
+				set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
 			}
 
 		case .Query_Info:

+ 681 - 314
core/runtime/dynamic_map_internal.odin

@@ -3,426 +3,793 @@ package runtime
 import "core:intrinsics"
 _ :: intrinsics
 
-INITIAL_MAP_CAP :: 16
+// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
+// data structure with various optimizations for Odin.
+//
+// Copyright 2022 (c) Dale Weiler
+//
+// The core of the hash map data structure is the Raw_Map struct which is a
+// type-erased representation of the map. This type-erased representation is
+// used in two ways: static and dynamic. When static type information is known,
+// the procedures suffixed with _static should be used instead of _dynamic. The
+// static procedures are optimized since they have type information. Hashing of
+// keys, comparison of keys, and data lookup are all optimized. When type
+// information is not known, the procedures suffixed with _dynamic should be
+// used. The representation of the map is the same for both static and dynamic,
+// and procedures of each can be mixed and matched. The purpose of the dynamic
+// representation is to enable reflection and runtime manipulation of the map.
+// The dynamic procedures all take an additional Map_Info structure parameter
+// which carries runtime values describing the size, alignment, and offset of
+// various traits of a given key and value type pair. The Map_Info value can
+// be created by calling map_info(K, V) with the key and value typeids.
+//
+// This map implementation makes extensive use of uintptr for representing
+// sizes, lengths, capacities, masks, pointers, offsets, and addresses to avoid
+// expensive sign extension and masking that would be generated if types were
+// casted all over. The only place regular ints show up is in the cap() and
+// len() implementations.
+//
+// To make this map cache-friendly it uses a novel strategy to ensure keys and
+// values of the map are always cache-line aligned and that no single key or
+// value of any type ever straddles a cache-line. This cache efficiency makes
+// for quick lookups because the linear-probe always addresses data in a cache
+// friendly way. This is enabled through the use of a special meta-type called
+// a Map_Cell which packs as many values of a given type into a local array adding
+// internal padding to round to MAP_CACHE_LINE_SIZE. One other benefit to storing
+// the internal data in this manner is false sharing no longer occurs when using
+// a map, enabling efficient concurrent access of the map data structure with
+// minimal locking if desired.
+
+// With Robin Hood hashing a maximum load factor of 75% is ideal.
+MAP_LOAD_FACTOR :: 75
+
+// Minimum log2 capacity.
+MAP_MIN_LOG2_CAPACITY :: 6 // 64 elements
+
+// Has to be less than 100% though.
+#assert(MAP_LOAD_FACTOR < 100)
+
+// This is safe to change. The log2 size of a cache-line. At minimum it has to
+// be six though. Higher cache line sizes are permitted.
+MAP_CACHE_LINE_LOG2 :: 6
+
+// The size of a cache-line.
+MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2
+
+// The minimum cache-line size allowed by this implementation is 64 bytes since
+// we need 6 bits in the base pointer to store the integer log2 capacity, which
+// at maximum is 63. Odin uses signed integers to represent length and capacity,
+// so only 63 bits are needed in the maximum case.
+#assert(MAP_CACHE_LINE_SIZE >= 64)
+
+// Map_Cell type that packs multiple T in such a way to ensure that each T stays
+// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0
+//
+// This means a value of type T will never straddle a cache-line.
+//
+// When multiple Ts can fit in a single cache-line the data array will have more
+// than one element. When it cannot, the data array will have one element and
+// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE.
+//
+// We rely on the type system to do all the arithmetic and padding for us here.
+//
+// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit
+// more involved as there now may be internal padding. The indexing now becomes
+//
+//  N :: len(Map_Cell(T){}.data)
+//  i := index / N
+//  j := index % N
+//  cell[i].data[j]
+//
+// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some
+// optimizations we can do to eliminate the need for any divisions as N will
+// be bounded by [1, 64).
+//
+// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated
+// as a regular array of T, which is the case for hashes.
+Map_Cell :: struct($T: typeid) #align MAP_CACHE_LINE_SIZE {
+	data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T,
+}
+
+// So we can operate on a cell data structure at runtime without any type
+// information, we have a simple table that stores some traits about the cell.
+//
+// 32-bytes on 64-bit
+// 16-bytes on 32-bit
+Map_Cell_Info :: struct {
+	size_of_type:      uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
+	align_of_type:     uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
+	size_of_cell:      uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
+	elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
+}
+
+// map_cell_info :: proc "contextless" ($T: typeid) -> ^Map_Cell_Info {...}
+map_cell_info :: intrinsics.type_map_cell_info
+
+// Same as the above procedure but at runtime with the cell Map_Cell_Info value.
+@(require_results)
+map_cell_index_dynamic :: #force_inline proc "contextless" (base: uintptr, #no_alias info: ^Map_Cell_Info, index: uintptr) -> uintptr {
+	// Micro-optimize the common cases to save on integer division.
+	elements_per_cell := uintptr(info.elements_per_cell)
+	size_of_cell      := uintptr(info.size_of_cell)
+	switch elements_per_cell {
+	case 1:
+		return base + (index * size_of_cell)
+	case 2:
+		cell_index   := index >> 1
+		data_index   := index & 1
+		size_of_type := uintptr(info.size_of_type)
+		return base + (cell_index * size_of_cell) + (data_index * size_of_type)
+	case:
+		cell_index   := index / elements_per_cell
+		data_index   := index % elements_per_cell
+		size_of_type := uintptr(info.size_of_type)
+		return base + (cell_index * size_of_cell) + (data_index * size_of_type)
+	}
+}
 
-// Temporary data structure for comparing hashes and keys
-Map_Hash :: struct {
-	hash:    uintptr,
-	key_ptr: rawptr, // address of Map_Entry_Header.key
+// Same as above procedure but with compile-time constant index.
+@(require_results)
+map_cell_index_dynamic_const :: proc "contextless" (base: uintptr, #no_alias info: ^Map_Cell_Info, $INDEX: uintptr) -> uintptr {
+	elements_per_cell := uintptr(info.elements_per_cell)
+	size_of_cell      := uintptr(info.size_of_cell)
+	size_of_type      := uintptr(info.size_of_type)
+	cell_index        := INDEX / elements_per_cell
+	data_index        := INDEX % elements_per_cell
+	return base + (cell_index * size_of_cell) + (data_index * size_of_type)
+}
+
+// We always round the capacity to a power of two so this becomes [16]Foo, which
+// works out to [4]Cell(Foo).
+//
+// The following compile-time procedure indexes such a [N]Cell(T) structure as
+// if it were a flat array accounting for the internal padding introduced by the
+// Cell structure.
+@(require_results)
+map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T), index: uintptr) -> ^T #no_bounds_check {
+	N :: size_of(Map_Cell(T){}.data) / size_of(T) when size_of(T) > 0 else 1
+
+	#assert(N <= MAP_CACHE_LINE_SIZE)
+
+	when size_of(Map_Cell(T)) == size_of([N]T) {
+		// No padding case, can treat as a regular array of []T.
+
+		return &([^]T)(cells)[index]
+	} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
+		// Likely case, N is a power of two because T is a power of two.
+
+		// Compute the integer log 2 of N, this is the shift amount to index the
+		// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
+		// constant, hence this approach. We only need to check up to N = 64.
+		SHIFT :: 1 when N < 2  else
+		         2 when N < 4  else
+		         3 when N < 8  else
+		         4 when N < 16 else
+		         5 when N < 32 else 6
+		#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
+		// Unique case, no need to index data here since only one element.
+		when N == 1 {
+			return &cells[index >> SHIFT].data[0]
+		} else {
+			return &cells[index >> SHIFT].data[index & (N - 1)]
+		}
+	} else {
+		// Least likely (and worst case), we pay for a division operation but we
+		// assume the compiler does not actually generate a division. N will be in the
+		// range [1, CACHE_LINE_SIZE) and not a power of two.
+		return &cells[index / N].data[index % N]
+	}
 }
 
-__get_map_key_hash :: #force_inline proc "contextless" (k: ^$K) -> uintptr {
-	hasher := intrinsics.type_hasher_proc(K)
-	return hasher(k, 0)
+// len() for map
+@(require_results)
+map_len :: #force_inline proc "contextless" (m: Raw_Map) -> int {
+	return m.len
 }
 
-__get_map_entry_key_ptr :: #force_inline proc "contextless" (h: Map_Header_Table, entry: ^Map_Entry_Header) -> rawptr {
-	return rawptr(uintptr(entry) + h.key_offset)
+// cap() for map
+@(require_results)
+map_cap :: #force_inline proc "contextless" (m: Raw_Map) -> int {
+	// The data uintptr stores the capacity in the lower six bits which gives the
+	// a maximum value of 2^6-1, or 63. We store the integer log2 of capacity
+	// since our capacity is always a power of two. We only need 63 bits as Odin
+	// represents length and capacity as a signed integer.
+	return 0 if m.data == 0 else 1 << map_log2_cap(m)
 }
 
-Map_Index :: distinct uint
-MAP_SENTINEL :: ~Map_Index(0)
+// Query the load factor of the map. This is not actually configurable, but
+// some math is needed to compute it. Compute it as a fixed point percentage to
+// avoid floating point operations. This division can be optimized out by
+// multiplying by the multiplicative inverse of 100.
+@(require_results)
+map_load_factor :: #force_inline proc "contextless" (log2_capacity: uintptr) -> uintptr {
+	return ((uintptr(1) << log2_capacity) * MAP_LOAD_FACTOR) / 100
+}
 
-Map_Find_Result :: struct {
-	hash_index:  Map_Index,
-	entry_prev:  Map_Index,
-	entry_index: Map_Index,
+@(require_results)
+map_resize_threshold :: #force_inline proc "contextless" (m: Raw_Map) -> int {
+	return int(map_load_factor(map_log2_cap(m)))
 }
 
-Map_Entry_Header :: struct {
-	hash: uintptr,
-	next: Map_Index,
-/*
-	key:   Key_Value,
-	value: Value_Type,
-*/
+// The data stores the log2 capacity in the lower six bits. This is primarily
+// used in the implementation rather than map_cap since the check for data = 0
+// isn't necessary in the implementation. cap() on the otherhand needs to work
+// when called on an empty map.
+@(require_results)
+map_log2_cap :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
+	return m.data & (64 - 1)
 }
 
-Map_Header_Table :: struct {
-	equal:         Equal_Proc,
+// Canonicalize the data by removing the tagged capacity stored in the lower six
+// bits of the data uintptr.
+@(require_results)
+map_data :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
+	return m.data &~ uintptr(64 - 1)
+}
 
-	entry_size:    int,
-	entry_align:   int,
 
-	key_offset:    uintptr,
-	key_size:      int,
+Map_Hash :: uintptr
 
-	value_offset:  uintptr,
-	value_size:    int,
+// Procedure to check if a slot is empty for a given hash. This is represented
+// by the zero value to make the zero value useful. This is a procedure just
+// for prose reasons.
+@(require_results)
+map_hash_is_empty :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
+	return hash == 0
 }
 
-Map_Header :: struct {
-	m: ^Raw_Map,
-	using table: Map_Header_Table,
+@(require_results)
+map_hash_is_deleted :: #force_no_inline proc "contextless" (hash: Map_Hash) -> bool {
+	// The MSB indicates a tombstone
+	N :: size_of(Map_Hash)*8 - 1
+	return hash >> N != 0
 }
-
-// USED INTERNALLY BY THE COMPILER
-__dynamic_map_get :: proc "contextless" (m: rawptr, table: Map_Header_Table, key_hash: uintptr, key_ptr: rawptr) -> rawptr {
-	if m != nil {
-		h := Map_Header{(^Raw_Map)(m), table}
-		index := __dynamic_map_find(h, key_hash, key_ptr).entry_index
-		if index != MAP_SENTINEL {
-			data := uintptr(__dynamic_map_get_entry(h, index))
-			return rawptr(data + h.value_offset)
-		}
-	}
-	return nil
+@(require_results)
+map_hash_is_valid :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
+	// The MSB indicates a tombstone
+	N :: size_of(Map_Hash)*8 - 1
+	return (hash != 0) & (hash >> N == 0)
 }
 
-// USED INTERNALLY BY THE COMPILER
-__dynamic_map_set :: proc "odin" (m: rawptr, table: Map_Header_Table, key_hash: uintptr, key_ptr: rawptr, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check {
-	add_entry :: proc "odin" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr, loc := #caller_location) -> Map_Index {
-		prev := Map_Index(h.m.entries.len)
-		c := Map_Index(__dynamic_array_append_nothing(&h.m.entries, h.entry_size, h.entry_align, loc))
-		if c != prev {
-			end := __dynamic_map_get_entry(h, c-1)
-			end.hash = key_hash
-			mem_copy(rawptr(uintptr(end) + h.key_offset), key_ptr, h.key_size)
-			end.next = MAP_SENTINEL
-		}
-		return prev
-	}
 
-	h := Map_Header{(^Raw_Map)(m), table}
+// Computes the desired position in the array. This is just index % capacity,
+// but a procedure as there's some math involved here to recover the capacity.
+@(require_results)
+map_desired_position :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Hash) -> uintptr {
+	// We do not use map_cap since we know the capacity will not be zero here.
+	capacity := uintptr(1) << map_log2_cap(m)
+	return uintptr(hash & Map_Hash(capacity - 1))
+}
 
-	index := MAP_SENTINEL
+@(require_results)
+map_probe_distance :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Hash, slot: uintptr) -> uintptr {
+	// We do not use map_cap since we know the capacity will not be zero here.
+	capacity := uintptr(1) << map_log2_cap(m)
+	return (slot + capacity - map_desired_position(m, hash)) & (capacity - 1)
+}
 
-	if len(h.m.hashes) == 0 {
-		__dynamic_map_reserve(m, table, INITIAL_MAP_CAP, loc)
-		__dynamic_map_grow(h, loc)
-	}
+// When working with the type-erased structure at runtime we need information
+// about the map to make working with it possible. This info structure stores
+// that.
+//
+// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be
+// modified after creation
+//
+// 32-bytes on 64-bit
+// 16-bytes on 32-bit
+Map_Info :: struct {
+	ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit
+	vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit
+	key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit
+	key_equal:  proc "contextless" (lhs, rhs: rawptr) -> bool,                // 8-bytes on 64-bit, 4-bytes on 32-bit
+}
 
-	fr := __dynamic_map_find(h, key_hash, key_ptr)
-	if fr.entry_index != MAP_SENTINEL {
-		index = fr.entry_index
-	} else {
-		index = add_entry(h, key_hash, key_ptr, loc)
-		if fr.entry_prev != MAP_SENTINEL {
-			entry := __dynamic_map_get_entry(h, fr.entry_prev)
-			entry.next = index
-		} else if fr.hash_index != MAP_SENTINEL {
-			h.m.hashes[fr.hash_index] = index
-		} else {
-			return nil
-		}
-	}
 
-	e := __dynamic_map_get_entry(h, index)
-	e.hash = key_hash
+// The Map_Info structure is basically a pseudo-table of information for a given K and V pair.
+// map_info :: proc "contextless" ($T: typeid/map[$K]$V) -> ^Map_Info {...}
+map_info :: intrinsics.type_map_info
 
-	key := rawptr(uintptr(e) + h.key_offset)
-	val := rawptr(uintptr(e) + h.value_offset)
+@(require_results)
+map_kvh_data_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info) -> (ks: uintptr, vs: uintptr, hs: [^]Map_Hash, sk: uintptr, sv: uintptr) {
+	INFO_HS := intrinsics.type_map_cell_info(Map_Hash)
 
-	mem_copy(key, key_ptr, h.key_size)
-	mem_copy(val, value, h.value_size)
+	capacity := uintptr(1) << map_log2_cap(m)
+	ks   = map_data(m)
+	vs   = map_cell_index_dynamic(ks,  info.ks, capacity) // Skip past ks to get start of vs
+	hs_ := map_cell_index_dynamic(vs,  info.vs, capacity) // Skip past vs to get start of hs
+	sk   = map_cell_index_dynamic(hs_, INFO_HS, capacity) // Skip past hs to get start of sk
+	// Need to skip past two elements in the scratch key space to get to the start
+	// of the scratch value space, of which there's only two elements as well.
+	sv = map_cell_index_dynamic_const(sk, info.ks, 2)
 
-	if __dynamic_map_full(h) {
-		__dynamic_map_grow(h, loc)
-	}
+	hs = ([^]Map_Hash)(hs_)
+	return
+}
 
-	return __dynamic_map_get_entry(h, index)
+@(require_results)
+map_kvh_data_values_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info) -> (vs: uintptr) {
+	capacity := uintptr(1) << map_log2_cap(m)
+	return map_cell_index_dynamic(map_data(m), info.ks, capacity) // Skip past ks to get start of vs
 }
 
-// USED INTERNALLY BY THE COMPILER
-__dynamic_map_reserve :: proc "odin" (m: rawptr, table: Map_Header_Table, cap: uint, loc := #caller_location) {
-	h := Map_Header{(^Raw_Map)(m), table}
 
-	c := context
-	if h.m.entries.allocator.procedure != nil {
-		c.allocator = h.m.entries.allocator
+@(private, require_results)
+map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
+	round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
+		CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
+		return (value + CACHE_MASK) &~ CACHE_MASK
+	}
+	INFO_HS := intrinsics.type_map_cell_info(Map_Hash)
+
+	size := uintptr(0)
+	size = round(map_cell_index_dynamic(size, info.ks, capacity))
+	size = round(map_cell_index_dynamic(size, info.vs, capacity))
+	size = round(map_cell_index_dynamic(size, INFO_HS, capacity))
+	size = round(map_cell_index_dynamic(size, info.ks, 2)) // Two additional ks for scratch storage
+	size = round(map_cell_index_dynamic(size, info.vs, 2)) // Two additional vs for scratch storage
+	return size
+}
+
+// The only procedure which needs access to the context is the one which allocates the map.
+@(require_results)
+map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
+	result.allocator = allocator // set the allocator always
+	if log2_capacity == 0 {
+		return
+	}
+
+	if log2_capacity >= 64 {
+		// Overflowed, would be caused by log2_capacity > 64
+		return {}, .Out_Of_Memory
 	}
-	context = c
 
-	cap := cap
-	cap = ceil_to_pow2(cap)
+	capacity := uintptr(1) << max(log2_capacity, MAP_MIN_LOG2_CAPACITY)
 
-	__dynamic_array_reserve(&h.m.entries, h.entry_size, h.entry_align, int(cap), loc)
+	CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
 
-	if h.m.entries.len*2 < len(h.m.hashes) {
+	size := map_total_allocation_size(capacity, info)
+
+	data := mem_alloc_non_zeroed(int(size), MAP_CACHE_LINE_SIZE, allocator, loc) or_return
+	data_ptr := uintptr(raw_data(data))
+	if data_ptr == 0 {
+		err = .Out_Of_Memory
 		return
 	}
-	if __slice_resize(&h.m.hashes, int(cap*2), h.m.entries.allocator, loc) {
-		__dynamic_map_reset_entries(h, loc)
+	if intrinsics.expect(data_ptr & CACHE_MASK != 0, false) {
+		panic("allocation not aligned to a cache line", loc)
+	} else {
+		result.data = data_ptr | log2_capacity // Tagged pointer representation for capacity.
+		result.len = 0
+
+		map_clear_dynamic(&result, info)
 	}
+	return
 }
 
+// This procedure has to stack allocate storage to store local keys during the
+// Robin Hood hashing technique where elements are swapped in the backing
+// arrays to reduce variance. This swapping can only be done with memcpy since
+// there is no type information.
+//
+// This procedure returns the address of the just inserted value.
+@(require_results)
+map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
+	h        := h
+	pos      := map_desired_position(m^, h)
+	distance := uintptr(0)
+	mask     := (uintptr(1) << map_log2_cap(m^)) - 1
 
-INITIAL_HASH_SEED :: 0xcbf29ce484222325
+	ks, vs, hs, sk, sv := map_kvh_data_dynamic(m^, info)
+
+	// Avoid redundant loads of these values
+	size_of_k := info.ks.size_of_type
+	size_of_v := info.vs.size_of_type
 
-_fnv64a :: proc "contextless" (data: []byte, seed: u64 = INITIAL_HASH_SEED) -> u64 {
-	h: u64 = seed
-	for b in data {
-		h = (h ~ u64(b)) * 0x100000001b3
+	k := map_cell_index_dynamic(sk, info.ks, 0)
+	v := map_cell_index_dynamic(sv, info.vs, 0)
+	intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(ik), size_of_k)
+	intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(iv), size_of_v)
+
+	// Temporary k and v dynamic storage for swap below
+	tk := map_cell_index_dynamic(sk, info.ks, 1)
+	tv := map_cell_index_dynamic(sv, info.vs, 1)
+
+
+	for {
+		hp := &hs[pos]
+		element_hash := hp^
+
+		if map_hash_is_empty(element_hash) {
+			k_dst := map_cell_index_dynamic(ks, info.ks, pos)
+			v_dst := map_cell_index_dynamic(vs, info.vs, pos)
+			intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+			hp^ = h
+
+			return result if result != 0 else v_dst
+		}
+
+		if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
+			if map_hash_is_deleted(element_hash) {
+				k_dst := map_cell_index_dynamic(ks, info.ks, pos)
+				v_dst := map_cell_index_dynamic(vs, info.vs, pos)
+				intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
+				intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+				hp^ = h
+
+				return result if result != 0 else v_dst
+			}
+
+			if result == 0 {
+				result = map_cell_index_dynamic(vs, info.vs, pos)
+			}
+
+			kp := map_cell_index_dynamic(ks, info.ks, pos)
+			vp := map_cell_index_dynamic(vs, info.vs, pos)
+
+			intrinsics.mem_copy_non_overlapping(rawptr(tk), rawptr(k), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(k),  rawptr(kp), size_of_k)
+			intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(tk), size_of_k)
+
+			intrinsics.mem_copy_non_overlapping(rawptr(tv), rawptr(v), size_of_v)
+			intrinsics.mem_copy_non_overlapping(rawptr(v),  rawptr(vp), size_of_v)
+			intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v)
+
+			th := h
+			h = hp^
+			hp^ = th
+
+			distance = probe_distance
+		}
+
+		pos = (pos + 1) & mask
+		distance += 1
 	}
-	return h
 }
 
-default_hash :: #force_inline proc "contextless" (data: []byte) -> uintptr {
-	return uintptr(_fnv64a(data))
-}
-default_hash_string :: #force_inline proc "contextless" (s: string) -> uintptr {
-	return default_hash(transmute([]byte)(s))
-}
-default_hash_ptr :: #force_inline proc "contextless" (data: rawptr, size: int) -> uintptr {
-	s := Raw_Slice{data, size}
-	return default_hash(transmute([]byte)(s))
+@(require_results)
+map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
+	log2_capacity := map_log2_cap(m^)
+	new_capacity := uintptr(1) << max(log2_capacity + 1, MAP_MIN_LOG2_CAPACITY)
+	return map_reserve_dynamic(m, info, new_capacity, loc)
 }
 
-@(private)
-_default_hasher_const :: #force_inline proc "contextless" (data: rawptr, seed: uintptr, $N: uint) -> uintptr where N <= 16 {
-	h := u64(seed) + 0xcbf29ce484222325
-	p := uintptr(data)
-	#unroll for _ in 0..<N {
-		b := u64((^byte)(p)^)
-		h = (h ~ b) * 0x100000001b3
-		p += 1
-	}
-	return uintptr(h)
-}
 
-default_hasher_n :: #force_inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr {
-	h := u64(seed) + 0xcbf29ce484222325
-	p := uintptr(data)
-	for _ in 0..<N {
-		b := u64((^byte)(p)^)
-		h = (h ~ b) * 0x100000001b3
-		p += 1
+@(require_results)
+map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
+	@(require_results)
+	ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
+		z := intrinsics.count_leading_zeros(x)
+		if z > 0 && x & (x-1) != 0 {
+			z -= 1
+		}
+		return size_of(uintptr)*8 - 1 - z
 	}
-	return uintptr(h)
-}
-
-// NOTE(bill): There are loads of predefined ones to improve optimizations for small types
-
-default_hasher1  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  1) }
-default_hasher2  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  2) }
-default_hasher3  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  3) }
-default_hasher4  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  4) }
-default_hasher5  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  5) }
-default_hasher6  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  6) }
-default_hasher7  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  7) }
-default_hasher8  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  8) }
-default_hasher9  :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed,  9) }
-default_hasher10 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 10) }
-default_hasher11 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 11) }
-default_hasher12 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 12) }
-default_hasher13 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 13) }
-default_hasher14 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 14) }
-default_hasher15 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 15) }
-default_hasher16 :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr { return #force_inline _default_hasher_const(data, seed, 16) }
 
-default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
-	h := u64(seed) + 0xcbf29ce484222325
-	str := (^[]byte)(data)^
-	for b in str {
-		h = (h ~ u64(b)) * 0x100000001b3
+	if m.allocator.procedure == nil {
+		m.allocator = context.allocator
 	}
-	return uintptr(h)
-}
-default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
-	h := u64(seed) + 0xcbf29ce484222325
-	ptr := (^uintptr)(data)^
-	for (^byte)(ptr)^ != 0 {
-		b := (^byte)(ptr)^
-		h = (h ~ u64(b)) * 0x100000001b3
-		ptr += 1
-	}
-	return uintptr(h)
-}
 
+	new_capacity := new_capacity
+	old_capacity := uintptr(map_cap(m^))
 
-__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> (header: Map_Header) {
-	header.m = (^Raw_Map)(m)
-	header.table = #force_inline __get_map_header_table(T)
-	return
-}
+	if old_capacity >= new_capacity {
+		return nil
+	}
 
-__get_map_header_runtime :: proc "contextless" (m: ^Raw_Map, ti: Type_Info_Map) -> (header: Map_Header) {
-	header.m = m
-	header.table = #force_inline __get_map_header_table_runtime(ti)
-	return
-}
+	// ceiling nearest power of two
+	log2_new_capacity := ceil_log2(new_capacity)
 
-__get_map_header_table :: proc "contextless" ($T: typeid/map[$K]$V) -> (header: Map_Header_Table) {
-	Entry :: struct {
-		hash:  uintptr,
-		next:  Map_Index,
-		key:   K,
-		value: V,
+	log2_min_cap := max(MAP_MIN_LOG2_CAPACITY, log2_new_capacity)
+
+	if m.data == 0 {
+		m^ = map_alloc_dynamic(info, log2_min_cap, m.allocator, loc) or_return
+		return nil
 	}
 
-	header.equal = intrinsics.type_equal_proc(K)
+	resized := map_alloc_dynamic(info, log2_min_cap, m.allocator, loc) or_return
 
-	header.entry_size    = size_of(Entry)
-	header.entry_align   = align_of(Entry)
+	ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
 
-	header.key_offset    = offset_of(Entry, key)
-	header.key_size      = size_of(K)
+	// Cache these loads to avoid hitting them in the for loop.
+	n := m.len
+	for i in 0..<old_capacity {
+		hash := hs[i]
+		if map_hash_is_empty(hash) {
+			continue
+		}
+		if map_hash_is_deleted(hash) {
+			continue
+		}
+		k := map_cell_index_dynamic(ks, info.ks, i)
+		v := map_cell_index_dynamic(vs, info.vs, i)
+		_ = map_insert_hash_dynamic(&resized, info, hash, k, v)
+		// Only need to do this comparison on each actually added pair, so do not
+		// fold it into the for loop comparator as a micro-optimization.
+		n -= 1
+		if n == 0 {
+			break
+		}
+	}
 
-	header.value_offset  = offset_of(Entry, value)
-	header.value_size    = size_of(V)
+	map_free_dynamic(m^, info, loc) or_return
 
-	return
+	m.data = resized.data
+
+	return nil
 }
 
-__get_map_header_table_runtime :: proc "contextless" (ti: Type_Info_Map) -> (header: Map_Header) {
-	header.equal = ti.key_equal
 
-	entries := ti.generated_struct.variant.(Type_Info_Struct).types[1]
-	entry := entries.variant.(Type_Info_Dynamic_Array).elem
-	e := entry.variant.(Type_Info_Struct)
+@(require_results)
+map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
+	if m.allocator.procedure == nil {
+		m.allocator = context.allocator
+	}
 
-	header.entry_size    = entry.size
-	header.entry_align   = entry.align
+	// Cannot shrink the capacity if the number of items in the map would exceed
+	// one minus the current log2 capacity's resize threshold. That is the shrunk
+	// map needs to be within the max load factor.
+	log2_capacity := map_log2_cap(m^)
+	if uintptr(m.len) >= map_load_factor(log2_capacity - 1) {
+		return nil
+	}
 
-	header.key_offset    = e.offsets[2]
-	header.key_size      = e.types[2].size
+	shrunk := map_alloc_dynamic(info, log2_capacity - 1, m.allocator) or_return
 
-	header.value_offset  = e.offsets[3]
-	header.value_size    = e.types[3].size
+	capacity := uintptr(1) << log2_capacity
 
-	return
-}
+	ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
 
+	n := m.len
+	for i in 0..<capacity {
+		hash := hs[i]
+		if map_hash_is_empty(hash) {
+			continue
+		}
+		if map_hash_is_deleted(hash) {
+			continue
+		}
 
+		k := map_cell_index_dynamic(ks, info.ks, i)
+		v := map_cell_index_dynamic(vs, info.vs, i)
+		_ = map_insert_hash_dynamic(&shrunk, info, hash, k, v)
+		// Only need to do this comparison on each actually added pair, so do not
+		// fold it into the for loop comparator as a micro-optimization.
+		n -= 1
+		if n == 0 {
+			break
+		}
+	}
 
-__slice_resize :: proc "odin" (array_: ^$T/[]$E, new_count: int, allocator: Allocator, loc := #caller_location) -> bool {
-	array := (^Raw_Slice)(array_)
+	map_free_dynamic(m^, info, loc) or_return
 
-	if new_count < array.len {
-		return true
-	}
+	m.data = shrunk.data
 
-	old_size := array.len*size_of(T)
-	new_size := new_count*size_of(T)
+	return nil
+}
+
+@(require_results)
+map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
+	ptr := rawptr(map_data(m))
+	size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
+	return mem_free_with_size(ptr, size, m.allocator, loc)
+}
 
-	new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc)
-	if err != nil {
+@(require_results)
+map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
+	if map_len(m) == 0 {
+		return 0, false
+	}
+	h := info.key_hasher(rawptr(k), 0)
+	p := map_desired_position(m, h)
+	d := uintptr(0)
+	c := (uintptr(1) << map_log2_cap(m)) - 1
+	ks, _, hs, _, _ := map_kvh_data_dynamic(m, info)
+	for {
+		element_hash := hs[p]
+		if map_hash_is_empty(element_hash) {
+			return 0, false
+		} else if d > map_probe_distance(m, element_hash, p) {
+			return 0, false
+		} else if element_hash == h && info.key_equal(rawptr(k), rawptr(map_cell_index_dynamic(ks, info.ks, p))) {
+			return p, true
+		}
+		p = (p + 1) & c
+		d += 1
+	}
+}
+@(require_results)
+map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
+	if map_len(m) == 0 {
 		return false
 	}
-	if new_data != nil || size_of(E) == 0 {
-		array.data = raw_data(new_data)
-		array.len = new_count
-		return true
+	h := info.key_hasher(rawptr(k), 0)
+	p := map_desired_position(m, h)
+	d := uintptr(0)
+	c := (uintptr(1) << map_log2_cap(m)) - 1
+	ks, _, hs, _, _ := map_kvh_data_dynamic(m, info)
+	for {
+		element_hash := hs[p]
+		if map_hash_is_empty(element_hash) {
+			return false
+		} else if d > map_probe_distance(m, element_hash, p) {
+			return false
+		} else if element_hash == h && info.key_equal(rawptr(k), rawptr(map_cell_index_dynamic(ks, info.ks, p))) {
+			return true
+		}
+		p = (p + 1) & c
+		d += 1
 	}
-	return false
 }
 
-__dynamic_map_reset_entries :: proc "contextless" (h: Map_Header, loc := #caller_location) {
-	for i in 0..<len(h.m.hashes) {
-		h.m.hashes[i] = MAP_SENTINEL
-	}
 
-	for i in 0..<Map_Index(h.m.entries.len) {
-		entry_header := __dynamic_map_get_entry(h, i)
-		entry_header.next = MAP_SENTINEL
 
-		fr := __dynamic_map_find_from_entry(h, entry_header)
-		if fr.entry_prev != MAP_SENTINEL {
-			e := __dynamic_map_get_entry(h, fr.entry_prev)
-			e.next = i
-		} else {
-			h.m.hashes[fr.hash_index] = i
-		}
-	}
+@(require_results)
+map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) {
+	MASK :: 1 << (size_of(Map_Hash)*8 - 1)
+
+	index := map_lookup_dynamic(m^, info, k) or_return
+	ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
+	hs[index] |= MASK
+	old_k = map_cell_index_dynamic(ks, info.ks, index)
+	old_v = map_cell_index_dynamic(vs, info.vs, index)
+	m.len -= 1
+	ok = true
+	return
 }
 
-__dynamic_map_shrink :: proc "odin" (h: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) {
-	c := context
-	if h.m.entries.allocator.procedure != nil {
-		c.allocator = h.m.entries.allocator
+map_clear_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info) {
+	if m.data == 0 {
+		return
 	}
-	context = c
+	_, _, hs, _, _ := map_kvh_data_dynamic(m^, info)
+	intrinsics.mem_zero(rawptr(hs), map_cap(m^) * size_of(Map_Hash))
+	m.len = 0
+}
+
 
-	return __dynamic_array_shrink(&h.m.entries, h.entry_size, h.entry_align, cap, loc)
+@(require_results)
+map_kvh_data_static :: #force_inline proc "contextless" (m: $T/map[$K]$V) -> (ks: [^]Map_Cell(K), vs: [^]Map_Cell(V), hs: [^]Map_Hash) {
+	capacity := uintptr(cap(m))
+	ks = ([^]Map_Cell(K))(map_data(transmute(Raw_Map)m))
+	vs = ([^]Map_Cell(V))(map_cell_index_static(ks, capacity))
+	hs = ([^]Map_Hash)(map_cell_index_static(vs, capacity))
+	return
 }
 
 
-@(private="file")
-ceil_to_pow2 :: proc "contextless" (n: uint) -> uint {
-	if n <= 2 {
-		return n
+@(require_results)
+map_get :: proc "contextless" (m: $T/map[$K]$V, key: K) -> (stored_key: K, stored_value: V, ok: bool) {
+	rm := transmute(Raw_Map)m
+	if rm.len == 0 {
+		return
 	}
-	n := n
-	n -= 1
-	n |= n >> 1
-	n |= n >> 2
-	n |= n >> 4
-	n |= n >> 8
-	n |= n >> 16
-	when size_of(int) == 8 {
-		n |= n >> 32
+	info := intrinsics.type_map_info(T)
+	key := key
+
+	h := info.key_hasher(&key, 0)
+	pos := map_desired_position(rm, h)
+	distance := uintptr(0)
+	mask := (uintptr(1) << map_log2_cap(rm)) - 1
+	ks, vs, hs := map_kvh_data_static(m)
+	for {
+		element_hash := hs[pos]
+		if map_hash_is_empty(element_hash) {
+			return
+		} else if distance > map_probe_distance(rm, element_hash, pos) {
+			return
+		} else if element_hash == h {
+			element_key := map_cell_index_static(ks, pos)
+			if info.key_equal(&key, rawptr(element_key)) {
+				element_value := map_cell_index_static(vs, pos)
+				stored_key   = (^K)(element_key)^
+				stored_value = (^V)(element_value)^
+				ok = true
+				return
+			}
+
+		}
+		pos = (pos + 1) & mask
+		distance += 1
 	}
-	n += 1
-	return n
 }
 
-__dynamic_map_grow :: proc "odin" (h: Map_Header, loc := #caller_location) {
-	new_count := max(uint(h.m.entries.cap) * 2, INITIAL_MAP_CAP)
-	// Rehash through Reserve
-	__dynamic_map_reserve(h.m, h.table, new_count, loc)
+// IMPORTANT: USED WITHIN THE COMPILER
+__dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, key: rawptr) -> (ptr: rawptr) {
+	if m.len == 0 {
+		return nil
+	}
+	pos := map_desired_position(m^, h)
+	distance := uintptr(0)
+	mask := (uintptr(1) << map_log2_cap(m^)) - 1
+	ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
+	for {
+		element_hash := hs[pos]
+		if map_hash_is_empty(element_hash) {
+			return nil
+		} else if distance > map_probe_distance(m^, element_hash, pos) {
+			return nil
+		} else if element_hash == h && info.key_equal(key, rawptr(map_cell_index_dynamic(ks, info.ks, pos))) {
+			return rawptr(map_cell_index_dynamic(vs, info.vs, pos))
+		}
+		pos = (pos + 1) & mask
+		distance += 1
+	}
 }
 
-__dynamic_map_full :: #force_inline proc "contextless" (h: Map_Header) -> bool {
-	return int(0.75 * f64(len(h.m.hashes))) <= h.m.entries.len
+// IMPORTANT: USED WITHIN THE COMPILER
+__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
+	if m.len  >= map_resize_threshold(m^) {
+		return map_grow_dynamic(m, info, loc)
+	}
+	return nil
 }
 
-__dynamic_map_find_from_entry :: proc "contextless" (h: Map_Header, e: ^Map_Entry_Header) -> Map_Find_Result #no_bounds_check {
-	key_ptr := __get_map_entry_key_ptr(h, e)
-	return __dynamic_map_find(h, e.hash, key_ptr)
-
+__dynamic_map_set_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> rawptr {
+	return __dynamic_map_set(m, info, info.key_hasher(key, 0), key, value, loc)
 }
 
-__dynamic_map_find :: proc "contextless" (h: Map_Header, key_hash: uintptr, key_ptr: rawptr) -> Map_Find_Result #no_bounds_check {
-	fr := Map_Find_Result{MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}
-	if n := uintptr(len(h.m.hashes)); n != 0 {
-		fr.hash_index = Map_Index(key_hash & (n-1))
-		fr.entry_index = h.m.hashes[fr.hash_index]
-		for fr.entry_index != MAP_SENTINEL {
-			entry := __dynamic_map_get_entry(h, fr.entry_index)
-			entry_key_ptr := __get_map_entry_key_ptr(h, entry)
-			if entry.hash == key_hash && h.equal(entry_key_ptr, key_ptr) {
-				return fr
-			}
-			
-			fr.entry_prev = fr.entry_index
-			fr.entry_index = entry.next
-		}
+
+// IMPORTANT: USED WITHIN THE COMPILER
+__dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> rawptr {
+	if found := __dynamic_map_get(m, info, hash, key); found != nil {
+		intrinsics.mem_copy_non_overlapping(found, value, info.vs.size_of_type)
+		return found
 	}
-	return fr
-}
 
-// Utility procedure used by other runtime procedures
-__map_find :: proc "contextless" (h: Map_Header, key_ptr: ^$K) -> Map_Find_Result #no_bounds_check {
-	hash := __get_map_key_hash(key_ptr)
-	return #force_inline __dynamic_map_find(h, hash, key_ptr)
+	if __dynamic_map_check_grow(m, info, loc) != nil {
+		return nil
+	}
+
+	result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
+	m.len += 1
+	return rawptr(result)
 }
 
-__dynamic_map_get_entry :: #force_inline proc "contextless" (h: Map_Header, index: Map_Index) -> ^Map_Entry_Header {
-	return (^Map_Entry_Header)(uintptr(h.m.entries.data) + uintptr(index*Map_Index(h.entry_size)))
+// IMPORTANT: USED WITHIN THE COMPILER
+@(private)
+__dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error {
+	return map_reserve_dynamic(m, info, uintptr(new_capacity), loc)
 }
 
-__dynamic_map_erase :: proc "contextless" (h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
-	if fr.entry_prev != MAP_SENTINEL {
-		prev := __dynamic_map_get_entry(h, fr.entry_prev)
-		curr := __dynamic_map_get_entry(h, fr.entry_index)
-		prev.next = curr.next
-	} else {
-		h.m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next
+
+
+// NOTE: the default hashing algorithm derives from fnv64a, with some minor modifications to work for `map` type:
+//
+//     * Convert a `0` result to `1`
+//         * "empty entry"
+//     * Prevent the top bit from being set
+//         * "deleted entry"
+//
+// Both of these modification are necessary for the implementation of the `map`
+
+INITIAL_HASH_SEED :: 0xcbf29ce484222325
+
+HASH_MASK :: 1 << (8*size_of(uintptr) - 1) -1
+
+default_hasher :: #force_inline proc "contextless" (data: rawptr, seed: uintptr, N: int) -> uintptr {
+	h := u64(seed) + INITIAL_HASH_SEED
+	p := ([^]byte)(data)
+	for _ in 0..<N {
+		h = (h ~ u64(p[0])) * 0x100000001b3
+		p = p[1:]
 	}
-	last_index := Map_Index(h.m.entries.len-1)
-	if fr.entry_index != last_index {
-		old := __dynamic_map_get_entry(h, fr.entry_index)
-		end := __dynamic_map_get_entry(h, last_index)
-		mem_copy(old, end, h.entry_size)
-
-		last := __dynamic_map_find_from_entry(h, old)
-		if last.entry_prev != MAP_SENTINEL {
-			e := __dynamic_map_get_entry(h, last.entry_prev)
-			e.next = fr.entry_index
-		} else {
-			h.m.hashes[last.hash_index] = fr.entry_index
+	h &= HASH_MASK
+	return uintptr(h) | uintptr(uintptr(h) == 0)
+}
+
+default_hasher_string :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
+	str := (^[]byte)(data)
+	return default_hasher(raw_data(str^), seed, len(str))
+}
+default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr {
+	h := u64(seed) + INITIAL_HASH_SEED
+	if ptr := (^[^]byte)(data)^; ptr != nil {
+		for ptr[0] != 0 {
+			h = (h ~ u64(ptr[0])) * 0x100000001b3
+			ptr = ptr[1:]
 		}
 	}
-
-	h.m.entries.len -= 1
+	h &= HASH_MASK
+	return uintptr(h) | uintptr(uintptr(h) == 0)
 }

+ 7 - 0
core/runtime/internal.odin

@@ -145,6 +145,13 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
 	return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
 }
 
+mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	if size == 0 || allocator.procedure == nil {
+		return nil, nil
+	}
+	return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
+}
+
 mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
 	if ptr == nil || allocator.procedure == nil {
 		return nil

+ 6 - 6
core/runtime/os_specific_windows.odin

@@ -58,9 +58,9 @@ _os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_b
 	return
 }
 
-heap_alloc :: proc "contextless" (size: int) -> rawptr {
+heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
 	HEAP_ZERO_MEMORY :: 0x00000008
-	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uint(size))
+	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
 }
 heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
 	if new_size == 0 {
@@ -91,7 +91,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
 
 
 
-_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
+_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
 	if size == 0 {
 		_windows_default_free(old_ptr)
 		return nil, nil
@@ -105,7 +105,7 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
 		original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
 		allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
 	} else {
-		allocated_mem = heap_alloc(space+size_of(rawptr))
+		allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
 	}
 	aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
 
@@ -122,8 +122,8 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
 	return byte_slice(aligned_mem, size), nil
 }
 
-_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
-	return _windows_default_alloc_or_resize(size, alignment, nil)
+_windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
+	return _windows_default_alloc_or_resize(size, alignment, nil, zero_memory)
 }
 
 

+ 22 - 26
core/slice/map.odin

@@ -6,8 +6,8 @@ import "core:runtime"
 _ :: intrinsics
 _ :: runtime
 
-map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
-	keys = make(type_of(keys), len(m), allocator)
+map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K, err: runtime.Allocator_Error) {
+	keys = make(type_of(keys), len(m), allocator) or_return
 	i := 0
 	for key in m {
 		keys[i] = key
@@ -15,8 +15,8 @@ map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K)
 	}
 	return
 }
-map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V) {
-	values = make(type_of(values), len(m), allocator)
+map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V, err: runtime.Allocator_Error) {
+	values = make(type_of(values), len(m), allocator) or_return
 	i := 0
 	for _, value in m {
 		values[i] = value
@@ -37,8 +37,8 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
 }
 
 
-map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V)) {
-	entries = make(type_of(entries), len(m), allocator)
+map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator) {
+	entries = make(type_of(entries), len(m), allocator) or_return
 	i := 0
 	for key, value in m {
 		entries[i].key   = key
@@ -52,28 +52,24 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
 	m := m
 	rm := (^runtime.Raw_Map)(&m)
 
-	info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
-	gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
-	ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
-	entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
-	key_offset :=  entry_type.offsets[2]
-	value_offset :=  entry_type.offsets[3]
-	entry_size := uintptr(ed.elem_size)
+	info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
+	if info.map_info != nil {
+		entries = make(type_of(entries), len(m), allocator) or_return
 
-	entries = make(type_of(entries), rm.entries.len)
+		map_cap := uintptr(cap(m))
+		ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
+		entry_index := 0
+		for bucket_index in 0..<map_cap {
+			if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
+				key   := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
+				value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
+				entries[entry_index].hash  = hash
+				entries[entry_index].key   = (^K)(key)^
+				entries[entry_index].value = (^V)(value)^
 
-	data := uintptr(rm.entries.data)
-	for i in 0..<rm.entries.len {
-		header := (^runtime.Map_Entry_Header)(data)
-
-		hash  := header.hash
-		key   := (^K)(data + key_offset)^
-		value := (^V)(data + value_offset)^
-
-		entries[i] = {hash, key, value}
-
-		data += entry_size
+				entry_index += 1
+			}
+		}
 	}
-
 	return
 }

+ 83 - 0
core/strconv/decimal/decimal.odin

@@ -9,6 +9,89 @@ Decimal :: struct {
 	neg, trunc:    bool,
 }
 
+set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
+	d^ = {}
+
+	if len(s) == 0 {
+		return
+	}
+
+	i := 0
+	switch s[i] {
+	case '+': i += 1
+	case '-': i += 1; d.neg = true
+	}
+
+	// digits
+	saw_dot := false
+	saw_digits := false
+	for ; i < len(s); i += 1 {
+		switch {
+		case s[i] == '_':
+			// ignore underscores
+			continue
+		case s[i] == '.':
+			if saw_dot {
+				return
+			}
+			saw_dot = true
+			d.decimal_point = d.count
+			continue
+
+		case '0' <= s[i] && s[i] <= '9':
+			saw_digits = true
+			if s[i] == '0' && d.count == 0 {
+				d.decimal_point -= 1
+				continue
+			}
+			if d.count < len(d.digits) {
+				d.digits[d.count] = s[i]
+				d.count += 1
+			} else if s[i] != '0' {
+				d.trunc = true
+			}
+			continue
+		}
+		break
+	}
+	if !saw_digits {
+		return
+	}
+	if !saw_dot {
+		d.decimal_point = d.count
+	}
+
+	lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
+
+	if i < len(s) && lower(s[i]) == 'e' {
+		i += 1
+		if i >= len(s) {
+			return
+		}
+		exp_sign := 1
+		switch s[i] {
+		case '+': i += 1
+		case '-': i += 1; exp_sign = -1
+		}
+		if i >= len(s) || s[i] < '0' || s[i] > '9' {
+			return
+		}
+		e := 0
+		for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
+			if s[i] == '_' {
+				// ignore underscores
+				continue
+			}
+			if e < 1e4 {
+				e = e*10 + int(s[i]) - '0'
+			}
+		}
+		d.decimal_point += e * exp_sign
+	}
+
+	return i == len(s)
+}
+
 decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
 	digit_zero :: proc(buf: []byte) -> int {
 		for _, i in buf {

+ 86 - 0
core/strconv/generic_float.odin

@@ -284,3 +284,89 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
 	}
 
 }
+
+@(private)
+decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
+	end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (b: u64) {
+		bits := mant & (u64(1)<<info.mantbits - 1)
+		bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
+		if d.neg {
+			bits |= 1<< info.mantbits << info.expbits
+		}
+		return bits
+	}
+	set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
+		mant^ = 0
+		exp^ = 1<<info.expbits - 1 + info.bias
+		return true
+	}
+
+	mant: u64
+	exp: int
+	if d.decimal_point == 0 {
+		mant = 0
+		exp = info.bias
+		b = end(d, mant, exp, info)
+		return
+	}
+
+	if d.decimal_point > 310 {
+		set_overflow(&mant, &exp, info)
+		b = end(d, mant, exp, info)
+		return
+	} else if d.decimal_point < -330 {
+		mant = 0
+		exp = info.bias
+		b = end(d, mant, exp, info)
+		return
+	}
+
+	@static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
+
+	exp = 0
+	for d.decimal_point > 0 {
+		n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
+		decimal.shift(d, n)
+		exp += n
+	}
+	for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
+		n := 27 if -d.decimal_point >= len(power_table) else power_table[-d.decimal_point]
+		decimal.shift(d, n)
+		exp -= n
+	}
+
+	// go from [0.5, 1) to [1, 2)
+	exp -= 1
+
+	if exp < info.bias + 1 {
+		n := info.bias + 1 - exp
+		decimal.shift(d, n)
+		exp += n
+	}
+
+	if (exp-info.bias) >= (1<<info.expbits - 1) {
+		set_overflow(&mant, &exp, info)
+		b = end(d, mant, exp, info)
+		return
+	}
+
+	decimal.shift(d, int(1 + info.mantbits))
+	mant = decimal.rounded_integer(d)
+
+	if mant == 2<<info.mantbits {
+		mant >>= 1
+		exp += 1
+		if (exp-info.bias) >= (1<<info.expbits - 1) {
+			set_overflow(&mant, &exp, info)
+			b = end(d, mant, exp, info)
+			return
+		}
+	}
+
+	if mant & (1<<info.mantbits) == 0 {
+		exp = info.bias
+	}
+
+	b = end(d, mant, exp, info)
+	return
+}

+ 252 - 98
core/strconv/strconv.odin

@@ -1,6 +1,7 @@
 package strconv
 
 import "core:unicode/utf8"
+import "decimal"
 
 parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
 	switch s {
@@ -532,6 +533,8 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o
 parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
 
 
+@(private)
+lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
 
 
 
@@ -566,133 +569,284 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
 // assert(n == 12.34 && ok);
 // ```
 parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
-	s := str
-	defer if n != nil { n^ = len(str) - len(s) }
-	if s == "" {
-		return
+	common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int {
+		n := len(prefix)
+		if n > len(s) {
+			n = len(s)
+		}
+		for i in 0..<n {
+			c := s[i]
+			if 'A' <= c && c <= 'Z' {
+				c += 'a' - 'A'
+			}
+			if c != prefix[i] {
+				return i
+			}
+		}
+		return n
 	}
-
-	i := 0
-
-	sign: f64 = 1
-	seen_sign := true
-	switch s[i] {
-	case '-': i += 1; sign = -1
-	case '+': i += 1
-	case: seen_sign = false
+	check_special :: proc "contextless" (s: string) -> (f: f64, n: int, ok: bool) {
+		s := s
+		if len(s) > 0 {
+			sign := 1
+			nsign := 0
+			switch s[0] {
+			case '+', '-':
+				if s[0] == '-' {
+					sign = -1
+				}
+				nsign = 1
+				s = s[1:]
+				fallthrough
+			case 'i', 'I':
+				n := common_prefix_len_ignore_case(s, "infinity")
+				if 3 < n && n < 8 { // "inf" or "infinity"
+					n = 3
+				}
+				if n == 3 || n == 8 {
+					f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
+					n = nsign + 3
+					ok = true
+					return
+				}
+			case 'n', 'N':
+				if common_prefix_len_ignore_case(s, "nan") == 3 {
+					f = 0h7ff80000_00000001
+					n = nsign + 3
+					ok = true
+					return
+				}
+			}
+		}
+		return
 	}
+	parse_components :: proc "contextless" (s: string) -> (mantissa: u64, exp: int, neg, trunc, hex: bool, i: int, ok: bool) {
+		if len(s) == 0 {
+			return
+		}
+		switch s[i] {
+		case '+': i += 1
+		case '-': i += 1; neg = true
+		}
 
-	for ; i < len(s); i += 1 {
-		r := rune(s[i])
-		if r == '_' {
-			continue
+		base := u64(10)
+		MAX_MANT_DIGITS := 19
+		exp_char := byte('e')
+		// support stupid 0x1.ABp100 hex floats even if Odin doesn't
+		if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' {
+			base = 16
+			MAX_MANT_DIGITS = 16
+			i += 2
+			exp_char = 'p'
+			hex = true
 		}
 
-		v := _digit_value(r)
-		if v >= 10 {
-			if r == '.' || r == 'e' || r == 'E' { // Skip parsing NaN and Inf if it's probably a regular float
-				break
-			}
-			if len(s) >= 3 + i {
-				buf: [4]u8
-				copy(buf[:], s[i:][:3])
-
-				v2 := transmute(u32)buf
-				v2 &= 0xDFDFDFDF // Knock out lower-case bits
-
-				buf = transmute([4]u8)v2
-
-				when ODIN_ENDIAN == .Little {
-					if v2 == 0x464e49 { // "INF"
-						s = s[3+i:]
-						value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
-						return value, len(s) == 0
-					} else if v2 == 0x4e414e { // "NAN"
-						s = s[3+i:]
-						return 0h7ff80000_00000001, len(s) == 0
-					}
+		underscores := false
+		saw_dot, saw_digits := false, false
+		nd := 0
+		nd_mant := 0
+		decimal_point := 0
+		loop: for ; i < len(s); i += 1 {
+			switch c := s[i]; true {
+			case c == '_':
+				underscores = true
+				continue loop
+			case c == '.':
+				if saw_dot {
+					break loop
+				}
+				saw_dot = true
+				decimal_point = nd
+				continue loop
+
+			case '0' <= c && c <= '9':
+				saw_digits = true
+				if c == '0' && nd == 0 {
+					decimal_point -= 1
+					continue loop
+				}
+				nd += 1
+				if nd_mant < MAX_MANT_DIGITS {
+					mantissa *= base
+					mantissa += u64(c - '0')
+					nd_mant += 1
+				} else if c != '0' {
+					trunc = true
+				}
+				continue loop
+			case base == 16 && 'a' <= lower(c) && lower(c) <= 'f':
+				saw_digits = true
+				nd += 1
+				if nd_mant < MAX_MANT_DIGITS {
+					MAX_MANT_DIGITS *= 16
+					MAX_MANT_DIGITS += int(lower(c) - 'a' + 10)
+					nd_mant += 1
 				} else {
-					if v2 == 0x494e4600 { // "\0FNI"
-						s = s[3+i:]
-						value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
-						return value, len(s) == 0
-					} else if v2 == 0x4e414e00 { // "\0NAN"
-						s = s[3+i:]
-						return 0h7ff80000_00000001, len(s) == 0
-					}
+					trunc = true
 				}
+				continue loop
 			}
-			break
+			break loop
 		}
-		value *= 10
-		value += f64(v)
-	}
 
-	if i < len(s) && s[i] == '.' {
-		pow10: f64 = 10
-		i += 1
+		if !saw_digits {
+			return
+		}
+		if !saw_dot {
+			decimal_point = nd
+		}
+		if base == 16 {
+			decimal_point *= 4
+			nd_mant *= 4
+		}
 
-		for ; i < len(s); i += 1 {
-			r := rune(s[i])
-			if r == '_' {
-				continue
+		if i < len(s) && lower(s[i]) == exp_char {
+			i += 1
+			if i >= len(s) { return }
+			exp_sign := 1
+			switch s[i] {
+			case '+': i += 1
+			case '-': i += 1; exp_sign = -1
 			}
-
-			v := _digit_value(r)
-			if v >= 10 {
-				break
+			if i >= len(s) || s[i] < '0' || s[i] > '9' {
+				return
+			}
+			e := 0
+			for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
+				if s[i] == '_' {
+					underscores = true
+					continue
+				}
+				if e < 1e5 {
+					e = e*10 + int(s[i]) - '0'
+				}
 			}
-			value += f64(v)/pow10
-			pow10 *= 10
+			decimal_point += e * exp_sign
+		} else if base == 16 {
+			return
+		}
+
+		if mantissa != 0 {
+			exp = decimal_point - nd_mant
 		}
+		// TODO(bill): check underscore correctness
+		ok = true
+		return
 	}
 
-	frac := false
-	scale: f64 = 1
+	parse_hex :: proc(s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
+		info := &_f64_info
 
-	if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
-		i += 1
+		mantissa, exp := mantissa, exp
 
-		if i < len(s) {
-			switch s[i] {
-			case '-': i += 1; frac = true
-			case '+': i += 1
-			}
+		MAX_EXP := 1<<info.expbits + info.bias - 2
+		MIN_EXP := info.bias + 1
+		exp += int(info.mantbits)
 
-			exp: u32 = 0
-			for ; i < len(s); i += 1 {
-				r := rune(s[i])
-				if r == '_' {
-					continue
-				}
+		for mantissa != 0 && mantissa >> (info.mantbits+2) == 0 {
+			mantissa <<= 1
+			exp -= 1
+		}
+		if trunc {
+			mantissa |= 1
+		}
 
-				d := u32(_digit_value(r))
-				if d >= 10 {
-					break
-				}
-				exp = exp * 10 + d
+		for mantissa >> (info.mantbits+2) == 0 {
+			mantissa = mantissa>>1 | mantissa&1
+			exp += 1
+		}
+
+		// denormalize
+		if mantissa > 1 && exp < MIN_EXP-2 {
+			mantissa = mantissa>>1 | mantissa&1
+			exp += 1
+		}
+
+		round := mantissa & 3
+		mantissa >>= 2
+		round |= mantissa & 1 // round to even
+		exp += 2
+		if round == 3 {
+			mantissa += 1
+			if mantissa == 1 << (1 + info.mantbits) {
+				mantissa >>= 1
+				exp += 1
 			}
-			if exp > 308 { exp = 308 }
+		}
+		if mantissa>>info.mantbits == 0 {
+			// zero or denormal
+			exp = info.bias
+		}
+
+		ok := true
+		if exp > MAX_EXP {
+			// infinity or invalid
+			mantissa = 1<<info.mantbits
+			exp = MAX_EXP + 1
+			ok = false
+		}
 
-			for exp >= 50 { scale *= 1e50; exp -= 50 }
-			for exp >=  8 { scale *=  1e8; exp -=  8 }
-			for exp >   0 { scale *=   10; exp -=  1 }
+		bits := mantissa & (1<<info.mantbits - 1)
+		bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
+		if neg {
+			bits |= 1 << info.mantbits << info.expbits
 		}
+		return transmute(f64)bits, ok
 	}
 
-	// If we only consumed a sign, return false
-	if i == 1 && seen_sign {
-		return 0, false
+
+	nr: int
+	defer if n != nil { n^ = nr }
+
+	if value, nr, ok = check_special(str); ok {
+		return
 	}
 
-	s = s[i:]
-	if frac {
-		value = sign * (value/scale)
-	} else {
-		value = sign * (value*scale)
+	mantissa: u64
+	exp:      int
+	neg, trunc, hex: bool
+	mantissa, exp, neg, trunc, hex, nr = parse_components(str) or_return
+
+	if hex {
+		return parse_hex(str, mantissa, exp, neg, trunc)
 	}
 
-	ok = len(s) == 0
+	trunc_block: if !trunc {
+		@static pow10 := [?]f64{
+			1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,
+			1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+			1e20, 1e21, 1e22,
+		}
+
+		if mantissa>>_f64_info.mantbits != 0 {
+			return
+		}
+		f := f64(mantissa)
+		if neg {
+			f = -f
+		}
+		switch {
+		case exp == 0:
+			return f, true
+		case exp > 0 && exp <= 15+22:
+			if exp > 22 {
+				f *= pow10[exp-22]
+				exp = 22
+			}
+			if f > 1e15 || f < 1e-15 {
+				break trunc_block
+			}
+			return f * pow10[exp], true
+		case -22 <= exp && exp < 0:
+			return f / pow10[-exp], true
+		}
+	}
+
+	d: decimal.Decimal
+	decimal.set(&d, str[:nr])
+	b, overflow := decimal_to_float_bits(&d, &_f64_info)
+	value = transmute(f64)b
+	ok = !overflow
 	return
 }
 

+ 44 - 0
core/strings/builder.odin

@@ -299,6 +299,50 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
 	return
 }
 
+// writes a f64 value into the builder, returns the written amount of characters
+write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
+	buf: [384]byte
+	s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
+	// If the result starts with a `+` then unless we always want signed results,
+	// we skip it unless it's followed by an `I` (because of +Inf).
+	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
+		s = s[1:]
+	}
+	return write_string(b, s)
+}
+
+// writes a f16 value into the builder, returns the written amount of characters
+write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
+	buf: [384]byte
+	s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
+	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
+		s = s[1:]
+	}
+	return write_string(b, s)
+}
+
+// writes a f32 value into the builder, returns the written amount of characters
+write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
+	buf: [384]byte
+	s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
+	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
+		s = s[1:]
+	}
+	return write_string(b, s)
+}
+
+// writes a f64 value into the builder, returns the written amount of characters
+write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
+	buf: [384]byte
+	s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
+	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
+		s = s[1:]
+	}
+	return write_string(b, s)
+}
+
+
+
 // writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
 write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
 	buf: [32]byte

+ 1 - 1
core/sys/info/doc.odin

@@ -13,7 +13,7 @@
 	CPU feature flags can be tested against `cpu_features`, where applicable, e.g.
 	`if .aes in si.aes { ... }`
 */
-// +ignore
+//+build ignore
 package sysinfo
 
 import "core:fmt"

+ 1 - 0
core/sys/windows/shell32.odin

@@ -14,6 +14,7 @@ foreign shell32 {
 		lpDirectory: LPCWSTR,
 		nShowCmd: INT,
 	) -> HINSTANCE ---
+	ShellExecuteExW :: proc(pExecInfo: ^SHELLEXECUTEINFOW) -> BOOL ---
 	SHCreateDirectoryExW :: proc(
 		hwnd: HWND,
 		pszPath: LPCWSTR,

+ 52 - 0
core/sys/windows/types.odin

@@ -1419,6 +1419,58 @@ WMSZ_BOTTOM      :: 6
 WMSZ_BOTTOMLEFT  :: 7
 WMSZ_BOTTOMRIGHT :: 8
 
+// Note CLASSKEY overrides CLASSNAME
+SEE_MASK_DEFAULT   :: 0x00000000
+SEE_MASK_CLASSNAME :: 0x00000001   // SHELLEXECUTEINFO.lpClass is valid
+SEE_MASK_CLASSKEY  :: 0x00000003   // SHELLEXECUTEINFO.hkeyClass is valid
+// Note SEE_MASK_INVOKEIDLIST(0xC) implies SEE_MASK_IDLIST(0x04)
+SEE_MASK_IDLIST            :: 0x00000004   // SHELLEXECUTEINFO.lpIDList is valid
+SEE_MASK_INVOKEIDLIST      :: 0x0000000c   // enable IContextMenu based verbs
+SEE_MASK_ICON              :: 0x00000010   // not used
+SEE_MASK_HOTKEY            :: 0x00000020   // SHELLEXECUTEINFO.dwHotKey is valid
+SEE_MASK_NOCLOSEPROCESS    :: 0x00000040   // SHELLEXECUTEINFO.hProcess
+SEE_MASK_CONNECTNETDRV     :: 0x00000080   // enables re-connecting disconnected network drives
+SEE_MASK_NOASYNC           :: 0x00000100   // block on the call until the invoke has completed, use for callers that exit after calling ShellExecuteEx()
+SEE_MASK_FLAG_DDEWAIT      :: SEE_MASK_NOASYNC // Use SEE_MASK_NOASYNC instead of SEE_MASK_FLAG_DDEWAIT as it more accuratly describes the behavior
+SEE_MASK_DOENVSUBST        :: 0x00000200   // indicates that SHELLEXECUTEINFO.lpFile contains env vars that should be expanded
+SEE_MASK_FLAG_NO_UI        :: 0x00000400   // disable UI including error messages
+SEE_MASK_UNICODE           :: 0x00004000
+SEE_MASK_NO_CONSOLE        :: 0x00008000
+SEE_MASK_ASYNCOK           :: 0x00100000
+SEE_MASK_HMONITOR          :: 0x00200000   // SHELLEXECUTEINFO.hMonitor
+SEE_MASK_NOZONECHECKS      :: 0x00800000
+SEE_MASK_NOQUERYCLASSSTORE :: 0x01000000
+SEE_MASK_WAITFORINPUTIDLE  :: 0x02000000
+SEE_MASK_FLAG_LOG_USAGE    :: 0x04000000
+
+// When SEE_MASK_FLAG_HINST_IS_SITE is specified SHELLEXECUTEINFO.hInstApp is used as an
+// _In_ parameter and specifies a IUnknown* to be used as a site pointer. The site pointer
+// is used to provide services to shell execute, the handler binding process and the verb handlers
+// once they are invoked.
+SEE_MASK_FLAG_HINST_IS_SITE :: 0x08000000
+
+SHELLEXECUTEINFOW :: struct {
+	cbSize: DWORD,               // in, required, sizeof of this structure
+	fMask: ULONG,                // in, SEE_MASK_XXX values
+	hwnd: HWND,                  // in, optional
+	lpVerb: LPCWSTR,            // in, optional when unspecified the default verb is choosen
+	lpFile: LPCWSTR,            // in, either this value or lpIDList must be specified
+	lpParameters: LPCWSTR,      // in, optional
+	lpDirectory: LPCWSTR,       // in, optional
+	nShow: c.int,                  // in, required
+	hInstApp: HINSTANCE,         // out when SEE_MASK_NOCLOSEPROCESS is specified
+	lpIDList: rawptr,             // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLIST
+	lpClass: LPCWSTR,           // in, valid when SEE_MASK_CLASSNAME is specified
+	hkeyClass: HKEY,             // in, valid when SEE_MASK_CLASSKEY is specified
+	dwHotKey: DWORD,             // in, valid when SEE_MASK_HOTKEY is specified
+	DUMMYUNIONNAME: struct #raw_union {
+		hIcon: HANDLE,           // not used
+		hMonitor: HANDLE,        // in, valid when SEE_MASK_HMONITOR specified
+	},
+	hProcess: HANDLE,            // out, valid when SEE_MASK_NOCLOSEPROCESS specified
+}
+LPSHELLEXECUTEINFOW :: ^SHELLEXECUTEINFOW
+
 // Key State Masks for Mouse Messages
 MK_LBUTTON  :: 0x0001
 MK_RBUTTON  :: 0x0002

+ 413 - 0
core/text/edit/text_edit.odin

@@ -0,0 +1,413 @@
+package text_edit
+
+/*
+	Based off the articles by rxi:
+		* https://rxi.github.io/textbox_behaviour.html
+		* https://rxi.github.io/a_simple_undo_system.html
+*/
+
+import "core:runtime"
+import "core:time"
+import "core:mem"
+import "core:strings"
+import "core:unicode/utf8"
+
+DEFAULT_UNDO_TIMEOUT :: 300 * time.Millisecond
+
+State :: struct {
+	selection: [2]int,
+	line_start, line_end: int,
+
+	// initialized each "frame" with `begin`
+	builder: ^strings.Builder, // let the caller store the text buffer data
+
+	up_index, down_index: int, // multi-lines
+
+
+	// undo
+	undo: [dynamic]^Undo_State,
+	redo: [dynamic]^Undo_State,
+	undo_text_allocator: runtime.Allocator,
+
+	id: u64, // useful for immediate mode GUIs
+
+	// Timeout information
+	current_time:   time.Tick,
+	last_edit_time: time.Tick,
+	undo_timeout:   time.Duration,
+
+	// Set these if you want cut/copy/paste functionality
+	set_clipboard: proc(user_data: rawptr, text: string) -> (ok: bool),
+	get_clipboard: proc(user_data: rawptr) -> (text: string, ok: bool),
+	clipboard_user_data: rawptr,
+}
+
+Undo_State :: struct {
+	selection: [2]int,
+	len:       int,
+	text:      [0]byte, // string(us.text[:us.len]) --- requiring #no_bounds_check
+}
+
+Translation :: enum u32 {
+	Start,
+	End,
+	Left,
+	Right,
+	Up,
+	Down,
+	Word_Left,
+	Word_Right,
+	Word_Start,
+	Word_End,
+	Soft_Line_Start,
+	Soft_Line_End,
+}
+
+
+init :: proc(s: ^State, undo_text_allocator, undo_state_allocator: runtime.Allocator, undo_timeout := DEFAULT_UNDO_TIMEOUT) {
+	s.undo_timeout = undo_timeout
+
+	// Used for allocating `Undo_State`
+	s.undo_text_allocator = undo_text_allocator
+
+	s.undo.allocator = undo_state_allocator
+	s.redo.allocator = undo_state_allocator
+}
+
+destroy :: proc(s: ^State) {
+	undo_clear(s, &s.undo)
+	undo_clear(s, &s.redo)
+	delete(s.undo)
+	delete(s.redo)
+	s.builder = nil
+}
+
+
+// Call at the beginning of each frame
+begin :: proc(s: ^State, id: u64, builder: ^strings.Builder) {
+	assert(builder != nil)
+	if s.id != 0 {
+		end(s)
+	}
+	s.id = id
+	s.selection = {len(builder.buf), 0}
+	s.builder = builder
+	s.current_time = time.tick_now()
+	if s.undo_timeout <= 0 {
+		s.undo_timeout = DEFAULT_UNDO_TIMEOUT
+	}
+	set_text(s, string(s.builder.buf[:]))
+	undo_clear(s, &s.undo)
+	undo_clear(s, &s.redo)
+}
+
+// Call at the end of each frame
+end :: proc(s: ^State) {
+	s.id = 0
+	s.builder = nil
+}
+
+set_text :: proc(s: ^State, text: string) {
+	strings.builder_reset(s.builder)
+	strings.write_string(s.builder, text)
+}
+
+
+undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
+	text := string(s.builder.buf[:])
+	item := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator))
+	item.selection = s.selection
+	item.len = len(text)
+	#no_bounds_check {
+		runtime.copy(item.text[:len(text)], text)
+	}
+	append(undo, item)
+}
+
+undo :: proc(s: ^State, undo, redo: ^[dynamic]^Undo_State) {
+	if len(undo) > 0 {
+		undo_state_push(s, redo)
+		item := pop(undo)
+		s.selection = item.selection
+		#no_bounds_check {
+			set_text(s, string(item.text[:item.len]))
+		}
+		free(item, s.undo_text_allocator)
+	}
+}
+
+undo_clear :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
+	for len(undo) > 0 {
+		item := pop(undo)
+		free(item, s.undo_text_allocator)
+	}
+}
+
+undo_check :: proc(s: ^State) {
+	undo_clear(s, &s.redo)
+	if time.tick_diff(s.last_edit_time, s.current_time) > s.undo_timeout {
+		undo_state_push(s, &s.undo)
+	}
+	s.last_edit_time = s.current_time
+}
+
+
+
+input_text :: proc(s: ^State, text: string) {
+	if len(text) == 0 {
+		return
+	}
+	if has_selection(s) {
+		selection_delete(s)
+	}
+	insert(s, s.selection[0], text)
+	offset := s.selection[0] + len(text)
+	s.selection = {offset, offset}
+}
+
+input_runes :: proc(s: ^State, text: []rune) {
+	if len(text) == 0 {
+		return
+	}
+	if has_selection(s) {
+		selection_delete(s)
+	}
+	offset := s.selection[0]
+	for r in text {
+		b, w := utf8.encode_rune(r)
+		insert(s, offset, string(b[:w]))
+		offset += w
+	}
+	s.selection = {offset, offset}
+}
+
+
+insert :: proc(s: ^State, at: int, text: string) {
+	undo_check(s)
+	inject_at(&s.builder.buf, at, text)
+}
+
+remove :: proc(s: ^State, lo, hi: int) {
+	undo_check(s)
+	remove_range(&s.builder.buf, lo, hi)
+}
+
+
+
+has_selection :: proc(s: ^State) -> bool {
+	return s.selection[0] != s.selection[1]
+}
+
+sorted_selection :: proc(s: ^State) -> (lo, hi: int) {
+	lo = min(s.selection[0], s.selection[1])
+	hi = max(s.selection[0], s.selection[1])
+	lo = clamp(lo, 0, len(s.builder.buf))
+	hi = clamp(hi, 0, len(s.builder.buf))
+	s.selection[0] = lo
+	s.selection[1] = hi
+	return
+}
+
+
+selection_delete :: proc(s: ^State) {
+	lo, hi := sorted_selection(s)
+	remove(s, lo, hi)
+	s.selection = {lo, lo}
+}
+
+
+
+translate_position :: proc(s: ^State, pos: int, t: Translation) -> int {
+	is_continuation_byte :: proc(b: byte) -> bool {
+		return b <= 0x80 && b < 0xc0
+	}
+	is_space :: proc(b: byte) -> bool {
+		return b == ' ' || b == '\t' || b == '\n'
+	}
+
+	buf := s.builder.buf[:]
+
+	pos := pos
+	pos = clamp(pos, 0, len(buf))
+
+	switch t {
+	case .Start:
+		pos = 0
+	case .End:
+		pos = len(buf)
+	case .Left:
+		pos -= 1
+		for pos >= 0 && is_continuation_byte(buf[pos]) {
+			pos -= 1
+		}
+	case .Right:
+		pos += 1
+		for pos < len(buf) && is_continuation_byte(buf[pos]) {
+			pos += 1
+		}
+	case .Up:
+		pos = s.up_index
+	case .Down:
+		pos = s.down_index
+	case .Word_Left:
+		for pos > 0 && is_space(buf[pos-1]) {
+			pos -= 1
+		}
+		for pos > 0 && !is_space(buf[pos-1]) {
+			pos -= 1
+		}
+	case .Word_Right:
+		for pos < len(buf) && !is_space(buf[pos]) {
+			pos += 1
+		}
+		for pos < len(buf) && is_space(buf[pos]) {
+			pos += 1
+		}
+	case .Word_Start:
+		for pos > 0 && !is_space(buf[pos-1]) {
+			pos -= 1
+		}
+	case .Word_End:
+		for pos < len(buf) && !is_space(buf[pos]) {
+			pos += 1
+		}
+	case .Soft_Line_Start:
+		pos = s.line_start
+	case .Soft_Line_End:
+		pos = s.line_end
+	}
+	return clamp(pos, 0, len(buf))
+}
+
+move_to :: proc(s: ^State, t: Translation) {
+	if t == .Left && has_selection(s) {
+		lo, _ := sorted_selection(s)
+		s.selection = {lo, lo}
+	} else if t == .Right && has_selection(s) {
+		_, hi := sorted_selection(s)
+		s.selection = {hi, hi}
+	} else {
+		pos := translate_position(s, s.selection[0], t)
+		s.selection = {pos, pos}
+	}
+}
+select_to :: proc(s: ^State, t: Translation) {
+	s.selection[0] = translate_position(s, s.selection[0], t)
+}
+delete_to :: proc(s: ^State, t: Translation) {
+	if has_selection(s) {
+		selection_delete(s)
+	} else {
+		lo := s.selection[0]
+		hi := translate_position(s, lo, t)
+		lo, hi = min(lo, hi), max(lo, hi)
+		remove(s, lo, hi)
+		s.selection = {lo, lo}
+	}
+}
+
+
+current_selected_text :: proc(s: ^State) -> string {
+	lo, hi := sorted_selection(s)
+	return string(s.builder.buf[lo:hi])
+}
+
+
+cut :: proc(s: ^State) -> bool {
+	if copy(s) {
+		selection_delete(s)
+		return true
+	}
+	return false
+}
+
+copy :: proc(s: ^State) -> bool {
+	if s.set_clipboard != nil {
+		return s.set_clipboard(s.clipboard_user_data, current_selected_text(s))
+	}
+	return s.set_clipboard != nil
+}
+
+paste :: proc(s: ^State) -> bool {
+	if s.get_clipboard != nil {
+		input_text(s, s.get_clipboard(s.clipboard_user_data) or_return)
+	}
+	return s.get_clipboard != nil
+}
+
+
+Command_Set :: distinct bit_set[Command; u32]
+
+Command :: enum u32 {
+	None,
+	Undo,
+	Redo,
+	New_Line,    // multi-lines
+	Cut,
+	Copy,
+	Paste,
+	Select_All,
+	Backspace,
+	Delete,
+	Delete_Word_Left,
+	Delete_Word_Right,
+	Left,
+	Right,
+	Up,          // multi-lines
+	Down,        // multi-lines
+	Word_Left,
+	Word_Right,
+	Start,
+	End,
+	Line_Start,
+	Line_End,
+	Select_Left,
+	Select_Right,
+	Select_Up,   // multi-lines
+	Select_Down, // multi-lines
+	Select_Word_Left,
+	Select_Word_Right,
+	Select_Start,
+	Select_End,
+	Select_Line_Start,
+	Select_Line_End,
+}
+
+MULTILINE_COMMANDS :: Command_Set{.New_Line, .Up, .Down, .Select_Up, .Select_Down}
+
+perform_command :: proc(s: ^State, cmd: Command) {
+	switch cmd {
+	case .None:              /**/
+	case .Undo:              undo(s, &s.undo, &s.redo)
+	case .Redo:              undo(s, &s.redo, &s.undo)
+	case .New_Line:          input_text(s, "\n")
+	case .Cut:               cut(s)
+	case .Copy:              copy(s)
+	case .Paste:             paste(s)
+	case .Select_All:        s.selection = {len(s.builder.buf), 0}
+	case .Backspace:         delete_to(s, .Left)
+	case .Delete:            delete_to(s, .Right)
+	case .Delete_Word_Left:  delete_to(s, .Word_Left)
+	case .Delete_Word_Right: delete_to(s, .Word_Right)
+	case .Left:              move_to(s, .Left)
+	case .Right:             move_to(s, .Right)
+	case .Up:                move_to(s, .Up)
+	case .Down:              move_to(s, .Down)
+	case .Word_Left:         move_to(s, .Word_Left)
+	case .Word_Right:        move_to(s, .Word_Right)
+	case .Start:             move_to(s, .Start)
+	case .End:               move_to(s, .End)
+	case .Line_Start:        move_to(s, .Soft_Line_Start)
+	case .Line_End:          move_to(s, .Soft_Line_End)
+	case .Select_Left:       select_to(s, .Left)
+	case .Select_Right:      select_to(s, .Right)
+	case .Select_Up:         select_to(s, .Up)
+	case .Select_Down:       select_to(s, .Down)
+	case .Select_Word_Left:  select_to(s, .Word_Left)
+	case .Select_Word_Right: select_to(s, .Word_Right)
+	case .Select_Start:      select_to(s, .Start)
+	case .Select_End:        select_to(s, .End)
+	case .Select_Line_Start: select_to(s, .Soft_Line_Start)
+	case .Select_Line_End:   select_to(s, .Soft_Line_End)
+	}
+}

+ 1 - 1
core/text/i18n/doc.odin

@@ -1,4 +1,4 @@
-//+ignore
+//+build ignore
 package i18n
 
 /*

+ 2 - 0
core/time/time_wasi.odin

@@ -22,3 +22,5 @@ _tick_now :: proc "contextless" () -> Tick {
 	return {}
 }
 
+_yield :: proc "contextless" () {
+}

+ 2 - 0
examples/all/all_vendor.odin

@@ -10,6 +10,7 @@ import miniaudio  "vendor:miniaudio"
 import PM         "vendor:portmidi"
 import rl         "vendor:raylib"
 import exr        "vendor:OpenEXRCore"
+import cgltf      "vendor:cgltf"
 
 import SDL        "vendor:sdl2"
 import SDLNet     "vendor:sdl2/net"
@@ -33,6 +34,7 @@ _ :: miniaudio
 _ :: PM
 _ :: rl
 _ :: exr
+_ :: cgltf
 
 _ :: SDL
 _ :: SDLNet

+ 14 - 7
src/build_settings.cpp

@@ -298,17 +298,19 @@ struct BuildContext {
 	bool   ignore_microsoft_magic;
 	bool   linker_map_file;
 
-	bool use_separate_modules;
-	bool threaded_checker;
+	bool   use_separate_modules;
+	bool   threaded_checker;
 
-	bool show_debug_messages;
+	bool   show_debug_messages;
 	
-	bool copy_file_contents;
+	bool   copy_file_contents;
 
-	bool disallow_rtti;
+	bool   disallow_rtti;
+
+	bool   use_static_map_calls;
 
 	RelocMode reloc_mode;
-	bool disable_red_zone;
+	bool   disable_red_zone;
 
 
 	u32 cmd_doc_flags;
@@ -326,7 +328,7 @@ struct BuildContext {
 	BlockingMutex target_features_mutex;
 	StringSet target_features_set;
 	String target_features_string;
-
+	String minimum_os_version_string;
 };
 
 gb_global BuildContext build_context = {0};
@@ -1380,6 +1382,11 @@ bool init_build_paths(String init_filename) {
 		produces_output_file = true;
 	}
 
+
+	if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+		bc->no_dynamic_literals = true;
+	}
+
 	if (!produces_output_file) {
 		// Command doesn't produce output files. We're done.
 		return true;

+ 97 - 0
src/check_builtin.cpp

@@ -3651,6 +3651,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		operand->mode = Addressing_NoValue;
 		break;
 
+	case BuiltinProc_raw_data:
+		{
+			Operand x = {};
+			check_expr(c, &x, ce->args[0]);
+			if (x.mode == Addressing_Invalid) {
+				return false;
+			}
+			if (!is_operand_value(x)) {
+				gbString s = expr_to_string(x.expr);
+				error(call, "'%.*s' expects a string, slice, dynamic array, or pointer to array type, got %s", LIT(builtin_name), s);
+				gb_string_free(s);
+				return false;
+			}
+			Type *t = base_type(x.type);
+
+			operand->mode = Addressing_Value;
+			operand->type = nullptr;
+			switch (t->kind) {
+			case Type_Slice:
+				operand->type = alloc_type_multi_pointer(t->MultiPointer.elem);
+				break;
+			case Type_DynamicArray:
+				operand->type = alloc_type_multi_pointer(t->DynamicArray.elem);
+				break;
+			case Type_Basic:
+				if (t->Basic.kind == Basic_string) {
+					operand->type = alloc_type_multi_pointer(t_u8);
+				}
+				break;
+			case Type_Pointer:
+			case Type_MultiPointer:
+				{
+					Type *base = base_type(type_deref(t, true));
+					switch (base->kind) {
+					case Type_Array:
+					case Type_EnumeratedArray:
+					case Type_SimdVector:
+						operand->type = alloc_type_multi_pointer(base_array_type(base));
+						break;
+					}
+				}
+				break;
+			}
+
+			if (operand->type == nullptr) {
+				gbString s = type_to_string(x.type);
+				error(call, "'%.*s' expects a string, slice, dynamic array, or pointer to array type, got %s", LIT(builtin_name), s);
+				gb_string_free(s);
+				return false;
+			}
+		}
+		break;
+
 	case BuiltinProc_read_cycle_counter:
 		operand->mode = Addressing_Value;
 		operand->type = t_i64;
@@ -4129,6 +4182,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				return false;
 			}
 
+			Type *elem = type_deref(ptr0.type);
+			if (type_size_of(elem) == 0) {
+				gbString str = type_to_string(ptr0.type);
+				error(ptr0.expr, "Expected a pointer to a non-zero sized element for '%.*s', got %s", LIT(builtin_name), str);
+				gb_string_free(str);
+				return false;
+			}
 		}
 		break;
 
@@ -5310,6 +5370,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			break;
 		}
 
+	case BuiltinProc_type_map_info:
+		{
+			Operand op = {};
+			Type *bt = check_type(c, ce->args[0]);
+			Type *type = base_type(bt);
+			if (type == nullptr || type == t_invalid) {
+				error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+				return false;
+			}
+			if (!is_type_map(type)) {
+				gbString t = type_to_string(type);
+				error(ce->args[0], "Expected a map type for '%.*s', got %s", LIT(builtin_name), t);
+				gb_string_free(t);
+				return false;
+			}
+
+			add_map_key_type_dependencies(c, type);
+
+			operand->mode = Addressing_Value;
+			operand->type = t_map_info_ptr;
+			break;
+		}
+	case BuiltinProc_type_map_cell_info:
+		{
+			Operand op = {};
+			Type *bt = check_type(c, ce->args[0]);
+			Type *type = base_type(bt);
+			if (type == nullptr || type == t_invalid) {
+				error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+				return false;
+			}
+
+			operand->mode = Addressing_Value;
+			operand->type = t_map_cell_info_ptr;
+			break;
+		}
+
 	case BuiltinProc_constant_utf16_cstring:
 		{
 			String value = {};

+ 88 - 35
src/check_expr.cpp

@@ -285,6 +285,37 @@ void error_operand_no_value(Operand *o) {
 	}
 }
 
+void add_map_get_dependencies(CheckerContext *c) {
+	if (build_context.use_static_map_calls) {
+		add_package_dependency(c, "runtime", "map_desired_position");
+		add_package_dependency(c, "runtime", "map_probe_distance");
+	} else {
+		add_package_dependency(c, "runtime", "__dynamic_map_get");
+	}
+}
+
+void add_map_set_dependencies(CheckerContext *c) {
+	init_core_source_code_location(c->checker);
+
+	if (t_map_set_proc == nullptr) {
+		Type *map_set_args[5] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr, /*value*/t_rawptr, /*#caller_location*/t_source_code_location};
+		t_map_set_proc = alloc_type_proc_from_types(map_set_args, gb_count_of(map_set_args), t_rawptr, false, ProcCC_Odin);
+	}
+
+	if (build_context.use_static_map_calls) {
+		add_package_dependency(c, "runtime", "__dynamic_map_check_grow");
+		add_package_dependency(c, "runtime", "map_insert_hash_dynamic");
+	} else {
+		add_package_dependency(c, "runtime", "__dynamic_map_set");
+	}
+}
+
+void add_map_reserve_dependencies(CheckerContext *c) {
+	init_core_source_code_location(c->checker);
+	add_package_dependency(c, "runtime", "__dynamic_map_reserve");
+}
+
+
 
 void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
 	Scope *s = c->scope;
@@ -580,6 +611,9 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 		}
 		return -1;
 	}
+	if (operand->mode == Addressing_ProcGroup && !is_type_proc(type)) {
+		return -1;
+	}
 
 	Type *s = operand->type;
 
@@ -731,16 +765,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 		}
 
 		// TODO(bill): Determine which rule is a better on in practice
-		#if 1
-			if (dst->Union.variants.count == 1) {
-				Type *vt = dst->Union.variants[0];
-				i64 score = check_distance_between_types(c, operand, vt);
-				if (score >= 0) {
-					return score+2;
-				}
+		if (dst->Union.variants.count == 1) {
+			Type *vt = dst->Union.variants[0];
+			i64 score = check_distance_between_types(c, operand, vt);
+			if (score >= 0) {
+				return score+2;
 			}
-		#else
-			// NOTE(bill): check to see you can assign to it with one of the variants?
+		} else if (is_type_untyped(src)) {
 			i64 prev_lowest_score = -1;
 			i64 lowest_score = -1;
 			for_array(i, dst->Union.variants) {
@@ -764,7 +795,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 					return lowest_score+2;
 				}
 			}
-		#endif
+		}
 	}
 
 	if (is_type_relative_pointer(dst)) {
@@ -972,22 +1003,24 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
 	}
 
 	if (operand->mode == Addressing_ProcGroup) {
-		Array<Entity *> procs = proc_group_entities(c, *operand);
 		bool good = false;
-		// NOTE(bill): These should be done
-		for_array(i, procs) {
-			Type *t = base_type(procs[i]->type);
-			if (t == t_invalid) {
-				continue;
-			}
-			Operand x = {};
-			x.mode = Addressing_Value;
-			x.type = t;
-			if (check_is_assignable_to(c, &x, type)) {
-				Entity *e = procs[i];
-				add_entity_use(c, operand->expr, e);
-				good = true;
-				break;
+		if (type != nullptr && is_type_proc(type)) {
+			Array<Entity *> procs = proc_group_entities(c, *operand);
+			// NOTE(bill): These should be done
+			for_array(i, procs) {
+				Type *t = base_type(procs[i]->type);
+				if (t == t_invalid) {
+					continue;
+				}
+				Operand x = {};
+				x.mode = Addressing_Value;
+				x.type = t;
+				if (check_is_assignable_to(c, &x, type)) {
+					Entity *e = procs[i];
+					add_entity_use(c, operand->expr, e);
+					good = true;
+					break;
+				}
 			}
 		}
 
@@ -1364,8 +1397,6 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
 			bool key   = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type);
 			bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type);
 			if (key || value) {
-				poly->Map.entry_type = nullptr;
-				poly->Map.internal_type = nullptr;
 				poly->Map.lookup_result_type = nullptr;
 				init_map_internal_types(poly);
 				return true;
@@ -1478,7 +1509,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
 		Array<Entity *> procs = pge->entities;
 		bool skip = false;
 
-		if (type_hint != nullptr) {
+		if (type_hint != nullptr && is_type_proc(type_hint)) {
 			// NOTE(bill): These should be done
 			for_array(i, procs) {
 				Type *t = base_type(procs[i]->type);
@@ -1998,7 +2029,7 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
 		error_line("\tSuggestion: a string may be transmuted to %s\n", b);
 		error_line("\t            This is an UNSAFE operation as string data is assumed to be immutable, \n");
 		error_line("\t            whereas slices in general are assumed to be mutable.\n");
-	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
+	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
 		error_line("\tSuggestion: the expression may be casted to %s\n", b);
 	}
 }
@@ -2039,7 +2070,7 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
 		}
 	} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
 		error_line("\tSuggestion: a string may be transmuted to %s\n", b);
-	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
+	} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
 		error_line("\tSuggestion: the expression may be casted to %s\n", b);
 	}
 }
@@ -3246,7 +3277,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 				check_assignment(c, x, yt->Map.key, str_lit("map 'not_in'"));
 			}
 
-			add_package_dependency(c, "runtime", "__dynamic_map_get");
+			add_map_get_dependencies(c);
 		} else if (is_type_bit_set(rhs_type)) {
 			Type *yt = base_type(rhs_type);
 
@@ -3338,6 +3369,24 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 		error(y->expr, "built-in expression in binary expression");
 		return;
 	}
+	if (x->mode == Addressing_ProcGroup) {
+		x->mode = Addressing_Invalid;
+		if (x->proc_group != nullptr) {
+			error(x->expr, "procedure group '%.*s' used in binary expression", LIT(x->proc_group->token.string));
+		} else {
+			error(x->expr, "procedure group used in binary expression");
+		}
+		return;
+	}
+	if (y->mode == Addressing_ProcGroup) {
+		x->mode = Addressing_Invalid;
+		if (x->proc_group != nullptr) {
+			error(y->expr, "procedure group '%.*s' used in binary expression", LIT(y->proc_group->token.string));
+		} else {
+			error(y->expr, "procedure group used in binary expression");
+		}
+		return;
+	}
 
 	if (token_is_shift(op.kind)) {
 		check_shift(c, x, y, node, type_hint);
@@ -5496,6 +5545,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 			GB_ASSERT(is_type_proc(gept));
 			proc_type = gept;
 			pt = &gept->Proc;
+		} else {
+			err = CallArgumentError_WrongTypes;
 		}
 	}
 
@@ -5830,6 +5881,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 				Entity *proc = procs[proc_index];
 				Type *pt = base_type(proc->type);
 				if (!(pt != nullptr && is_type_proc(pt))) {
+					proc_index++;
 					continue;
 				}
 
@@ -6127,6 +6179,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 			}
 			result_type = t_invalid;
 		} else {
+			GB_ASSERT(valids.count == 1);
 			Ast *ident = operand->expr;
 			while (ident->kind == Ast_SelectorExpr) {
 				Ast *s = ident->SelectorExpr.selector;
@@ -8557,8 +8610,8 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
 		if (build_context.no_dynamic_literals && cl->elems.count) {
 			error(node, "Compound literals of dynamic types have been disabled");
 		} else {
-			add_package_dependency(c, "runtime", "__dynamic_map_reserve");
-			add_package_dependency(c, "runtime", "__dynamic_map_set");
+			add_map_reserve_dependencies(c);
+			add_map_set_dependencies(c);
 		}
 		break;
 	}
@@ -8994,8 +9047,8 @@ ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
 		o->type = t->Map.value;
 		o->expr = node;
 
-		add_package_dependency(c, "runtime", "__dynamic_map_get");
-		add_package_dependency(c, "runtime", "__dynamic_map_set");
+		add_map_get_dependencies(c);
+		add_map_set_dependencies(c);
 		return Expr_Expr;
 	}
 

+ 35 - 74
src/check_type.cpp

@@ -1629,6 +1629,8 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 						// This is just to add the error message to determine_type_from_polymorphic which
 						// depends on valid position information
 						op.expr = _params;
+						op.mode = Addressing_Invalid;
+						op.type = t_invalid;
 					}
 					if (is_type_polymorphic_type) {
 						type = determine_type_from_polymorphic(ctx, type, op);
@@ -2176,70 +2178,36 @@ Type *make_optional_ok_type(Type *value, bool typed) {
 	return t;
 }
 
-void init_map_entry_type(Type *type) {
-	GB_ASSERT(type->kind == Type_Map);
-	if (type->Map.entry_type != nullptr) return;
-
-	// NOTE(bill): The preload types may have not been set yet
-	GB_ASSERT(t_map_hash != nullptr);
-
-	/*
-	struct {
-		hash:  uintptr,
-		next:  int,
-		key:   Key,
-		value: Value,
-	}
-	*/
-	Scope *s = create_scope(nullptr, builtin_pkg->scope);
-
-	auto fields = slice_make<Entity *>(permanent_allocator(), 4);
-	fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hash")),  t_uintptr,       false, 0, EntityState_Resolved);
-	fields[1] = alloc_entity_field(s, make_token_ident(str_lit("next")),  t_int,           false, 1, EntityState_Resolved);
-	fields[2] = alloc_entity_field(s, make_token_ident(str_lit("key")),   type->Map.key,   false, 2, EntityState_Resolved);
-	fields[3] = alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 3, EntityState_Resolved);
-
-	Type *entry_type = alloc_type_struct();
-	entry_type->Struct.fields  = fields;
-	entry_type->Struct.tags    = gb_alloc_array(permanent_allocator(), String, fields.count);
-	
-	type_set_offsets(entry_type);
-	type->Map.entry_type = entry_type;
+
+// IMPORTANT NOTE(bill): This must match the definition in dynamic_map_internal.odin
+enum : i64 {
+	MAP_CACHE_LINE_LOG2 = 6,
+	MAP_CACHE_LINE_SIZE = 1 << MAP_CACHE_LINE_LOG2
+};
+GB_STATIC_ASSERT(MAP_CACHE_LINE_SIZE >= 64);
+void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
+	i64 elem_sz = type_size_of(type);
+
+	i64 len = 1;
+	if (0 < elem_sz && elem_sz < MAP_CACHE_LINE_SIZE) {
+		len = MAP_CACHE_LINE_SIZE / elem_sz;
+	}
+	i64 size = align_formula(elem_sz * len, MAP_CACHE_LINE_SIZE);
+	if (size_) *size_ = size;
+	if (len_)  *len_ = len;
 }
 
 void init_map_internal_types(Type *type) {
 	GB_ASSERT(type->kind == Type_Map);
-	init_map_entry_type(type);
-	if (type->Map.internal_type != nullptr) return;
+	GB_ASSERT(t_allocator != nullptr);
+	if (type->Map.lookup_result_type != nullptr) return;
 
 	Type *key   = type->Map.key;
 	Type *value = type->Map.value;
 	GB_ASSERT(key != nullptr);
 	GB_ASSERT(value != nullptr);
 
-	Type *generated_struct_type = alloc_type_struct();
-
-	/*
-	struct {
-		hashes:  []int;
-		entries: [dynamic]EntryType;
-	}
-	*/
-	Scope *s = create_scope(nullptr, builtin_pkg->scope);
-
-	Type *hashes_type  = alloc_type_slice(t_int);
-	Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
-
-
-	auto fields = slice_make<Entity *>(permanent_allocator(), 2);
-	fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hashes")),  hashes_type,  false, 0, EntityState_Resolved);
-	fields[1] = alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved);
-
-	generated_struct_type->Struct.fields = fields;
-	type_set_offsets(generated_struct_type);
-	
-	type->Map.internal_type         = generated_struct_type;
-	type->Map.lookup_result_type    = make_optional_ok_type(value);
+	type->Map.lookup_result_type = make_optional_ok_type(value);
 }
 
 void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
@@ -2255,35 +2223,27 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
 		}
 
 		if (is_type_simple_compare(key)) {
-			i64 sz = type_size_of(key);
-			if (1 <= sz && sz <= 16) {
-				char buf[20] = {};
-				gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
-				add_package_dependency(ctx, "runtime", buf);
-				return;
-			} else {
-				add_package_dependency(ctx, "runtime", "default_hasher_n");
-				return;
-			}
+			add_package_dependency(ctx, "runtime", "default_hasher");
+			return;
 		}
 
 		if (key->kind == Type_Struct) {
-			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			add_package_dependency(ctx, "runtime", "default_hasher");
 			for_array(i, key->Struct.fields) {
 				Entity *field = key->Struct.fields[i];
 				add_map_key_type_dependencies(ctx, field->type);
 			}
 		} else if (key->kind == Type_Union) {
-			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			add_package_dependency(ctx, "runtime", "default_hasher");
 			for_array(i, key->Union.variants) {
 				Type *v = key->Union.variants[i];
 				add_map_key_type_dependencies(ctx, v);
 			}
 		} else if (key->kind == Type_EnumeratedArray) {
-			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			add_package_dependency(ctx, "runtime", "default_hasher");
 			add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
 		} else if (key->kind == Type_Array) {
-			add_package_dependency(ctx, "runtime", "default_hasher_n");
+			add_package_dependency(ctx, "runtime", "default_hasher");
 			add_map_key_type_dependencies(ctx, key->Array.elem);
 		}
 	}
@@ -2330,9 +2290,7 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
 	
 	i64 row_count = check_array_count(ctx, &row, mt->row_count);
 	i64 column_count = check_array_count(ctx, &column, mt->column_count);
-	
-	Type *elem = check_type_expr(ctx, mt->elem, nullptr);
-	
+
 	Type *generic_row = nullptr;
 	Type *generic_column = nullptr;
 	
@@ -2344,22 +2302,25 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
 		generic_column = column.type;
 	}
 	
-	if (row_count < MATRIX_ELEMENT_COUNT_MIN && generic_row == nullptr) {
+	if (generic_row == nullptr && row_count < MATRIX_ELEMENT_COUNT_MIN) {
 		gbString s = expr_to_string(row.expr);
 		error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
 		gb_string_free(s);
 	}
 	
-	if (column_count < MATRIX_ELEMENT_COUNT_MIN && generic_column == nullptr) {
+	if (generic_column == nullptr && column_count < MATRIX_ELEMENT_COUNT_MIN) {
 		gbString s = expr_to_string(column.expr);
 		error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
 		gb_string_free(s);
 	}
 	
-	if (row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
+	if ((generic_row == nullptr && generic_column == nullptr) && row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
 		i64 element_count = row_count*column_count;
 		error(column.expr, "Matrix types are limited to a maximum of %d elements, got %lld", MATRIX_ELEMENT_COUNT_MAX, cast(long long)element_count);
 	}
+
+
+	Type *elem = check_type_expr(ctx, mt->elem, nullptr);
 	
 	if (!is_type_valid_for_matrix_elems(elem)) {
 		if (elem == t_typeid) {

+ 31 - 10
src/checker.cpp

@@ -922,10 +922,13 @@ void init_universal(void) {
 
 	{
 		Type *equal_args[2] = {t_rawptr, t_rawptr};
-		t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless);
+		t_equal_proc = alloc_type_proc_from_types(equal_args, gb_count_of(equal_args), t_bool, false, ProcCC_Contextless);
 
 		Type *hasher_args[2] = {t_rawptr, t_uintptr};
-		t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless);
+		t_hasher_proc = alloc_type_proc_from_types(hasher_args, gb_count_of(hasher_args), t_uintptr, false, ProcCC_Contextless);
+
+		Type *map_get_args[3] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr};
+		t_map_get_proc = alloc_type_proc_from_types(map_get_args, gb_count_of(map_get_args), t_rawptr, false, ProcCC_Contextless);
 	}
 
 // Constants
@@ -1673,7 +1676,18 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
 }
 
 void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported) {
-	GB_ASSERT(identifier->kind == Ast_Ident);
+	if (identifier == nullptr) {
+		// NOTE(bill): Should only happen on errors
+		error(e->token, "Invalid variable declaration");
+		return;
+	}
+	if (identifier->kind != Ast_Ident) {
+		// NOTE(bill): This is a safety check
+		gbString s = expr_to_string(identifier);
+		error(identifier, "A variable declaration must be an identifer, got %s", s);
+		gb_string_free(s);
+		return;
+	}
 	GB_ASSERT(e != nullptr && d != nullptr);
 	GB_ASSERT(identifier->Ident.token.string == e->token.string);
 
@@ -1922,7 +1936,8 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
 		init_map_internal_types(bt);
 		add_type_info_type_internal(c, bt->Map.key);
 		add_type_info_type_internal(c, bt->Map.value);
-		add_type_info_type_internal(c, bt->Map.internal_type);
+		add_type_info_type_internal(c, t_uintptr); // hash value
+		add_type_info_type_internal(c, t_allocator);
 		break;
 
 	case Type_Tuple:
@@ -2144,7 +2159,8 @@ void add_min_dep_type_info(Checker *c, Type *t) {
 		init_map_internal_types(bt);
 		add_min_dep_type_info(c, bt->Map.key);
 		add_min_dep_type_info(c, bt->Map.value);
-		add_min_dep_type_info(c, bt->Map.internal_type);
+		add_min_dep_type_info(c, t_uintptr); // hash value
+		add_min_dep_type_info(c, t_allocator);
 		break;
 
 	case Type_Tuple:
@@ -2827,16 +2843,21 @@ void init_core_source_code_location(Checker *c) {
 		return;
 	}
 	t_source_code_location = find_core_type(c, str_lit("Source_Code_Location"));
-	t_source_code_location_ptr = alloc_type_pointer(t_allocator);
+	t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
 }
 
 void init_core_map_type(Checker *c) {
-	if (t_map_hash != nullptr) {
+	if (t_map_info != nullptr) {
 		return;
 	}
-	t_map_hash = find_core_type(c, str_lit("Map_Hash"));
-	t_map_header = find_core_type(c, str_lit("Map_Header"));
-	t_map_header_table = find_core_type(c, str_lit("Map_Header_Table"));
+	init_mem_allocator(c);
+	t_map_info      = find_core_type(c, str_lit("Map_Info"));
+	t_map_cell_info = find_core_type(c, str_lit("Map_Cell_Info"));
+	t_raw_map       = find_core_type(c, str_lit("Raw_Map"));
+
+	t_map_info_ptr      = alloc_type_pointer(t_map_info);
+	t_map_cell_info_ptr = alloc_type_pointer(t_map_cell_info);
+	t_raw_map_ptr       = alloc_type_pointer(t_raw_map);
 }
 
 void init_preload(Checker *c) {

+ 10 - 2
src/checker_builtin_procs.hpp

@@ -42,6 +42,8 @@ enum BuiltinProcId {
 
 	BuiltinProc_unreachable,
 
+	BuiltinProc_raw_data,
+
 	BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
 
 	// "Intrinsics"
@@ -275,6 +277,8 @@ BuiltinProc__type_simple_boolean_end,
 
 	BuiltinProc_type_equal_proc,
 	BuiltinProc_type_hasher_proc,
+	BuiltinProc_type_map_info,
+	BuiltinProc_type_map_cell_info,
 
 BuiltinProc__type_end,
 
@@ -338,6 +342,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 	{STR_LIT("unreachable"),      0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true},
 
+	{STR_LIT("raw_data"),         1, false, Expr_Expr, BuiltinProcPkg_builtin},
+
 	{STR_LIT(""),                 0, true,  Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
 
 
@@ -566,8 +572,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 	{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
-	{STR_LIT("type_equal_proc"),  1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-	{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_equal_proc"),    1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_hasher_proc"),   1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_map_info"),      1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 
 	{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},

+ 7 - 0
src/exact_value.cpp

@@ -499,6 +499,13 @@ i64 exact_value_to_i64(ExactValue v) {
 	}
 	return 0;
 }
+u64 exact_value_to_u64(ExactValue v) {
+	v = exact_value_to_integer(v);
+	if (v.kind == ExactValue_Integer) {
+		return big_int_to_u64(&v.value_integer);
+	}
+	return 0;
+}
 f64 exact_value_to_f64(ExactValue v) {
 	v = exact_value_to_float(v);
 	if (v.kind == ExactValue_Float) {

+ 26 - 1
src/llvm_abi.cpp

@@ -537,6 +537,22 @@ namespace lbAbiAmd64SysV {
 		return false;
 	}
 
+	bool is_llvm_type_slice_like(LLVMTypeRef type) {
+		if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
+			return false;
+		}
+		if (LLVMCountStructElementTypes(type) != 2) {
+			return false;
+		}
+		LLVMTypeRef fields[2] = {};
+		LLVMGetStructElementTypes(type, fields);
+		if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
+			return false;
+		}
+		return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
+
+	}
+
 	lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
 		if (is_register(type)) {
 			LLVMAttributeRef attribute = nullptr;
@@ -559,7 +575,16 @@ namespace lbAbiAmd64SysV {
 			}
 			return lb_arg_type_indirect(type, attribute);
 		} else {
-			return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr);
+			LLVMTypeRef reg_type = nullptr;
+			if (is_llvm_type_slice_like(type)) {
+				// NOTE(bill): This is to make the ABI look closer to what the
+				// original code is just for slices/strings whilst still adhering
+				// the ABI rules for SysV
+				reg_type = type;
+			} else {
+				reg_type = llreg(c, cls);
+			}
+			return lb_arg_type_direct(type, reg_type, nullptr, nullptr);
 		}
 	}
 

+ 434 - 84
src/llvm_backend.cpp

@@ -140,7 +140,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx) {
 }
 
 
-lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
+lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
 	type = base_type(type);
 	GB_ASSERT(is_type_comparable(type));
 
@@ -157,8 +157,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
 
 	static u32 proc_index = 0;
 
-	char buf[16] = {};
-	isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index);
 	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
 	String proc_name = make_string_c(str);
 
@@ -166,6 +166,9 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
 	map_set(&m->equal_procs, type, p);
 	lb_begin_procedure_body(p);
 
+	lb_add_attribute_to_proc(m, p->value, "readonly");
+	lb_add_attribute_to_proc(m, p->value, "nounwind");
+
 	LLVMValueRef x = LLVMGetParam(p->value, 0);
 	LLVMValueRef y = LLVMGetParam(p->value, 1);
 	x = LLVMBuildPointerCast(p->builder, x, ptr_type, "");
@@ -173,6 +176,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
 	lbValue lhs = {x, pt};
 	lbValue rhs = {y, pt};
 
+	lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
+	lb_add_proc_attribute_at_index(p, 1+1, "nonnull");
 
 	lbBlock *block_same_ptr = lb_create_block(p, "same_ptr");
 	lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr");
@@ -277,28 +282,20 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
 lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
 	GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
 
-	i64 sz = type_size_of(type);
-
-	if (1 <= sz && sz <= 16) {
-		char name[20] = {};
-		gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
-
-		auto args = array_make<lbValue>(permanent_allocator(), 2);
-		args[0] = data;
-		args[1] = seed;
-		return lb_emit_runtime_call(p, name, args);
-	}
-
 	auto args = array_make<lbValue>(permanent_allocator(), 3);
 	args[0] = data;
 	args[1] = seed;
 	args[2] = lb_const_int(p->module, t_int, type_size_of(type));
-	return lb_emit_runtime_call(p, "default_hasher_n", args);
+	return lb_emit_runtime_call(p, "default_hasher", args);
 }
 
-lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
+void lb_add_callsite_force_inline(lbProcedure *p, lbValue ret_value) {
+	LLVMAddCallSiteAttribute(ret_value.value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(p->module->ctx, "alwaysinline"));
+}
+
+lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
 	type = core_type(type);
-	GB_ASSERT(is_type_valid_for_keys(type));
+	GB_ASSERT_MSG(is_type_valid_for_keys(type), "%s", type_to_string(type));
 
 	Type *pt = alloc_type_pointer(type);
 
@@ -310,8 +307,8 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 
 	static u32 proc_index = 0;
 
-	char buf[16] = {};
-	isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$hasher%u", ++proc_index);
 	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
 	String proc_name = make_string_c(str);
 
@@ -320,16 +317,20 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 	lb_begin_procedure_body(p);
 	defer (lb_end_procedure_body(p));
 
+	lb_add_attribute_to_proc(m, p->value, "readonly");
+	lb_add_attribute_to_proc(m, p->value, "nounwind");
+
 	LLVMValueRef x = LLVMGetParam(p->value, 0);
 	LLVMValueRef y = LLVMGetParam(p->value, 1);
 	lbValue data = {x, t_rawptr};
 	lbValue seed = {y, t_uintptr};
 
-	LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(m->ctx, "nonnull");
-	LLVMAddAttributeAtIndex(p->value, 1+0, nonnull_attr);
+	lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
+	lb_add_proc_attribute_at_index(p, 1+0, "readonly");
 
 	if (is_type_simple_compare(type)) {
 		lbValue res = lb_simple_compare_hash(p, type, data, seed);
+		lb_add_callsite_force_inline(p, res);
 		LLVMBuildRet(p->builder, res.value);
 		return {p->value, p->type};
 	}
@@ -343,7 +344,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 			GB_ASSERT(type->Struct.offsets != nullptr);
 			i64 offset = type->Struct.offsets[i];
 			Entity *field = type->Struct.fields[i];
-			lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type);
+			lbValue field_hasher = lb_hasher_proc_for_type(m, field->type);
 			lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset));
 
 			args[0] = ptr;
@@ -356,11 +357,12 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 
 		if (is_type_union_maybe_pointer(type)) {
 			Type *v = type->Union.variants[0];
-			lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v);
+			lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
 
 			args[0] = data;
 			args[1] = seed;
 			lbValue res = lb_emit_call(p, variant_hasher, args);
+			lb_add_callsite_force_inline(p, res);
 			LLVMBuildRet(p->builder, res.value);
 		}
 
@@ -379,7 +381,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 			Type *v = type->Union.variants[i];
 			lbValue case_tag = lb_const_union_tag(p->module, type, v);
 
-			lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v);
+			lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
 
 			args[0] = data;
 			args[1] = seed;
@@ -397,7 +399,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 		lb_addr_store(p, pres, seed);
 
 		auto args = array_make<lbValue>(permanent_allocator(), 2);
-		lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem);
+		lbValue elem_hasher = lb_hasher_proc_for_type(m, type->Array.elem);
 
 		auto loop_data = lb_loop_start(p, cast(isize)type->Array.count, t_i32);
 
@@ -418,7 +420,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 		lb_addr_store(p, res, seed);
 
 		auto args = array_make<lbValue>(permanent_allocator(), 2);
-		lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem);
+		lbValue elem_hasher = lb_hasher_proc_for_type(m, type->EnumeratedArray.elem);
 
 		auto loop_data = lb_loop_start(p, cast(isize)type->EnumeratedArray.count, t_i32);
 
@@ -439,12 +441,14 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 		args[0] = data;
 		args[1] = seed;
 		lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args);
+		lb_add_callsite_force_inline(p, res);
 		LLVMBuildRet(p->builder, res.value);
 	} else if (is_type_string(type)) {
 		auto args = array_make<lbValue>(permanent_allocator(), 2);
 		args[0] = data;
 		args[1] = seed;
 		lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
+		lb_add_callsite_force_inline(p, res);
 		LLVMBuildRet(p->builder, res.value);
 	} else {
 		GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
@@ -454,6 +458,279 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
 }
 
 
+lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
+	GB_ASSERT(build_context.use_static_map_calls);
+	type = base_type(type);
+	GB_ASSERT(type->kind == Type_Map);
+
+
+	lbProcedure **found = map_get(&m->map_get_procs, type);
+	if (found) {
+		GB_ASSERT(*found != nullptr);
+		return {(*found)->value, (*found)->type};
+	}
+	static u32 proc_index = 0;
+
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$map_get-%u", ++proc_index);
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+	lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
+	map_set(&m->map_get_procs, type, p);
+	lb_begin_procedure_body(p);
+	defer (lb_end_procedure_body(p));
+
+	LLVMSetLinkage(p->value, LLVMInternalLinkage);
+	lb_add_attribute_to_proc(m, p->value, "readonly");
+	lb_add_attribute_to_proc(m, p->value, "nounwind");
+	if (build_context.ODIN_DEBUG) {
+		lb_add_attribute_to_proc(m, p->value, "noinline");
+	}
+
+	LLVMValueRef x = LLVMGetParam(p->value, 0);
+	LLVMValueRef y = LLVMGetParam(p->value, 1);
+	LLVMValueRef z = LLVMGetParam(p->value, 2);
+	lbValue map_ptr = {x, t_rawptr};
+	lbValue h       = {y, t_uintptr};
+	lbValue key_ptr = {z, t_rawptr};
+
+	lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
+	lb_add_proc_attribute_at_index(p, 1+0, "noalias");
+	lb_add_proc_attribute_at_index(p, 1+0, "readonly");
+
+	lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
+	lb_add_proc_attribute_at_index(p, 1+2, "noalias");
+	lb_add_proc_attribute_at_index(p, 1+2, "readonly");
+
+	lbBlock *loop_block = lb_create_block(p, "loop");
+	lbBlock *hash_block = lb_create_block(p, "hash");
+	lbBlock *probe_block = lb_create_block(p, "probe");
+	lbBlock *increment_block = lb_create_block(p, "increment");
+	lbBlock *hash_compare_block = lb_create_block(p, "hash_compare");
+	lbBlock *key_compare_block = lb_create_block(p, "key_compare");
+	lbBlock *value_block = lb_create_block(p, "value");
+	lbBlock *nil_block = lb_create_block(p, "nil");
+
+	map_ptr = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
+	lbValue map = lb_emit_load(p, map_ptr);
+
+	lbValue length = lb_map_len(p, map);
+
+	lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, length, lb_const_nil(m, t_int)), nil_block, hash_block);
+	lb_start_block(p, hash_block);
+
+	key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
+	lbValue key = lb_emit_load(p, key_ptr);
+
+	lbAddr pos = lb_add_local_generated(p, t_uintptr, false);
+	lbAddr distance = lb_add_local_generated(p, t_uintptr, true);
+	lbValue capacity = lb_map_cap(p, map);
+	lbValue mask = lb_emit_conv(p, lb_emit_arith(p, Token_Sub, capacity, lb_const_int(m, t_int, 1), t_int), t_uintptr);
+
+	{
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = map;
+		args[1] = h;
+		lb_addr_store(p, pos, lb_emit_runtime_call(p, "map_desired_position", args));
+	}
+	lbValue zero_uintptr = lb_const_int(m, t_uintptr, 0);
+	lbValue one_uintptr = lb_const_int(m, t_uintptr, 1);
+
+	lbValue ks = lb_map_data_uintptr(p, map);
+	lbValue vs = lb_map_cell_index_static(p, type->Map.key, ks, capacity);
+	lbValue hs = lb_map_cell_index_static(p, type->Map.value, vs, capacity);
+
+	ks = lb_emit_conv(p, ks, alloc_type_pointer(type->Map.key));
+	vs = lb_emit_conv(p, vs, alloc_type_pointer(type->Map.value));
+	hs = lb_emit_conv(p, hs, alloc_type_pointer(t_uintptr));
+
+	lb_emit_jump(p, loop_block);
+	lb_start_block(p, loop_block);
+
+	lbValue element_hash = lb_emit_load(p, lb_emit_ptr_offset(p, hs, lb_addr_load(p, pos)));
+	{
+		// if element_hash == 0 { return nil }
+		lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, zero_uintptr), nil_block, probe_block);
+	}
+
+	lb_start_block(p, probe_block);
+	{
+		auto args = array_make<lbValue>(heap_allocator(), 3);
+		args[0] = map;
+		args[1] = element_hash;
+		args[2] = lb_addr_load(p, pos);
+		lbValue probe_distance = lb_emit_runtime_call(p, "map_probe_distance", args);
+		lbValue cond = lb_emit_comp(p, Token_Gt, lb_addr_load(p, distance), probe_distance);
+		lb_emit_if(p, cond, nil_block, hash_compare_block);
+	}
+
+	lb_start_block(p, hash_compare_block);
+	{
+		lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, h), key_compare_block, increment_block);
+	}
+
+	lb_start_block(p, key_compare_block);
+	{
+		lbValue element_key = lb_map_cell_index_static(p, type->Map.key, ks, lb_addr_load(p, pos));
+		element_key = lb_emit_conv(p, element_key, ks.type);
+		lbValue cond = lb_emit_comp(p, Token_CmpEq, lb_emit_load(p, element_key), key);
+		lb_emit_if(p, cond, value_block, increment_block);
+	}
+
+	lb_start_block(p, value_block);
+	{
+		lbValue element_value = lb_map_cell_index_static(p, type->Map.value, vs, lb_addr_load(p, pos));
+		element_value = lb_emit_conv(p, element_value, t_rawptr);
+		LLVMBuildRet(p->builder, element_value.value);
+	}
+
+	lb_start_block(p, increment_block);
+	{
+		lbValue pp = lb_addr_load(p, pos);
+		pp = lb_emit_arith(p, Token_Add, pp, one_uintptr, t_uintptr);
+		pp = lb_emit_arith(p, Token_And, pp, mask, t_uintptr);
+		lb_addr_store(p, pos, pp);
+		lb_emit_increment(p, distance.addr);
+	}
+	lb_emit_jump(p, loop_block);
+
+	lb_start_block(p, nil_block);
+	{
+		lbValue res = lb_const_nil(m, t_rawptr);
+		LLVMBuildRet(p->builder, res.value);
+	}
+
+
+	return {p->value, p->type};
+}
+
+void lb_debug_print(lbProcedure *p, String const &str) {
+	auto args = array_make<lbValue>(heap_allocator(), 1);
+	args[0] = lb_const_string(p->module, str);
+	lb_emit_runtime_call(p, "print_string", args);
+}
+
+lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
+	GB_ASSERT(build_context.use_static_map_calls);
+	type = base_type(type);
+	GB_ASSERT(type->kind == Type_Map);
+
+
+	lbProcedure **found = map_get(&m->map_set_procs, type);
+	if (found) {
+		GB_ASSERT(*found != nullptr);
+		return {(*found)->value, (*found)->type};
+	}
+	static u32 proc_index = 0;
+
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index);
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+	lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
+	map_set(&m->map_set_procs, type, p);
+	lb_begin_procedure_body(p);
+	defer (lb_end_procedure_body(p));
+
+	LLVMSetLinkage(p->value, LLVMInternalLinkage);
+	lb_add_attribute_to_proc(m, p->value, "nounwind");
+	if (build_context.ODIN_DEBUG) {
+		lb_add_attribute_to_proc(m, p->value, "noinline");
+	}
+
+	lbValue map_ptr      = {LLVMGetParam(p->value, 0), t_rawptr};
+	lbValue hash         = {LLVMGetParam(p->value, 1), t_uintptr};
+	lbValue key_ptr      = {LLVMGetParam(p->value, 2), t_rawptr};
+	lbValue value_ptr    = {LLVMGetParam(p->value, 3), t_rawptr};
+	lbValue location_ptr = {LLVMGetParam(p->value, 4), t_source_code_location_ptr};
+
+	map_ptr = lb_emit_conv(p, map_ptr, alloc_type_pointer(type));
+	key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
+
+	lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
+	lb_add_proc_attribute_at_index(p, 1+0, "noalias");
+
+	lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
+	if (!are_types_identical(type->Map.key, type->Map.value)) {
+		lb_add_proc_attribute_at_index(p, 1+2, "noalias");
+	}
+	lb_add_proc_attribute_at_index(p, 1+2, "readonly");
+
+	lb_add_proc_attribute_at_index(p, 1+3, "nonnull");
+	if (!are_types_identical(type->Map.key, type->Map.value)) {
+		lb_add_proc_attribute_at_index(p, 1+3, "noalias");
+	}
+	lb_add_proc_attribute_at_index(p, 1+3, "readonly");
+
+	lb_add_proc_attribute_at_index(p, 1+4, "nonnull");
+	lb_add_proc_attribute_at_index(p, 1+4, "noalias");
+	lb_add_proc_attribute_at_index(p, 1+4, "readonly");
+
+	////
+	lbValue found_ptr = {};
+	{
+		lbValue map_get_proc = lb_map_get_proc_for_type(m, type);
+
+		auto args = array_make<lbValue>(permanent_allocator(), 3);
+		args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
+		args[1] = hash;
+		args[2] = key_ptr;
+
+		found_ptr = lb_emit_call(p, map_get_proc, args);
+	}
+
+
+	lbBlock *found_block      = lb_create_block(p, "found");
+	lbBlock *check_grow_block = lb_create_block(p, "check-grow");
+	lbBlock *grow_fail_block  = lb_create_block(p, "grow-fail");
+	lbBlock *insert_block     = lb_create_block(p, "insert");
+
+	lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, found_ptr), found_block, check_grow_block);
+	lb_start_block(p, found_block);
+	{
+		lb_mem_copy_non_overlapping(p, found_ptr, value_ptr, lb_const_int(m, t_int, type_size_of(type->Map.value)));
+		LLVMBuildRet(p->builder, lb_emit_conv(p, found_ptr, t_rawptr).value);
+	}
+	lb_start_block(p, check_grow_block);
+
+
+	lbValue map_info = lb_gen_map_info_ptr(p->module, type);
+
+	{
+		auto args = array_make<lbValue>(permanent_allocator(), 3);
+		args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
+		args[1] = map_info;
+		args[2] = lb_emit_load(p, location_ptr);
+		lbValue grow_err = lb_emit_runtime_call(p, "__dynamic_map_check_grow", args);
+
+		lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, grow_err), grow_fail_block, insert_block);
+
+		lb_start_block(p, grow_fail_block);
+		LLVMBuildRet(p->builder, LLVMConstNull(lb_type(m, t_rawptr)));
+	}
+
+	lb_start_block(p, insert_block);
+	{
+		auto args = array_make<lbValue>(permanent_allocator(), 5);
+		args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
+		args[1] = map_info;
+		args[2] = hash;
+		args[3] = lb_emit_conv(p, key_ptr,   t_uintptr);
+		args[4] = lb_emit_conv(p, value_ptr, t_uintptr);
+
+		lbValue result = lb_emit_runtime_call(p, "map_insert_hash_dynamic", args);
+
+		lb_emit_increment(p, lb_map_len_ptr(p, map_ptr));
+
+		LLVMBuildRet(p->builder, lb_emit_conv(p, result, t_rawptr).value);
+	}
+
+	return {p->value, p->type};
+}
+
+
 lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
 	lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
 	if (found) {
@@ -500,51 +777,60 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
 	return value;
 }
 
-lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) {
-	lbModule *m = p->module;
-
-	map_type = base_type(map_type);
-	GB_ASSERT(map_type->kind == Type_Map);
 
-	lbAddr *found = map_get(&m->map_header_table_map, map_type);
+lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
+	lbAddr *found = map_get(&m->map_cell_info_map, type);
 	if (found) {
-		return lb_addr_load(p, *found);
+		return found->addr;
 	}
 
-	GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct);
-	i64 entry_size   = type_size_of  (map_type->Map.entry_type);
-	i64 entry_align  = type_align_of (map_type->Map.entry_type);
+	i64 size = 0, len = 0;
+	map_cell_size_and_len(type, &size, &len);
 
-	i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
-	i64 key_size   = type_size_of  (map_type->Map.key);
+	LLVMValueRef const_values[4] = {};
+	const_values[0] = lb_const_int(m, t_uintptr, type_size_of(type)).value;
+	const_values[1] = lb_const_int(m, t_uintptr, type_align_of(type)).value;
+	const_values[2] = lb_const_int(m, t_uintptr, size).value;
+	const_values[3] = lb_const_int(m, t_uintptr, len).value;
+	LLVMValueRef llvm_res =  llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
+	lbValue res = {llvm_res, t_map_cell_info};
 
-	i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
-	i64 value_size   = type_size_of  (map_type->Map.value);
+	lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
+	lb_make_global_private_const(addr);
 
-	Type *key_type = map_type->Map.key;
-	Type *val_type = map_type->Map.value;
-	gb_unused(val_type);
+	map_set(&m->map_cell_info_map, type, addr);
 
-	Type *st = base_type(t_map_header_table);
-	GB_ASSERT(st->Struct.fields.count == 7);
+	return addr.addr;
+}
+lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
+	map_type = base_type(map_type);
+	GB_ASSERT(map_type->kind == Type_Map);
+
+	lbAddr *found = map_get(&m->map_info_map, map_type);
+	if (found) {
+		return found->addr;
+	}
+
+	GB_ASSERT(t_map_info != nullptr);
+	GB_ASSERT(t_map_cell_info != nullptr);
 
-	LLVMValueRef const_values[7] = {};
-	const_values[0] = lb_get_equal_proc_for_type(m, key_type)    .value;
-	const_values[1] = lb_const_int(m, t_int,        entry_size)  .value;
-	const_values[2] = lb_const_int(m, t_int,        entry_align) .value;
-	const_values[3] = lb_const_int(m, t_uintptr,    key_offset)  .value;
-	const_values[4] = lb_const_int(m, t_int,        key_size)    .value;
-	const_values[5] = lb_const_int(m, t_uintptr,    value_offset).value;
-	const_values[6] = lb_const_int(m, t_int,        value_size)  .value;
+	LLVMValueRef key_cell_info   = lb_gen_map_cell_info_ptr(m, map_type->Map.key).value;
+	LLVMValueRef value_cell_info = lb_gen_map_cell_info_ptr(m, map_type->Map.value).value;
 
-	LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values));
-	lbValue res = {llvm_res, t_map_header_table};
+	LLVMValueRef const_values[4] = {};
+	const_values[0] = key_cell_info;
+	const_values[1] = value_cell_info;
+	const_values[2] = lb_hasher_proc_for_type(m, map_type->Map.key).value;
+	const_values[3] = lb_equal_proc_for_type(m, map_type->Map.key).value;
 
-	lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr);
+	LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
+	lbValue res = {llvm_res, t_map_info};
+
+	lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
 	lb_make_global_private_const(addr);
 
-	map_set(&m->map_header_table_map, map_type, addr);
-	return lb_addr_load(p, addr);
+	map_set(&m->map_info_map, map_type, addr);
+	return addr.addr;
 }
 
 lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
@@ -602,7 +888,7 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
 
 	lbValue hashed_key = lb_const_hash(p->module, key, key_type);
 	if (hashed_key.value == nullptr) {
-		lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type);
+		lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
 
 		auto args = array_make<lbValue>(permanent_allocator(), 2);
 		args[0] = key_ptr;
@@ -615,42 +901,68 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
 
 lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) {
 	Type *map_type = base_type(type_deref(map_ptr.type));
+	GB_ASSERT(map_type->kind == Type_Map);
 
+	lbValue ptr = {};
 	lbValue key_ptr = {};
-	auto args = array_make<lbValue>(permanent_allocator(), 4);
-	args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
-	args[1] = lb_gen_map_header_table_internal(p, map_type);
-	args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
-	args[3] = key_ptr;
+	lbValue hash = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
 
-	lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
+	if (build_context.use_static_map_calls) {
+		lbValue map_get_proc = lb_map_get_proc_for_type(p->module, map_type);
 
+		auto args = array_make<lbValue>(permanent_allocator(), 3);
+		args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
+		args[1] = hash;
+		args[2] = key_ptr;
+
+		ptr = lb_emit_call(p, map_get_proc, args);
+	} else {
+		auto args = array_make<lbValue>(permanent_allocator(), 4);
+		args[0] = lb_emit_transmute(p, map_ptr, t_raw_map_ptr);
+		args[1] = lb_gen_map_info_ptr(p->module, map_type);
+		args[2] = hash;
+		args[3] = key_ptr;
+
+		ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
+	}
 	return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
 }
 
-void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
-                                         lbValue const &map_key, lbValue const &map_value, Ast *node) {
+void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
+                                 lbValue const &map_key, lbValue const &map_value, Ast *node) {
 	map_type = base_type(map_type);
 	GB_ASSERT(map_type->kind == Type_Map);
 
 	lbValue key_ptr = {};
-	lbValue key_hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
+	lbValue hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
+
 	lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
+	lbValue value_ptr = lb_address_from_load_or_generate_local(p, v);
 
-	lbAddr value_addr = lb_add_local_generated(p, v.type, false);
-	lb_addr_store(p, value_addr, v);
+	if (build_context.use_static_map_calls) {
+		lbValue map_set_proc = lb_map_set_proc_for_type(p->module, map_type);
 
-	auto args = array_make<lbValue>(permanent_allocator(), 6);
-	args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
-	args[1] = lb_gen_map_header_table_internal(p, map_type);
-	args[2] = key_hash;
-	args[3] = key_ptr;
-	args[4] = lb_emit_conv(p, value_addr.addr, t_rawptr);
-	args[5] = lb_emit_source_code_location(p, node);
-	lb_emit_runtime_call(p, "__dynamic_map_set", args);
+		auto args = array_make<lbValue>(permanent_allocator(), 5);
+		args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
+		args[1] = hash;
+		args[2] = lb_emit_conv(p, key_ptr, t_rawptr);
+		args[3] = lb_emit_conv(p, value_ptr, t_rawptr);
+		args[4] = lb_emit_source_code_location_as_global(p, node);
+
+		lb_emit_call(p, map_set_proc, args);
+	} else {
+		auto args = array_make<lbValue>(permanent_allocator(), 6);
+		args[0] = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
+		args[1] = lb_gen_map_info_ptr(p->module, map_type);
+		args[2] = hash;
+		args[3] = lb_emit_conv(p, key_ptr, t_rawptr);
+		args[4] = lb_emit_conv(p, value_ptr, t_rawptr);
+		args[5] = lb_emit_source_code_location_as_global(p, node);
+		lb_emit_runtime_call(p, "__dynamic_map_set", args);
+	}
 }
 
-void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
+lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
 	GB_ASSERT(!build_context.no_dynamic_literals);
 
 	String proc_name = {};
@@ -660,10 +972,10 @@ void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const
 
 	auto args = array_make<lbValue>(permanent_allocator(), 4);
 	args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
-	args[1] = lb_gen_map_header_table_internal(p, type_deref(map_ptr.type));
-	args[2] = lb_const_int(p->module, t_int, capacity);
-	args[3] = lb_emit_source_code_location(p, proc_name, pos);
-	lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
+	args[1] = lb_gen_map_info_ptr(p->module, type_deref(map_ptr.type));
+	args[2] = lb_const_int(p->module, t_uint, capacity);
+	args[3] = lb_emit_source_code_location_as_global(p, proc_name, pos);
+	return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
 }
 
 
@@ -688,6 +1000,8 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
 	p->is_startup = true;
 	LLVMSetLinkage(p->value, LLVMInternalLinkage);
 
+	lb_add_attribute_to_proc(m, p->value, "nounwind");
+
 	lb_begin_procedure_body(p);
 
 	lb_setup_type_info_data(p);
@@ -712,6 +1026,7 @@ lbProcedure *lb_create_objc_names(lbModule *main_module) {
 	}
 	Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
 	lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
+	lb_add_attribute_to_proc(p->module, p->value, "nounwind");
 	p->is_startup = true;
 	return p;
 }
@@ -1198,6 +1513,14 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) {
 		lbProcedure *p = m->hasher_procs.entries[i].value;
 		lb_run_function_pass_manager(default_function_pass_manager, p);
 	}
+	for_array(i, m->map_get_procs.entries) {
+		lbProcedure *p = m->map_get_procs.entries[i].value;
+		lb_run_function_pass_manager(default_function_pass_manager, p);
+	}
+	for_array(i, m->map_set_procs.entries) {
+		lbProcedure *p = m->map_set_procs.entries[i].value;
+		lb_run_function_pass_manager(default_function_pass_manager, p);
+	}
 
 	return 0;
 }
@@ -1736,6 +2059,13 @@ void lb_generate_code(lbGenerator *gen) {
 	lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
 	gb_unused(startup_runtime);
 
+	if (build_context.ODIN_DEBUG) {
+		for_array(i, builtin_pkg->scope->elements.entries) {
+			Entity *e = builtin_pkg->scope->elements.entries[i].value;
+			add_debug_info_for_global_constant_from_entity(gen, e);
+		}
+	}
+
 	TIME_SECTION("LLVM Global Procedures and Types");
 	for_array(i, info->entities) {
 		Entity *e = info->entities[i];
@@ -1759,6 +2089,11 @@ void lb_generate_code(lbGenerator *gen) {
 		case Entity_TypeName:
 		case Entity_Procedure:
 			break;
+		case Entity_Constant:
+			if (build_context.ODIN_DEBUG) {
+				add_debug_info_for_global_constant_from_entity(gen, e);
+			}
+			break;
 		}
 
 		bool polymorphic_struct = false;
@@ -1820,6 +2155,21 @@ void lb_generate_code(lbGenerator *gen) {
 	lb_finalize_objc_names(objc_names);
 
 	if (build_context.ODIN_DEBUG) {
+		TIME_SECTION("LLVM Debug Info for global constant value declarations");
+		{
+			// lbModule *m = default_module;
+
+
+		}
+		// if (gen->modules.entries.count == 1) {
+		// } else {
+		// 	for_array(j, gen->modules.entries) {
+		// 		lbModule *m = gen->modules.entries[j].value;
+		// 		if (m->debug_builder != nullptr) {
+		// 		}
+		// 	}
+		// }
+
 		TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
 		for_array(j, gen->modules.entries) {
 			lbModule *m = gen->modules.entries[j].value;

+ 12 - 8
src/llvm_backend.hpp

@@ -144,6 +144,8 @@ struct lbModule {
 
 	PtrMap<Type *, lbProcedure *> equal_procs;
 	PtrMap<Type *, lbProcedure *> hasher_procs;
+	PtrMap<Type *, lbProcedure *> map_get_procs;
+	PtrMap<Type *, lbProcedure *> map_set_procs;
 
 	u32 nested_type_name_guid;
 
@@ -160,7 +162,8 @@ struct lbModule {
 	StringMap<lbAddr> objc_classes;
 	StringMap<lbAddr> objc_selectors;
 
-	PtrMap<Type *, lbAddr> map_header_table_map;
+	PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
+	PtrMap<Type *, lbAddr> map_info_map;      // address of runtime.Map_Cell_Info
 };
 
 struct lbGenerator {
@@ -298,6 +301,7 @@ struct lbProcedure {
 	lbBlock *        entry_block;
 	lbBlock *        curr_block;
 	lbTargetList *   target_list;
+	PtrMap<Entity *, lbValue> direct_parameters;
 
 	Ast *curr_stmt;
 
@@ -421,8 +425,6 @@ lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
 lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
 lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
 lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
-lbValue lb_map_entries(lbProcedure *p, lbValue value);
-lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value);
 lbValue lb_map_len(lbProcedure *p, lbValue value);
 lbValue lb_map_cap(lbProcedure *p, lbValue value);
 lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
@@ -446,22 +448,24 @@ String lb_get_const_string(lbModule *m, lbValue value);
 lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
 lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
 lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
+lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
+lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
 
 lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
-void    lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
-void    lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
+void    lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
+lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
 
 lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
 lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
 
 void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
 lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
-lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos);
+lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
 
 lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
 
-lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type);
-lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type);
+lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
+lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
 lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
 
 LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);

+ 29 - 3
src/llvm_backend_const.cpp

@@ -256,7 +256,7 @@ lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) {
 	return lb_const_value(m, t, tv.value);
 }
 
-lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos) {
 	lbModule *m = p->module;
 
 	LLVMValueRef fields[4] = {};
@@ -271,7 +271,7 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To
 	return res;
 }
 
-lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
+lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
 	String proc_name = {};
 	if (p->entity) {
 		proc_name = p->entity->token.string;
@@ -280,9 +280,35 @@ lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
 	if (node) {
 		pos = ast_token(node).pos;
 	}
-	return lb_emit_source_code_location(p, proc_name, pos);
+	return lb_emit_source_code_location_const(p, proc_name, pos);
 }
 
+
+lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+	lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
+	lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
+	lb_make_global_private_const(addr);
+	return addr.addr;
+}
+
+
+lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
+	lbValue loc = lb_emit_source_code_location_const(p, node);
+	lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
+	lb_make_global_private_const(addr);
+	return addr.addr;
+}
+
+lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+	return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
+}
+
+lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
+	return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, node));
+}
+
+
+
 LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
 	bool is_local = allow_local && m->curr_procedure != nullptr;
 	bool is_const = true;

+ 116 - 1
src/llvm_backend_debug.cpp

@@ -294,6 +294,7 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 		GB_PANIC("Type_Named should be handled in lb_debug_type separately");
 
 	case Type_SoaPointer:
+		return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->SoaPointer.elem), word_bits, word_bits, 0, nullptr, 0);
 	case Type_Pointer:
 		return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0);
 	case Type_MultiPointer:
@@ -671,7 +672,8 @@ void lb_debug_complete_types(lbModule *m) {
 				break;
 
 			case Type_Map:
-				bt = bt->Map.internal_type;
+				GB_ASSERT(t_raw_map != nullptr);
+				bt = base_type(t_raw_map);
 				/*fallthrough*/
 			case Type_Struct:
 				if (file == nullptr) {
@@ -1080,3 +1082,116 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
 
 	lb_add_debug_local_variable(p, ptr, t_context, token);
 }
+
+
+String debug_info_mangle_constant_name(Entity *e, bool *did_allocate_) {
+	String name = e->token.string;
+	if (e->pkg && e->pkg->name.len > 0) {
+		// NOTE(bill): C++ NONSENSE FOR DEBUG SHITE!
+		name = concatenate3_strings(heap_allocator(), e->pkg->name, str_lit("::"), name);
+		if (did_allocate_) *did_allocate_ = true;
+	}
+	return name;
+}
+
+void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
+	LLVMMetadataRef scope = nullptr;
+	LLVMMetadataRef file = nullptr;
+	unsigned line = 0;
+
+	LLVMMetadataRef decl = nullptr;
+
+	LLVMDIBuilderCreateGlobalVariableExpression(
+		m->debug_builder, scope,
+		cast(char const *)name.text, cast(size_t)name.len,
+		"", 0, // Linkage
+		file, line, dtype,
+		false, // local to unit
+		expr, decl, 8/*AlignInBits*/);
+}
+
+void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
+	LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v);
+
+	bool did_allocate = false;
+	String name = debug_info_mangle_constant_name(e, &did_allocate);
+	defer (if (did_allocate) {
+		gb_free(heap_allocator(), name.text);
+	});
+
+	add_debug_info_global_variable_expr(m, name, dtype, expr);
+	if ((e->pkg && e->pkg->kind == Package_Init) ||
+	    (e->scope && (e->scope->flags & ScopeFlag_Global))) {
+		add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
+	}
+}
+
+void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
+	if (e == nullptr || e->kind != Entity_Constant) {
+		return;
+	}
+	if (is_blank_ident(e->token)) {
+		return;
+	}
+	lbModule *m = &gen->default_module;
+	if (USE_SEPARATE_MODULES) {
+		m = lb_pkg_module(gen, e->pkg);
+	}
+
+	if (is_type_integer(e->type)) {
+		ExactValue const &value = e->Constant.value;
+		if (value.kind == ExactValue_Integer) {
+			LLVMMetadataRef dtype = nullptr;
+			i64 v = 0;
+			bool is_signed = false;
+			if (big_int_is_neg(&value.value_integer)) {
+				v = exact_value_to_i64(value);
+				is_signed = true;
+			} else {
+				v = cast(i64)exact_value_to_u64(value);
+			}
+			if (is_type_untyped(e->type)) {
+				dtype = lb_debug_type(m, is_signed ? t_i64 : t_u64);
+			} else {
+				dtype = lb_debug_type(m, e->type);
+			}
+
+			add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+		}
+	} else if (is_type_rune(e->type)) {
+		ExactValue const &value = e->Constant.value;
+		if (value.kind == ExactValue_Integer) {
+			LLVMMetadataRef dtype = lb_debug_type(m, t_rune);
+			i64 v = exact_value_to_i64(value);
+			add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+		}
+	} else if (is_type_boolean(e->type)) {
+		ExactValue const &value = e->Constant.value;
+		if (value.kind == ExactValue_Bool) {
+			LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
+			i64 v = cast(i64)value.value_bool;
+
+			add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+		}
+	} else if (is_type_enum(e->type)) {
+		ExactValue const &value = e->Constant.value;
+		if (value.kind == ExactValue_Integer) {
+			LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
+			i64 v = 0;
+			if (big_int_is_neg(&value.value_integer)) {
+				v = exact_value_to_i64(value);
+			} else {
+				v = cast(i64)exact_value_to_u64(value);
+			}
+
+			add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+		}
+	} else if (is_type_pointer(e->type)) {
+		ExactValue const &value = e->Constant.value;
+		if (value.kind == ExactValue_Integer) {
+			LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
+			i64 v = cast(i64)exact_value_to_u64(value);
+			add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
+		}
+	}
+}

+ 32 - 37
src/llvm_backend_expr.cpp

@@ -1952,34 +1952,33 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		Type *dt = t;
 
 		GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
-		String field_name = lookup_subtype_polymorphic_field(t, src_type);
-		if (field_name.len > 0) {
-			// NOTE(bill): It can be casted
-			Selection sel = lookup_field(st, field_name, false, true);
-			if (sel.entity != nullptr) {
-				if (st_is_ptr) {
-					lbValue res = lb_emit_deep_field_gep(p, value, sel);
-					Type *rt = res.type;
+		Selection sel = {};
+		sel.index.allocator = heap_allocator();
+		defer (array_free(&sel.index));
+		if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
+			if (sel.entity == nullptr) {
+				GB_PANIC("invalid subtype cast  %s -> ", type_to_string(src_type), type_to_string(t));
+			}
+			if (st_is_ptr) {
+				lbValue res = lb_emit_deep_field_gep(p, value, sel);
+				Type *rt = res.type;
+				if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
+					res = lb_emit_load(p, res);
+				}
+				return res;
+			} else {
+				if (is_type_pointer(value.type)) {
+					Type *rt = value.type;
 					if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
-						res = lb_emit_load(p, res);
-					}
-					return res;
-				} else {
-					if (is_type_pointer(value.type)) {
-						Type *rt = value.type;
-						if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
-							value = lb_emit_load(p, value);
-						} else {
-							value = lb_emit_deep_field_gep(p, value, sel);
-							return lb_emit_load(p, value);
-						}
+						value = lb_emit_load(p, value);
+					} else {
+						value = lb_emit_deep_field_gep(p, value, sel);
+						return lb_emit_load(p, value);
 					}
+				}
 
-					return lb_emit_deep_field_ev(p, value, sel);
+				return lb_emit_deep_field_ev(p, value, sel);
 
-				}
-			} else {
-				GB_PANIC("invalid subtype cast  %s.%.*s", type_to_string(src_type), LIT(field_name));
 			}
 		}
 	}
@@ -2216,7 +2215,7 @@ lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbVa
 		args[2] = lb_const_int(p->module, t_int, type_size_of(type));
 		res = lb_emit_runtime_call(p, "memory_equal", args);
 	} else {
-		lbValue value = lb_get_equal_proc_for_type(p->module, type);
+		lbValue value = lb_equal_proc_for_type(p->module, type);
 		auto args = array_make<lbValue>(permanent_allocator(), 2);
 		args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
 		args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
@@ -2725,18 +2724,13 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
 
 	case Type_Map:
 		{
-			lbValue map_ptr = lb_address_from_load_or_generate_local(p, x);
-
-			unsigned indices[2] = {0, 0};
-			lbValue hashes_data = lb_emit_struct_ep(p, map_ptr, 0);
-			lbValue hashes_data_ptr_ptr = lb_emit_struct_ep(p, hashes_data, 0);
-			LLVMValueRef hashes_data_ptr = LLVMBuildLoad2(p->builder, llvm_addr_type(p->module, hashes_data_ptr_ptr), hashes_data_ptr_ptr.value, "");
+			lbValue data_ptr = lb_emit_struct_ev(p, x, 0);
 
 			if (op_kind == Token_CmpEq) {
-				res.value = LLVMBuildIsNull(p->builder, hashes_data_ptr, "");
+				res.value = LLVMBuildIsNull(p->builder, data_ptr.value, "");
 				return res;
 			} else {
-				res.value = LLVMBuildIsNotNull(p->builder, hashes_data_ptr, "");
+				res.value = LLVMBuildIsNotNull(p->builder, data_ptr.value, "");
 				return res;
 			}
 		}
@@ -4132,7 +4126,8 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 		}
 		GB_ASSERT(!build_context.no_dynamic_literals);
 
-		lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
+		lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
+		gb_unused(err);
 
 		for_array(field_index, cl->elems) {
 			Ast *elem = cl->elems[field_index];
@@ -4140,7 +4135,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 
 			lbValue key   = lb_build_expr(p, fv->field);
 			lbValue value = lb_build_expr(p, fv->value);
-			lb_insert_dynamic_map_key_and_value(p, v.addr, type, key, value, elem);
+			lb_internal_dynamic_map_set(p, v.addr, type, key, value, elem);
 		}
 		break;
 	}
@@ -4232,7 +4227,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 			args[1] = size;
 			args[2] = align;
 			args[3] = lb_const_int(p->module, t_int, item_count);
-			args[4] = lb_emit_source_code_location(p, proc_name, pos);
+			args[4] = lb_emit_source_code_location_as_global(p, proc_name, pos);
 			lb_emit_runtime_call(p, "__dynamic_array_reserve", args);
 		}
 
@@ -4253,7 +4248,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
 			args[2] = align;
 			args[3] = lb_emit_conv(p, items, t_rawptr);
 			args[4] = lb_const_int(p->module, t_int, item_count);
-			args[5] = lb_emit_source_code_location(p, proc_name, pos);
+			args[5] = lb_emit_source_code_location_as_global(p, proc_name, pos);
 			lb_emit_runtime_call(p, "__dynamic_array_append", args);
 		}
 		break;

+ 38 - 42
src/llvm_backend_general.cpp

@@ -67,6 +67,8 @@ void lb_init_module(lbModule *m, Checker *c) {
 	map_init(&m->function_type_map, a);
 	map_init(&m->equal_procs, a);
 	map_init(&m->hasher_procs, a);
+	map_init(&m->map_get_procs, a);
+	map_init(&m->map_set_procs, a);
 	array_init(&m->procedures_to_generate, a, 0, 1024);
 	array_init(&m->missing_procedures_to_check, a, 0, 16);
 	map_init(&m->debug_values, a);
@@ -75,7 +77,8 @@ void lb_init_module(lbModule *m, Checker *c) {
 	string_map_init(&m->objc_classes, a);
 	string_map_init(&m->objc_selectors, a);
 
-	map_init(&m->map_header_table_map, a, 0);
+	map_init(&m->map_info_map, a, 0);
+	map_init(&m->map_cell_info_map, a, 0);
 
 }
 
@@ -383,16 +386,27 @@ Type *lb_addr_type(lbAddr const &addr) {
 	if (addr.addr.value == nullptr) {
 		return nullptr;
 	}
-	if (addr.kind == lbAddr_Map) {
-		Type *t = base_type(addr.map.type);
-		GB_ASSERT(is_type_map(t));
-		return t->Map.value;
-	}
-	if (addr.kind == lbAddr_Swizzle) {
+	switch (addr.kind) {
+	case lbAddr_Map:
+		{
+			Type *t = base_type(addr.map.type);
+			GB_ASSERT(is_type_map(t));
+			return t->Map.value;
+		}
+	case lbAddr_Swizzle:
 		return addr.swizzle.type;
-	}
-	if (addr.kind == lbAddr_SwizzleLarge) {
+	case lbAddr_SwizzleLarge:
 		return addr.swizzle_large.type;
+	case lbAddr_Context:
+		if (addr.ctx.sel.index.count > 0) {
+			Type *t = t_context;
+			for_array(i, addr.ctx.sel.index) {
+				GB_ASSERT(is_type_struct(t));
+				t = base_type(t)->Struct.fields[addr.ctx.sel.index[i]]->type;
+			}
+			return t;
+		}
+		break;
 	}
 	return type_deref(addr.addr.type);
 }
@@ -714,7 +728,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
 
 		return;
 	} else if (addr.kind == lbAddr_Map) {
-		lb_insert_dynamic_map_key_and_value(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
+		lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
 		return;
 	} else if (addr.kind == lbAddr_Context) {
 		lbAddr old_addr = lb_find_or_generate_context_ptr(p);
@@ -1510,6 +1524,9 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
 	bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count);
 	if (type->Proc.result_count != 0) {
 		Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
+		 if (is_type_proc(single_ret)) {
+			single_ret = t_rawptr;
+		}
 		ret = lb_type(m, single_ret);
 		if (ret != nullptr) {
 			if (is_type_boolean(single_ret) &&
@@ -1920,38 +1937,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 
 	case Type_Map:
 		init_map_internal_types(type);
-		{
-			Type *internal_type = type->Map.internal_type;
-			GB_ASSERT(internal_type->kind == Type_Struct);
-
-			m->internal_type_level -= 1;
-			defer (m->internal_type_level += 1);
-
-			unsigned field_count = cast(unsigned)(internal_type->Struct.fields.count);
-			GB_ASSERT(field_count == 2);
-			LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
-
-			LLVMTypeRef entries_fields[] = {
-				lb_type(m, t_rawptr), // data
-				lb_type(m, t_int), // len
-				lb_type(m, t_int), // cap
-				lb_type(m, t_allocator), // allocator
-			};
-
-			fields[0] = lb_type(m, internal_type->Struct.fields[0]->type);
-			fields[1] = LLVMStructTypeInContext(ctx, entries_fields, gb_count_of(entries_fields), false);
-			
-			{ // Add this to simplify things
-				lbStructFieldRemapping entries_field_remapping = {};
-				slice_init(&entries_field_remapping, permanent_allocator(), gb_count_of(entries_fields));
-				for_array(i, entries_field_remapping) {
-					entries_field_remapping[i] = cast(i32)i;
-				}
-				map_set(&m->struct_field_remapping, cast(void *)fields[1], entries_field_remapping);
-			}
-			
-			return LLVMStructTypeInContext(ctx, fields, field_count, false);
-		}
+		GB_ASSERT(t_raw_map != nullptr);
+		return lb_type_internal(m, t_raw_map);
 
 	case Type_Struct:
 		{
@@ -2590,6 +2577,15 @@ lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String co
 
 
 lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
+	if (e->flags & EntityFlag_Param) {
+		// NOTE(bill): Bypass the stack copied variable for
+		// direct parameters as there is no need for the direct load
+		auto *found = map_get(&p->direct_parameters, e);
+		if (found) {
+			return *found;
+		}
+	}
+
 	auto *found = map_get(&m->values, e);
 	if (found) {
 		auto v = *found;

+ 67 - 17
src/llvm_backend_proc.cpp

@@ -160,10 +160,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 
 	switch (entity->Procedure.optimization_mode) {
 	case ProcedureOptimizationMode_None:
-		lb_add_attribute_to_proc(m, p->value, "optnone");
 		break;
 	case ProcedureOptimizationMode_Minimal:
 		lb_add_attribute_to_proc(m, p->value, "optnone");
+		lb_add_attribute_to_proc(m, p->value, "noinline");
 		break;
 	case ProcedureOptimizationMode_Size:
 		lb_add_attribute_to_proc(m, p->value, "optsize");
@@ -486,6 +486,8 @@ void lb_begin_procedure_body(lbProcedure *p) {
 	p->entry_block = lb_create_block(p, "entry", true);
 	lb_start_block(p, p->entry_block);
 
+	map_init(&p->direct_parameters, heap_allocator());
+
 	GB_ASSERT(p->type != nullptr);
 
 	lb_ensure_abi_function_type(p->module, p);
@@ -539,6 +541,8 @@ void lb_begin_procedure_body(lbProcedure *p) {
 						param.value = value;
 						param.type = e->type;
 
+						map_set(&p->direct_parameters, e, param);
+
 						lbValue ptr = lb_address_from_load_or_generate_local(p, param);
 						GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
 						lb_add_entity(p->module, e, ptr);
@@ -582,6 +586,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
 					//                 defer x = ... // defer is executed after the `defer`
 					//                 return // the values returned should be zeroed
 					//         }
+					// NOTE(bill): REALLY, don't even bother.
 					lbAddr res = lb_add_local(p, e->type, e);
 					if (e->Variable.param_value.kind != ParameterValue_Invalid) {
 						lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
@@ -907,6 +912,9 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 	auto processed_args = array_make<lbValue>(permanent_allocator(), 0, args.count);
 
 	{
+
+		bool is_odin_cc = is_calling_convention_odin(pt->Proc.calling_convention);
+
 		lbFunctionType *ft = lb_get_function_type(m, p, pt);
 		bool return_by_pointer = ft->ret.kind == lbArg_Indirect;
 
@@ -942,8 +950,12 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
 			} else if (arg->kind == lbArg_Indirect) {
 				lbValue ptr = {};
 				if (arg->is_byval) {
-					ptr = lb_copy_value_to_ptr(p, x, original_type, arg->byval_alignment);
-				} else if (is_calling_convention_odin(pt->Proc.calling_convention)) {
+					if (is_odin_cc && are_types_identical(original_type, t_source_code_location)) {
+						ptr = lb_address_from_load_or_generate_local(p, x);
+					} else {
+						ptr = lb_copy_value_to_ptr(p, x, original_type, arg->byval_alignment);
+					}
+				} else if (is_odin_cc) {
 					// NOTE(bill): Odin parameters are immutable so the original value can be passed if possible
 					// i.e. `T const &` in C++
 					ptr = lb_address_from_load_or_generate_local(p, x);
@@ -1499,7 +1511,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			pos = e->token.pos;
 
 		}
-		return lb_emit_source_code_location(p, procedure, pos);
+		return lb_emit_source_code_location_as_global(p, procedure, pos);
 	}
 
 	case BuiltinProc_type_info_of: {
@@ -1713,7 +1725,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 				return lb_emit_struct_ev(p, val, 0);
 			} else if (t->kind == Type_Array) {
 				GB_ASSERT(t->Array.count == 1);
-				return lb_emit_array_epi(p, val, 0);
+				return lb_emit_struct_ev(p, val, 0);
 			} else {
 				GB_PANIC("Unknown type of expand_to_tuple");
 			}
@@ -1850,6 +1862,37 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		lb_emit_unreachable(p);
 		return {};
 
+	case BuiltinProc_raw_data:
+		{
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			Type *t = base_type(x.type);
+			lbValue res = {};
+			switch (t->kind) {
+			case Type_Slice:
+				res = lb_slice_elem(p, x);
+				res = lb_emit_conv(p, res, tv.type);
+				break;
+			case Type_DynamicArray:
+				res = lb_dynamic_array_elem(p, x);
+				res = lb_emit_conv(p, res, tv.type);
+				break;
+			case Type_Basic:
+				if (t->Basic.kind == Basic_string) {
+					res = lb_string_elem(p, x);
+					res = lb_emit_conv(p, res, tv.type);
+				} else if (t->Basic.kind == Basic_cstring) {
+					res = lb_emit_conv(p, x, tv.type);
+				}
+				break;
+			case Type_Pointer:
+			case Type_MultiPointer:
+				res = lb_emit_conv(p, x, tv.type);
+				break;
+			}
+			GB_ASSERT(res.value != nullptr);
+			return res;
+		}
+
 
 	// "Intrinsics"
 
@@ -2083,17 +2126,17 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 		}
 	case BuiltinProc_ptr_sub:
 		{
-			lbValue ptr0 = lb_build_expr(p, ce->args[0]);
-			lbValue ptr1 = lb_build_expr(p, ce->args[1]);
+			Type *elem0 = type_deref(type_of_expr(ce->args[0]));
+			Type *elem1 = type_deref(type_of_expr(ce->args[1]));
+			GB_ASSERT(are_types_identical(elem0, elem1));
+			Type *elem = elem0;
 
-			LLVMTypeRef type_int = lb_type(p->module, t_int);
-			LLVMValueRef diff = LLVMBuildPtrDiff2(p->builder, lb_type(p->module, ptr0.type), ptr0.value, ptr1.value, "");
-			diff = LLVMBuildIntCast2(p->builder, diff, type_int, /*signed*/true, "");
+			lbValue ptr0 = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr);
+			lbValue ptr1 = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr);
 
-			lbValue res = {};
-			res.type = t_int;
-			res.value = diff;
-			return res;
+			lbValue diff = lb_emit_arith(p, Token_Sub, ptr0, ptr1, t_uintptr);
+			diff = lb_emit_conv(p, diff, t_int);
+			return lb_emit_arith(p, Token_Quo, diff, lb_const_int(p->module, t_int, type_size_of(elem)), t_int);
 		}
 
 
@@ -2277,10 +2320,17 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 
 
 	case BuiltinProc_type_equal_proc:
-		return lb_get_equal_proc_for_type(p->module, ce->args[0]->tav.type);
+		return lb_equal_proc_for_type(p->module, ce->args[0]->tav.type);
 
 	case BuiltinProc_type_hasher_proc:
-		return lb_get_hasher_proc_for_type(p->module, ce->args[0]->tav.type);
+		return lb_hasher_proc_for_type(p->module, ce->args[0]->tav.type);
+
+	case BuiltinProc_type_map_info:
+		return lb_gen_map_info_ptr(p->module, ce->args[0]->tav.type);
+
+	case BuiltinProc_type_map_cell_info:
+		return lb_gen_map_cell_info_ptr(p->module, ce->args[0]->tav.type);
+
 
 	case BuiltinProc_fixed_point_mul:
 	case BuiltinProc_fixed_point_div:
@@ -2839,7 +2889,7 @@ lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterVal
 			if (p->entity != nullptr) {
 				proc_name = p->entity->token.string;
 			}
-			return lb_emit_source_code_location(p, proc_name, pos);
+			return lb_emit_source_code_location_as_global(p, proc_name, pos);
 		}
 	case ParameterValue_Value:
 		return lb_build_expr(p, param_value.ast_value);

+ 124 - 13
src/llvm_backend_stmt.cpp

@@ -354,16 +354,6 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
 		}
 		break;
 	}
-	case Type_Map: {
-		lbValue entries = lb_map_entries_ptr(p, expr);
-		lbValue elem = lb_emit_struct_ep(p, entries, 0);
-		elem = lb_emit_load(p, elem);
-		lbValue entry = lb_emit_ptr_offset(p, elem, idx);		
-		idx = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2));
-		val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 3));
-
-		break;
-	}
 	case Type_Struct: {
 		GB_ASSERT(is_type_soa_struct(expr_type));
 		break;
@@ -380,6 +370,129 @@ void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValu
 	if (done_) *done_ = done;
 }
 
+lbValue lb_map_cell_index_static(lbProcedure *p, Type *type, lbValue cells_ptr, lbValue index) {
+	i64 size, len;
+	i64 elem_sz = type_size_of(type);
+	map_cell_size_and_len(type, &size, &len);
+
+	index = lb_emit_conv(p, index, t_uintptr);
+
+	if (size == len*elem_sz) {
+		lbValue elems_ptr = lb_emit_conv(p, cells_ptr, alloc_type_pointer(type));
+		return lb_emit_ptr_offset(p, elems_ptr, index);
+	}
+
+	lbValue cell_index = {};
+	lbValue data_index = {};
+
+	lbValue size_const = lb_const_int(p->module, t_uintptr, size);
+	lbValue len_const = lb_const_int(p->module, t_uintptr, len);
+
+	if (is_power_of_two(len)) {
+		u64 log2_len = floor_log2(cast(u64)len);
+		cell_index = log2_len == 0 ? index : lb_emit_arith(p, Token_Shr, index, lb_const_int(p->module, t_uintptr, log2_len), t_uintptr);
+		data_index = lb_emit_arith(p, Token_And, index, lb_const_int(p->module, t_uintptr, len-1), t_uintptr);
+	} else {
+		cell_index = lb_emit_arith(p, Token_Quo, index, len_const, t_uintptr);
+		data_index = lb_emit_arith(p, Token_Mod, index, len_const, t_uintptr);
+	}
+
+	lbValue elems_ptr = lb_emit_conv(p, cells_ptr, t_uintptr);
+	lbValue cell_offset = lb_emit_arith(p, Token_Mul, size_const, cell_index, t_uintptr);
+	elems_ptr = lb_emit_arith(p, Token_Add, elems_ptr, cell_offset, t_uintptr);
+
+	elems_ptr = lb_emit_conv(p, elems_ptr, alloc_type_pointer(type));
+
+	return lb_emit_ptr_offset(p, elems_ptr, data_index);
+}
+
+void lb_map_kvh_data_static(lbProcedure *p, lbValue map_value, lbValue *ks_, lbValue *vs_, lbValue *hs_) {
+	lbValue capacity = lb_map_cap(p, map_value);
+	lbValue ks = lb_map_data_uintptr(p, map_value);
+	lbValue vs = {};
+	lbValue hs = {};
+	if (ks_) *ks_ = ks;
+	if (vs_) *vs_ = vs;
+	if (hs_) *hs_ = hs;
+}
+
+lbValue lb_map_hash_is_valid(lbProcedure *p, lbValue hash) {
+	// N :: size_of(uintptr)*8 - 1
+	// (hash != 0) & (hash>>N == 0)
+
+	u64 top_bit_index = cast(u64)(type_size_of(t_uintptr)*8 - 1);
+	lbValue shift_amount = lb_const_int(p->module, t_uintptr, top_bit_index);
+	lbValue zero = lb_const_int(p->module, t_uintptr, 0);
+
+	lbValue not_empty = lb_emit_comp(p, Token_NotEq, hash, zero);
+
+	lbValue not_deleted = lb_emit_arith(p, Token_Shr, hash, shift_amount, t_uintptr);
+	not_deleted = lb_emit_comp(p, Token_CmpEq, not_deleted, zero);
+
+	return lb_emit_arith(p, Token_And, not_deleted, not_empty, t_uintptr);
+}
+
+void lb_build_range_map(lbProcedure *p, lbValue expr, Type *val_type,
+                        lbValue *val_, lbValue *key_, lbBlock **loop_, lbBlock **done_) {
+	lbModule *m = p->module;
+
+	Type *type = base_type(type_deref(expr.type));
+	GB_ASSERT(type->kind == Type_Map);
+
+	lbValue idx = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	lbBlock *body = nullptr;
+	lbBlock *hash_check = nullptr;
+
+
+	lbAddr index = lb_add_local_generated(p, t_int, false);
+	lb_addr_store(p, index, lb_const_int(m, t_int, cast(u64)-1));
+
+	loop = lb_create_block(p, "for.index.loop");
+	lb_emit_jump(p, loop);
+	lb_start_block(p, loop);
+
+	lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(m, t_int, 1), t_int);
+	lb_addr_store(p, index, incr);
+
+	hash_check = lb_create_block(p, "for.index.hash_check");
+	body = lb_create_block(p, "for.index.body");
+	done = lb_create_block(p, "for.index.done");
+
+	lbValue map_value = lb_emit_load(p, expr);
+	lbValue capacity = lb_map_cap(p, map_value);
+	lbValue cond = lb_emit_comp(p, Token_Lt, incr, capacity);
+	lb_emit_if(p, cond, hash_check, done);
+	lb_start_block(p, hash_check);
+
+	idx = lb_addr_load(p, index);
+
+	lbValue ks = lb_map_data_uintptr(p, map_value);
+	lbValue vs = lb_emit_conv(p, lb_map_cell_index_static(p, type->Map.key, ks, capacity), alloc_type_pointer(type->Map.value));
+	lbValue hs = lb_emit_conv(p, lb_map_cell_index_static(p, type->Map.value, vs, capacity), alloc_type_pointer(t_uintptr));
+
+	// NOTE(bill): no need to use lb_map_cell_index_static for that hashes
+	// since it will always be packed without padding into the cells
+	lbValue hash = lb_emit_load(p, lb_emit_ptr_offset(p, hs, idx));
+
+	lbValue hash_cond = lb_map_hash_is_valid(p, hash);
+	lb_emit_if(p, hash_cond, body, loop);
+	lb_start_block(p, body);
+
+
+	lbValue key_ptr = lb_map_cell_index_static(p, type->Map.key, ks, idx);
+	lbValue val_ptr = lb_map_cell_index_static(p, type->Map.value, vs, idx);
+	lbValue key = lb_emit_load(p, key_ptr);
+	lbValue val = lb_emit_load(p, val_ptr);
+
+	if (val_)  *val_  = val;
+	if (key_)  *key_  = key;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+
 
 void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
                             lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
@@ -749,9 +862,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
 			if (is_type_pointer(type_deref(map.type))) {
 				map = lb_emit_load(p, map);
 			}
-			lbValue entries_ptr = lb_map_entries_ptr(p, map);
-			lbValue count_ptr = lb_emit_struct_ep(p, entries_ptr, 1);
-			lb_build_range_indexed(p, map, val1_type, count_ptr, &val, &key, &loop, &done);
+			lb_build_range_map(p, map, val1_type, &val, &key, &loop, &done);
 			break;
 		}
 		case Type_Array: {

+ 5 - 9
src/llvm_backend_type.cpp

@@ -235,7 +235,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 			}
 			TokenPos pos = t->Named.type_name->token.pos;
 
-			lbValue loc = lb_emit_source_code_location(p, proc_name, pos);
+			lbValue loc = lb_emit_source_code_location_const(p, proc_name, pos);
 
 			LLVMValueRef vals[4] = {
 				lb_const_string(p->module, t->Named.type_name->token.string).value,
@@ -666,7 +666,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 				}
 
 				if (is_type_comparable(t) && !is_type_simple_compare(t)) {
-					vals[3] = lb_get_equal_proc_for_type(m, t).value;
+					vals[3] = lb_equal_proc_for_type(m, t).value;
 				}
 
 				vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
@@ -702,7 +702,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 				vals[6] = is_raw_union.value;
 				vals[7] = is_custom_align.value;
 				if (is_type_comparable(t) && !is_type_simple_compare(t)) {
-					vals[8] = lb_get_equal_proc_for_type(m, t).value;
+					vals[8] = lb_equal_proc_for_type(m, t).value;
 				}
 
 
@@ -788,15 +788,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 		case Type_Map: {
 			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
 			init_map_internal_types(t);
-			
-			lbValue gst = lb_type_info(m, t->Map.internal_type);
 
-			LLVMValueRef vals[5] = {
+			LLVMValueRef vals[3] = {
 				lb_type_info(m, t->Map.key).value,
 				lb_type_info(m, t->Map.value).value,
-				gst.value,
-				lb_get_equal_proc_for_type(m, t->Map.key).value,
-				lb_get_hasher_proc_for_type(m, t->Map.key).value
+				lb_gen_map_info_ptr(p->module, t).value
 			};
 
 			lbValue res = {};

+ 71 - 51
src/llvm_backend_utility.cpp

@@ -200,26 +200,32 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
 	GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: '%s' to '%s'", type_to_string(src_type), type_to_string(t));
 
 	// NOTE(bill): Casting between an integer and a pointer cannot be done through a bitcast
-	if (is_type_uintptr(src) && is_type_internally_pointer_like(dst)) {
-		res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
-		return res;
-	}
-	if (is_type_internally_pointer_like(src) && is_type_uintptr(dst)) {
-		res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
-		return res;
-	}
-
-	if (is_type_integer(src) && is_type_internally_pointer_like(dst)) {
-		res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
-		return res;
-	} else if (is_type_internally_pointer_like(src) && is_type_integer(dst)) {
-		res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
-		return res;
+	if (is_type_internally_pointer_like(src)) {
+		if (is_type_integer(dst)) {
+			res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
+			return res;
+		} else if (is_type_internally_pointer_like(dst)) {
+			res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(p->module, t), "");
+			return res;
+		} else if (is_type_float(dst)) {
+			LLVMValueRef the_int = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t_uintptr), "");
+			res.value = LLVMBuildBitCast(p->builder, the_int, lb_type(m, t), "");
+			return res;
+		}
 	}
 
-	if (is_type_internally_pointer_like(src) && is_type_internally_pointer_like(dst)) {
-		res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(p->module, t), "");
-		return res;
+	if (is_type_internally_pointer_like(dst)) {
+		if (is_type_uintptr(src) && is_type_internally_pointer_like(dst)) {
+			res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+			return res;
+		} else if (is_type_integer(src) && is_type_internally_pointer_like(dst)) {
+			res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+			return res;
+		} else if (is_type_float(src)) {
+			LLVMValueRef the_int = LLVMBuildBitCast(p->builder, value.value, lb_type(m, t_uintptr), "");
+			res.value = LLVMBuildIntToPtr(p->builder, the_int, lb_type(m, t), "");
+			return res;
+		}
 	}
 
 	if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
@@ -239,15 +245,17 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
 		ap = lb_emit_conv(p, ap, alloc_type_pointer(value.type));
 		lb_emit_store(p, ap, value);
 		return lb_addr_load(p, addr);
-	}
-
-	if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
+	} else if (is_type_map(src) && are_types_identical(t_raw_map, t)) {
+		res.value = value.value;
+		res.type = t;
+		return res;
+	} else if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
 		lbValue s = lb_address_from_load_or_generate_local(p, value);
 		lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t));
 		return lb_emit_load(p, d);
 	}
 
-	res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
+	res.value = OdinLLVMBuildTransmute(p, value.value, lb_type(m, res.type));
 	return res;
 }
 
@@ -990,14 +998,13 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
 		}
 	} else if (is_type_map(t)) {
 		init_map_internal_types(t);
-		Type *itp = alloc_type_pointer(t->Map.internal_type);
+		Type *itp = alloc_type_pointer(t_raw_map);
 		s = lb_emit_transmute(p, s, itp);
 
-		Type *gst = t->Map.internal_type;
-		GB_ASSERT(gst->kind == Type_Struct);
 		switch (index) {
-		case 0: result_type = get_struct_field_type(gst, 0); break;
-		case 1: result_type = get_struct_field_type(gst, 1); break;
+		case 0: result_type = get_struct_field_type(t_raw_map, 0); break;
+		case 1: result_type = get_struct_field_type(t_raw_map, 1); break;
+		case 2: result_type = get_struct_field_type(t_raw_map, 2); break;
 		}
 	} else if (is_type_array(t)) {
 		return lb_emit_array_epi(p, s, index);
@@ -1130,10 +1137,10 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 	case Type_Map:
 		{
 			init_map_internal_types(t);
-			Type *gst = t->Map.internal_type;
 			switch (index) {
-			case 0: result_type = get_struct_field_type(gst, 0); break;
-			case 1: result_type = get_struct_field_type(gst, 1); break;
+			case 0: result_type = get_struct_field_type(t_raw_map, 0); break;
+			case 1: result_type = get_struct_field_type(t_raw_map, 1); break;
+			case 2: result_type = get_struct_field_type(t_raw_map, 2); break;
 			}
 		}
 		break;
@@ -1439,34 +1446,47 @@ lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da) {
 	return lb_emit_struct_ev(p, da, 3);
 }
 
-lbValue lb_map_entries(lbProcedure *p, lbValue value) {
-	Type *t = base_type(value.type);
-	GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
-	init_map_internal_types(t);
-	i32 index = 1;
-	lbValue entries = lb_emit_struct_ev(p, value, index);
-	return entries;
+lbValue lb_map_len(lbProcedure *p, lbValue value) {
+	GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type));
+	lbValue len = lb_emit_struct_ev(p, value, 1);
+	return lb_emit_conv(p, len, t_int);
 }
-
-lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value) {
-	Type *t = base_type(type_deref(value.type));
-	GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
-	init_map_internal_types(t);
-	i32 index = 1;
-	lbValue entries = lb_emit_struct_ep(p, value, index);	
-	return entries;
+lbValue lb_map_len_ptr(lbProcedure *p, lbValue map_ptr) {
+	Type *type = map_ptr.type;
+	GB_ASSERT(is_type_pointer(type));
+	type = type_deref(type);
+	GB_ASSERT_MSG(is_type_map(type) || are_types_identical(type, t_raw_map), "%s", type_to_string(type));
+	return lb_emit_struct_ep(p, map_ptr, 1);
 }
 
-lbValue lb_map_len(lbProcedure *p, lbValue value) {
-	lbValue entries = lb_map_entries(p, value);
-	return lb_dynamic_array_len(p, entries);
+lbValue lb_map_cap(lbProcedure *p, lbValue value) {
+	GB_ASSERT_MSG(is_type_map(value.type) || are_types_identical(value.type, t_raw_map), "%s", type_to_string(value.type));
+	lbValue zero = lb_const_int(p->module, t_uintptr, 0);
+	lbValue one = lb_const_int(p->module, t_uintptr, 1);
+
+	lbValue mask = lb_const_int(p->module, t_uintptr, MAP_CACHE_LINE_SIZE-1);
+
+	lbValue data = lb_emit_struct_ev(p, value, 0);
+	lbValue log2_cap = lb_emit_arith(p, Token_And, data, mask, t_uintptr);
+	lbValue cap = lb_emit_arith(p, Token_Shl, one, log2_cap, t_uintptr);
+	lbValue cmp = lb_emit_comp(p, Token_CmpEq, data, zero);
+	return lb_emit_conv(p, lb_emit_select(p, cmp, zero, cap), t_int);
 }
 
-lbValue lb_map_cap(lbProcedure *p, lbValue value) {
-	lbValue entries = lb_map_entries(p, value);
-	return lb_dynamic_array_cap(p, entries);
+lbValue lb_map_data_uintptr(lbProcedure *p, lbValue value) {
+	GB_ASSERT(is_type_map(value.type) || are_types_identical(value.type, t_raw_map));
+	lbValue data = lb_emit_struct_ev(p, value, 0);
+	u64 mask_value = 0;
+	if (build_context.word_size == 4) {
+		mask_value = 0xfffffffful & ~(MAP_CACHE_LINE_SIZE-1);
+	} else {
+		mask_value = 0xffffffffffffffffull & ~(MAP_CACHE_LINE_SIZE-1);
+	}
+	lbValue mask = lb_const_int(p->module, t_uintptr, mask_value);
+	return lb_emit_arith(p, Token_And, data, mask, t_uintptr);
 }
 
+
 lbValue lb_soa_struct_len(lbProcedure *p, lbValue value) {
 	Type *t = base_type(value.type);
 	bool is_ptr = false;

+ 43 - 48
src/main.cpp

@@ -53,6 +53,9 @@ gb_global Timings global_timings = {0};
 	#if LLVM_VERSION_MAJOR < 11
 	#error LLVM Version 11+ is required => "brew install llvm@11"
 	#endif
+	#if LLVM_VERSION_MAJOR > 14
+	#error LLVM Version 11..=14 is required => "brew install llvm@14"
+	#endif
 #endif
 
 #include "query_data.cpp"
@@ -478,9 +481,9 @@ i32 linker_stage(lbGenerator *gen) {
 
 			if (build_context.metrics.os == TargetOs_darwin) {
 				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
-				// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
-				//       make sure to also change the 'mtriple' param passed to 'opt'
-				if (build_context.metrics.arch == TargetArch_arm64) {
+				if (build_context.minimum_os_version_string.len) {
+					link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
+				} else if (build_context.metrics.arch == TargetArch_arm64) {
 					link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0  ");
 				} else {
 					link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
@@ -588,7 +591,6 @@ enum BuildFlagKind {
 	BuildFlag_SingleFile,
 
 	BuildFlag_OutFile,
-	BuildFlag_OptimizationLevel,
 	BuildFlag_OptimizationMode,
 	BuildFlag_ShowTimings,
 	BuildFlag_ShowUnused,
@@ -622,6 +624,7 @@ enum BuildFlagKind {
 	BuildFlag_ExtraAssemblerFlags,
 	BuildFlag_Microarch,
 	BuildFlag_TargetFeatures,
+	BuildFlag_MinimumOSVersion,
 
 	BuildFlag_RelocMode,
 	BuildFlag_DisableRedZone,
@@ -635,6 +638,7 @@ enum BuildFlagKind {
 	BuildFlag_StrictStyleInitOnly,
 	BuildFlag_ForeignErrorProcedures,
 	BuildFlag_DisallowRTTI,
+	BuildFlag_UseStaticMapCalls,
 
 	BuildFlag_Compact,
 	BuildFlag_GlobalDefinitions,
@@ -742,13 +746,25 @@ ExactValue build_param_to_exact_value(String name, String param) {
 	return value;
 }
 
+// Writes a did-you-mean message for formerly deprecated flags.
+void did_you_mean_flag(String flag) {
+	gbAllocator a = heap_allocator();
+	String name = copy_string(a, flag);
+	defer (gb_free(a, name.text));
+	string_to_lower(&name);
+
+	if (name == "opt") {
+		gb_printf_err("`-opt` is an unrecognized option. Did you mean `-o`?\n");
+		return;
+	}
+	gb_printf_err("Unknown flag: '%.*s'\n", LIT(flag));
+}
 
 bool parse_build_flags(Array<String> args) {
 	auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
 	add_flag(&build_flags, BuildFlag_Help,                    str_lit("help"),                      BuildFlagParam_None,    Command_all);
 	add_flag(&build_flags, BuildFlag_SingleFile,              str_lit("file"),                      BuildFlagParam_None,    Command__does_build | Command__does_check);
 	add_flag(&build_flags, BuildFlag_OutFile,                 str_lit("out"),                       BuildFlagParam_String,  Command__does_build &~ Command_test);
-	add_flag(&build_flags, BuildFlag_OptimizationLevel,       str_lit("opt"),                       BuildFlagParam_Integer, Command__does_build);
 	add_flag(&build_flags, BuildFlag_OptimizationMode,        str_lit("o"),                         BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_OptimizationMode,        str_lit("O"),                         BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_ShowTimings,             str_lit("show-timings"),              BuildFlagParam_None,    Command__does_check);
@@ -783,6 +799,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags,     str_lit("extra-assembler-flags"),     BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_Microarch,               str_lit("microarch"),                 BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_TargetFeatures,          str_lit("target-features"),           BuildFlagParam_String,  Command__does_build);
+	add_flag(&build_flags, BuildFlag_MinimumOSVersion,        str_lit("minimum-os-version"),        BuildFlagParam_String,  Command__does_build);
 
 	add_flag(&build_flags, BuildFlag_RelocMode,               str_lit("reloc-mode"),                BuildFlagParam_String,  Command__does_build);
 	add_flag(&build_flags, BuildFlag_DisableRedZone,          str_lit("disable-red-zone"),          BuildFlagParam_None,    Command__does_build);
@@ -798,6 +815,8 @@ bool parse_build_flags(Array<String> args) {
 
 	add_flag(&build_flags, BuildFlag_DisallowRTTI,            str_lit("disallow-rtti"),             BuildFlagParam_None,    Command__does_check);
 
+	add_flag(&build_flags, BuildFlag_UseStaticMapCalls,       str_lit("use-static-map-calls"),      BuildFlagParam_None,    Command__does_check);
+
 
 	add_flag(&build_flags, BuildFlag_Compact,                 str_lit("compact"),                   BuildFlagParam_None,    Command_query);
 	add_flag(&build_flags, BuildFlag_GlobalDefinitions,       str_lit("global-definitions"),        BuildFlagParam_None,    Command_query);
@@ -850,16 +869,14 @@ bool parse_build_flags(Array<String> args) {
 				break;
 			}
 		}
-		name = substring(name, 0, end);
-		if (have_equals && name != "opt") {
-			gb_printf_err("`flag=value` has been deprecated and will be removed next release. Use `%.*s:` instead.\n", LIT(name));
-		}
 
+		name = substring(name, 0, end);
 		String param = {};
 		if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
 
 		bool is_supported = true;
 		bool found = false;
+
 		BuildFlag found_bf = {};
 		for_array(build_flag_index, build_flags) {
 			BuildFlag bf = build_flags[build_flag_index];
@@ -979,37 +996,8 @@ bool parse_build_flags(Array<String> args) {
 							}
 							break;
 						}
-						case BuildFlag_OptimizationLevel: {
-							GB_ASSERT(value.kind == ExactValue_Integer);
-							if (set_flags[BuildFlag_OptimizationMode]) {
-								gb_printf_err("Mixture of -opt and -o is not allowed\n");
-								bad_flags = true;
-								break;
-							}
-
-							build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
-							if (build_context.optimization_level < 0 || build_context.optimization_level > 3) {
-								gb_printf_err("Invalid optimization level for -o:<integer>, got %d\n", build_context.optimization_level);
-								gb_printf_err("Valid optimization levels:\n");
-								gb_printf_err("\t0\n");
-								gb_printf_err("\t1\n");
-								gb_printf_err("\t2\n");
-								gb_printf_err("\t3\n");
-								bad_flags = true;
-							}
-
-							// Deprecation warning.
-							gb_printf_err("`-opt` has been deprecated and will be removed next release. Use `-o:minimal`, etc.\n");
-							break;
-						}
 						case BuildFlag_OptimizationMode: {
 							GB_ASSERT(value.kind == ExactValue_String);
-							if (set_flags[BuildFlag_OptimizationLevel]) {
-								gb_printf_err("Mixture of -opt and -o is not allowed\n");
-								bad_flags = true;
-								break;
-							}
-
 							if (value.value_string == "minimal") {
 								build_context.optimization_level = 0;
 							} else if (value.value_string == "size") {
@@ -1378,6 +1366,11 @@ bool parse_build_flags(Array<String> args) {
 							string_to_lower(&build_context.target_features_string);
 							break;
 						}
+						case BuildFlag_MinimumOSVersion: {
+							GB_ASSERT(value.kind == ExactValue_String);
+							build_context.minimum_os_version_string = value.value_string;
+							break;
+						}
 						case BuildFlag_RelocMode: {
 							GB_ASSERT(value.kind == ExactValue_String);
 							String v = value.value_string;
@@ -1424,6 +1417,9 @@ bool parse_build_flags(Array<String> args) {
 						case BuildFlag_DisallowRTTI:
 							build_context.disallow_rtti = true;
 							break;
+						case BuildFlag_UseStaticMapCalls:
+							build_context.use_static_map_calls = true;
+							break;
 						case BuildFlag_DefaultToNilAllocator:
 							build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
 							break;
@@ -1625,7 +1621,7 @@ bool parse_build_flags(Array<String> args) {
 			gb_printf_err("\n");
 			bad_flags = true;
 		} else if (!found) {
-			gb_printf_err("Unknown flag: '%.*s'\n", LIT(name));
+			did_you_mean_flag(name);
 			bad_flags = true;
 		}
 	}
@@ -1989,12 +1985,6 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(2, "Example: -out:foo.exe");
 		print_usage_line(0, "");
 
-		print_usage_line(1, "-opt:<integer>");
-		print_usage_line(2, "Set the optimization level for compilation");
-		print_usage_line(2, "Accepted values: 0, 1, 2, 3");
-		print_usage_line(2, "Example: -opt:2");
-		print_usage_line(0, "");
-
 		print_usage_line(1, "-o:<string>");
 		print_usage_line(2, "Set the optimization mode for compilation");
 		print_usage_line(2, "Accepted values: minimal, size, speed");
@@ -2056,8 +2046,8 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(3, "import \"shared:foo\"");
 		print_usage_line(0, "");
 
-		print_usage_line(1, "-define:<name>=<expression>");
-		print_usage_line(2, "Defines a global constant with a value");
+		print_usage_line(1, "-define:<name>=<value>");
+		print_usage_line(2, "Defines a scalar boolean, integer or string as global constant");
 		print_usage_line(2, "Example: -define:SPAM=123");
 		print_usage_line(2, "To use:  #config(SPAM, default_value)");
 		print_usage_line(0, "");
@@ -2158,6 +2148,12 @@ void print_show_help(String const arg0, String const &command) {
 	}
 
 	if (run_or_build) {
+		print_usage_line(1, "-minimum-os-version:<string>");
+		print_usage_line(2, "Sets the minimum OS version targeted by the application");
+		print_usage_line(2, "e.g. -minimum-os-version:12.0.0");
+		print_usage_line(2, "(Only used when target is Darwin)");
+		print_usage_line(0, "");
+
 		print_usage_line(1, "-extra-linker-flags:<string>");
 		print_usage_line(2, "Adds extra linker specific flags in a string");
 		print_usage_line(0, "");
@@ -2166,7 +2162,6 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(2, "Adds extra assembler specific flags in a string");
 		print_usage_line(0, "");
 
-
 		print_usage_line(1, "-microarch:<string>");
 		print_usage_line(2, "Specifies the specific micro-architecture for the build in a string");
 		print_usage_line(2, "Examples:");

+ 1 - 1
src/microsoft_craziness.h

@@ -108,7 +108,7 @@ HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
  	defer (mc_free(wildcard_wide));
 
  	HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data);
- 	if (handle == INVALID_HANDLE_VALUE) return false;
+ 	if (handle == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
 
  	find_data->file_attributes = _find_data.dwFileAttributes;
  	find_data->filename        = mc_wstring_to_string(_find_data.cFileName);

+ 17 - 4
src/parser.cpp

@@ -1,7 +1,7 @@
 #include "parser_pos.cpp"
 
 // #undef at the bottom of this file
-#define ALLOW_NEWLINE build_context.strict_style
+#define ALLOW_NEWLINE (!build_context.strict_style)
 
 Token token_end_of_line(AstFile *f, Token tok) {
 	u8 const *start = f->tokenizer.start + tok.pos.offset;
@@ -1384,7 +1384,13 @@ Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) {
 	Token prev = f->curr_token;
 	if (prev.kind != kind) {
 		String p = token_to_string(prev);
-		syntax_error(f->curr_token, "Expected '%.*s' after %s, got '%.*s'",
+		Token token = f->curr_token;
+		if (token_is_newline(prev)) {
+			token = prev;
+			token.pos.column -= 1;
+			skip_possible_newline(f);
+		}
+		syntax_error(token, "Expected '%.*s' after %s, got '%.*s'",
 		             LIT(token_strings[kind]),
 		             msg,
 		             LIT(p));
@@ -3420,7 +3426,6 @@ Ast *parse_results(AstFile *f, bool *diverging) {
 
 	isize prev_level = f->expr_level;
 	defer (f->expr_level = prev_level);
-	// f->expr_level = -1;
 
 	if (f->curr_token.kind != Token_OpenParen) {
 		Token begin_token = f->curr_token;
@@ -3435,6 +3440,9 @@ Ast *parse_results(AstFile *f, bool *diverging) {
 	Ast *list = nullptr;
 	expect_token(f, Token_OpenParen);
 	list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false);
+	if (ALLOW_NEWLINE) {
+		skip_possible_newline(f);
+	}
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	return list;
 }
@@ -3492,6 +3500,9 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) {
 
 	expect_token(f, Token_OpenParen);
 	params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
+	if (ALLOW_NEWLINE) {
+		skip_possible_newline(f);
+	}
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	results = parse_results(f, &diverging);
 
@@ -3713,7 +3724,7 @@ bool allow_field_separator(AstFile *f) {
 	if (allow_token(f, Token_Comma)) {
 		return true;
 	}
-	if (ALLOW_NEWLINE && token.kind == Token_Semicolon) {
+	if (ALLOW_NEWLINE && token.kind == Token_Semicolon && !token_is_newline(token)) {
 		String p = token_to_string(token);
 		syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
 		advance_token(f);
@@ -5634,6 +5645,8 @@ bool parse_file(Parser *p, AstFile *f) {
 						} else {
 							f->flags |= AstFile_IsLazy;
 						}
+					} else {
+						warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
 					}
 				}
 			}

+ 59 - 22
src/types.cpp

@@ -226,8 +226,6 @@ struct TypeProc {
 	TYPE_KIND(Map, struct {                                   \
 		Type *key;                                        \
 		Type *value;                                      \
-		Type *entry_type;                                 \
-		Type *internal_type;                              \
 		Type *lookup_result_type;                         \
 	})                                                        \
 	TYPE_KIND(Struct,  TypeStruct)                            \
@@ -685,13 +683,18 @@ gb_global Type *t_allocator_error                = nullptr;
 gb_global Type *t_source_code_location           = nullptr;
 gb_global Type *t_source_code_location_ptr       = nullptr;
 
-gb_global Type *t_map_hash                       = nullptr;
-gb_global Type *t_map_header                     = nullptr;
-gb_global Type *t_map_header_table               = nullptr;
+gb_global Type *t_map_info                       = nullptr;
+gb_global Type *t_map_cell_info                  = nullptr;
+gb_global Type *t_raw_map                        = nullptr;
+gb_global Type *t_map_info_ptr                   = nullptr;
+gb_global Type *t_map_cell_info_ptr              = nullptr;
+gb_global Type *t_raw_map_ptr                    = nullptr;
 
 
 gb_global Type *t_equal_proc  = nullptr;
 gb_global Type *t_hasher_proc = nullptr;
+gb_global Type *t_map_get_proc = nullptr;
+gb_global Type *t_map_set_proc = nullptr;
 
 gb_global Type *t_objc_object   = nullptr;
 gb_global Type *t_objc_selector = nullptr;
@@ -1257,6 +1260,9 @@ bool is_type_typed(Type *t) {
 }
 bool is_type_untyped(Type *t) {
 	t = base_type(t);
+	if (t == nullptr) {
+		return false;
+	}
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_Untyped) != 0;
 	}
@@ -1923,7 +1929,7 @@ bool is_type_valid_for_keys(Type *t) {
 	if (is_type_untyped(t)) {
 		return false;
 	}
-	return is_type_comparable(t);
+	return type_size_of(t) > 0 && is_type_comparable(t);
 }
 
 bool is_type_valid_bit_set_elem(Type *t) {
@@ -2526,6 +2532,44 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
 	return str_lit("");
 }
 
+bool lookup_subtype_polymorphic_selection(Type *dst, Type *src, Selection *sel) {
+	Type *prev_src = src;
+	// Type *prev_dst = dst;
+	src = base_type(type_deref(src));
+	// dst = base_type(type_deref(dst));
+	bool src_is_ptr = src != prev_src;
+	// bool dst_is_ptr = dst != prev_dst;
+
+	GB_ASSERT(is_type_struct(src) || is_type_union(src));
+	for_array(i, src->Struct.fields) {
+		Entity *f = src->Struct.fields[i];
+		if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) {
+			if (are_types_identical(dst, f->type)) {
+				array_add(&sel->index, cast(i32)i);
+				sel->entity = f;
+				return true;
+			}
+			if (src_is_ptr && is_type_pointer(dst)) {
+				if (are_types_identical(type_deref(dst), f->type)) {
+					array_add(&sel->index, cast(i32)i);
+					sel->indirect = true;
+					sel->entity = f;
+					return true;
+				}
+			}
+			if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
+				String name = lookup_subtype_polymorphic_field(dst, f->type);
+				if (name.len > 0) {
+					array_add(&sel->index, cast(i32)i);
+					return lookup_subtype_polymorphic_selection(dst, f->type, sel);
+				}
+			}
+		}
+	}
+	return false;
+}
+
+
 
 
 Type *strip_type_aliasing(Type *x) {
@@ -2661,6 +2705,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
 					if (xf->token.string != yf->token.string) {
 						return false;
 					}
+					if (x->Struct.tags[i] != y->Struct.tags[i]) {
+						return false;
+					}
 					u64 xf_flags = (xf->flags&EntityFlags_IsSubtype);
 					u64 yf_flags = (yf->flags&EntityFlags_IsSubtype);
 					if (xf_flags != yf_flags) {
@@ -3292,8 +3339,6 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
 			}
 		}
 	} else if (type->kind == Type_DynamicArray) {
-		// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
-		// `Raw_Dynamic_Array` type?
 		GB_ASSERT(t_allocator != nullptr);
 		String allocator_str = str_lit("allocator");
 		gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 3);
@@ -3304,15 +3349,12 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
 			return sel;
 		}
 	} else if (type->kind == Type_Map) {
-		// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
-		// `Raw_Map` type?
 		GB_ASSERT(t_allocator != nullptr);
 		String allocator_str = str_lit("allocator");
-		gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 3);
+		gb_local_persist Entity *entity__allocator = alloc_entity_field(nullptr, make_token_ident(allocator_str), t_allocator, false, 2);
 
 		if (field_name == allocator_str) {
-			selection_add_index(&sel, 1);
-			selection_add_index(&sel, 3);
+			selection_add_index(&sel, 2);
 			sel.entity = entity__allocator;
 			return sel;
 		}
@@ -3757,11 +3799,12 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
 	case Type_Map:
 		/*
 			struct {
-				hashes:  []int,               // 2 words
-				entries: [dynamic]Entry_Type, // 5 words
+				data:      uintptr,           // 1 word
+				size:      uintptr,           // 1 word
+				allocator: runtime.Allocator, // 2 words
 			}
 		*/
-		return (2 + (3 + 2))*build_context.word_size;
+		return (1 + 1 + 2)*build_context.word_size;
 
 	case Type_Tuple: {
 		i64 count, align, size;
@@ -3876,13 +3919,7 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
 	}
 	
 	case Type_Matrix: {
-		bool pop = type_path_push(path, t->Matrix.elem);
-		if (path->failure) {
-			return FAILURE_SIZE;
-		}
 		i64 stride_in_bytes = matrix_type_stride_in_bytes(t, path);
-		if (pop) type_path_pop(path);
-
 		return stride_in_bytes * t->Matrix.column_count;
 	}
 

+ 0 - 1
tests/core/math/big/test.odin

@@ -1,4 +1,3 @@
-//+ignore
 /*
 	Copyright 2021 Jeroen van Rijn <[email protected]>.
 	Made available under Odin's BSD-3 license.

+ 6 - 0
tests/internal/Makefile

@@ -0,0 +1,6 @@
+ODIN=../../odin
+
+all: map_test
+
+map_test:
+	$(ODIN) run test_map.odin -file -vet -strict-style -o:minimal

+ 4 - 0
tests/internal/build.bat

@@ -0,0 +1,4 @@
+@echo off
+set PATH_TO_ODIN==..\..\odin
+%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal
+rem -define:SEED=42

+ 382 - 0
tests/internal/test_map.odin

@@ -0,0 +1,382 @@
+package test_internal_map
+
+import "core:fmt"
+import "core:intrinsics"
+import "core:math/rand"
+import "core:mem"
+import "core:os"
+import "core:testing"
+
+seed: u64
+
+ENTRY_COUNTS := []int{11, 101, 1_001, 10_001, 100_001, 1_000_001}
+
+@test
+map_insert_random_key_value :: proc(t: ^testing.T) {
+	seed_incr := u64(0)
+	for entries in ENTRY_COUNTS {
+		fmt.printf("[map_insert_random_key_value] Testing %v entries.\n", entries)
+		m: map[i64]i64
+		defer delete(m)
+
+		unique_keys := 0
+		r := rand.create(seed + seed_incr)
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			if k not_in m {
+				unique_keys += 1
+			}
+			m[k] = v
+		}
+
+		key_count := 0
+		for k in m {
+			key_count += 1
+		}
+
+		expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
+		expect(t, len(m)    == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v",  unique_keys, len(m)))
+
+		// Reset randomizer and verify
+		r = rand.create(seed + seed_incr)
+
+		num_fails := 0
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			cond := m[k] == v
+			if !cond {
+				num_fails += 1
+				if num_fails > 5 {
+					fmt.println("... and more")
+					break
+				}
+				expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k]))
+			}
+		}
+		seed_incr += 1
+	}
+}
+
+@test
+map_update_random_key_value :: proc(t: ^testing.T) {
+	seed_incr := u64(0)
+	for entries in ENTRY_COUNTS {
+		fmt.printf("[map_update_random_key_value] Testing %v entries.\n", entries)
+		m: map[i64]i64
+		defer delete(m)
+
+		unique_keys := 0
+		r := rand.create(seed + seed_incr)
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			if k not_in m {
+				unique_keys += 1
+			}
+			m[k] = v
+		}
+
+		key_count := 0
+		for k in m {
+			key_count += 1
+		}
+
+		expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
+		expect(t, len(m)    == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v",  unique_keys, len(m)))
+
+		half_entries := entries / 2
+
+		// Reset randomizer and update half the entries
+		r = rand.create(seed + seed_incr)
+		for _ in 0..<half_entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			m[k] = v + 42
+		}
+
+		// Reset randomizer and verify
+		r = rand.create(seed + seed_incr)
+
+		num_fails := 0
+		for i in 0..<entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			diff := i64(42) if i < half_entries else i64(0)
+			cond := m[k] == (v + diff)
+			if !cond {
+				num_fails += 1
+				if num_fails > 5 {
+					fmt.println("... and more")
+					break
+				}
+				expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k]))
+			}
+		}
+		seed_incr += 1
+	}
+}
+
+@test
+map_delete_random_key_value :: proc(t: ^testing.T) {
+	seed_incr := u64(0)
+	for entries in ENTRY_COUNTS {
+		fmt.printf("[map_delete_random_key_value] Testing %v entries.\n", entries)
+		m: map[i64]i64
+		defer delete(m)
+
+		unique_keys := 0
+		r := rand.create(seed + seed_incr)
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			if k not_in m {
+				unique_keys += 1
+			}
+			m[k] = v
+		}
+
+		key_count := 0
+		for k in m {
+			key_count += 1
+		}
+
+		expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
+		expect(t, len(m)    == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v",  unique_keys, len(m)))
+
+		half_entries := entries / 2
+
+		// Reset randomizer and delete half the entries
+		r = rand.create(seed + seed_incr)
+		for _ in 0..<half_entries {
+			k := rand.int63(&r)
+			_  = rand.int63(&r)
+
+			delete_key(&m, k)
+		}
+
+		// Reset randomizer and verify
+		r = rand.create(seed + seed_incr)
+
+		num_fails := 0
+		for i in 0..<entries {
+			k := rand.int63(&r)
+			v := rand.int63(&r)
+
+			if i < half_entries {
+				if k in m {
+					num_fails += 1
+					if num_fails > 5 {
+						fmt.println("... and more")
+						break
+					}
+					expect(t, false, fmt.tprintf("Unexpected key present. Expected m[%v] to have been deleted, got %v", k, m[k]))
+				}
+			} else {
+				if k not_in m {
+					num_fails += 1
+					if num_fails > 5 {
+						fmt.println("... and more")
+						break
+					}
+					expect(t, false, fmt.tprintf("Expected key not present. Expected m[%v] = %v", k, v))
+				} else if m[k] != v {
+					num_fails += 1
+					if num_fails > 5 {
+						fmt.println("... and more")
+						break
+					}
+					expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] = %v, got %v", k, v, m[k]))
+				}
+			}
+		}
+		seed_incr += 1
+	}
+}
+
+@test
+set_insert_random_key_value :: proc(t: ^testing.T) {
+	seed_incr := u64(0)
+	for entries in ENTRY_COUNTS {
+		fmt.printf("[set_insert_random_key_value] Testing %v entries.\n", entries)
+		m: map[i64]struct{}
+		defer delete(m)
+
+		unique_keys := 0
+		r := rand.create(seed + seed_incr)
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+			if k not_in m {
+				unique_keys += 1
+			}
+			m[k] = {}
+		}
+
+		key_count := 0
+		for k in m {
+			key_count += 1
+		}
+
+		expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
+		expect(t, len(m)    == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v",  unique_keys, len(m)))
+
+		// Reset randomizer and verify
+		r = rand.create(seed + seed_incr)
+
+		num_fails := 0
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+
+			cond := k in m
+			if !cond {
+				num_fails += 1
+				if num_fails > 5 {
+					fmt.println("... and more")
+					break
+				}
+				expect(t, false, fmt.tprintf("Unexpected value. Expected m[%v] to exist", k))
+			}
+		}
+		seed_incr += 1
+	}
+}
+
+@test
+set_delete_random_key_value :: proc(t: ^testing.T) {
+	seed_incr := u64(0)
+	for entries in ENTRY_COUNTS {
+		fmt.printf("[set_delete_random_key_value] Testing %v entries.\n", entries)
+		m: map[i64]struct{}
+		defer delete(m)
+
+		unique_keys := 0
+		r := rand.create(seed + seed_incr)
+		for _ in 0..<entries {
+			k := rand.int63(&r)
+
+			if k not_in m {
+				unique_keys += 1
+			}
+			m[k] = {}
+		}
+
+		key_count := 0
+		for k in m {
+			key_count += 1
+		}
+
+		expect(t, key_count == unique_keys, fmt.tprintf("Expected key_count to equal %v, got %v", unique_keys, key_count))
+		expect(t, len(m)    == unique_keys, fmt.tprintf("Expected len(map) to equal %v, got %v",  unique_keys, len(m)))
+
+		half_entries := entries / 2
+
+		// Reset randomizer and delete half the entries
+		r = rand.create(seed + seed_incr)
+		for _ in 0..<half_entries {
+			k := rand.int63(&r)
+			delete_key(&m, k)
+		}
+
+		// Reset randomizer and verify
+		r = rand.create(seed + seed_incr)
+
+		num_fails := 0
+		for i in 0..<entries {
+			k := rand.int63(&r)
+
+			if i < half_entries {
+				if k in m {
+					num_fails += 1
+					if num_fails > 5 {
+						fmt.println("... and more")
+						break
+					}
+					expect(t, false, fmt.tprintf("Unexpected key present. Expected m[%v] to have been deleted", k))
+				}
+			} else {
+				if k not_in m {
+					num_fails += 1
+					if num_fails > 5 {
+						fmt.println("... and more")
+						break
+					}
+					expect(t, false, fmt.tprintf("Expected key not present. Expected m[%v] to exist", k))
+				}
+			}
+		}
+		seed_incr += 1
+	}
+}
+
+// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
+
+main :: proc() {
+	t := testing.T{}
+
+	// Allow tests to be repeatable
+	SEED :: #config(SEED, -1)
+	when SEED > 0 {
+		seed = u64(SEED)
+	} else {
+		seed = u64(intrinsics.read_cycle_counter())
+	}
+	fmt.println("Initialized seed to", seed)
+
+	mem_track_test(&t, map_insert_random_key_value)
+	mem_track_test(&t, map_update_random_key_value)
+	mem_track_test(&t, map_delete_random_key_value)
+
+	mem_track_test(&t, set_insert_random_key_value)
+	mem_track_test(&t, set_delete_random_key_value)
+
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
+}
+
+mem_track_test :: proc(t: ^testing.T, test: proc(t: ^testing.T)) {
+	track: mem.Tracking_Allocator
+	mem.tracking_allocator_init(&track, context.allocator)
+	context.allocator = mem.tracking_allocator(&track)
+
+	test(t)
+
+	expect(t, len(track.allocation_map) == 0, "Expected no leaks.")
+	expect(t, len(track.bad_free_array) == 0, "Expected no leaks.")
+
+	for _, leak in track.allocation_map {
+		fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
+	}
+	for bad_free in track.bad_free_array {
+		fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
+	}
+}
+
+TEST_count := 0
+TEST_fail  := 0
+
+when ODIN_TEST {
+	expect  :: testing.expect
+	log     :: testing.log
+} else {
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
+}

文件差异内容过多而无法显示
+ 958 - 542
vendor/OpenGL/constants.odin


+ 1831 - 1404
vendor/OpenGL/enums.odin

@@ -4,1410 +4,1837 @@ GL_Enum :: enum u64 {
 	FALSE = 0, 
 	TRUE = 1, 
 
-	DEPTH_BUFFER_BIT = DEPTH_BUFFER_BIT, 
-	STENCIL_BUFFER_BIT = STENCIL_BUFFER_BIT, 
-	COLOR_BUFFER_BIT = COLOR_BUFFER_BIT, 
-	POINTS = POINTS, 
-	LINES = LINES, 
-	LINE_LOOP = LINE_LOOP, 
-	LINE_STRIP = LINE_STRIP, 
-	TRIANGLES = TRIANGLES, 
-	TRIANGLE_STRIP = TRIANGLE_STRIP, 
-	TRIANGLE_FAN = TRIANGLE_FAN, 
-	QUADS = QUADS, 
-	NEVER = NEVER, 
-	LESS = LESS, 
-	EQUAL = EQUAL, 
-	LEQUAL = LEQUAL, 
-	GREATER = GREATER, 
-	NOTEQUAL = NOTEQUAL, 
-	GEQUAL = GEQUAL, 
-	ALWAYS = ALWAYS, 
-	ZERO = ZERO, 
-	ONE = ONE, 
-	SRC_COLOR = SRC_COLOR, 
-	ONE_MINUS_SRC_COLOR = ONE_MINUS_SRC_COLOR, 
-	SRC_ALPHA = SRC_ALPHA, 
-	ONE_MINUS_SRC_ALPHA = ONE_MINUS_SRC_ALPHA, 
-	DST_ALPHA = DST_ALPHA, 
-	ONE_MINUS_DST_ALPHA = ONE_MINUS_DST_ALPHA, 
-	DST_COLOR = DST_COLOR, 
-	ONE_MINUS_DST_COLOR = ONE_MINUS_DST_COLOR, 
-	SRC_ALPHA_SATURATE = SRC_ALPHA_SATURATE, 
-	NONE = NONE, 
-	FRONT_LEFT = FRONT_LEFT, 
-	FRONT_RIGHT = FRONT_RIGHT, 
-	BACK_LEFT = BACK_LEFT, 
-	BACK_RIGHT = BACK_RIGHT, 
-	FRONT = FRONT, 
-	BACK = BACK, 
-	LEFT = LEFT, 
-	RIGHT = RIGHT, 
-	FRONT_AND_BACK = FRONT_AND_BACK, 
-	NO_ERROR = NO_ERROR, 
-	INVALID_ENUM = INVALID_ENUM, 
-	INVALID_VALUE = INVALID_VALUE, 
-	INVALID_OPERATION = INVALID_OPERATION, 
-	OUT_OF_MEMORY = OUT_OF_MEMORY, 
-	CW = CW, 
-	CCW = CCW, 
-	POINT_SIZE = POINT_SIZE, 
-	POINT_SIZE_RANGE = POINT_SIZE_RANGE, 
-	POINT_SIZE_GRANULARITY = POINT_SIZE_GRANULARITY, 
-	LINE_SMOOTH = LINE_SMOOTH, 
-	LINE_WIDTH = LINE_WIDTH, 
-	LINE_WIDTH_RANGE = LINE_WIDTH_RANGE, 
-	LINE_WIDTH_GRANULARITY = LINE_WIDTH_GRANULARITY, 
-	POLYGON_MODE = POLYGON_MODE, 
-	POLYGON_SMOOTH = POLYGON_SMOOTH, 
-	CULL_FACE = CULL_FACE, 
-	CULL_FACE_MODE = CULL_FACE_MODE, 
-	FRONT_FACE = FRONT_FACE, 
-	DEPTH_RANGE = DEPTH_RANGE, 
-	DEPTH_TEST = DEPTH_TEST, 
-	DEPTH_WRITEMASK = DEPTH_WRITEMASK, 
-	DEPTH_CLEAR_VALUE = DEPTH_CLEAR_VALUE, 
-	DEPTH_FUNC = DEPTH_FUNC, 
-	STENCIL_TEST = STENCIL_TEST, 
-	STENCIL_CLEAR_VALUE = STENCIL_CLEAR_VALUE, 
-	STENCIL_FUNC = STENCIL_FUNC, 
-	STENCIL_VALUE_MASK = STENCIL_VALUE_MASK, 
-	STENCIL_FAIL = STENCIL_FAIL, 
-	STENCIL_PASS_DEPTH_FAIL = STENCIL_PASS_DEPTH_FAIL, 
-	STENCIL_PASS_DEPTH_PASS = STENCIL_PASS_DEPTH_PASS, 
-	STENCIL_REF = STENCIL_REF, 
-	STENCIL_WRITEMASK = STENCIL_WRITEMASK, 
-	VIEWPORT = VIEWPORT, 
-	DITHER = DITHER, 
-	BLEND_DST = BLEND_DST, 
-	BLEND_SRC = BLEND_SRC, 
-	BLEND = BLEND, 
-	LOGIC_OP_MODE = LOGIC_OP_MODE, 
-	COLOR_LOGIC_OP = COLOR_LOGIC_OP, 
-	DRAW_BUFFER = DRAW_BUFFER, 
-	READ_BUFFER = READ_BUFFER, 
-	SCISSOR_BOX = SCISSOR_BOX, 
-	SCISSOR_TEST = SCISSOR_TEST, 
-	COLOR_CLEAR_VALUE = COLOR_CLEAR_VALUE, 
-	COLOR_WRITEMASK = COLOR_WRITEMASK, 
-	DOUBLEBUFFER = DOUBLEBUFFER, 
-	STEREO = STEREO, 
-	LINE_SMOOTH_HINT = LINE_SMOOTH_HINT, 
-	POLYGON_SMOOTH_HINT = POLYGON_SMOOTH_HINT, 
-	UNPACK_SWAP_BYTES = UNPACK_SWAP_BYTES, 
-	UNPACK_LSB_FIRST = UNPACK_LSB_FIRST, 
-	UNPACK_ROW_LENGTH = UNPACK_ROW_LENGTH, 
-	UNPACK_SKIP_ROWS = UNPACK_SKIP_ROWS, 
-	UNPACK_SKIP_PIXELS = UNPACK_SKIP_PIXELS, 
-	UNPACK_ALIGNMENT = UNPACK_ALIGNMENT, 
-	PACK_SWAP_BYTES = PACK_SWAP_BYTES, 
-	PACK_LSB_FIRST = PACK_LSB_FIRST, 
-	PACK_ROW_LENGTH = PACK_ROW_LENGTH, 
-	PACK_SKIP_ROWS = PACK_SKIP_ROWS, 
-	PACK_SKIP_PIXELS = PACK_SKIP_PIXELS, 
-	PACK_ALIGNMENT = PACK_ALIGNMENT, 
-	MAX_TEXTURE_SIZE = MAX_TEXTURE_SIZE, 
-	MAX_VIEWPORT_DIMS = MAX_VIEWPORT_DIMS, 
-	SUBPIXEL_BITS = SUBPIXEL_BITS, 
-	TEXTURE_1D = TEXTURE_1D, 
-	TEXTURE_2D = TEXTURE_2D, 
-	POLYGON_OFFSET_UNITS = POLYGON_OFFSET_UNITS, 
-	POLYGON_OFFSET_POINT = POLYGON_OFFSET_POINT, 
-	POLYGON_OFFSET_LINE = POLYGON_OFFSET_LINE, 
-	POLYGON_OFFSET_FILL = POLYGON_OFFSET_FILL, 
-	POLYGON_OFFSET_FACTOR = POLYGON_OFFSET_FACTOR, 
-	TEXTURE_BINDING_1D = TEXTURE_BINDING_1D, 
-	TEXTURE_BINDING_2D = TEXTURE_BINDING_2D, 
-	TEXTURE_WIDTH = TEXTURE_WIDTH, 
-	TEXTURE_HEIGHT = TEXTURE_HEIGHT, 
-	TEXTURE_INTERNAL_FORMAT = TEXTURE_INTERNAL_FORMAT, 
-	TEXTURE_BORDER_COLOR = TEXTURE_BORDER_COLOR, 
-	TEXTURE_RED_SIZE = TEXTURE_RED_SIZE, 
-	TEXTURE_GREEN_SIZE = TEXTURE_GREEN_SIZE, 
-	TEXTURE_BLUE_SIZE = TEXTURE_BLUE_SIZE, 
-	TEXTURE_ALPHA_SIZE = TEXTURE_ALPHA_SIZE, 
-	DONT_CARE = DONT_CARE, 
-	FASTEST = FASTEST, 
-	NICEST = NICEST, 
-	BYTE = BYTE, 
-	UNSIGNED_BYTE = UNSIGNED_BYTE, 
-	SHORT = SHORT, 
-	UNSIGNED_SHORT = UNSIGNED_SHORT, 
-	INT = INT, 
-	UNSIGNED_INT = UNSIGNED_INT, 
-	FLOAT = FLOAT, 
-	DOUBLE = DOUBLE, 
-	STACK_OVERFLOW = STACK_OVERFLOW, 
-	STACK_UNDERFLOW = STACK_UNDERFLOW, 
-	CLEAR = CLEAR, 
-	AND = AND, 
-	AND_REVERSE = AND_REVERSE, 
-	COPY = COPY, 
-	AND_INVERTED = AND_INVERTED, 
-	NOOP = NOOP, 
-	XOR = XOR, 
-	OR = OR, 
-	NOR = NOR, 
-	EQUIV = EQUIV, 
-	INVERT = INVERT, 
-	OR_REVERSE = OR_REVERSE, 
-	COPY_INVERTED = COPY_INVERTED, 
-	OR_INVERTED = OR_INVERTED, 
-	NAND = NAND, 
-	SET = SET, 
-	TEXTURE = TEXTURE, 
-	COLOR = COLOR, 
-	DEPTH = DEPTH, 
-	STENCIL = STENCIL, 
-	STENCIL_INDEX = STENCIL_INDEX, 
-	DEPTH_COMPONENT = DEPTH_COMPONENT, 
-	RED = RED, 
-	GREEN = GREEN, 
-	BLUE = BLUE, 
-	ALPHA = ALPHA, 
-	RGB = RGB, 
-	RGBA = RGBA, 
-	POINT = POINT, 
-	LINE = LINE, 
-	FILL = FILL, 
-	KEEP = KEEP, 
-	REPLACE = REPLACE, 
-	INCR = INCR, 
-	DECR = DECR, 
-	VENDOR = VENDOR, 
-	RENDERER = RENDERER, 
-	VERSION = VERSION, 
-	EXTENSIONS = EXTENSIONS, 
-	NEAREST = NEAREST, 
-	LINEAR = LINEAR, 
-	NEAREST_MIPMAP_NEAREST = NEAREST_MIPMAP_NEAREST, 
-	LINEAR_MIPMAP_NEAREST = LINEAR_MIPMAP_NEAREST, 
-	NEAREST_MIPMAP_LINEAR = NEAREST_MIPMAP_LINEAR, 
-	LINEAR_MIPMAP_LINEAR = LINEAR_MIPMAP_LINEAR, 
-	TEXTURE_MAG_FILTER = TEXTURE_MAG_FILTER, 
-	TEXTURE_MIN_FILTER = TEXTURE_MIN_FILTER, 
-	TEXTURE_WRAP_S = TEXTURE_WRAP_S, 
-	TEXTURE_WRAP_T = TEXTURE_WRAP_T, 
-	PROXY_TEXTURE_1D = PROXY_TEXTURE_1D, 
-	PROXY_TEXTURE_2D = PROXY_TEXTURE_2D, 
-	REPEAT = REPEAT, 
-	R3_G3_B2 = R3_G3_B2, 
-	RGB4 = RGB4, 
-	RGB5 = RGB5, 
-	RGB8 = RGB8, 
-	RGB10 = RGB10, 
-	RGB12 = RGB12, 
-	RGB16 = RGB16, 
-	RGBA2 = RGBA2, 
-	RGBA4 = RGBA4, 
-	RGB5_A1 = RGB5_A1, 
-	RGBA8 = RGBA8, 
-	RGB10_A2 = RGB10_A2, 
-	RGBA12 = RGBA12, 
-	RGBA16 = RGBA16, 
-	VERTEX_ARRAY = VERTEX_ARRAY, 
+	DEPTH_BUFFER_BIT = DEPTH_BUFFER_BIT,
+	STENCIL_BUFFER_BIT = STENCIL_BUFFER_BIT,
+	COLOR_BUFFER_BIT = COLOR_BUFFER_BIT,
+	POINTS = POINTS,
+	LINES = LINES,
+	LINE_LOOP = LINE_LOOP,
+	LINE_STRIP = LINE_STRIP,
+	TRIANGLES = TRIANGLES,
+	TRIANGLE_STRIP = TRIANGLE_STRIP,
+	TRIANGLE_FAN = TRIANGLE_FAN,
+	QUADS = QUADS,
+	NEVER = NEVER,
+	LESS = LESS,
+	EQUAL = EQUAL,
+	LEQUAL = LEQUAL,
+	GREATER = GREATER,
+	NOTEQUAL = NOTEQUAL,
+	GEQUAL = GEQUAL,
+	ALWAYS = ALWAYS,
+	ZERO = ZERO,
+	ONE = ONE,
+	SRC_COLOR = SRC_COLOR,
+	ONE_MINUS_SRC_COLOR = ONE_MINUS_SRC_COLOR,
+	SRC_ALPHA = SRC_ALPHA,
+	ONE_MINUS_SRC_ALPHA = ONE_MINUS_SRC_ALPHA,
+	DST_ALPHA = DST_ALPHA,
+	ONE_MINUS_DST_ALPHA = ONE_MINUS_DST_ALPHA,
+	DST_COLOR = DST_COLOR,
+	ONE_MINUS_DST_COLOR = ONE_MINUS_DST_COLOR,
+	SRC_ALPHA_SATURATE = SRC_ALPHA_SATURATE,
+	NONE = NONE,
+	FRONT_LEFT = FRONT_LEFT,
+	FRONT_RIGHT = FRONT_RIGHT,
+	BACK_LEFT = BACK_LEFT,
+	BACK_RIGHT = BACK_RIGHT,
+	FRONT = FRONT,
+	BACK = BACK,
+	LEFT = LEFT,
+	RIGHT = RIGHT,
+	FRONT_AND_BACK = FRONT_AND_BACK,
+	NO_ERROR = NO_ERROR,
+	INVALID_ENUM = INVALID_ENUM,
+	INVALID_VALUE = INVALID_VALUE,
+	INVALID_OPERATION = INVALID_OPERATION,
+	OUT_OF_MEMORY = OUT_OF_MEMORY,
+	CW = CW,
+	CCW = CCW,
+	POINT_SIZE = POINT_SIZE,
+	POINT_SIZE_RANGE = POINT_SIZE_RANGE,
+	POINT_SIZE_GRANULARITY = POINT_SIZE_GRANULARITY,
+	LINE_SMOOTH = LINE_SMOOTH,
+	LINE_WIDTH = LINE_WIDTH,
+	LINE_WIDTH_RANGE = LINE_WIDTH_RANGE,
+	LINE_WIDTH_GRANULARITY = LINE_WIDTH_GRANULARITY,
+	POLYGON_MODE = POLYGON_MODE,
+	POLYGON_SMOOTH = POLYGON_SMOOTH,
+	CULL_FACE = CULL_FACE,
+	CULL_FACE_MODE = CULL_FACE_MODE,
+	FRONT_FACE = FRONT_FACE,
+	DEPTH_RANGE = DEPTH_RANGE,
+	DEPTH_TEST = DEPTH_TEST,
+	DEPTH_WRITEMASK = DEPTH_WRITEMASK,
+	DEPTH_CLEAR_VALUE = DEPTH_CLEAR_VALUE,
+	DEPTH_FUNC = DEPTH_FUNC,
+	STENCIL_TEST = STENCIL_TEST,
+	STENCIL_CLEAR_VALUE = STENCIL_CLEAR_VALUE,
+	STENCIL_FUNC = STENCIL_FUNC,
+	STENCIL_VALUE_MASK = STENCIL_VALUE_MASK,
+	STENCIL_FAIL = STENCIL_FAIL,
+	STENCIL_PASS_DEPTH_FAIL = STENCIL_PASS_DEPTH_FAIL,
+	STENCIL_PASS_DEPTH_PASS = STENCIL_PASS_DEPTH_PASS,
+	STENCIL_REF = STENCIL_REF,
+	STENCIL_WRITEMASK = STENCIL_WRITEMASK,
+	VIEWPORT = VIEWPORT,
+	DITHER = DITHER,
+	BLEND_DST = BLEND_DST,
+	BLEND_SRC = BLEND_SRC,
+	BLEND = BLEND,
+	LOGIC_OP_MODE = LOGIC_OP_MODE,
+	DRAW_BUFFER = DRAW_BUFFER,
+	READ_BUFFER = READ_BUFFER,
+	SCISSOR_BOX = SCISSOR_BOX,
+	SCISSOR_TEST = SCISSOR_TEST,
+	COLOR_CLEAR_VALUE = COLOR_CLEAR_VALUE,
+	COLOR_WRITEMASK = COLOR_WRITEMASK,
+	DOUBLEBUFFER = DOUBLEBUFFER,
+	STEREO = STEREO,
+	LINE_SMOOTH_HINT = LINE_SMOOTH_HINT,
+	POLYGON_SMOOTH_HINT = POLYGON_SMOOTH_HINT,
+	UNPACK_SWAP_BYTES = UNPACK_SWAP_BYTES,
+	UNPACK_LSB_FIRST = UNPACK_LSB_FIRST,
+	UNPACK_ROW_LENGTH = UNPACK_ROW_LENGTH,
+	UNPACK_SKIP_ROWS = UNPACK_SKIP_ROWS,
+	UNPACK_SKIP_PIXELS = UNPACK_SKIP_PIXELS,
+	UNPACK_ALIGNMENT = UNPACK_ALIGNMENT,
+	PACK_SWAP_BYTES = PACK_SWAP_BYTES,
+	PACK_LSB_FIRST = PACK_LSB_FIRST,
+	PACK_ROW_LENGTH = PACK_ROW_LENGTH,
+	PACK_SKIP_ROWS = PACK_SKIP_ROWS,
+	PACK_SKIP_PIXELS = PACK_SKIP_PIXELS,
+	PACK_ALIGNMENT = PACK_ALIGNMENT,
+	MAX_TEXTURE_SIZE = MAX_TEXTURE_SIZE,
+	MAX_VIEWPORT_DIMS = MAX_VIEWPORT_DIMS,
+	SUBPIXEL_BITS = SUBPIXEL_BITS,
+	TEXTURE_1D = TEXTURE_1D,
+	TEXTURE_2D = TEXTURE_2D,
+	TEXTURE_WIDTH = TEXTURE_WIDTH,
+	TEXTURE_HEIGHT = TEXTURE_HEIGHT,
+	TEXTURE_BORDER_COLOR = TEXTURE_BORDER_COLOR,
+	DONT_CARE = DONT_CARE,
+	FASTEST = FASTEST,
+	NICEST = NICEST,
+	BYTE = BYTE,
+	UNSIGNED_BYTE = UNSIGNED_BYTE,
+	SHORT = SHORT,
+	UNSIGNED_SHORT = UNSIGNED_SHORT,
+	INT = INT,
+	UNSIGNED_INT = UNSIGNED_INT,
+	FLOAT = FLOAT,
+	STACK_OVERFLOW = STACK_OVERFLOW,
+	STACK_UNDERFLOW = STACK_UNDERFLOW,
+	CLEAR = CLEAR,
+	AND = AND,
+	AND_REVERSE = AND_REVERSE,
+	COPY = COPY,
+	AND_INVERTED = AND_INVERTED,
+	NOOP = NOOP,
+	XOR = XOR,
+	OR = OR,
+	NOR = NOR,
+	EQUIV = EQUIV,
+	INVERT = INVERT,
+	OR_REVERSE = OR_REVERSE,
+	COPY_INVERTED = COPY_INVERTED,
+	OR_INVERTED = OR_INVERTED,
+	NAND = NAND,
+	SET = SET,
+	TEXTURE = TEXTURE,
+	COLOR = COLOR,
+	DEPTH = DEPTH,
+	STENCIL = STENCIL,
+	STENCIL_INDEX = STENCIL_INDEX,
+	DEPTH_COMPONENT = DEPTH_COMPONENT,
+	RED = RED,
+	GREEN = GREEN,
+	BLUE = BLUE,
+	ALPHA = ALPHA,
+	RGB = RGB,
+	RGBA = RGBA,
+	POINT = POINT,
+	LINE = LINE,
+	FILL = FILL,
+	KEEP = KEEP,
+	REPLACE = REPLACE,
+	INCR = INCR,
+	DECR = DECR,
+	VENDOR = VENDOR,
+	RENDERER = RENDERER,
+	VERSION = VERSION,
+	EXTENSIONS = EXTENSIONS,
+	NEAREST = NEAREST,
+	LINEAR = LINEAR,
+	NEAREST_MIPMAP_NEAREST = NEAREST_MIPMAP_NEAREST,
+	LINEAR_MIPMAP_NEAREST = LINEAR_MIPMAP_NEAREST,
+	NEAREST_MIPMAP_LINEAR = NEAREST_MIPMAP_LINEAR,
+	LINEAR_MIPMAP_LINEAR = LINEAR_MIPMAP_LINEAR,
+	TEXTURE_MAG_FILTER = TEXTURE_MAG_FILTER,
+	TEXTURE_MIN_FILTER = TEXTURE_MIN_FILTER,
+	TEXTURE_WRAP_S = TEXTURE_WRAP_S,
+	TEXTURE_WRAP_T = TEXTURE_WRAP_T,
+	REPEAT = REPEAT,
+	CURRENT_BIT = CURRENT_BIT,
+	POINT_BIT = POINT_BIT,
+	LINE_BIT = LINE_BIT,
+	POLYGON_BIT = POLYGON_BIT,
+	POLYGON_STIPPLE_BIT = POLYGON_STIPPLE_BIT,
+	PIXEL_MODE_BIT = PIXEL_MODE_BIT,
+	LIGHTING_BIT = LIGHTING_BIT,
+	FOG_BIT = FOG_BIT,
+	ACCUM_BUFFER_BIT = ACCUM_BUFFER_BIT,
+	VIEWPORT_BIT = VIEWPORT_BIT,
+	TRANSFORM_BIT = TRANSFORM_BIT,
+	ENABLE_BIT = ENABLE_BIT,
+	HINT_BIT = HINT_BIT,
+	EVAL_BIT = EVAL_BIT,
+	LIST_BIT = LIST_BIT,
+	TEXTURE_BIT = TEXTURE_BIT,
+	SCISSOR_BIT = SCISSOR_BIT,
+	ALL_ATTRIB_BITS = ALL_ATTRIB_BITS,
+	QUAD_STRIP = QUAD_STRIP,
+	POLYGON = POLYGON,
+	ACCUM = ACCUM,
+	LOAD = LOAD,
+	RETURN = RETURN,
+	MULT = MULT,
+	ADD = ADD,
+	AUX0 = AUX0,
+	AUX1 = AUX1,
+	AUX2 = AUX2,
+	AUX3 = AUX3,
+	_2D = _2D,
+	_3D = _3D,
+	_3D_COLOR = _3D_COLOR,
+	_3D_COLOR_TEXTURE = _3D_COLOR_TEXTURE,
+	_4D_COLOR_TEXTURE = _4D_COLOR_TEXTURE,
+	PASS_THROUGH_TOKEN = PASS_THROUGH_TOKEN,
+	POINT_TOKEN = POINT_TOKEN,
+	LINE_TOKEN = LINE_TOKEN,
+	POLYGON_TOKEN = POLYGON_TOKEN,
+	BITMAP_TOKEN = BITMAP_TOKEN,
+	DRAW_PIXEL_TOKEN = DRAW_PIXEL_TOKEN,
+	COPY_PIXEL_TOKEN = COPY_PIXEL_TOKEN,
+	LINE_RESET_TOKEN = LINE_RESET_TOKEN,
+	EXP = EXP,
+	EXP2 = EXP2,
+	COEFF = COEFF,
+	ORDER = ORDER,
+	DOMAIN = DOMAIN,
+	PIXEL_MAP_I_TO_I = PIXEL_MAP_I_TO_I,
+	PIXEL_MAP_S_TO_S = PIXEL_MAP_S_TO_S,
+	PIXEL_MAP_I_TO_R = PIXEL_MAP_I_TO_R,
+	PIXEL_MAP_I_TO_G = PIXEL_MAP_I_TO_G,
+	PIXEL_MAP_I_TO_B = PIXEL_MAP_I_TO_B,
+	PIXEL_MAP_I_TO_A = PIXEL_MAP_I_TO_A,
+	PIXEL_MAP_R_TO_R = PIXEL_MAP_R_TO_R,
+	PIXEL_MAP_G_TO_G = PIXEL_MAP_G_TO_G,
+	PIXEL_MAP_B_TO_B = PIXEL_MAP_B_TO_B,
+	PIXEL_MAP_A_TO_A = PIXEL_MAP_A_TO_A,
+	CURRENT_COLOR = CURRENT_COLOR,
+	CURRENT_INDEX = CURRENT_INDEX,
+	CURRENT_NORMAL = CURRENT_NORMAL,
+	CURRENT_TEXTURE_COORDS = CURRENT_TEXTURE_COORDS,
+	CURRENT_RASTER_COLOR = CURRENT_RASTER_COLOR,
+	CURRENT_RASTER_INDEX = CURRENT_RASTER_INDEX,
+	CURRENT_RASTER_TEXTURE_COORDS = CURRENT_RASTER_TEXTURE_COORDS,
+	CURRENT_RASTER_POSITION = CURRENT_RASTER_POSITION,
+	CURRENT_RASTER_POSITION_VALID = CURRENT_RASTER_POSITION_VALID,
+	CURRENT_RASTER_DISTANCE = CURRENT_RASTER_DISTANCE,
+	POINT_SMOOTH = POINT_SMOOTH,
+	LINE_STIPPLE = LINE_STIPPLE,
+	LINE_STIPPLE_PATTERN = LINE_STIPPLE_PATTERN,
+	LINE_STIPPLE_REPEAT = LINE_STIPPLE_REPEAT,
+	LIST_MODE = LIST_MODE,
+	MAX_LIST_NESTING = MAX_LIST_NESTING,
+	LIST_BASE = LIST_BASE,
+	LIST_INDEX = LIST_INDEX,
+	POLYGON_STIPPLE = POLYGON_STIPPLE,
+	EDGE_FLAG = EDGE_FLAG,
+	LIGHTING = LIGHTING,
+	LIGHT_MODEL_LOCAL_VIEWER = LIGHT_MODEL_LOCAL_VIEWER,
+	LIGHT_MODEL_TWO_SIDE = LIGHT_MODEL_TWO_SIDE,
+	LIGHT_MODEL_AMBIENT = LIGHT_MODEL_AMBIENT,
+	SHADE_MODEL = SHADE_MODEL,
+	COLOR_MATERIAL_FACE = COLOR_MATERIAL_FACE,
+	COLOR_MATERIAL_PARAMETER = COLOR_MATERIAL_PARAMETER,
+	COLOR_MATERIAL = COLOR_MATERIAL,
+	FOG = FOG,
+	FOG_INDEX = FOG_INDEX,
+	FOG_DENSITY = FOG_DENSITY,
+	FOG_START = FOG_START,
+	FOG_END = FOG_END,
+	FOG_MODE = FOG_MODE,
+	FOG_COLOR = FOG_COLOR,
+	ACCUM_CLEAR_VALUE = ACCUM_CLEAR_VALUE,
+	MATRIX_MODE = MATRIX_MODE,
+	NORMALIZE = NORMALIZE,
+	MODELVIEW_STACK_DEPTH = MODELVIEW_STACK_DEPTH,
+	PROJECTION_STACK_DEPTH = PROJECTION_STACK_DEPTH,
+	TEXTURE_STACK_DEPTH = TEXTURE_STACK_DEPTH,
+	MODELVIEW_MATRIX = MODELVIEW_MATRIX,
+	PROJECTION_MATRIX = PROJECTION_MATRIX,
+	TEXTURE_MATRIX = TEXTURE_MATRIX,
+	ATTRIB_STACK_DEPTH = ATTRIB_STACK_DEPTH,
+	ALPHA_TEST = ALPHA_TEST,
+	ALPHA_TEST_FUNC = ALPHA_TEST_FUNC,
+	ALPHA_TEST_REF = ALPHA_TEST_REF,
+	LOGIC_OP = LOGIC_OP,
+	AUX_BUFFERS = AUX_BUFFERS,
+	INDEX_CLEAR_VALUE = INDEX_CLEAR_VALUE,
+	INDEX_WRITEMASK = INDEX_WRITEMASK,
+	INDEX_MODE = INDEX_MODE,
+	RGBA_MODE = RGBA_MODE,
+	RENDER_MODE = RENDER_MODE,
+	PERSPECTIVE_CORRECTION_HINT = PERSPECTIVE_CORRECTION_HINT,
+	POINT_SMOOTH_HINT = POINT_SMOOTH_HINT,
+	FOG_HINT = FOG_HINT,
+	TEXTURE_GEN_S = TEXTURE_GEN_S,
+	TEXTURE_GEN_T = TEXTURE_GEN_T,
+	TEXTURE_GEN_R = TEXTURE_GEN_R,
+	TEXTURE_GEN_Q = TEXTURE_GEN_Q,
+	PIXEL_MAP_I_TO_I_SIZE = PIXEL_MAP_I_TO_I_SIZE,
+	PIXEL_MAP_S_TO_S_SIZE = PIXEL_MAP_S_TO_S_SIZE,
+	PIXEL_MAP_I_TO_R_SIZE = PIXEL_MAP_I_TO_R_SIZE,
+	PIXEL_MAP_I_TO_G_SIZE = PIXEL_MAP_I_TO_G_SIZE,
+	PIXEL_MAP_I_TO_B_SIZE = PIXEL_MAP_I_TO_B_SIZE,
+	PIXEL_MAP_I_TO_A_SIZE = PIXEL_MAP_I_TO_A_SIZE,
+	PIXEL_MAP_R_TO_R_SIZE = PIXEL_MAP_R_TO_R_SIZE,
+	PIXEL_MAP_G_TO_G_SIZE = PIXEL_MAP_G_TO_G_SIZE,
+	PIXEL_MAP_B_TO_B_SIZE = PIXEL_MAP_B_TO_B_SIZE,
+	PIXEL_MAP_A_TO_A_SIZE = PIXEL_MAP_A_TO_A_SIZE,
+	MAP_COLOR = MAP_COLOR,
+	MAP_STENCIL = MAP_STENCIL,
+	INDEX_SHIFT = INDEX_SHIFT,
+	INDEX_OFFSET = INDEX_OFFSET,
+	RED_SCALE = RED_SCALE,
+	RED_BIAS = RED_BIAS,
+	ZOOM_X = ZOOM_X,
+	ZOOM_Y = ZOOM_Y,
+	GREEN_SCALE = GREEN_SCALE,
+	GREEN_BIAS = GREEN_BIAS,
+	BLUE_SCALE = BLUE_SCALE,
+	BLUE_BIAS = BLUE_BIAS,
+	ALPHA_SCALE = ALPHA_SCALE,
+	ALPHA_BIAS = ALPHA_BIAS,
+	DEPTH_SCALE = DEPTH_SCALE,
+	DEPTH_BIAS = DEPTH_BIAS,
+	MAX_EVAL_ORDER = MAX_EVAL_ORDER,
+	MAX_LIGHTS = MAX_LIGHTS,
+	MAX_CLIP_PLANES = MAX_CLIP_PLANES,
+	MAX_PIXEL_MAP_TABLE = MAX_PIXEL_MAP_TABLE,
+	MAX_ATTRIB_STACK_DEPTH = MAX_ATTRIB_STACK_DEPTH,
+	MAX_MODELVIEW_STACK_DEPTH = MAX_MODELVIEW_STACK_DEPTH,
+	MAX_NAME_STACK_DEPTH = MAX_NAME_STACK_DEPTH,
+	MAX_PROJECTION_STACK_DEPTH = MAX_PROJECTION_STACK_DEPTH,
+	MAX_TEXTURE_STACK_DEPTH = MAX_TEXTURE_STACK_DEPTH,
+	INDEX_BITS = INDEX_BITS,
+	RED_BITS = RED_BITS,
+	GREEN_BITS = GREEN_BITS,
+	BLUE_BITS = BLUE_BITS,
+	ALPHA_BITS = ALPHA_BITS,
+	DEPTH_BITS = DEPTH_BITS,
+	STENCIL_BITS = STENCIL_BITS,
+	ACCUM_RED_BITS = ACCUM_RED_BITS,
+	ACCUM_GREEN_BITS = ACCUM_GREEN_BITS,
+	ACCUM_BLUE_BITS = ACCUM_BLUE_BITS,
+	ACCUM_ALPHA_BITS = ACCUM_ALPHA_BITS,
+	NAME_STACK_DEPTH = NAME_STACK_DEPTH,
+	AUTO_NORMAL = AUTO_NORMAL,
+	MAP1_COLOR_4 = MAP1_COLOR_4,
+	MAP1_INDEX = MAP1_INDEX,
+	MAP1_NORMAL = MAP1_NORMAL,
+	MAP1_TEXTURE_COORD_1 = MAP1_TEXTURE_COORD_1,
+	MAP1_TEXTURE_COORD_2 = MAP1_TEXTURE_COORD_2,
+	MAP1_TEXTURE_COORD_3 = MAP1_TEXTURE_COORD_3,
+	MAP1_TEXTURE_COORD_4 = MAP1_TEXTURE_COORD_4,
+	MAP1_VERTEX_3 = MAP1_VERTEX_3,
+	MAP1_VERTEX_4 = MAP1_VERTEX_4,
+	MAP2_COLOR_4 = MAP2_COLOR_4,
+	MAP2_INDEX = MAP2_INDEX,
+	MAP2_NORMAL = MAP2_NORMAL,
+	MAP2_TEXTURE_COORD_1 = MAP2_TEXTURE_COORD_1,
+	MAP2_TEXTURE_COORD_2 = MAP2_TEXTURE_COORD_2,
+	MAP2_TEXTURE_COORD_3 = MAP2_TEXTURE_COORD_3,
+	MAP2_TEXTURE_COORD_4 = MAP2_TEXTURE_COORD_4,
+	MAP2_VERTEX_3 = MAP2_VERTEX_3,
+	MAP2_VERTEX_4 = MAP2_VERTEX_4,
+	MAP1_GRID_DOMAIN = MAP1_GRID_DOMAIN,
+	MAP1_GRID_SEGMENTS = MAP1_GRID_SEGMENTS,
+	MAP2_GRID_DOMAIN = MAP2_GRID_DOMAIN,
+	MAP2_GRID_SEGMENTS = MAP2_GRID_SEGMENTS,
+	TEXTURE_COMPONENTS = TEXTURE_COMPONENTS,
+	TEXTURE_BORDER = TEXTURE_BORDER,
+	AMBIENT = AMBIENT,
+	DIFFUSE = DIFFUSE,
+	SPECULAR = SPECULAR,
+	POSITION = POSITION,
+	SPOT_DIRECTION = SPOT_DIRECTION,
+	SPOT_EXPONENT = SPOT_EXPONENT,
+	SPOT_CUTOFF = SPOT_CUTOFF,
+	CONSTANT_ATTENUATION = CONSTANT_ATTENUATION,
+	LINEAR_ATTENUATION = LINEAR_ATTENUATION,
+	QUADRATIC_ATTENUATION = QUADRATIC_ATTENUATION,
+	COMPILE = COMPILE,
+	COMPILE_AND_EXECUTE = COMPILE_AND_EXECUTE,
+	_2_BYTES = _2_BYTES,
+	_3_BYTES = _3_BYTES,
+	_4_BYTES = _4_BYTES,
+	EMISSION = EMISSION,
+	SHININESS = SHININESS,
+	AMBIENT_AND_DIFFUSE = AMBIENT_AND_DIFFUSE,
+	COLOR_INDEXES = COLOR_INDEXES,
+	MODELVIEW = MODELVIEW,
+	PROJECTION = PROJECTION,
+	COLOR_INDEX = COLOR_INDEX,
+	LUMINANCE = LUMINANCE,
+	LUMINANCE_ALPHA = LUMINANCE_ALPHA,
+	BITMAP = BITMAP,
+	RENDER = RENDER,
+	FEEDBACK = FEEDBACK,
+	SELECT = SELECT,
+	FLAT = FLAT,
+	SMOOTH = SMOOTH,
+	S = S,
+	T = T,
+	R = R,
+	Q = Q,
+	MODULATE = MODULATE,
+	DECAL = DECAL,
+	TEXTURE_ENV_MODE = TEXTURE_ENV_MODE,
+	TEXTURE_ENV_COLOR = TEXTURE_ENV_COLOR,
+	TEXTURE_ENV = TEXTURE_ENV,
+	EYE_LINEAR = EYE_LINEAR,
+	OBJECT_LINEAR = OBJECT_LINEAR,
+	SPHERE_MAP = SPHERE_MAP,
+	TEXTURE_GEN_MODE = TEXTURE_GEN_MODE,
+	OBJECT_PLANE = OBJECT_PLANE,
+	EYE_PLANE = EYE_PLANE,
+	CLAMP = CLAMP,
+	CLIP_PLANE0 = CLIP_PLANE0,
+	CLIP_PLANE1 = CLIP_PLANE1,
+	CLIP_PLANE2 = CLIP_PLANE2,
+	CLIP_PLANE3 = CLIP_PLANE3,
+	CLIP_PLANE4 = CLIP_PLANE4,
+	CLIP_PLANE5 = CLIP_PLANE5,
+	LIGHT0 = LIGHT0,
+	LIGHT1 = LIGHT1,
+	LIGHT2 = LIGHT2,
+	LIGHT3 = LIGHT3,
+	LIGHT4 = LIGHT4,
+	LIGHT5 = LIGHT5,
+	LIGHT6 = LIGHT6,
+	LIGHT7 = LIGHT7,
+	COLOR_LOGIC_OP = COLOR_LOGIC_OP,
+	POLYGON_OFFSET_UNITS = POLYGON_OFFSET_UNITS,
+	POLYGON_OFFSET_POINT = POLYGON_OFFSET_POINT,
+	POLYGON_OFFSET_LINE = POLYGON_OFFSET_LINE,
+	POLYGON_OFFSET_FILL = POLYGON_OFFSET_FILL,
+	POLYGON_OFFSET_FACTOR = POLYGON_OFFSET_FACTOR,
+	TEXTURE_BINDING_1D = TEXTURE_BINDING_1D,
+	TEXTURE_BINDING_2D = TEXTURE_BINDING_2D,
+	TEXTURE_INTERNAL_FORMAT = TEXTURE_INTERNAL_FORMAT,
+	TEXTURE_RED_SIZE = TEXTURE_RED_SIZE,
+	TEXTURE_GREEN_SIZE = TEXTURE_GREEN_SIZE,
+	TEXTURE_BLUE_SIZE = TEXTURE_BLUE_SIZE,
+	TEXTURE_ALPHA_SIZE = TEXTURE_ALPHA_SIZE,
+	DOUBLE = DOUBLE,
+	PROXY_TEXTURE_1D = PROXY_TEXTURE_1D,
+	PROXY_TEXTURE_2D = PROXY_TEXTURE_2D,
+	R3_G3_B2 = R3_G3_B2,
+	RGB4 = RGB4,
+	RGB5 = RGB5,
+	RGB8 = RGB8,
+	RGB10 = RGB10,
+	RGB12 = RGB12,
+	RGB16 = RGB16,
+	RGBA2 = RGBA2,
+	RGBA4 = RGBA4,
+	RGB5_A1 = RGB5_A1,
+	RGBA8 = RGBA8,
+	RGB10_A2 = RGB10_A2,
+	RGBA12 = RGBA12,
+	RGBA16 = RGBA16,
+	CLIENT_PIXEL_STORE_BIT = CLIENT_PIXEL_STORE_BIT,
+	CLIENT_VERTEX_ARRAY_BIT = CLIENT_VERTEX_ARRAY_BIT,
+	CLIENT_ALL_ATTRIB_BITS = CLIENT_ALL_ATTRIB_BITS,
+	VERTEX_ARRAY_POINTER = VERTEX_ARRAY_POINTER,
+	NORMAL_ARRAY_POINTER = NORMAL_ARRAY_POINTER,
+	COLOR_ARRAY_POINTER = COLOR_ARRAY_POINTER,
+	INDEX_ARRAY_POINTER = INDEX_ARRAY_POINTER,
+	TEXTURE_COORD_ARRAY_POINTER = TEXTURE_COORD_ARRAY_POINTER,
+	EDGE_FLAG_ARRAY_POINTER = EDGE_FLAG_ARRAY_POINTER,
+	FEEDBACK_BUFFER_POINTER = FEEDBACK_BUFFER_POINTER,
+	SELECTION_BUFFER_POINTER = SELECTION_BUFFER_POINTER,
+	CLIENT_ATTRIB_STACK_DEPTH = CLIENT_ATTRIB_STACK_DEPTH,
+	INDEX_LOGIC_OP = INDEX_LOGIC_OP,
+	MAX_CLIENT_ATTRIB_STACK_DEPTH = MAX_CLIENT_ATTRIB_STACK_DEPTH,
+	FEEDBACK_BUFFER_SIZE = FEEDBACK_BUFFER_SIZE,
+	FEEDBACK_BUFFER_TYPE = FEEDBACK_BUFFER_TYPE,
+	SELECTION_BUFFER_SIZE = SELECTION_BUFFER_SIZE,
+	VERTEX_ARRAY = VERTEX_ARRAY,
+	NORMAL_ARRAY = NORMAL_ARRAY,
+	COLOR_ARRAY = COLOR_ARRAY,
+	INDEX_ARRAY = INDEX_ARRAY,
+	TEXTURE_COORD_ARRAY = TEXTURE_COORD_ARRAY,
+	EDGE_FLAG_ARRAY = EDGE_FLAG_ARRAY,
+	VERTEX_ARRAY_SIZE = VERTEX_ARRAY_SIZE,
+	VERTEX_ARRAY_TYPE = VERTEX_ARRAY_TYPE,
+	VERTEX_ARRAY_STRIDE = VERTEX_ARRAY_STRIDE,
+	NORMAL_ARRAY_TYPE = NORMAL_ARRAY_TYPE,
+	NORMAL_ARRAY_STRIDE = NORMAL_ARRAY_STRIDE,
+	COLOR_ARRAY_SIZE = COLOR_ARRAY_SIZE,
+	COLOR_ARRAY_TYPE = COLOR_ARRAY_TYPE,
+	COLOR_ARRAY_STRIDE = COLOR_ARRAY_STRIDE,
+	INDEX_ARRAY_TYPE = INDEX_ARRAY_TYPE,
+	INDEX_ARRAY_STRIDE = INDEX_ARRAY_STRIDE,
+	TEXTURE_COORD_ARRAY_SIZE = TEXTURE_COORD_ARRAY_SIZE,
+	TEXTURE_COORD_ARRAY_TYPE = TEXTURE_COORD_ARRAY_TYPE,
+	TEXTURE_COORD_ARRAY_STRIDE = TEXTURE_COORD_ARRAY_STRIDE,
+	EDGE_FLAG_ARRAY_STRIDE = EDGE_FLAG_ARRAY_STRIDE,
+	TEXTURE_LUMINANCE_SIZE = TEXTURE_LUMINANCE_SIZE,
+	TEXTURE_INTENSITY_SIZE = TEXTURE_INTENSITY_SIZE,
+	TEXTURE_PRIORITY = TEXTURE_PRIORITY,
+	TEXTURE_RESIDENT = TEXTURE_RESIDENT,
+	ALPHA4 = ALPHA4,
+	ALPHA8 = ALPHA8,
+	ALPHA12 = ALPHA12,
+	ALPHA16 = ALPHA16,
+	LUMINANCE4 = LUMINANCE4,
+	LUMINANCE8 = LUMINANCE8,
+	LUMINANCE12 = LUMINANCE12,
+	LUMINANCE16 = LUMINANCE16,
+	LUMINANCE4_ALPHA4 = LUMINANCE4_ALPHA4,
+	LUMINANCE6_ALPHA2 = LUMINANCE6_ALPHA2,
+	LUMINANCE8_ALPHA8 = LUMINANCE8_ALPHA8,
+	LUMINANCE12_ALPHA4 = LUMINANCE12_ALPHA4,
+	LUMINANCE12_ALPHA12 = LUMINANCE12_ALPHA12,
+	LUMINANCE16_ALPHA16 = LUMINANCE16_ALPHA16,
+	INTENSITY = INTENSITY,
+	INTENSITY4 = INTENSITY4,
+	INTENSITY8 = INTENSITY8,
+	INTENSITY12 = INTENSITY12,
+	INTENSITY16 = INTENSITY16,
+	V2F = V2F,
+	V3F = V3F,
+	C4UB_V2F = C4UB_V2F,
+	C4UB_V3F = C4UB_V3F,
+	C3F_V3F = C3F_V3F,
+	N3F_V3F = N3F_V3F,
+	C4F_N3F_V3F = C4F_N3F_V3F,
+	T2F_V3F = T2F_V3F,
+	T4F_V4F = T4F_V4F,
+	T2F_C4UB_V3F = T2F_C4UB_V3F,
+	T2F_C3F_V3F = T2F_C3F_V3F,
+	T2F_N3F_V3F = T2F_N3F_V3F,
+	T2F_C4F_N3F_V3F = T2F_C4F_N3F_V3F,
+	T4F_C4F_N3F_V4F = T4F_C4F_N3F_V4F,
+	UNSIGNED_BYTE_3_3_2 = UNSIGNED_BYTE_3_3_2,
+	UNSIGNED_SHORT_4_4_4_4 = UNSIGNED_SHORT_4_4_4_4,
+	UNSIGNED_SHORT_5_5_5_1 = UNSIGNED_SHORT_5_5_5_1,
+	UNSIGNED_INT_8_8_8_8 = UNSIGNED_INT_8_8_8_8,
+	UNSIGNED_INT_10_10_10_2 = UNSIGNED_INT_10_10_10_2,
+	TEXTURE_BINDING_3D = TEXTURE_BINDING_3D,
+	PACK_SKIP_IMAGES = PACK_SKIP_IMAGES,
+	PACK_IMAGE_HEIGHT = PACK_IMAGE_HEIGHT,
+	UNPACK_SKIP_IMAGES = UNPACK_SKIP_IMAGES,
+	UNPACK_IMAGE_HEIGHT = UNPACK_IMAGE_HEIGHT,
+	TEXTURE_3D = TEXTURE_3D,
+	PROXY_TEXTURE_3D = PROXY_TEXTURE_3D,
+	TEXTURE_DEPTH = TEXTURE_DEPTH,
+	TEXTURE_WRAP_R = TEXTURE_WRAP_R,
+	MAX_3D_TEXTURE_SIZE = MAX_3D_TEXTURE_SIZE,
+	UNSIGNED_BYTE_2_3_3_REV = UNSIGNED_BYTE_2_3_3_REV,
+	UNSIGNED_SHORT_5_6_5 = UNSIGNED_SHORT_5_6_5,
+	UNSIGNED_SHORT_5_6_5_REV = UNSIGNED_SHORT_5_6_5_REV,
+	UNSIGNED_SHORT_4_4_4_4_REV = UNSIGNED_SHORT_4_4_4_4_REV,
+	UNSIGNED_SHORT_1_5_5_5_REV = UNSIGNED_SHORT_1_5_5_5_REV,
+	UNSIGNED_INT_8_8_8_8_REV = UNSIGNED_INT_8_8_8_8_REV,
+	UNSIGNED_INT_2_10_10_10_REV = UNSIGNED_INT_2_10_10_10_REV,
+	BGR = BGR,
+	BGRA = BGRA,
+	MAX_ELEMENTS_VERTICES = MAX_ELEMENTS_VERTICES,
+	MAX_ELEMENTS_INDICES = MAX_ELEMENTS_INDICES,
+	CLAMP_TO_EDGE = CLAMP_TO_EDGE,
+	TEXTURE_MIN_LOD = TEXTURE_MIN_LOD,
+	TEXTURE_MAX_LOD = TEXTURE_MAX_LOD,
+	TEXTURE_BASE_LEVEL = TEXTURE_BASE_LEVEL,
+	TEXTURE_MAX_LEVEL = TEXTURE_MAX_LEVEL,
+	SMOOTH_POINT_SIZE_RANGE = SMOOTH_POINT_SIZE_RANGE,
+	SMOOTH_POINT_SIZE_GRANULARITY = SMOOTH_POINT_SIZE_GRANULARITY,
+	SMOOTH_LINE_WIDTH_RANGE = SMOOTH_LINE_WIDTH_RANGE,
+	SMOOTH_LINE_WIDTH_GRANULARITY = SMOOTH_LINE_WIDTH_GRANULARITY,
+	ALIASED_LINE_WIDTH_RANGE = ALIASED_LINE_WIDTH_RANGE,
+	RESCALE_NORMAL = RESCALE_NORMAL,
+	LIGHT_MODEL_COLOR_CONTROL = LIGHT_MODEL_COLOR_CONTROL,
+	SINGLE_COLOR = SINGLE_COLOR,
+	SEPARATE_SPECULAR_COLOR = SEPARATE_SPECULAR_COLOR,
+	ALIASED_POINT_SIZE_RANGE = ALIASED_POINT_SIZE_RANGE,
+	TEXTURE0 = TEXTURE0,
+	TEXTURE1 = TEXTURE1,
+	TEXTURE2 = TEXTURE2,
+	TEXTURE3 = TEXTURE3,
+	TEXTURE4 = TEXTURE4,
+	TEXTURE5 = TEXTURE5,
+	TEXTURE6 = TEXTURE6,
+	TEXTURE7 = TEXTURE7,
+	TEXTURE8 = TEXTURE8,
+	TEXTURE9 = TEXTURE9,
+	TEXTURE10 = TEXTURE10,
+	TEXTURE11 = TEXTURE11,
+	TEXTURE12 = TEXTURE12,
+	TEXTURE13 = TEXTURE13,
+	TEXTURE14 = TEXTURE14,
+	TEXTURE15 = TEXTURE15,
+	TEXTURE16 = TEXTURE16,
+	TEXTURE17 = TEXTURE17,
+	TEXTURE18 = TEXTURE18,
+	TEXTURE19 = TEXTURE19,
+	TEXTURE20 = TEXTURE20,
+	TEXTURE21 = TEXTURE21,
+	TEXTURE22 = TEXTURE22,
+	TEXTURE23 = TEXTURE23,
+	TEXTURE24 = TEXTURE24,
+	TEXTURE25 = TEXTURE25,
+	TEXTURE26 = TEXTURE26,
+	TEXTURE27 = TEXTURE27,
+	TEXTURE28 = TEXTURE28,
+	TEXTURE29 = TEXTURE29,
+	TEXTURE30 = TEXTURE30,
+	TEXTURE31 = TEXTURE31,
+	ACTIVE_TEXTURE = ACTIVE_TEXTURE,
+	MULTISAMPLE = MULTISAMPLE,
+	SAMPLE_ALPHA_TO_COVERAGE = SAMPLE_ALPHA_TO_COVERAGE,
+	SAMPLE_ALPHA_TO_ONE = SAMPLE_ALPHA_TO_ONE,
+	SAMPLE_COVERAGE = SAMPLE_COVERAGE,
+	SAMPLE_BUFFERS = SAMPLE_BUFFERS,
+	SAMPLES = SAMPLES,
+	SAMPLE_COVERAGE_VALUE = SAMPLE_COVERAGE_VALUE,
+	SAMPLE_COVERAGE_INVERT = SAMPLE_COVERAGE_INVERT,
+	TEXTURE_CUBE_MAP = TEXTURE_CUBE_MAP,
+	TEXTURE_BINDING_CUBE_MAP = TEXTURE_BINDING_CUBE_MAP,
+	TEXTURE_CUBE_MAP_POSITIVE_X = TEXTURE_CUBE_MAP_POSITIVE_X,
+	TEXTURE_CUBE_MAP_NEGATIVE_X = TEXTURE_CUBE_MAP_NEGATIVE_X,
+	TEXTURE_CUBE_MAP_POSITIVE_Y = TEXTURE_CUBE_MAP_POSITIVE_Y,
+	TEXTURE_CUBE_MAP_NEGATIVE_Y = TEXTURE_CUBE_MAP_NEGATIVE_Y,
+	TEXTURE_CUBE_MAP_POSITIVE_Z = TEXTURE_CUBE_MAP_POSITIVE_Z,
+	TEXTURE_CUBE_MAP_NEGATIVE_Z = TEXTURE_CUBE_MAP_NEGATIVE_Z,
+	PROXY_TEXTURE_CUBE_MAP = PROXY_TEXTURE_CUBE_MAP,
+	MAX_CUBE_MAP_TEXTURE_SIZE = MAX_CUBE_MAP_TEXTURE_SIZE,
+	COMPRESSED_RGB = COMPRESSED_RGB,
+	COMPRESSED_RGBA = COMPRESSED_RGBA,
+	TEXTURE_COMPRESSION_HINT = TEXTURE_COMPRESSION_HINT,
+	TEXTURE_COMPRESSED_IMAGE_SIZE = TEXTURE_COMPRESSED_IMAGE_SIZE,
+	TEXTURE_COMPRESSED = TEXTURE_COMPRESSED,
+	NUM_COMPRESSED_TEXTURE_FORMATS = NUM_COMPRESSED_TEXTURE_FORMATS,
+	COMPRESSED_TEXTURE_FORMATS = COMPRESSED_TEXTURE_FORMATS,
+	CLAMP_TO_BORDER = CLAMP_TO_BORDER,
+	CLIENT_ACTIVE_TEXTURE = CLIENT_ACTIVE_TEXTURE,
+	MAX_TEXTURE_UNITS = MAX_TEXTURE_UNITS,
+	TRANSPOSE_MODELVIEW_MATRIX = TRANSPOSE_MODELVIEW_MATRIX,
+	TRANSPOSE_PROJECTION_MATRIX = TRANSPOSE_PROJECTION_MATRIX,
+	TRANSPOSE_TEXTURE_MATRIX = TRANSPOSE_TEXTURE_MATRIX,
+	TRANSPOSE_COLOR_MATRIX = TRANSPOSE_COLOR_MATRIX,
+	MULTISAMPLE_BIT = MULTISAMPLE_BIT,
+	NORMAL_MAP = NORMAL_MAP,
+	REFLECTION_MAP = REFLECTION_MAP,
+	COMPRESSED_ALPHA = COMPRESSED_ALPHA,
+	COMPRESSED_LUMINANCE = COMPRESSED_LUMINANCE,
+	COMPRESSED_LUMINANCE_ALPHA = COMPRESSED_LUMINANCE_ALPHA,
+	COMPRESSED_INTENSITY = COMPRESSED_INTENSITY,
+	COMBINE = COMBINE,
+	COMBINE_RGB = COMBINE_RGB,
+	COMBINE_ALPHA = COMBINE_ALPHA,
+	SOURCE0_RGB = SOURCE0_RGB,
+	SOURCE1_RGB = SOURCE1_RGB,
+	SOURCE2_RGB = SOURCE2_RGB,
+	SOURCE0_ALPHA = SOURCE0_ALPHA,
+	SOURCE1_ALPHA = SOURCE1_ALPHA,
+	SOURCE2_ALPHA = SOURCE2_ALPHA,
+	OPERAND0_RGB = OPERAND0_RGB,
+	OPERAND1_RGB = OPERAND1_RGB,
+	OPERAND2_RGB = OPERAND2_RGB,
+	OPERAND0_ALPHA = OPERAND0_ALPHA,
+	OPERAND1_ALPHA = OPERAND1_ALPHA,
+	OPERAND2_ALPHA = OPERAND2_ALPHA,
+	RGB_SCALE = RGB_SCALE,
+	ADD_SIGNED = ADD_SIGNED,
+	INTERPOLATE = INTERPOLATE,
+	SUBTRACT = SUBTRACT,
+	CONSTANT = CONSTANT,
+	PRIMARY_COLOR = PRIMARY_COLOR,
+	PREVIOUS = PREVIOUS,
+	DOT3_RGB = DOT3_RGB,
+	DOT3_RGBA = DOT3_RGBA,
+	BLEND_DST_RGB = BLEND_DST_RGB,
+	BLEND_SRC_RGB = BLEND_SRC_RGB,
+	BLEND_DST_ALPHA = BLEND_DST_ALPHA,
+	BLEND_SRC_ALPHA = BLEND_SRC_ALPHA,
+	POINT_FADE_THRESHOLD_SIZE = POINT_FADE_THRESHOLD_SIZE,
+	DEPTH_COMPONENT16 = DEPTH_COMPONENT16,
+	DEPTH_COMPONENT24 = DEPTH_COMPONENT24,
+	DEPTH_COMPONENT32 = DEPTH_COMPONENT32,
+	MIRRORED_REPEAT = MIRRORED_REPEAT,
+	MAX_TEXTURE_LOD_BIAS = MAX_TEXTURE_LOD_BIAS,
+	TEXTURE_LOD_BIAS = TEXTURE_LOD_BIAS,
+	INCR_WRAP = INCR_WRAP,
+	DECR_WRAP = DECR_WRAP,
+	TEXTURE_DEPTH_SIZE = TEXTURE_DEPTH_SIZE,
+	TEXTURE_COMPARE_MODE = TEXTURE_COMPARE_MODE,
+	TEXTURE_COMPARE_FUNC = TEXTURE_COMPARE_FUNC,
+	POINT_SIZE_MIN = POINT_SIZE_MIN,
+	POINT_SIZE_MAX = POINT_SIZE_MAX,
+	POINT_DISTANCE_ATTENUATION = POINT_DISTANCE_ATTENUATION,
+	GENERATE_MIPMAP = GENERATE_MIPMAP,
+	GENERATE_MIPMAP_HINT = GENERATE_MIPMAP_HINT,
+	FOG_COORDINATE_SOURCE = FOG_COORDINATE_SOURCE,
+	FOG_COORDINATE = FOG_COORDINATE,
+	FRAGMENT_DEPTH = FRAGMENT_DEPTH,
+	CURRENT_FOG_COORDINATE = CURRENT_FOG_COORDINATE,
+	FOG_COORDINATE_ARRAY_TYPE = FOG_COORDINATE_ARRAY_TYPE,
+	FOG_COORDINATE_ARRAY_STRIDE = FOG_COORDINATE_ARRAY_STRIDE,
+	FOG_COORDINATE_ARRAY_POINTER = FOG_COORDINATE_ARRAY_POINTER,
+	FOG_COORDINATE_ARRAY = FOG_COORDINATE_ARRAY,
+	COLOR_SUM = COLOR_SUM,
+	CURRENT_SECONDARY_COLOR = CURRENT_SECONDARY_COLOR,
+	SECONDARY_COLOR_ARRAY_SIZE = SECONDARY_COLOR_ARRAY_SIZE,
+	SECONDARY_COLOR_ARRAY_TYPE = SECONDARY_COLOR_ARRAY_TYPE,
+	SECONDARY_COLOR_ARRAY_STRIDE = SECONDARY_COLOR_ARRAY_STRIDE,
+	SECONDARY_COLOR_ARRAY_POINTER = SECONDARY_COLOR_ARRAY_POINTER,
+	SECONDARY_COLOR_ARRAY = SECONDARY_COLOR_ARRAY,
+	TEXTURE_FILTER_CONTROL = TEXTURE_FILTER_CONTROL,
+	DEPTH_TEXTURE_MODE = DEPTH_TEXTURE_MODE,
+	COMPARE_R_TO_TEXTURE = COMPARE_R_TO_TEXTURE,
+	BLEND_COLOR = BLEND_COLOR,
+	BLEND_EQUATION = BLEND_EQUATION,
+	CONSTANT_COLOR = CONSTANT_COLOR,
+	ONE_MINUS_CONSTANT_COLOR = ONE_MINUS_CONSTANT_COLOR,
+	CONSTANT_ALPHA = CONSTANT_ALPHA,
+	ONE_MINUS_CONSTANT_ALPHA = ONE_MINUS_CONSTANT_ALPHA,
+	FUNC_ADD = FUNC_ADD,
+	FUNC_REVERSE_SUBTRACT = FUNC_REVERSE_SUBTRACT,
+	FUNC_SUBTRACT = FUNC_SUBTRACT,
+	MIN = MIN,
+	MAX = MAX,
+	BUFFER_SIZE = BUFFER_SIZE,
+	BUFFER_USAGE = BUFFER_USAGE,
+	QUERY_COUNTER_BITS = QUERY_COUNTER_BITS,
+	CURRENT_QUERY = CURRENT_QUERY,
+	QUERY_RESULT = QUERY_RESULT,
+	QUERY_RESULT_AVAILABLE = QUERY_RESULT_AVAILABLE,
+	ARRAY_BUFFER = ARRAY_BUFFER,
+	ELEMENT_ARRAY_BUFFER = ELEMENT_ARRAY_BUFFER,
+	ARRAY_BUFFER_BINDING = ARRAY_BUFFER_BINDING,
+	ELEMENT_ARRAY_BUFFER_BINDING = ELEMENT_ARRAY_BUFFER_BINDING,
+	VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = VERTEX_ATTRIB_ARRAY_BUFFER_BINDING,
+	READ_ONLY = READ_ONLY,
+	WRITE_ONLY = WRITE_ONLY,
+	READ_WRITE = READ_WRITE,
+	BUFFER_ACCESS = BUFFER_ACCESS,
+	BUFFER_MAPPED = BUFFER_MAPPED,
+	BUFFER_MAP_POINTER = BUFFER_MAP_POINTER,
+	STREAM_DRAW = STREAM_DRAW,
+	STREAM_READ = STREAM_READ,
+	STREAM_COPY = STREAM_COPY,
+	STATIC_DRAW = STATIC_DRAW,
+	STATIC_READ = STATIC_READ,
+	STATIC_COPY = STATIC_COPY,
+	DYNAMIC_DRAW = DYNAMIC_DRAW,
+	DYNAMIC_READ = DYNAMIC_READ,
+	DYNAMIC_COPY = DYNAMIC_COPY,
+	SAMPLES_PASSED = SAMPLES_PASSED,
+	SRC1_ALPHA = SRC1_ALPHA,
+	VERTEX_ARRAY_BUFFER_BINDING = VERTEX_ARRAY_BUFFER_BINDING,
+	NORMAL_ARRAY_BUFFER_BINDING = NORMAL_ARRAY_BUFFER_BINDING,
+	COLOR_ARRAY_BUFFER_BINDING = COLOR_ARRAY_BUFFER_BINDING,
+	INDEX_ARRAY_BUFFER_BINDING = INDEX_ARRAY_BUFFER_BINDING,
+	TEXTURE_COORD_ARRAY_BUFFER_BINDING = TEXTURE_COORD_ARRAY_BUFFER_BINDING,
+	EDGE_FLAG_ARRAY_BUFFER_BINDING = EDGE_FLAG_ARRAY_BUFFER_BINDING,
+	SECONDARY_COLOR_ARRAY_BUFFER_BINDING = SECONDARY_COLOR_ARRAY_BUFFER_BINDING,
+	FOG_COORDINATE_ARRAY_BUFFER_BINDING = FOG_COORDINATE_ARRAY_BUFFER_BINDING,
+	WEIGHT_ARRAY_BUFFER_BINDING = WEIGHT_ARRAY_BUFFER_BINDING,
+	FOG_COORD_SRC = FOG_COORD_SRC,
+	FOG_COORD = FOG_COORD,
+	CURRENT_FOG_COORD = CURRENT_FOG_COORD,
+	FOG_COORD_ARRAY_TYPE = FOG_COORD_ARRAY_TYPE,
+	FOG_COORD_ARRAY_STRIDE = FOG_COORD_ARRAY_STRIDE,
+	FOG_COORD_ARRAY_POINTER = FOG_COORD_ARRAY_POINTER,
+	FOG_COORD_ARRAY = FOG_COORD_ARRAY,
+	FOG_COORD_ARRAY_BUFFER_BINDING = FOG_COORD_ARRAY_BUFFER_BINDING,
+	SRC0_RGB = SRC0_RGB,
+	SRC1_RGB = SRC1_RGB,
+	SRC2_RGB = SRC2_RGB,
+	SRC0_ALPHA = SRC0_ALPHA,
+	SRC2_ALPHA = SRC2_ALPHA,
+	BLEND_EQUATION_RGB = BLEND_EQUATION_RGB,
+	VERTEX_ATTRIB_ARRAY_ENABLED = VERTEX_ATTRIB_ARRAY_ENABLED,
+	VERTEX_ATTRIB_ARRAY_SIZE = VERTEX_ATTRIB_ARRAY_SIZE,
+	VERTEX_ATTRIB_ARRAY_STRIDE = VERTEX_ATTRIB_ARRAY_STRIDE,
+	VERTEX_ATTRIB_ARRAY_TYPE = VERTEX_ATTRIB_ARRAY_TYPE,
+	CURRENT_VERTEX_ATTRIB = CURRENT_VERTEX_ATTRIB,
+	VERTEX_PROGRAM_POINT_SIZE = VERTEX_PROGRAM_POINT_SIZE,
+	VERTEX_ATTRIB_ARRAY_POINTER = VERTEX_ATTRIB_ARRAY_POINTER,
+	STENCIL_BACK_FUNC = STENCIL_BACK_FUNC,
+	STENCIL_BACK_FAIL = STENCIL_BACK_FAIL,
+	STENCIL_BACK_PASS_DEPTH_FAIL = STENCIL_BACK_PASS_DEPTH_FAIL,
+	STENCIL_BACK_PASS_DEPTH_PASS = STENCIL_BACK_PASS_DEPTH_PASS,
+	MAX_DRAW_BUFFERS = MAX_DRAW_BUFFERS,
+	DRAW_BUFFER0 = DRAW_BUFFER0,
+	DRAW_BUFFER1 = DRAW_BUFFER1,
+	DRAW_BUFFER2 = DRAW_BUFFER2,
+	DRAW_BUFFER3 = DRAW_BUFFER3,
+	DRAW_BUFFER4 = DRAW_BUFFER4,
+	DRAW_BUFFER5 = DRAW_BUFFER5,
+	DRAW_BUFFER6 = DRAW_BUFFER6,
+	DRAW_BUFFER7 = DRAW_BUFFER7,
+	DRAW_BUFFER8 = DRAW_BUFFER8,
+	DRAW_BUFFER9 = DRAW_BUFFER9,
+	DRAW_BUFFER10 = DRAW_BUFFER10,
+	DRAW_BUFFER11 = DRAW_BUFFER11,
+	DRAW_BUFFER12 = DRAW_BUFFER12,
+	DRAW_BUFFER13 = DRAW_BUFFER13,
+	DRAW_BUFFER14 = DRAW_BUFFER14,
+	DRAW_BUFFER15 = DRAW_BUFFER15,
+	BLEND_EQUATION_ALPHA = BLEND_EQUATION_ALPHA,
+	MAX_VERTEX_ATTRIBS = MAX_VERTEX_ATTRIBS,
+	VERTEX_ATTRIB_ARRAY_NORMALIZED = VERTEX_ATTRIB_ARRAY_NORMALIZED,
+	MAX_TEXTURE_IMAGE_UNITS = MAX_TEXTURE_IMAGE_UNITS,
+	FRAGMENT_SHADER = FRAGMENT_SHADER,
+	VERTEX_SHADER = VERTEX_SHADER,
+	MAX_FRAGMENT_UNIFORM_COMPONENTS = MAX_FRAGMENT_UNIFORM_COMPONENTS,
+	MAX_VERTEX_UNIFORM_COMPONENTS = MAX_VERTEX_UNIFORM_COMPONENTS,
+	MAX_VARYING_FLOATS = MAX_VARYING_FLOATS,
+	MAX_VERTEX_TEXTURE_IMAGE_UNITS = MAX_VERTEX_TEXTURE_IMAGE_UNITS,
+	MAX_COMBINED_TEXTURE_IMAGE_UNITS = MAX_COMBINED_TEXTURE_IMAGE_UNITS,
+	SHADER_TYPE = SHADER_TYPE,
+	FLOAT_VEC2 = FLOAT_VEC2,
+	FLOAT_VEC3 = FLOAT_VEC3,
+	FLOAT_VEC4 = FLOAT_VEC4,
+	INT_VEC2 = INT_VEC2,
+	INT_VEC3 = INT_VEC3,
+	INT_VEC4 = INT_VEC4,
+	BOOL = BOOL,
+	BOOL_VEC2 = BOOL_VEC2,
+	BOOL_VEC3 = BOOL_VEC3,
+	BOOL_VEC4 = BOOL_VEC4,
+	FLOAT_MAT2 = FLOAT_MAT2,
+	FLOAT_MAT3 = FLOAT_MAT3,
+	FLOAT_MAT4 = FLOAT_MAT4,
+	SAMPLER_1D = SAMPLER_1D,
+	SAMPLER_2D = SAMPLER_2D,
+	SAMPLER_3D = SAMPLER_3D,
+	SAMPLER_CUBE = SAMPLER_CUBE,
+	SAMPLER_1D_SHADOW = SAMPLER_1D_SHADOW,
+	SAMPLER_2D_SHADOW = SAMPLER_2D_SHADOW,
+	DELETE_STATUS = DELETE_STATUS,
+	COMPILE_STATUS = COMPILE_STATUS,
+	LINK_STATUS = LINK_STATUS,
+	VALIDATE_STATUS = VALIDATE_STATUS,
+	INFO_LOG_LENGTH = INFO_LOG_LENGTH,
+	ATTACHED_SHADERS = ATTACHED_SHADERS,
+	ACTIVE_UNIFORMS = ACTIVE_UNIFORMS,
+	ACTIVE_UNIFORM_MAX_LENGTH = ACTIVE_UNIFORM_MAX_LENGTH,
+	SHADER_SOURCE_LENGTH = SHADER_SOURCE_LENGTH,
+	ACTIVE_ATTRIBUTES = ACTIVE_ATTRIBUTES,
+	ACTIVE_ATTRIBUTE_MAX_LENGTH = ACTIVE_ATTRIBUTE_MAX_LENGTH,
+	FRAGMENT_SHADER_DERIVATIVE_HINT = FRAGMENT_SHADER_DERIVATIVE_HINT,
+	SHADING_LANGUAGE_VERSION = SHADING_LANGUAGE_VERSION,
+	CURRENT_PROGRAM = CURRENT_PROGRAM,
+	POINT_SPRITE_COORD_ORIGIN = POINT_SPRITE_COORD_ORIGIN,
+	LOWER_LEFT = LOWER_LEFT,
+	UPPER_LEFT = UPPER_LEFT,
+	STENCIL_BACK_REF = STENCIL_BACK_REF,
+	STENCIL_BACK_VALUE_MASK = STENCIL_BACK_VALUE_MASK,
+	STENCIL_BACK_WRITEMASK = STENCIL_BACK_WRITEMASK,
+	VERTEX_PROGRAM_TWO_SIDE = VERTEX_PROGRAM_TWO_SIDE,
+	POINT_SPRITE = POINT_SPRITE,
+	COORD_REPLACE = COORD_REPLACE,
+	MAX_TEXTURE_COORDS = MAX_TEXTURE_COORDS,
+	PIXEL_PACK_BUFFER = PIXEL_PACK_BUFFER,
+	PIXEL_UNPACK_BUFFER = PIXEL_UNPACK_BUFFER,
+	PIXEL_PACK_BUFFER_BINDING = PIXEL_PACK_BUFFER_BINDING,
+	PIXEL_UNPACK_BUFFER_BINDING = PIXEL_UNPACK_BUFFER_BINDING,
+	FLOAT_MAT2x3 = FLOAT_MAT2x3,
+	FLOAT_MAT2x4 = FLOAT_MAT2x4,
+	FLOAT_MAT3x2 = FLOAT_MAT3x2,
+	FLOAT_MAT3x4 = FLOAT_MAT3x4,
+	FLOAT_MAT4x2 = FLOAT_MAT4x2,
+	FLOAT_MAT4x3 = FLOAT_MAT4x3,
+	SRGB = SRGB,
+	SRGB8 = SRGB8,
+	SRGB_ALPHA = SRGB_ALPHA,
+	SRGB8_ALPHA8 = SRGB8_ALPHA8,
+	COMPRESSED_SRGB = COMPRESSED_SRGB,
+	COMPRESSED_SRGB_ALPHA = COMPRESSED_SRGB_ALPHA,
+	CURRENT_RASTER_SECONDARY_COLOR = CURRENT_RASTER_SECONDARY_COLOR,
+	SLUMINANCE_ALPHA = SLUMINANCE_ALPHA,
+	SLUMINANCE8_ALPHA8 = SLUMINANCE8_ALPHA8,
+	SLUMINANCE = SLUMINANCE,
+	SLUMINANCE8 = SLUMINANCE8,
+	COMPRESSED_SLUMINANCE = COMPRESSED_SLUMINANCE,
+	COMPRESSED_SLUMINANCE_ALPHA = COMPRESSED_SLUMINANCE_ALPHA,
+	COMPARE_REF_TO_TEXTURE = COMPARE_REF_TO_TEXTURE,
+	CLIP_DISTANCE0 = CLIP_DISTANCE0,
+	CLIP_DISTANCE1 = CLIP_DISTANCE1,
+	CLIP_DISTANCE2 = CLIP_DISTANCE2,
+	CLIP_DISTANCE3 = CLIP_DISTANCE3,
+	CLIP_DISTANCE4 = CLIP_DISTANCE4,
+	CLIP_DISTANCE5 = CLIP_DISTANCE5,
+	CLIP_DISTANCE6 = CLIP_DISTANCE6,
+	CLIP_DISTANCE7 = CLIP_DISTANCE7,
+	MAX_CLIP_DISTANCES = MAX_CLIP_DISTANCES,
+	MAJOR_VERSION = MAJOR_VERSION,
+	MINOR_VERSION = MINOR_VERSION,
+	NUM_EXTENSIONS = NUM_EXTENSIONS,
+	CONTEXT_FLAGS = CONTEXT_FLAGS,
+	COMPRESSED_RED = COMPRESSED_RED,
+	COMPRESSED_RG = COMPRESSED_RG,
+	CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT,
+	RGBA32F = RGBA32F,
+	RGB32F = RGB32F,
+	RGBA16F = RGBA16F,
+	RGB16F = RGB16F,
+	VERTEX_ATTRIB_ARRAY_INTEGER = VERTEX_ATTRIB_ARRAY_INTEGER,
+	MAX_ARRAY_TEXTURE_LAYERS = MAX_ARRAY_TEXTURE_LAYERS,
+	MIN_PROGRAM_TEXEL_OFFSET = MIN_PROGRAM_TEXEL_OFFSET,
+	MAX_PROGRAM_TEXEL_OFFSET = MAX_PROGRAM_TEXEL_OFFSET,
+	CLAMP_READ_COLOR = CLAMP_READ_COLOR,
+	FIXED_ONLY = FIXED_ONLY,
+	MAX_VARYING_COMPONENTS = MAX_VARYING_COMPONENTS,
+	TEXTURE_1D_ARRAY = TEXTURE_1D_ARRAY,
+	PROXY_TEXTURE_1D_ARRAY = PROXY_TEXTURE_1D_ARRAY,
+	TEXTURE_2D_ARRAY = TEXTURE_2D_ARRAY,
+	PROXY_TEXTURE_2D_ARRAY = PROXY_TEXTURE_2D_ARRAY,
+	TEXTURE_BINDING_1D_ARRAY = TEXTURE_BINDING_1D_ARRAY,
+	TEXTURE_BINDING_2D_ARRAY = TEXTURE_BINDING_2D_ARRAY,
+	R11F_G11F_B10F = R11F_G11F_B10F,
+	UNSIGNED_INT_10F_11F_11F_REV = UNSIGNED_INT_10F_11F_11F_REV,
+	RGB9_E5 = RGB9_E5,
+	UNSIGNED_INT_5_9_9_9_REV = UNSIGNED_INT_5_9_9_9_REV,
+	TEXTURE_SHARED_SIZE = TEXTURE_SHARED_SIZE,
+	TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH = TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
+	TRANSFORM_FEEDBACK_BUFFER_MODE = TRANSFORM_FEEDBACK_BUFFER_MODE,
+	MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
+	TRANSFORM_FEEDBACK_VARYINGS = TRANSFORM_FEEDBACK_VARYINGS,
+	TRANSFORM_FEEDBACK_BUFFER_START = TRANSFORM_FEEDBACK_BUFFER_START,
+	TRANSFORM_FEEDBACK_BUFFER_SIZE = TRANSFORM_FEEDBACK_BUFFER_SIZE,
+	PRIMITIVES_GENERATED = PRIMITIVES_GENERATED,
+	TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
+	RASTERIZER_DISCARD = RASTERIZER_DISCARD,
+	MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
+	MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
+	INTERLEAVED_ATTRIBS = INTERLEAVED_ATTRIBS,
+	SEPARATE_ATTRIBS = SEPARATE_ATTRIBS,
+	TRANSFORM_FEEDBACK_BUFFER = TRANSFORM_FEEDBACK_BUFFER,
+	TRANSFORM_FEEDBACK_BUFFER_BINDING = TRANSFORM_FEEDBACK_BUFFER_BINDING,
+	RGBA32UI = RGBA32UI,
+	RGB32UI = RGB32UI,
+	RGBA16UI = RGBA16UI,
+	RGB16UI = RGB16UI,
+	RGBA8UI = RGBA8UI,
+	RGB8UI = RGB8UI,
+	RGBA32I = RGBA32I,
+	RGB32I = RGB32I,
+	RGBA16I = RGBA16I,
+	RGB16I = RGB16I,
+	RGBA8I = RGBA8I,
+	RGB8I = RGB8I,
+	RED_INTEGER = RED_INTEGER,
+	GREEN_INTEGER = GREEN_INTEGER,
+	BLUE_INTEGER = BLUE_INTEGER,
+	RGB_INTEGER = RGB_INTEGER,
+	RGBA_INTEGER = RGBA_INTEGER,
+	BGR_INTEGER = BGR_INTEGER,
+	BGRA_INTEGER = BGRA_INTEGER,
+	SAMPLER_1D_ARRAY = SAMPLER_1D_ARRAY,
+	SAMPLER_2D_ARRAY = SAMPLER_2D_ARRAY,
+	SAMPLER_1D_ARRAY_SHADOW = SAMPLER_1D_ARRAY_SHADOW,
+	SAMPLER_2D_ARRAY_SHADOW = SAMPLER_2D_ARRAY_SHADOW,
+	SAMPLER_CUBE_SHADOW = SAMPLER_CUBE_SHADOW,
+	UNSIGNED_INT_VEC2 = UNSIGNED_INT_VEC2,
+	UNSIGNED_INT_VEC3 = UNSIGNED_INT_VEC3,
+	UNSIGNED_INT_VEC4 = UNSIGNED_INT_VEC4,
+	INT_SAMPLER_1D = INT_SAMPLER_1D,
+	INT_SAMPLER_2D = INT_SAMPLER_2D,
+	INT_SAMPLER_3D = INT_SAMPLER_3D,
+	INT_SAMPLER_CUBE = INT_SAMPLER_CUBE,
+	INT_SAMPLER_1D_ARRAY = INT_SAMPLER_1D_ARRAY,
+	INT_SAMPLER_2D_ARRAY = INT_SAMPLER_2D_ARRAY,
+	UNSIGNED_INT_SAMPLER_1D = UNSIGNED_INT_SAMPLER_1D,
+	UNSIGNED_INT_SAMPLER_2D = UNSIGNED_INT_SAMPLER_2D,
+	UNSIGNED_INT_SAMPLER_3D = UNSIGNED_INT_SAMPLER_3D,
+	UNSIGNED_INT_SAMPLER_CUBE = UNSIGNED_INT_SAMPLER_CUBE,
+	UNSIGNED_INT_SAMPLER_1D_ARRAY = UNSIGNED_INT_SAMPLER_1D_ARRAY,
+	UNSIGNED_INT_SAMPLER_2D_ARRAY = UNSIGNED_INT_SAMPLER_2D_ARRAY,
+	QUERY_WAIT = QUERY_WAIT,
+	QUERY_NO_WAIT = QUERY_NO_WAIT,
+	QUERY_BY_REGION_WAIT = QUERY_BY_REGION_WAIT,
+	QUERY_BY_REGION_NO_WAIT = QUERY_BY_REGION_NO_WAIT,
+	BUFFER_ACCESS_FLAGS = BUFFER_ACCESS_FLAGS,
+	BUFFER_MAP_LENGTH = BUFFER_MAP_LENGTH,
+	BUFFER_MAP_OFFSET = BUFFER_MAP_OFFSET,
+	DEPTH_COMPONENT32F = DEPTH_COMPONENT32F,
+	DEPTH32F_STENCIL8 = DEPTH32F_STENCIL8,
+	FLOAT_32_UNSIGNED_INT_24_8_REV = FLOAT_32_UNSIGNED_INT_24_8_REV,
+	INVALID_FRAMEBUFFER_OPERATION = INVALID_FRAMEBUFFER_OPERATION,
+	FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
+	FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE = FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE,
+	FRAMEBUFFER_ATTACHMENT_RED_SIZE = FRAMEBUFFER_ATTACHMENT_RED_SIZE,
+	FRAMEBUFFER_ATTACHMENT_GREEN_SIZE = FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
+	FRAMEBUFFER_ATTACHMENT_BLUE_SIZE = FRAMEBUFFER_ATTACHMENT_BLUE_SIZE,
+	FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE = FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
+	FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE = FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
+	FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE = FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
+	FRAMEBUFFER_DEFAULT = FRAMEBUFFER_DEFAULT,
+	FRAMEBUFFER_UNDEFINED = FRAMEBUFFER_UNDEFINED,
+	DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL_ATTACHMENT,
+	MAX_RENDERBUFFER_SIZE = MAX_RENDERBUFFER_SIZE,
+	DEPTH_STENCIL = DEPTH_STENCIL,
+	UNSIGNED_INT_24_8 = UNSIGNED_INT_24_8,
+	DEPTH24_STENCIL8 = DEPTH24_STENCIL8,
+	TEXTURE_STENCIL_SIZE = TEXTURE_STENCIL_SIZE,
+	TEXTURE_RED_TYPE = TEXTURE_RED_TYPE,
+	TEXTURE_GREEN_TYPE = TEXTURE_GREEN_TYPE,
+	TEXTURE_BLUE_TYPE = TEXTURE_BLUE_TYPE,
+	TEXTURE_ALPHA_TYPE = TEXTURE_ALPHA_TYPE,
+	TEXTURE_DEPTH_TYPE = TEXTURE_DEPTH_TYPE,
+	UNSIGNED_NORMALIZED = UNSIGNED_NORMALIZED,
+	FRAMEBUFFER_BINDING = FRAMEBUFFER_BINDING,
+	DRAW_FRAMEBUFFER_BINDING = DRAW_FRAMEBUFFER_BINDING,
+	RENDERBUFFER_BINDING = RENDERBUFFER_BINDING,
+	READ_FRAMEBUFFER = READ_FRAMEBUFFER,
+	DRAW_FRAMEBUFFER = DRAW_FRAMEBUFFER,
+	READ_FRAMEBUFFER_BINDING = READ_FRAMEBUFFER_BINDING,
+	RENDERBUFFER_SAMPLES = RENDERBUFFER_SAMPLES,
+	FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
+	FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+	FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
+	FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
+	FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER,
+	FRAMEBUFFER_COMPLETE = FRAMEBUFFER_COMPLETE,
+	FRAMEBUFFER_INCOMPLETE_ATTACHMENT = FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+	FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
+	FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER,
+	FRAMEBUFFER_INCOMPLETE_READ_BUFFER = FRAMEBUFFER_INCOMPLETE_READ_BUFFER,
+	FRAMEBUFFER_UNSUPPORTED = FRAMEBUFFER_UNSUPPORTED,
+	MAX_COLOR_ATTACHMENTS = MAX_COLOR_ATTACHMENTS,
+	COLOR_ATTACHMENT0 = COLOR_ATTACHMENT0,
+	COLOR_ATTACHMENT1 = COLOR_ATTACHMENT1,
+	COLOR_ATTACHMENT2 = COLOR_ATTACHMENT2,
+	COLOR_ATTACHMENT3 = COLOR_ATTACHMENT3,
+	COLOR_ATTACHMENT4 = COLOR_ATTACHMENT4,
+	COLOR_ATTACHMENT5 = COLOR_ATTACHMENT5,
+	COLOR_ATTACHMENT6 = COLOR_ATTACHMENT6,
+	COLOR_ATTACHMENT7 = COLOR_ATTACHMENT7,
+	COLOR_ATTACHMENT8 = COLOR_ATTACHMENT8,
+	COLOR_ATTACHMENT9 = COLOR_ATTACHMENT9,
+	COLOR_ATTACHMENT10 = COLOR_ATTACHMENT10,
+	COLOR_ATTACHMENT11 = COLOR_ATTACHMENT11,
+	COLOR_ATTACHMENT12 = COLOR_ATTACHMENT12,
+	COLOR_ATTACHMENT13 = COLOR_ATTACHMENT13,
+	COLOR_ATTACHMENT14 = COLOR_ATTACHMENT14,
+	COLOR_ATTACHMENT15 = COLOR_ATTACHMENT15,
+	COLOR_ATTACHMENT16 = COLOR_ATTACHMENT16,
+	COLOR_ATTACHMENT17 = COLOR_ATTACHMENT17,
+	COLOR_ATTACHMENT18 = COLOR_ATTACHMENT18,
+	COLOR_ATTACHMENT19 = COLOR_ATTACHMENT19,
+	COLOR_ATTACHMENT20 = COLOR_ATTACHMENT20,
+	COLOR_ATTACHMENT21 = COLOR_ATTACHMENT21,
+	COLOR_ATTACHMENT22 = COLOR_ATTACHMENT22,
+	COLOR_ATTACHMENT23 = COLOR_ATTACHMENT23,
+	COLOR_ATTACHMENT24 = COLOR_ATTACHMENT24,
+	COLOR_ATTACHMENT25 = COLOR_ATTACHMENT25,
+	COLOR_ATTACHMENT26 = COLOR_ATTACHMENT26,
+	COLOR_ATTACHMENT27 = COLOR_ATTACHMENT27,
+	COLOR_ATTACHMENT28 = COLOR_ATTACHMENT28,
+	COLOR_ATTACHMENT29 = COLOR_ATTACHMENT29,
+	COLOR_ATTACHMENT30 = COLOR_ATTACHMENT30,
+	COLOR_ATTACHMENT31 = COLOR_ATTACHMENT31,
+	DEPTH_ATTACHMENT = DEPTH_ATTACHMENT,
+	STENCIL_ATTACHMENT = STENCIL_ATTACHMENT,
+	FRAMEBUFFER = FRAMEBUFFER,
+	RENDERBUFFER = RENDERBUFFER,
+	RENDERBUFFER_WIDTH = RENDERBUFFER_WIDTH,
+	RENDERBUFFER_HEIGHT = RENDERBUFFER_HEIGHT,
+	RENDERBUFFER_INTERNAL_FORMAT = RENDERBUFFER_INTERNAL_FORMAT,
+	STENCIL_INDEX1 = STENCIL_INDEX1,
+	STENCIL_INDEX4 = STENCIL_INDEX4,
+	STENCIL_INDEX8 = STENCIL_INDEX8,
+	STENCIL_INDEX16 = STENCIL_INDEX16,
+	RENDERBUFFER_RED_SIZE = RENDERBUFFER_RED_SIZE,
+	RENDERBUFFER_GREEN_SIZE = RENDERBUFFER_GREEN_SIZE,
+	RENDERBUFFER_BLUE_SIZE = RENDERBUFFER_BLUE_SIZE,
+	RENDERBUFFER_ALPHA_SIZE = RENDERBUFFER_ALPHA_SIZE,
+	RENDERBUFFER_DEPTH_SIZE = RENDERBUFFER_DEPTH_SIZE,
+	RENDERBUFFER_STENCIL_SIZE = RENDERBUFFER_STENCIL_SIZE,
+	FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
+	MAX_SAMPLES = MAX_SAMPLES,
+	INDEX = INDEX,
+	TEXTURE_LUMINANCE_TYPE = TEXTURE_LUMINANCE_TYPE,
+	TEXTURE_INTENSITY_TYPE = TEXTURE_INTENSITY_TYPE,
+	FRAMEBUFFER_SRGB = FRAMEBUFFER_SRGB,
+	HALF_FLOAT = HALF_FLOAT,
+	MAP_READ_BIT = MAP_READ_BIT,
+	MAP_WRITE_BIT = MAP_WRITE_BIT,
+	MAP_INVALIDATE_RANGE_BIT = MAP_INVALIDATE_RANGE_BIT,
+	MAP_INVALIDATE_BUFFER_BIT = MAP_INVALIDATE_BUFFER_BIT,
+	MAP_FLUSH_EXPLICIT_BIT = MAP_FLUSH_EXPLICIT_BIT,
+	MAP_UNSYNCHRONIZED_BIT = MAP_UNSYNCHRONIZED_BIT,
+	COMPRESSED_RED_RGTC1 = COMPRESSED_RED_RGTC1,
+	COMPRESSED_SIGNED_RED_RGTC1 = COMPRESSED_SIGNED_RED_RGTC1,
+	COMPRESSED_RG_RGTC2 = COMPRESSED_RG_RGTC2,
+	COMPRESSED_SIGNED_RG_RGTC2 = COMPRESSED_SIGNED_RG_RGTC2,
+	RG = RG,
+	RG_INTEGER = RG_INTEGER,
+	R8 = R8,
+	R16 = R16,
+	RG8 = RG8,
+	RG16 = RG16,
+	R16F = R16F,
+	R32F = R32F,
+	RG16F = RG16F,
+	RG32F = RG32F,
+	R8I = R8I,
+	R8UI = R8UI,
+	R16I = R16I,
+	R16UI = R16UI,
+	R32I = R32I,
+	R32UI = R32UI,
+	RG8I = RG8I,
+	RG8UI = RG8UI,
+	RG16I = RG16I,
+	RG16UI = RG16UI,
+	RG32I = RG32I,
+	RG32UI = RG32UI,
+	VERTEX_ARRAY_BINDING = VERTEX_ARRAY_BINDING,
+	CLAMP_VERTEX_COLOR = CLAMP_VERTEX_COLOR,
+	CLAMP_FRAGMENT_COLOR = CLAMP_FRAGMENT_COLOR,
+	ALPHA_INTEGER = ALPHA_INTEGER,
+	SAMPLER_2D_RECT = SAMPLER_2D_RECT,
+	SAMPLER_2D_RECT_SHADOW = SAMPLER_2D_RECT_SHADOW,
+	SAMPLER_BUFFER = SAMPLER_BUFFER,
+	INT_SAMPLER_2D_RECT = INT_SAMPLER_2D_RECT,
+	INT_SAMPLER_BUFFER = INT_SAMPLER_BUFFER,
+	UNSIGNED_INT_SAMPLER_2D_RECT = UNSIGNED_INT_SAMPLER_2D_RECT,
+	UNSIGNED_INT_SAMPLER_BUFFER = UNSIGNED_INT_SAMPLER_BUFFER,
+	TEXTURE_BUFFER = TEXTURE_BUFFER,
+	MAX_TEXTURE_BUFFER_SIZE = MAX_TEXTURE_BUFFER_SIZE,
+	TEXTURE_BINDING_BUFFER = TEXTURE_BINDING_BUFFER,
+	TEXTURE_BUFFER_DATA_STORE_BINDING = TEXTURE_BUFFER_DATA_STORE_BINDING,
+	TEXTURE_RECTANGLE = TEXTURE_RECTANGLE,
+	TEXTURE_BINDING_RECTANGLE = TEXTURE_BINDING_RECTANGLE,
+	PROXY_TEXTURE_RECTANGLE = PROXY_TEXTURE_RECTANGLE,
+	MAX_RECTANGLE_TEXTURE_SIZE = MAX_RECTANGLE_TEXTURE_SIZE,
+	R8_SNORM = R8_SNORM,
+	RG8_SNORM = RG8_SNORM,
+	RGB8_SNORM = RGB8_SNORM,
+	RGBA8_SNORM = RGBA8_SNORM,
+	R16_SNORM = R16_SNORM,
+	RG16_SNORM = RG16_SNORM,
+	RGB16_SNORM = RGB16_SNORM,
+	RGBA16_SNORM = RGBA16_SNORM,
+	SIGNED_NORMALIZED = SIGNED_NORMALIZED,
+	PRIMITIVE_RESTART = PRIMITIVE_RESTART,
+	PRIMITIVE_RESTART_INDEX = PRIMITIVE_RESTART_INDEX,
+	COPY_READ_BUFFER = COPY_READ_BUFFER,
+	COPY_WRITE_BUFFER = COPY_WRITE_BUFFER,
+	UNIFORM_BUFFER = UNIFORM_BUFFER,
+	UNIFORM_BUFFER_BINDING = UNIFORM_BUFFER_BINDING,
+	UNIFORM_BUFFER_START = UNIFORM_BUFFER_START,
+	UNIFORM_BUFFER_SIZE = UNIFORM_BUFFER_SIZE,
+	MAX_VERTEX_UNIFORM_BLOCKS = MAX_VERTEX_UNIFORM_BLOCKS,
+	MAX_GEOMETRY_UNIFORM_BLOCKS = MAX_GEOMETRY_UNIFORM_BLOCKS,
+	MAX_FRAGMENT_UNIFORM_BLOCKS = MAX_FRAGMENT_UNIFORM_BLOCKS,
+	MAX_COMBINED_UNIFORM_BLOCKS = MAX_COMBINED_UNIFORM_BLOCKS,
+	MAX_UNIFORM_BUFFER_BINDINGS = MAX_UNIFORM_BUFFER_BINDINGS,
+	MAX_UNIFORM_BLOCK_SIZE = MAX_UNIFORM_BLOCK_SIZE,
+	MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS,
+	MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS = MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS,
+	MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS,
+	UNIFORM_BUFFER_OFFSET_ALIGNMENT = UNIFORM_BUFFER_OFFSET_ALIGNMENT,
+	ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH = ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
+	ACTIVE_UNIFORM_BLOCKS = ACTIVE_UNIFORM_BLOCKS,
+	UNIFORM_TYPE = UNIFORM_TYPE,
+	UNIFORM_SIZE = UNIFORM_SIZE,
+	UNIFORM_NAME_LENGTH = UNIFORM_NAME_LENGTH,
+	UNIFORM_BLOCK_INDEX = UNIFORM_BLOCK_INDEX,
+	UNIFORM_OFFSET = UNIFORM_OFFSET,
+	UNIFORM_ARRAY_STRIDE = UNIFORM_ARRAY_STRIDE,
+	UNIFORM_MATRIX_STRIDE = UNIFORM_MATRIX_STRIDE,
+	UNIFORM_IS_ROW_MAJOR = UNIFORM_IS_ROW_MAJOR,
+	UNIFORM_BLOCK_BINDING = UNIFORM_BLOCK_BINDING,
+	UNIFORM_BLOCK_DATA_SIZE = UNIFORM_BLOCK_DATA_SIZE,
+	UNIFORM_BLOCK_NAME_LENGTH = UNIFORM_BLOCK_NAME_LENGTH,
+	UNIFORM_BLOCK_ACTIVE_UNIFORMS = UNIFORM_BLOCK_ACTIVE_UNIFORMS,
+	UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
+	UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER,
+	UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER = UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER,
+	UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER,
+	INVALID_INDEX = INVALID_INDEX,
+	CONTEXT_CORE_PROFILE_BIT = CONTEXT_CORE_PROFILE_BIT,
+	CONTEXT_COMPATIBILITY_PROFILE_BIT = CONTEXT_COMPATIBILITY_PROFILE_BIT,
+	LINES_ADJACENCY = LINES_ADJACENCY,
+	LINE_STRIP_ADJACENCY = LINE_STRIP_ADJACENCY,
+	TRIANGLES_ADJACENCY = TRIANGLES_ADJACENCY,
+	TRIANGLE_STRIP_ADJACENCY = TRIANGLE_STRIP_ADJACENCY,
+	PROGRAM_POINT_SIZE = PROGRAM_POINT_SIZE,
+	MAX_GEOMETRY_TEXTURE_IMAGE_UNITS = MAX_GEOMETRY_TEXTURE_IMAGE_UNITS,
+	FRAMEBUFFER_ATTACHMENT_LAYERED = FRAMEBUFFER_ATTACHMENT_LAYERED,
+	FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS = FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS,
+	GEOMETRY_SHADER = GEOMETRY_SHADER,
+	GEOMETRY_VERTICES_OUT = GEOMETRY_VERTICES_OUT,
+	GEOMETRY_INPUT_TYPE = GEOMETRY_INPUT_TYPE,
+	GEOMETRY_OUTPUT_TYPE = GEOMETRY_OUTPUT_TYPE,
+	MAX_GEOMETRY_UNIFORM_COMPONENTS = MAX_GEOMETRY_UNIFORM_COMPONENTS,
+	MAX_GEOMETRY_OUTPUT_VERTICES = MAX_GEOMETRY_OUTPUT_VERTICES,
+	MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS,
+	MAX_VERTEX_OUTPUT_COMPONENTS = MAX_VERTEX_OUTPUT_COMPONENTS,
+	MAX_GEOMETRY_INPUT_COMPONENTS = MAX_GEOMETRY_INPUT_COMPONENTS,
+	MAX_GEOMETRY_OUTPUT_COMPONENTS = MAX_GEOMETRY_OUTPUT_COMPONENTS,
+	MAX_FRAGMENT_INPUT_COMPONENTS = MAX_FRAGMENT_INPUT_COMPONENTS,
+	CONTEXT_PROFILE_MASK = CONTEXT_PROFILE_MASK,
+	DEPTH_CLAMP = DEPTH_CLAMP,
+	QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION = QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION,
+	FIRST_VERTEX_CONVENTION = FIRST_VERTEX_CONVENTION,
+	LAST_VERTEX_CONVENTION = LAST_VERTEX_CONVENTION,
+	PROVOKING_VERTEX = PROVOKING_VERTEX,
+	TEXTURE_CUBE_MAP_SEAMLESS = TEXTURE_CUBE_MAP_SEAMLESS,
+	MAX_SERVER_WAIT_TIMEOUT = MAX_SERVER_WAIT_TIMEOUT,
+	OBJECT_TYPE = OBJECT_TYPE,
+	SYNC_CONDITION = SYNC_CONDITION,
+	SYNC_STATUS = SYNC_STATUS,
+	SYNC_FLAGS = SYNC_FLAGS,
+	SYNC_FENCE = SYNC_FENCE,
+	SYNC_GPU_COMMANDS_COMPLETE = SYNC_GPU_COMMANDS_COMPLETE,
+	UNSIGNALED = UNSIGNALED,
+	SIGNALED = SIGNALED,
+	ALREADY_SIGNALED = ALREADY_SIGNALED,
+	TIMEOUT_EXPIRED = TIMEOUT_EXPIRED,
+	CONDITION_SATISFIED = CONDITION_SATISFIED,
+	WAIT_FAILED = WAIT_FAILED,
+	TIMEOUT_IGNORED = TIMEOUT_IGNORED,
+	SYNC_FLUSH_COMMANDS_BIT = SYNC_FLUSH_COMMANDS_BIT,
+	SAMPLE_POSITION = SAMPLE_POSITION,
+	SAMPLE_MASK = SAMPLE_MASK,
+	SAMPLE_MASK_VALUE = SAMPLE_MASK_VALUE,
+	MAX_SAMPLE_MASK_WORDS = MAX_SAMPLE_MASK_WORDS,
+	TEXTURE_2D_MULTISAMPLE = TEXTURE_2D_MULTISAMPLE,
+	PROXY_TEXTURE_2D_MULTISAMPLE = PROXY_TEXTURE_2D_MULTISAMPLE,
+	TEXTURE_2D_MULTISAMPLE_ARRAY = TEXTURE_2D_MULTISAMPLE_ARRAY,
+	PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY = PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY,
+	TEXTURE_BINDING_2D_MULTISAMPLE = TEXTURE_BINDING_2D_MULTISAMPLE,
+	TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY = TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,
+	TEXTURE_SAMPLES = TEXTURE_SAMPLES,
+	TEXTURE_FIXED_SAMPLE_LOCATIONS = TEXTURE_FIXED_SAMPLE_LOCATIONS,
+	SAMPLER_2D_MULTISAMPLE = SAMPLER_2D_MULTISAMPLE,
+	INT_SAMPLER_2D_MULTISAMPLE = INT_SAMPLER_2D_MULTISAMPLE,
+	UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE,
+	SAMPLER_2D_MULTISAMPLE_ARRAY = SAMPLER_2D_MULTISAMPLE_ARRAY,
+	INT_SAMPLER_2D_MULTISAMPLE_ARRAY = INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+	UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY,
+	MAX_COLOR_TEXTURE_SAMPLES = MAX_COLOR_TEXTURE_SAMPLES,
+	MAX_DEPTH_TEXTURE_SAMPLES = MAX_DEPTH_TEXTURE_SAMPLES,
+	MAX_INTEGER_SAMPLES = MAX_INTEGER_SAMPLES,
+	VERTEX_ATTRIB_ARRAY_DIVISOR = VERTEX_ATTRIB_ARRAY_DIVISOR,
+	SRC1_COLOR = SRC1_COLOR,
+	ONE_MINUS_SRC1_COLOR = ONE_MINUS_SRC1_COLOR,
+	ONE_MINUS_SRC1_ALPHA = ONE_MINUS_SRC1_ALPHA,
+	MAX_DUAL_SOURCE_DRAW_BUFFERS = MAX_DUAL_SOURCE_DRAW_BUFFERS,
+	ANY_SAMPLES_PASSED = ANY_SAMPLES_PASSED,
+	SAMPLER_BINDING = SAMPLER_BINDING,
+	RGB10_A2UI = RGB10_A2UI,
+	TEXTURE_SWIZZLE_R = TEXTURE_SWIZZLE_R,
+	TEXTURE_SWIZZLE_G = TEXTURE_SWIZZLE_G,
+	TEXTURE_SWIZZLE_B = TEXTURE_SWIZZLE_B,
+	TEXTURE_SWIZZLE_A = TEXTURE_SWIZZLE_A,
+	TEXTURE_SWIZZLE_RGBA = TEXTURE_SWIZZLE_RGBA,
+	TIME_ELAPSED = TIME_ELAPSED,
+	TIMESTAMP = TIMESTAMP,
+	INT_2_10_10_10_REV = INT_2_10_10_10_REV,
+	SAMPLE_SHADING = SAMPLE_SHADING,
+	MIN_SAMPLE_SHADING_VALUE = MIN_SAMPLE_SHADING_VALUE,
+	MIN_PROGRAM_TEXTURE_GATHER_OFFSET = MIN_PROGRAM_TEXTURE_GATHER_OFFSET,
+	MAX_PROGRAM_TEXTURE_GATHER_OFFSET = MAX_PROGRAM_TEXTURE_GATHER_OFFSET,
+	TEXTURE_CUBE_MAP_ARRAY = TEXTURE_CUBE_MAP_ARRAY,
+	TEXTURE_BINDING_CUBE_MAP_ARRAY = TEXTURE_BINDING_CUBE_MAP_ARRAY,
+	PROXY_TEXTURE_CUBE_MAP_ARRAY = PROXY_TEXTURE_CUBE_MAP_ARRAY,
+	SAMPLER_CUBE_MAP_ARRAY = SAMPLER_CUBE_MAP_ARRAY,
+	SAMPLER_CUBE_MAP_ARRAY_SHADOW = SAMPLER_CUBE_MAP_ARRAY_SHADOW,
+	INT_SAMPLER_CUBE_MAP_ARRAY = INT_SAMPLER_CUBE_MAP_ARRAY,
+	UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY = UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY,
+	DRAW_INDIRECT_BUFFER = DRAW_INDIRECT_BUFFER,
+	DRAW_INDIRECT_BUFFER_BINDING = DRAW_INDIRECT_BUFFER_BINDING,
+	GEOMETRY_SHADER_INVOCATIONS = GEOMETRY_SHADER_INVOCATIONS,
+	MAX_GEOMETRY_SHADER_INVOCATIONS = MAX_GEOMETRY_SHADER_INVOCATIONS,
+	MIN_FRAGMENT_INTERPOLATION_OFFSET = MIN_FRAGMENT_INTERPOLATION_OFFSET,
+	MAX_FRAGMENT_INTERPOLATION_OFFSET = MAX_FRAGMENT_INTERPOLATION_OFFSET,
+	FRAGMENT_INTERPOLATION_OFFSET_BITS = FRAGMENT_INTERPOLATION_OFFSET_BITS,
+	MAX_VERTEX_STREAMS = MAX_VERTEX_STREAMS,
+	DOUBLE_VEC2 = DOUBLE_VEC2,
+	DOUBLE_VEC3 = DOUBLE_VEC3,
+	DOUBLE_VEC4 = DOUBLE_VEC4,
+	DOUBLE_MAT2 = DOUBLE_MAT2,
+	DOUBLE_MAT3 = DOUBLE_MAT3,
+	DOUBLE_MAT4 = DOUBLE_MAT4,
+	DOUBLE_MAT2x3 = DOUBLE_MAT2x3,
+	DOUBLE_MAT2x4 = DOUBLE_MAT2x4,
+	DOUBLE_MAT3x2 = DOUBLE_MAT3x2,
+	DOUBLE_MAT3x4 = DOUBLE_MAT3x4,
+	DOUBLE_MAT4x2 = DOUBLE_MAT4x2,
+	DOUBLE_MAT4x3 = DOUBLE_MAT4x3,
+	ACTIVE_SUBROUTINES = ACTIVE_SUBROUTINES,
+	ACTIVE_SUBROUTINE_UNIFORMS = ACTIVE_SUBROUTINE_UNIFORMS,
+	ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS = ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS,
+	ACTIVE_SUBROUTINE_MAX_LENGTH = ACTIVE_SUBROUTINE_MAX_LENGTH,
+	ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH = ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH,
+	MAX_SUBROUTINES = MAX_SUBROUTINES,
+	MAX_SUBROUTINE_UNIFORM_LOCATIONS = MAX_SUBROUTINE_UNIFORM_LOCATIONS,
+	NUM_COMPATIBLE_SUBROUTINES = NUM_COMPATIBLE_SUBROUTINES,
+	COMPATIBLE_SUBROUTINES = COMPATIBLE_SUBROUTINES,
+	PATCHES = PATCHES,
+	PATCH_VERTICES = PATCH_VERTICES,
+	PATCH_DEFAULT_INNER_LEVEL = PATCH_DEFAULT_INNER_LEVEL,
+	PATCH_DEFAULT_OUTER_LEVEL = PATCH_DEFAULT_OUTER_LEVEL,
+	TESS_CONTROL_OUTPUT_VERTICES = TESS_CONTROL_OUTPUT_VERTICES,
+	TESS_GEN_MODE = TESS_GEN_MODE,
+	TESS_GEN_SPACING = TESS_GEN_SPACING,
+	TESS_GEN_VERTEX_ORDER = TESS_GEN_VERTEX_ORDER,
+	TESS_GEN_POINT_MODE = TESS_GEN_POINT_MODE,
+	ISOLINES = ISOLINES,
+	FRACTIONAL_ODD = FRACTIONAL_ODD,
+	FRACTIONAL_EVEN = FRACTIONAL_EVEN,
+	MAX_PATCH_VERTICES = MAX_PATCH_VERTICES,
+	MAX_TESS_GEN_LEVEL = MAX_TESS_GEN_LEVEL,
+	MAX_TESS_CONTROL_UNIFORM_COMPONENTS = MAX_TESS_CONTROL_UNIFORM_COMPONENTS,
+	MAX_TESS_EVALUATION_UNIFORM_COMPONENTS = MAX_TESS_EVALUATION_UNIFORM_COMPONENTS,
+	MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS = MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS,
+	MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS = MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS,
+	MAX_TESS_CONTROL_OUTPUT_COMPONENTS = MAX_TESS_CONTROL_OUTPUT_COMPONENTS,
+	MAX_TESS_PATCH_COMPONENTS = MAX_TESS_PATCH_COMPONENTS,
+	MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS = MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS,
+	MAX_TESS_EVALUATION_OUTPUT_COMPONENTS = MAX_TESS_EVALUATION_OUTPUT_COMPONENTS,
+	MAX_TESS_CONTROL_UNIFORM_BLOCKS = MAX_TESS_CONTROL_UNIFORM_BLOCKS,
+	MAX_TESS_EVALUATION_UNIFORM_BLOCKS = MAX_TESS_EVALUATION_UNIFORM_BLOCKS,
+	MAX_TESS_CONTROL_INPUT_COMPONENTS = MAX_TESS_CONTROL_INPUT_COMPONENTS,
+	MAX_TESS_EVALUATION_INPUT_COMPONENTS = MAX_TESS_EVALUATION_INPUT_COMPONENTS,
+	MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS = MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS,
+	MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS = MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS,
+	UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER = UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER,
+	UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER = UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER,
+	TESS_EVALUATION_SHADER = TESS_EVALUATION_SHADER,
+	TESS_CONTROL_SHADER = TESS_CONTROL_SHADER,
+	TRANSFORM_FEEDBACK = TRANSFORM_FEEDBACK,
+	TRANSFORM_FEEDBACK_BUFFER_PAUSED = TRANSFORM_FEEDBACK_BUFFER_PAUSED,
+	TRANSFORM_FEEDBACK_BUFFER_ACTIVE = TRANSFORM_FEEDBACK_BUFFER_ACTIVE,
+	TRANSFORM_FEEDBACK_BINDING = TRANSFORM_FEEDBACK_BINDING,
+	MAX_TRANSFORM_FEEDBACK_BUFFERS = MAX_TRANSFORM_FEEDBACK_BUFFERS,
+	FIXED = FIXED,
+	IMPLEMENTATION_COLOR_READ_TYPE = IMPLEMENTATION_COLOR_READ_TYPE,
+	IMPLEMENTATION_COLOR_READ_FORMAT = IMPLEMENTATION_COLOR_READ_FORMAT,
+	LOW_FLOAT = LOW_FLOAT,
+	MEDIUM_FLOAT = MEDIUM_FLOAT,
+	HIGH_FLOAT = HIGH_FLOAT,
+	LOW_INT = LOW_INT,
+	MEDIUM_INT = MEDIUM_INT,
+	HIGH_INT = HIGH_INT,
+	SHADER_COMPILER = SHADER_COMPILER,
+	SHADER_BINARY_FORMATS = SHADER_BINARY_FORMATS,
+	NUM_SHADER_BINARY_FORMATS = NUM_SHADER_BINARY_FORMATS,
+	MAX_VERTEX_UNIFORM_VECTORS = MAX_VERTEX_UNIFORM_VECTORS,
+	MAX_VARYING_VECTORS = MAX_VARYING_VECTORS,
+	MAX_FRAGMENT_UNIFORM_VECTORS = MAX_FRAGMENT_UNIFORM_VECTORS,
+	RGB565 = RGB565,
+	PROGRAM_BINARY_RETRIEVABLE_HINT = PROGRAM_BINARY_RETRIEVABLE_HINT,
+	PROGRAM_BINARY_LENGTH = PROGRAM_BINARY_LENGTH,
+	NUM_PROGRAM_BINARY_FORMATS = NUM_PROGRAM_BINARY_FORMATS,
+	PROGRAM_BINARY_FORMATS = PROGRAM_BINARY_FORMATS,
+	VERTEX_SHADER_BIT = VERTEX_SHADER_BIT,
+	FRAGMENT_SHADER_BIT = FRAGMENT_SHADER_BIT,
+	GEOMETRY_SHADER_BIT = GEOMETRY_SHADER_BIT,
+	TESS_CONTROL_SHADER_BIT = TESS_CONTROL_SHADER_BIT,
+	TESS_EVALUATION_SHADER_BIT = TESS_EVALUATION_SHADER_BIT,
+	ALL_SHADER_BITS = ALL_SHADER_BITS,
+	PROGRAM_SEPARABLE = PROGRAM_SEPARABLE,
+	ACTIVE_PROGRAM = ACTIVE_PROGRAM,
+	PROGRAM_PIPELINE_BINDING = PROGRAM_PIPELINE_BINDING,
+	MAX_VIEWPORTS = MAX_VIEWPORTS,
+	VIEWPORT_SUBPIXEL_BITS = VIEWPORT_SUBPIXEL_BITS,
+	VIEWPORT_BOUNDS_RANGE = VIEWPORT_BOUNDS_RANGE,
+	LAYER_PROVOKING_VERTEX = LAYER_PROVOKING_VERTEX,
+	VIEWPORT_INDEX_PROVOKING_VERTEX = VIEWPORT_INDEX_PROVOKING_VERTEX,
+	UNDEFINED_VERTEX = UNDEFINED_VERTEX,
+	COPY_READ_BUFFER_BINDING = COPY_READ_BUFFER_BINDING,
+	COPY_WRITE_BUFFER_BINDING = COPY_WRITE_BUFFER_BINDING,
+	TRANSFORM_FEEDBACK_ACTIVE = TRANSFORM_FEEDBACK_ACTIVE,
+	TRANSFORM_FEEDBACK_PAUSED = TRANSFORM_FEEDBACK_PAUSED,
+	UNPACK_COMPRESSED_BLOCK_WIDTH = UNPACK_COMPRESSED_BLOCK_WIDTH,
+	UNPACK_COMPRESSED_BLOCK_HEIGHT = UNPACK_COMPRESSED_BLOCK_HEIGHT,
+	UNPACK_COMPRESSED_BLOCK_DEPTH = UNPACK_COMPRESSED_BLOCK_DEPTH,
+	UNPACK_COMPRESSED_BLOCK_SIZE = UNPACK_COMPRESSED_BLOCK_SIZE,
+	PACK_COMPRESSED_BLOCK_WIDTH = PACK_COMPRESSED_BLOCK_WIDTH,
+	PACK_COMPRESSED_BLOCK_HEIGHT = PACK_COMPRESSED_BLOCK_HEIGHT,
+	PACK_COMPRESSED_BLOCK_DEPTH = PACK_COMPRESSED_BLOCK_DEPTH,
+	PACK_COMPRESSED_BLOCK_SIZE = PACK_COMPRESSED_BLOCK_SIZE,
+	NUM_SAMPLE_COUNTS = NUM_SAMPLE_COUNTS,
+	MIN_MAP_BUFFER_ALIGNMENT = MIN_MAP_BUFFER_ALIGNMENT,
+	ATOMIC_COUNTER_BUFFER = ATOMIC_COUNTER_BUFFER,
+	ATOMIC_COUNTER_BUFFER_BINDING = ATOMIC_COUNTER_BUFFER_BINDING,
+	ATOMIC_COUNTER_BUFFER_START = ATOMIC_COUNTER_BUFFER_START,
+	ATOMIC_COUNTER_BUFFER_SIZE = ATOMIC_COUNTER_BUFFER_SIZE,
+	ATOMIC_COUNTER_BUFFER_DATA_SIZE = ATOMIC_COUNTER_BUFFER_DATA_SIZE,
+	ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS = ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS,
+	ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES = ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES,
+	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER,
+	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER,
+	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER,
+	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER,
+	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER,
+	MAX_VERTEX_ATOMIC_COUNTER_BUFFERS = MAX_VERTEX_ATOMIC_COUNTER_BUFFERS,
+	MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS = MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS,
+	MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS = MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS,
+	MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS = MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS,
+	MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS = MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS,
+	MAX_COMBINED_ATOMIC_COUNTER_BUFFERS = MAX_COMBINED_ATOMIC_COUNTER_BUFFERS,
+	MAX_VERTEX_ATOMIC_COUNTERS = MAX_VERTEX_ATOMIC_COUNTERS,
+	MAX_TESS_CONTROL_ATOMIC_COUNTERS = MAX_TESS_CONTROL_ATOMIC_COUNTERS,
+	MAX_TESS_EVALUATION_ATOMIC_COUNTERS = MAX_TESS_EVALUATION_ATOMIC_COUNTERS,
+	MAX_GEOMETRY_ATOMIC_COUNTERS = MAX_GEOMETRY_ATOMIC_COUNTERS,
+	MAX_FRAGMENT_ATOMIC_COUNTERS = MAX_FRAGMENT_ATOMIC_COUNTERS,
+	MAX_COMBINED_ATOMIC_COUNTERS = MAX_COMBINED_ATOMIC_COUNTERS,
+	MAX_ATOMIC_COUNTER_BUFFER_SIZE = MAX_ATOMIC_COUNTER_BUFFER_SIZE,
+	MAX_ATOMIC_COUNTER_BUFFER_BINDINGS = MAX_ATOMIC_COUNTER_BUFFER_BINDINGS,
+	ACTIVE_ATOMIC_COUNTER_BUFFERS = ACTIVE_ATOMIC_COUNTER_BUFFERS,
+	UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX = UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX,
+	UNSIGNED_INT_ATOMIC_COUNTER = UNSIGNED_INT_ATOMIC_COUNTER,
+	VERTEX_ATTRIB_ARRAY_BARRIER_BIT = VERTEX_ATTRIB_ARRAY_BARRIER_BIT,
+	ELEMENT_ARRAY_BARRIER_BIT = ELEMENT_ARRAY_BARRIER_BIT,
+	UNIFORM_BARRIER_BIT = UNIFORM_BARRIER_BIT,
+	TEXTURE_FETCH_BARRIER_BIT = TEXTURE_FETCH_BARRIER_BIT,
+	SHADER_IMAGE_ACCESS_BARRIER_BIT = SHADER_IMAGE_ACCESS_BARRIER_BIT,
+	COMMAND_BARRIER_BIT = COMMAND_BARRIER_BIT,
+	PIXEL_BUFFER_BARRIER_BIT = PIXEL_BUFFER_BARRIER_BIT,
+	TEXTURE_UPDATE_BARRIER_BIT = TEXTURE_UPDATE_BARRIER_BIT,
+	BUFFER_UPDATE_BARRIER_BIT = BUFFER_UPDATE_BARRIER_BIT,
+	FRAMEBUFFER_BARRIER_BIT = FRAMEBUFFER_BARRIER_BIT,
+	TRANSFORM_FEEDBACK_BARRIER_BIT = TRANSFORM_FEEDBACK_BARRIER_BIT,
+	ATOMIC_COUNTER_BARRIER_BIT = ATOMIC_COUNTER_BARRIER_BIT,
+	ALL_BARRIER_BITS = ALL_BARRIER_BITS,
+	MAX_IMAGE_UNITS = MAX_IMAGE_UNITS,
+	MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS = MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS,
+	IMAGE_BINDING_NAME = IMAGE_BINDING_NAME,
+	IMAGE_BINDING_LEVEL = IMAGE_BINDING_LEVEL,
+	IMAGE_BINDING_LAYERED = IMAGE_BINDING_LAYERED,
+	IMAGE_BINDING_LAYER = IMAGE_BINDING_LAYER,
+	IMAGE_BINDING_ACCESS = IMAGE_BINDING_ACCESS,
+	IMAGE_1D = IMAGE_1D,
+	IMAGE_2D = IMAGE_2D,
+	IMAGE_3D = IMAGE_3D,
+	IMAGE_2D_RECT = IMAGE_2D_RECT,
+	IMAGE_CUBE = IMAGE_CUBE,
+	IMAGE_BUFFER = IMAGE_BUFFER,
+	IMAGE_1D_ARRAY = IMAGE_1D_ARRAY,
+	IMAGE_2D_ARRAY = IMAGE_2D_ARRAY,
+	IMAGE_CUBE_MAP_ARRAY = IMAGE_CUBE_MAP_ARRAY,
+	IMAGE_2D_MULTISAMPLE = IMAGE_2D_MULTISAMPLE,
+	IMAGE_2D_MULTISAMPLE_ARRAY = IMAGE_2D_MULTISAMPLE_ARRAY,
+	INT_IMAGE_1D = INT_IMAGE_1D,
+	INT_IMAGE_2D = INT_IMAGE_2D,
+	INT_IMAGE_3D = INT_IMAGE_3D,
+	INT_IMAGE_2D_RECT = INT_IMAGE_2D_RECT,
+	INT_IMAGE_CUBE = INT_IMAGE_CUBE,
+	INT_IMAGE_BUFFER = INT_IMAGE_BUFFER,
+	INT_IMAGE_1D_ARRAY = INT_IMAGE_1D_ARRAY,
+	INT_IMAGE_2D_ARRAY = INT_IMAGE_2D_ARRAY,
+	INT_IMAGE_CUBE_MAP_ARRAY = INT_IMAGE_CUBE_MAP_ARRAY,
+	INT_IMAGE_2D_MULTISAMPLE = INT_IMAGE_2D_MULTISAMPLE,
+	INT_IMAGE_2D_MULTISAMPLE_ARRAY = INT_IMAGE_2D_MULTISAMPLE_ARRAY,
+	UNSIGNED_INT_IMAGE_1D = UNSIGNED_INT_IMAGE_1D,
+	UNSIGNED_INT_IMAGE_2D = UNSIGNED_INT_IMAGE_2D,
+	UNSIGNED_INT_IMAGE_3D = UNSIGNED_INT_IMAGE_3D,
+	UNSIGNED_INT_IMAGE_2D_RECT = UNSIGNED_INT_IMAGE_2D_RECT,
+	UNSIGNED_INT_IMAGE_CUBE = UNSIGNED_INT_IMAGE_CUBE,
+	UNSIGNED_INT_IMAGE_BUFFER = UNSIGNED_INT_IMAGE_BUFFER,
+	UNSIGNED_INT_IMAGE_1D_ARRAY = UNSIGNED_INT_IMAGE_1D_ARRAY,
+	UNSIGNED_INT_IMAGE_2D_ARRAY = UNSIGNED_INT_IMAGE_2D_ARRAY,
+	UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY = UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY,
+	UNSIGNED_INT_IMAGE_2D_MULTISAMPLE = UNSIGNED_INT_IMAGE_2D_MULTISAMPLE,
+	UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY = UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY,
+	MAX_IMAGE_SAMPLES = MAX_IMAGE_SAMPLES,
+	IMAGE_BINDING_FORMAT = IMAGE_BINDING_FORMAT,
+	IMAGE_FORMAT_COMPATIBILITY_TYPE = IMAGE_FORMAT_COMPATIBILITY_TYPE,
+	IMAGE_FORMAT_COMPATIBILITY_BY_SIZE = IMAGE_FORMAT_COMPATIBILITY_BY_SIZE,
+	IMAGE_FORMAT_COMPATIBILITY_BY_CLASS = IMAGE_FORMAT_COMPATIBILITY_BY_CLASS,
+	MAX_VERTEX_IMAGE_UNIFORMS = MAX_VERTEX_IMAGE_UNIFORMS,
+	MAX_TESS_CONTROL_IMAGE_UNIFORMS = MAX_TESS_CONTROL_IMAGE_UNIFORMS,
+	MAX_TESS_EVALUATION_IMAGE_UNIFORMS = MAX_TESS_EVALUATION_IMAGE_UNIFORMS,
+	MAX_GEOMETRY_IMAGE_UNIFORMS = MAX_GEOMETRY_IMAGE_UNIFORMS,
+	MAX_FRAGMENT_IMAGE_UNIFORMS = MAX_FRAGMENT_IMAGE_UNIFORMS,
+	MAX_COMBINED_IMAGE_UNIFORMS = MAX_COMBINED_IMAGE_UNIFORMS,
+	COMPRESSED_RGBA_BPTC_UNORM = COMPRESSED_RGBA_BPTC_UNORM,
+	COMPRESSED_SRGB_ALPHA_BPTC_UNORM = COMPRESSED_SRGB_ALPHA_BPTC_UNORM,
+	COMPRESSED_RGB_BPTC_SIGNED_FLOAT = COMPRESSED_RGB_BPTC_SIGNED_FLOAT,
+	COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT,
+	TEXTURE_IMMUTABLE_FORMAT = TEXTURE_IMMUTABLE_FORMAT,
+	NUM_SHADING_LANGUAGE_VERSIONS = NUM_SHADING_LANGUAGE_VERSIONS,
+	VERTEX_ATTRIB_ARRAY_LONG = VERTEX_ATTRIB_ARRAY_LONG,
+	COMPRESSED_RGB8_ETC2 = COMPRESSED_RGB8_ETC2,
+	COMPRESSED_SRGB8_ETC2 = COMPRESSED_SRGB8_ETC2,
+	COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+	COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
+	COMPRESSED_RGBA8_ETC2_EAC = COMPRESSED_RGBA8_ETC2_EAC,
+	COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
+	COMPRESSED_R11_EAC = COMPRESSED_R11_EAC,
+	COMPRESSED_SIGNED_R11_EAC = COMPRESSED_SIGNED_R11_EAC,
+	COMPRESSED_RG11_EAC = COMPRESSED_RG11_EAC,
+	COMPRESSED_SIGNED_RG11_EAC = COMPRESSED_SIGNED_RG11_EAC,
+	PRIMITIVE_RESTART_FIXED_INDEX = PRIMITIVE_RESTART_FIXED_INDEX,
+	ANY_SAMPLES_PASSED_CONSERVATIVE = ANY_SAMPLES_PASSED_CONSERVATIVE,
+	MAX_ELEMENT_INDEX = MAX_ELEMENT_INDEX,
+	COMPUTE_SHADER = COMPUTE_SHADER,
+	MAX_COMPUTE_UNIFORM_BLOCKS = MAX_COMPUTE_UNIFORM_BLOCKS,
+	MAX_COMPUTE_TEXTURE_IMAGE_UNITS = MAX_COMPUTE_TEXTURE_IMAGE_UNITS,
+	MAX_COMPUTE_IMAGE_UNIFORMS = MAX_COMPUTE_IMAGE_UNIFORMS,
+	MAX_COMPUTE_SHARED_MEMORY_SIZE = MAX_COMPUTE_SHARED_MEMORY_SIZE,
+	MAX_COMPUTE_UNIFORM_COMPONENTS = MAX_COMPUTE_UNIFORM_COMPONENTS,
+	MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS = MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS,
+	MAX_COMPUTE_ATOMIC_COUNTERS = MAX_COMPUTE_ATOMIC_COUNTERS,
+	MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS = MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS,
+	MAX_COMPUTE_WORK_GROUP_INVOCATIONS = MAX_COMPUTE_WORK_GROUP_INVOCATIONS,
+	MAX_COMPUTE_WORK_GROUP_COUNT = MAX_COMPUTE_WORK_GROUP_COUNT,
+	MAX_COMPUTE_WORK_GROUP_SIZE = MAX_COMPUTE_WORK_GROUP_SIZE,
+	COMPUTE_WORK_GROUP_SIZE = COMPUTE_WORK_GROUP_SIZE,
+	UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER = UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER,
+	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER,
+	DISPATCH_INDIRECT_BUFFER = DISPATCH_INDIRECT_BUFFER,
+	DISPATCH_INDIRECT_BUFFER_BINDING = DISPATCH_INDIRECT_BUFFER_BINDING,
+	COMPUTE_SHADER_BIT = COMPUTE_SHADER_BIT,
+	DEBUG_OUTPUT_SYNCHRONOUS = DEBUG_OUTPUT_SYNCHRONOUS,
+	DEBUG_NEXT_LOGGED_MESSAGE_LENGTH = DEBUG_NEXT_LOGGED_MESSAGE_LENGTH,
+	DEBUG_CALLBACK_FUNCTION = DEBUG_CALLBACK_FUNCTION,
+	DEBUG_CALLBACK_USER_PARAM = DEBUG_CALLBACK_USER_PARAM,
+	DEBUG_SOURCE_API = DEBUG_SOURCE_API,
+	DEBUG_SOURCE_WINDOW_SYSTEM = DEBUG_SOURCE_WINDOW_SYSTEM,
+	DEBUG_SOURCE_SHADER_COMPILER = DEBUG_SOURCE_SHADER_COMPILER,
+	DEBUG_SOURCE_THIRD_PARTY = DEBUG_SOURCE_THIRD_PARTY,
+	DEBUG_SOURCE_APPLICATION = DEBUG_SOURCE_APPLICATION,
+	DEBUG_SOURCE_OTHER = DEBUG_SOURCE_OTHER,
+	DEBUG_TYPE_ERROR = DEBUG_TYPE_ERROR,
+	DEBUG_TYPE_DEPRECATED_BEHAVIOR = DEBUG_TYPE_DEPRECATED_BEHAVIOR,
+	DEBUG_TYPE_UNDEFINED_BEHAVIOR = DEBUG_TYPE_UNDEFINED_BEHAVIOR,
+	DEBUG_TYPE_PORTABILITY = DEBUG_TYPE_PORTABILITY,
+	DEBUG_TYPE_PERFORMANCE = DEBUG_TYPE_PERFORMANCE,
+	DEBUG_TYPE_OTHER = DEBUG_TYPE_OTHER,
+	MAX_DEBUG_MESSAGE_LENGTH = MAX_DEBUG_MESSAGE_LENGTH,
+	MAX_DEBUG_LOGGED_MESSAGES = MAX_DEBUG_LOGGED_MESSAGES,
+	DEBUG_LOGGED_MESSAGES = DEBUG_LOGGED_MESSAGES,
+	DEBUG_SEVERITY_HIGH = DEBUG_SEVERITY_HIGH,
+	DEBUG_SEVERITY_MEDIUM = DEBUG_SEVERITY_MEDIUM,
+	DEBUG_SEVERITY_LOW = DEBUG_SEVERITY_LOW,
+	DEBUG_TYPE_MARKER = DEBUG_TYPE_MARKER,
+	DEBUG_TYPE_PUSH_GROUP = DEBUG_TYPE_PUSH_GROUP,
+	DEBUG_TYPE_POP_GROUP = DEBUG_TYPE_POP_GROUP,
+	DEBUG_SEVERITY_NOTIFICATION = DEBUG_SEVERITY_NOTIFICATION,
+	MAX_DEBUG_GROUP_STACK_DEPTH = MAX_DEBUG_GROUP_STACK_DEPTH,
+	DEBUG_GROUP_STACK_DEPTH = DEBUG_GROUP_STACK_DEPTH,
+	BUFFER = BUFFER,
+	SHADER = SHADER,
+	PROGRAM = PROGRAM,
+	QUERY = QUERY,
+	PROGRAM_PIPELINE = PROGRAM_PIPELINE,
+	SAMPLER = SAMPLER,
+	MAX_LABEL_LENGTH = MAX_LABEL_LENGTH,
+	DEBUG_OUTPUT = DEBUG_OUTPUT,
+	CONTEXT_FLAG_DEBUG_BIT = CONTEXT_FLAG_DEBUG_BIT,
+	MAX_UNIFORM_LOCATIONS = MAX_UNIFORM_LOCATIONS,
+	FRAMEBUFFER_DEFAULT_WIDTH = FRAMEBUFFER_DEFAULT_WIDTH,
+	FRAMEBUFFER_DEFAULT_HEIGHT = FRAMEBUFFER_DEFAULT_HEIGHT,
+	FRAMEBUFFER_DEFAULT_LAYERS = FRAMEBUFFER_DEFAULT_LAYERS,
+	FRAMEBUFFER_DEFAULT_SAMPLES = FRAMEBUFFER_DEFAULT_SAMPLES,
+	FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS,
+	MAX_FRAMEBUFFER_WIDTH = MAX_FRAMEBUFFER_WIDTH,
+	MAX_FRAMEBUFFER_HEIGHT = MAX_FRAMEBUFFER_HEIGHT,
+	MAX_FRAMEBUFFER_LAYERS = MAX_FRAMEBUFFER_LAYERS,
+	MAX_FRAMEBUFFER_SAMPLES = MAX_FRAMEBUFFER_SAMPLES,
+	INTERNALFORMAT_SUPPORTED = INTERNALFORMAT_SUPPORTED,
+	INTERNALFORMAT_PREFERRED = INTERNALFORMAT_PREFERRED,
+	INTERNALFORMAT_RED_SIZE = INTERNALFORMAT_RED_SIZE,
+	INTERNALFORMAT_GREEN_SIZE = INTERNALFORMAT_GREEN_SIZE,
+	INTERNALFORMAT_BLUE_SIZE = INTERNALFORMAT_BLUE_SIZE,
+	INTERNALFORMAT_ALPHA_SIZE = INTERNALFORMAT_ALPHA_SIZE,
+	INTERNALFORMAT_DEPTH_SIZE = INTERNALFORMAT_DEPTH_SIZE,
+	INTERNALFORMAT_STENCIL_SIZE = INTERNALFORMAT_STENCIL_SIZE,
+	INTERNALFORMAT_SHARED_SIZE = INTERNALFORMAT_SHARED_SIZE,
+	INTERNALFORMAT_RED_TYPE = INTERNALFORMAT_RED_TYPE,
+	INTERNALFORMAT_GREEN_TYPE = INTERNALFORMAT_GREEN_TYPE,
+	INTERNALFORMAT_BLUE_TYPE = INTERNALFORMAT_BLUE_TYPE,
+	INTERNALFORMAT_ALPHA_TYPE = INTERNALFORMAT_ALPHA_TYPE,
+	INTERNALFORMAT_DEPTH_TYPE = INTERNALFORMAT_DEPTH_TYPE,
+	INTERNALFORMAT_STENCIL_TYPE = INTERNALFORMAT_STENCIL_TYPE,
+	MAX_WIDTH = MAX_WIDTH,
+	MAX_HEIGHT = MAX_HEIGHT,
+	MAX_DEPTH = MAX_DEPTH,
+	MAX_LAYERS = MAX_LAYERS,
+	MAX_COMBINED_DIMENSIONS = MAX_COMBINED_DIMENSIONS,
+	COLOR_COMPONENTS = COLOR_COMPONENTS,
+	DEPTH_COMPONENTS = DEPTH_COMPONENTS,
+	STENCIL_COMPONENTS = STENCIL_COMPONENTS,
+	COLOR_RENDERABLE = COLOR_RENDERABLE,
+	DEPTH_RENDERABLE = DEPTH_RENDERABLE,
+	STENCIL_RENDERABLE = STENCIL_RENDERABLE,
+	FRAMEBUFFER_RENDERABLE = FRAMEBUFFER_RENDERABLE,
+	FRAMEBUFFER_RENDERABLE_LAYERED = FRAMEBUFFER_RENDERABLE_LAYERED,
+	FRAMEBUFFER_BLEND = FRAMEBUFFER_BLEND,
+	READ_PIXELS = READ_PIXELS,
+	READ_PIXELS_FORMAT = READ_PIXELS_FORMAT,
+	READ_PIXELS_TYPE = READ_PIXELS_TYPE,
+	TEXTURE_IMAGE_FORMAT = TEXTURE_IMAGE_FORMAT,
+	TEXTURE_IMAGE_TYPE = TEXTURE_IMAGE_TYPE,
+	GET_TEXTURE_IMAGE_FORMAT = GET_TEXTURE_IMAGE_FORMAT,
+	GET_TEXTURE_IMAGE_TYPE = GET_TEXTURE_IMAGE_TYPE,
+	MIPMAP = MIPMAP,
+	MANUAL_GENERATE_MIPMAP = MANUAL_GENERATE_MIPMAP,
+	AUTO_GENERATE_MIPMAP = AUTO_GENERATE_MIPMAP,
+	COLOR_ENCODING = COLOR_ENCODING,
+	SRGB_READ = SRGB_READ,
+	SRGB_WRITE = SRGB_WRITE,
+	FILTER = FILTER,
+	VERTEX_TEXTURE = VERTEX_TEXTURE,
+	TESS_CONTROL_TEXTURE = TESS_CONTROL_TEXTURE,
+	TESS_EVALUATION_TEXTURE = TESS_EVALUATION_TEXTURE,
+	GEOMETRY_TEXTURE = GEOMETRY_TEXTURE,
+	FRAGMENT_TEXTURE = FRAGMENT_TEXTURE,
+	COMPUTE_TEXTURE = COMPUTE_TEXTURE,
+	TEXTURE_SHADOW = TEXTURE_SHADOW,
+	TEXTURE_GATHER = TEXTURE_GATHER,
+	TEXTURE_GATHER_SHADOW = TEXTURE_GATHER_SHADOW,
+	SHADER_IMAGE_LOAD = SHADER_IMAGE_LOAD,
+	SHADER_IMAGE_STORE = SHADER_IMAGE_STORE,
+	SHADER_IMAGE_ATOMIC = SHADER_IMAGE_ATOMIC,
+	IMAGE_TEXEL_SIZE = IMAGE_TEXEL_SIZE,
+	IMAGE_COMPATIBILITY_CLASS = IMAGE_COMPATIBILITY_CLASS,
+	IMAGE_PIXEL_FORMAT = IMAGE_PIXEL_FORMAT,
+	IMAGE_PIXEL_TYPE = IMAGE_PIXEL_TYPE,
+	SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST = SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST,
+	SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST = SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST,
+	SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE = SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE,
+	SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE = SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE,
+	TEXTURE_COMPRESSED_BLOCK_WIDTH = TEXTURE_COMPRESSED_BLOCK_WIDTH,
+	TEXTURE_COMPRESSED_BLOCK_HEIGHT = TEXTURE_COMPRESSED_BLOCK_HEIGHT,
+	TEXTURE_COMPRESSED_BLOCK_SIZE = TEXTURE_COMPRESSED_BLOCK_SIZE,
+	CLEAR_BUFFER = CLEAR_BUFFER,
+	TEXTURE_VIEW = TEXTURE_VIEW,
+	VIEW_COMPATIBILITY_CLASS = VIEW_COMPATIBILITY_CLASS,
+	FULL_SUPPORT = FULL_SUPPORT,
+	CAVEAT_SUPPORT = CAVEAT_SUPPORT,
+	IMAGE_CLASS_4_X_32 = IMAGE_CLASS_4_X_32,
+	IMAGE_CLASS_2_X_32 = IMAGE_CLASS_2_X_32,
+	IMAGE_CLASS_1_X_32 = IMAGE_CLASS_1_X_32,
+	IMAGE_CLASS_4_X_16 = IMAGE_CLASS_4_X_16,
+	IMAGE_CLASS_2_X_16 = IMAGE_CLASS_2_X_16,
+	IMAGE_CLASS_1_X_16 = IMAGE_CLASS_1_X_16,
+	IMAGE_CLASS_4_X_8 = IMAGE_CLASS_4_X_8,
+	IMAGE_CLASS_2_X_8 = IMAGE_CLASS_2_X_8,
+	IMAGE_CLASS_1_X_8 = IMAGE_CLASS_1_X_8,
+	IMAGE_CLASS_11_11_10 = IMAGE_CLASS_11_11_10,
+	IMAGE_CLASS_10_10_10_2 = IMAGE_CLASS_10_10_10_2,
+	VIEW_CLASS_128_BITS = VIEW_CLASS_128_BITS,
+	VIEW_CLASS_96_BITS = VIEW_CLASS_96_BITS,
+	VIEW_CLASS_64_BITS = VIEW_CLASS_64_BITS,
+	VIEW_CLASS_48_BITS = VIEW_CLASS_48_BITS,
+	VIEW_CLASS_32_BITS = VIEW_CLASS_32_BITS,
+	VIEW_CLASS_24_BITS = VIEW_CLASS_24_BITS,
+	VIEW_CLASS_16_BITS = VIEW_CLASS_16_BITS,
+	VIEW_CLASS_8_BITS = VIEW_CLASS_8_BITS,
+	VIEW_CLASS_S3TC_DXT1_RGB = VIEW_CLASS_S3TC_DXT1_RGB,
+	VIEW_CLASS_S3TC_DXT1_RGBA = VIEW_CLASS_S3TC_DXT1_RGBA,
+	VIEW_CLASS_S3TC_DXT3_RGBA = VIEW_CLASS_S3TC_DXT3_RGBA,
+	VIEW_CLASS_S3TC_DXT5_RGBA = VIEW_CLASS_S3TC_DXT5_RGBA,
+	VIEW_CLASS_RGTC1_RED = VIEW_CLASS_RGTC1_RED,
+	VIEW_CLASS_RGTC2_RG = VIEW_CLASS_RGTC2_RG,
+	VIEW_CLASS_BPTC_UNORM = VIEW_CLASS_BPTC_UNORM,
+	VIEW_CLASS_BPTC_FLOAT = VIEW_CLASS_BPTC_FLOAT,
+	UNIFORM = UNIFORM,
+	UNIFORM_BLOCK = UNIFORM_BLOCK,
+	PROGRAM_INPUT = PROGRAM_INPUT,
+	PROGRAM_OUTPUT = PROGRAM_OUTPUT,
+	BUFFER_VARIABLE = BUFFER_VARIABLE,
+	SHADER_STORAGE_BLOCK = SHADER_STORAGE_BLOCK,
+	VERTEX_SUBROUTINE = VERTEX_SUBROUTINE,
+	TESS_CONTROL_SUBROUTINE = TESS_CONTROL_SUBROUTINE,
+	TESS_EVALUATION_SUBROUTINE = TESS_EVALUATION_SUBROUTINE,
+	GEOMETRY_SUBROUTINE = GEOMETRY_SUBROUTINE,
+	FRAGMENT_SUBROUTINE = FRAGMENT_SUBROUTINE,
+	COMPUTE_SUBROUTINE = COMPUTE_SUBROUTINE,
+	VERTEX_SUBROUTINE_UNIFORM = VERTEX_SUBROUTINE_UNIFORM,
+	TESS_CONTROL_SUBROUTINE_UNIFORM = TESS_CONTROL_SUBROUTINE_UNIFORM,
+	TESS_EVALUATION_SUBROUTINE_UNIFORM = TESS_EVALUATION_SUBROUTINE_UNIFORM,
+	GEOMETRY_SUBROUTINE_UNIFORM = GEOMETRY_SUBROUTINE_UNIFORM,
+	FRAGMENT_SUBROUTINE_UNIFORM = FRAGMENT_SUBROUTINE_UNIFORM,
+	COMPUTE_SUBROUTINE_UNIFORM = COMPUTE_SUBROUTINE_UNIFORM,
+	TRANSFORM_FEEDBACK_VARYING = TRANSFORM_FEEDBACK_VARYING,
+	ACTIVE_RESOURCES = ACTIVE_RESOURCES,
+	MAX_NAME_LENGTH = MAX_NAME_LENGTH,
+	MAX_NUM_ACTIVE_VARIABLES = MAX_NUM_ACTIVE_VARIABLES,
+	MAX_NUM_COMPATIBLE_SUBROUTINES = MAX_NUM_COMPATIBLE_SUBROUTINES,
+	NAME_LENGTH = NAME_LENGTH,
+	TYPE = TYPE,
+	ARRAY_SIZE = ARRAY_SIZE,
+	OFFSET = OFFSET,
+	BLOCK_INDEX = BLOCK_INDEX,
+	ARRAY_STRIDE = ARRAY_STRIDE,
+	MATRIX_STRIDE = MATRIX_STRIDE,
+	IS_ROW_MAJOR = IS_ROW_MAJOR,
+	ATOMIC_COUNTER_BUFFER_INDEX = ATOMIC_COUNTER_BUFFER_INDEX,
+	BUFFER_BINDING = BUFFER_BINDING,
+	BUFFER_DATA_SIZE = BUFFER_DATA_SIZE,
+	NUM_ACTIVE_VARIABLES = NUM_ACTIVE_VARIABLES,
+	ACTIVE_VARIABLES = ACTIVE_VARIABLES,
+	REFERENCED_BY_VERTEX_SHADER = REFERENCED_BY_VERTEX_SHADER,
+	REFERENCED_BY_TESS_CONTROL_SHADER = REFERENCED_BY_TESS_CONTROL_SHADER,
+	REFERENCED_BY_TESS_EVALUATION_SHADER = REFERENCED_BY_TESS_EVALUATION_SHADER,
+	REFERENCED_BY_GEOMETRY_SHADER = REFERENCED_BY_GEOMETRY_SHADER,
+	REFERENCED_BY_FRAGMENT_SHADER = REFERENCED_BY_FRAGMENT_SHADER,
+	REFERENCED_BY_COMPUTE_SHADER = REFERENCED_BY_COMPUTE_SHADER,
+	TOP_LEVEL_ARRAY_SIZE = TOP_LEVEL_ARRAY_SIZE,
+	TOP_LEVEL_ARRAY_STRIDE = TOP_LEVEL_ARRAY_STRIDE,
+	LOCATION = LOCATION,
+	LOCATION_INDEX = LOCATION_INDEX,
+	IS_PER_PATCH = IS_PER_PATCH,
+	SHADER_STORAGE_BUFFER = SHADER_STORAGE_BUFFER,
+	SHADER_STORAGE_BUFFER_BINDING = SHADER_STORAGE_BUFFER_BINDING,
+	SHADER_STORAGE_BUFFER_START = SHADER_STORAGE_BUFFER_START,
+	SHADER_STORAGE_BUFFER_SIZE = SHADER_STORAGE_BUFFER_SIZE,
+	MAX_VERTEX_SHADER_STORAGE_BLOCKS = MAX_VERTEX_SHADER_STORAGE_BLOCKS,
+	MAX_GEOMETRY_SHADER_STORAGE_BLOCKS = MAX_GEOMETRY_SHADER_STORAGE_BLOCKS,
+	MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS = MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
+	MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS = MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
+	MAX_FRAGMENT_SHADER_STORAGE_BLOCKS = MAX_FRAGMENT_SHADER_STORAGE_BLOCKS,
+	MAX_COMPUTE_SHADER_STORAGE_BLOCKS = MAX_COMPUTE_SHADER_STORAGE_BLOCKS,
+	MAX_COMBINED_SHADER_STORAGE_BLOCKS = MAX_COMBINED_SHADER_STORAGE_BLOCKS,
+	MAX_SHADER_STORAGE_BUFFER_BINDINGS = MAX_SHADER_STORAGE_BUFFER_BINDINGS,
+	MAX_SHADER_STORAGE_BLOCK_SIZE = MAX_SHADER_STORAGE_BLOCK_SIZE,
+	SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT = SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT,
+	SHADER_STORAGE_BARRIER_BIT = SHADER_STORAGE_BARRIER_BIT,
+	MAX_COMBINED_SHADER_OUTPUT_RESOURCES = MAX_COMBINED_SHADER_OUTPUT_RESOURCES,
+	DEPTH_STENCIL_TEXTURE_MODE = DEPTH_STENCIL_TEXTURE_MODE,
+	TEXTURE_BUFFER_OFFSET = TEXTURE_BUFFER_OFFSET,
+	TEXTURE_BUFFER_SIZE = TEXTURE_BUFFER_SIZE,
+	TEXTURE_BUFFER_OFFSET_ALIGNMENT = TEXTURE_BUFFER_OFFSET_ALIGNMENT,
+	TEXTURE_VIEW_MIN_LEVEL = TEXTURE_VIEW_MIN_LEVEL,
+	TEXTURE_VIEW_NUM_LEVELS = TEXTURE_VIEW_NUM_LEVELS,
+	TEXTURE_VIEW_MIN_LAYER = TEXTURE_VIEW_MIN_LAYER,
+	TEXTURE_VIEW_NUM_LAYERS = TEXTURE_VIEW_NUM_LAYERS,
+	TEXTURE_IMMUTABLE_LEVELS = TEXTURE_IMMUTABLE_LEVELS,
+	VERTEX_ATTRIB_BINDING = VERTEX_ATTRIB_BINDING,
+	VERTEX_ATTRIB_RELATIVE_OFFSET = VERTEX_ATTRIB_RELATIVE_OFFSET,
+	VERTEX_BINDING_DIVISOR = VERTEX_BINDING_DIVISOR,
+	VERTEX_BINDING_OFFSET = VERTEX_BINDING_OFFSET,
+	VERTEX_BINDING_STRIDE = VERTEX_BINDING_STRIDE,
+	MAX_VERTEX_ATTRIB_RELATIVE_OFFSET = MAX_VERTEX_ATTRIB_RELATIVE_OFFSET,
+	MAX_VERTEX_ATTRIB_BINDINGS = MAX_VERTEX_ATTRIB_BINDINGS,
+	VERTEX_BINDING_BUFFER = VERTEX_BINDING_BUFFER,
+	DISPLAY_LIST = DISPLAY_LIST,
+	MAX_VERTEX_ATTRIB_STRIDE = MAX_VERTEX_ATTRIB_STRIDE,
+	PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED = PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED,
+	TEXTURE_BUFFER_BINDING = TEXTURE_BUFFER_BINDING,
+	MAP_PERSISTENT_BIT = MAP_PERSISTENT_BIT,
+	MAP_COHERENT_BIT = MAP_COHERENT_BIT,
+	DYNAMIC_STORAGE_BIT = DYNAMIC_STORAGE_BIT,
+	CLIENT_STORAGE_BIT = CLIENT_STORAGE_BIT,
+	CLIENT_MAPPED_BUFFER_BARRIER_BIT = CLIENT_MAPPED_BUFFER_BARRIER_BIT,
+	BUFFER_IMMUTABLE_STORAGE = BUFFER_IMMUTABLE_STORAGE,
+	BUFFER_STORAGE_FLAGS = BUFFER_STORAGE_FLAGS,
+	CLEAR_TEXTURE = CLEAR_TEXTURE,
+	LOCATION_COMPONENT = LOCATION_COMPONENT,
+	TRANSFORM_FEEDBACK_BUFFER_INDEX = TRANSFORM_FEEDBACK_BUFFER_INDEX,
+	TRANSFORM_FEEDBACK_BUFFER_STRIDE = TRANSFORM_FEEDBACK_BUFFER_STRIDE,
+	QUERY_BUFFER = QUERY_BUFFER,
+	QUERY_BUFFER_BARRIER_BIT = QUERY_BUFFER_BARRIER_BIT,
+	QUERY_BUFFER_BINDING = QUERY_BUFFER_BINDING,
+	QUERY_RESULT_NO_WAIT = QUERY_RESULT_NO_WAIT,
+	MIRROR_CLAMP_TO_EDGE = MIRROR_CLAMP_TO_EDGE,
+	CONTEXT_LOST = CONTEXT_LOST,
+	NEGATIVE_ONE_TO_ONE = NEGATIVE_ONE_TO_ONE,
+	ZERO_TO_ONE = ZERO_TO_ONE,
+	CLIP_ORIGIN = CLIP_ORIGIN,
+	CLIP_DEPTH_MODE = CLIP_DEPTH_MODE,
+	QUERY_WAIT_INVERTED = QUERY_WAIT_INVERTED,
+	QUERY_NO_WAIT_INVERTED = QUERY_NO_WAIT_INVERTED,
+	QUERY_BY_REGION_WAIT_INVERTED = QUERY_BY_REGION_WAIT_INVERTED,
+	QUERY_BY_REGION_NO_WAIT_INVERTED = QUERY_BY_REGION_NO_WAIT_INVERTED,
+	MAX_CULL_DISTANCES = MAX_CULL_DISTANCES,
+	MAX_COMBINED_CLIP_AND_CULL_DISTANCES = MAX_COMBINED_CLIP_AND_CULL_DISTANCES,
+	TEXTURE_TARGET = TEXTURE_TARGET,
+	QUERY_TARGET = QUERY_TARGET,
+	GUILTY_CONTEXT_RESET = GUILTY_CONTEXT_RESET,
+	INNOCENT_CONTEXT_RESET = INNOCENT_CONTEXT_RESET,
+	UNKNOWN_CONTEXT_RESET = UNKNOWN_CONTEXT_RESET,
+	RESET_NOTIFICATION_STRATEGY = RESET_NOTIFICATION_STRATEGY,
+	LOSE_CONTEXT_ON_RESET = LOSE_CONTEXT_ON_RESET,
+	NO_RESET_NOTIFICATION = NO_RESET_NOTIFICATION,
+	CONTEXT_FLAG_ROBUST_ACCESS_BIT = CONTEXT_FLAG_ROBUST_ACCESS_BIT,
+	COLOR_TABLE = COLOR_TABLE,
+	POST_CONVOLUTION_COLOR_TABLE = POST_CONVOLUTION_COLOR_TABLE,
+	POST_COLOR_MATRIX_COLOR_TABLE = POST_COLOR_MATRIX_COLOR_TABLE,
+	PROXY_COLOR_TABLE = PROXY_COLOR_TABLE,
+	PROXY_POST_CONVOLUTION_COLOR_TABLE = PROXY_POST_CONVOLUTION_COLOR_TABLE,
+	PROXY_POST_COLOR_MATRIX_COLOR_TABLE = PROXY_POST_COLOR_MATRIX_COLOR_TABLE,
+	CONVOLUTION_1D = CONVOLUTION_1D,
+	CONVOLUTION_2D = CONVOLUTION_2D,
+	SEPARABLE_2D = SEPARABLE_2D,
+	HISTOGRAM = HISTOGRAM,
+	PROXY_HISTOGRAM = PROXY_HISTOGRAM,
+	MINMAX = MINMAX,
+	CONTEXT_RELEASE_BEHAVIOR = CONTEXT_RELEASE_BEHAVIOR,
+	CONTEXT_RELEASE_BEHAVIOR_FLUSH = CONTEXT_RELEASE_BEHAVIOR_FLUSH,
+	SHADER_BINARY_FORMAT_SPIR_V = SHADER_BINARY_FORMAT_SPIR_V,
+	SPIR_V_BINARY = SPIR_V_BINARY,
+	PARAMETER_BUFFER = PARAMETER_BUFFER,
+	PARAMETER_BUFFER_BINDING = PARAMETER_BUFFER_BINDING,
+	CONTEXT_FLAG_NO_ERROR_BIT = CONTEXT_FLAG_NO_ERROR_BIT,
+	VERTICES_SUBMITTED = VERTICES_SUBMITTED,
+	PRIMITIVES_SUBMITTED = PRIMITIVES_SUBMITTED,
+	VERTEX_SHADER_INVOCATIONS = VERTEX_SHADER_INVOCATIONS,
+	TESS_CONTROL_SHADER_PATCHES = TESS_CONTROL_SHADER_PATCHES,
+	TESS_EVALUATION_SHADER_INVOCATIONS = TESS_EVALUATION_SHADER_INVOCATIONS,
+	GEOMETRY_SHADER_PRIMITIVES_EMITTED = GEOMETRY_SHADER_PRIMITIVES_EMITTED,
+	FRAGMENT_SHADER_INVOCATIONS = FRAGMENT_SHADER_INVOCATIONS,
+	COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS,
+	CLIPPING_INPUT_PRIMITIVES = CLIPPING_INPUT_PRIMITIVES,
+	CLIPPING_OUTPUT_PRIMITIVES = CLIPPING_OUTPUT_PRIMITIVES,
+	POLYGON_OFFSET_CLAMP = POLYGON_OFFSET_CLAMP,
+	SPIR_V_EXTENSIONS = SPIR_V_EXTENSIONS,
+	NUM_SPIR_V_EXTENSIONS = NUM_SPIR_V_EXTENSIONS,
+	TEXTURE_MAX_ANISOTROPY = TEXTURE_MAX_ANISOTROPY,
+	MAX_TEXTURE_MAX_ANISOTROPY = MAX_TEXTURE_MAX_ANISOTROPY,
+	TRANSFORM_FEEDBACK_OVERFLOW = TRANSFORM_FEEDBACK_OVERFLOW,
+	TRANSFORM_FEEDBACK_STREAM_OVERFLOW = TRANSFORM_FEEDBACK_STREAM_OVERFLOW,
 
-	UNSIGNED_BYTE_3_3_2 = UNSIGNED_BYTE_3_3_2, 
-	UNSIGNED_SHORT_4_4_4_4 = UNSIGNED_SHORT_4_4_4_4, 
-	UNSIGNED_SHORT_5_5_5_1 = UNSIGNED_SHORT_5_5_5_1, 
-	UNSIGNED_INT_8_8_8_8 = UNSIGNED_INT_8_8_8_8, 
-	UNSIGNED_INT_10_10_10_2 = UNSIGNED_INT_10_10_10_2, 
-	TEXTURE_BINDING_3D = TEXTURE_BINDING_3D, 
-	PACK_SKIP_IMAGES = PACK_SKIP_IMAGES, 
-	PACK_IMAGE_HEIGHT = PACK_IMAGE_HEIGHT, 
-	UNPACK_SKIP_IMAGES = UNPACK_SKIP_IMAGES, 
-	UNPACK_IMAGE_HEIGHT = UNPACK_IMAGE_HEIGHT, 
-	TEXTURE_3D = TEXTURE_3D, 
-	PROXY_TEXTURE_3D = PROXY_TEXTURE_3D, 
-	TEXTURE_DEPTH = TEXTURE_DEPTH, 
-	TEXTURE_WRAP_R = TEXTURE_WRAP_R, 
-	MAX_3D_TEXTURE_SIZE = MAX_3D_TEXTURE_SIZE, 
-	UNSIGNED_BYTE_2_3_3_REV = UNSIGNED_BYTE_2_3_3_REV, 
-	UNSIGNED_SHORT_5_6_5 = UNSIGNED_SHORT_5_6_5, 
-	UNSIGNED_SHORT_5_6_5_REV = UNSIGNED_SHORT_5_6_5_REV, 
-	UNSIGNED_SHORT_4_4_4_4_REV = UNSIGNED_SHORT_4_4_4_4_REV, 
-	UNSIGNED_SHORT_1_5_5_5_REV = UNSIGNED_SHORT_1_5_5_5_REV, 
-	UNSIGNED_INT_8_8_8_8_REV = UNSIGNED_INT_8_8_8_8_REV, 
-	UNSIGNED_INT_2_10_10_10_REV = UNSIGNED_INT_2_10_10_10_REV, 
-	BGR = BGR, 
-	BGRA = BGRA, 
-	MAX_ELEMENTS_VERTICES = MAX_ELEMENTS_VERTICES, 
-	MAX_ELEMENTS_INDICES = MAX_ELEMENTS_INDICES, 
-	CLAMP_TO_EDGE = CLAMP_TO_EDGE, 
-	TEXTURE_MIN_LOD = TEXTURE_MIN_LOD, 
-	TEXTURE_MAX_LOD = TEXTURE_MAX_LOD, 
-	TEXTURE_BASE_LEVEL = TEXTURE_BASE_LEVEL, 
-	TEXTURE_MAX_LEVEL = TEXTURE_MAX_LEVEL, 
-	SMOOTH_POINT_SIZE_RANGE = SMOOTH_POINT_SIZE_RANGE, 
-	SMOOTH_POINT_SIZE_GRANULARITY = SMOOTH_POINT_SIZE_GRANULARITY, 
-	SMOOTH_LINE_WIDTH_RANGE = SMOOTH_LINE_WIDTH_RANGE, 
-	SMOOTH_LINE_WIDTH_GRANULARITY = SMOOTH_LINE_WIDTH_GRANULARITY, 
-	ALIASED_LINE_WIDTH_RANGE = ALIASED_LINE_WIDTH_RANGE, 
+	// debug
+	DEBUG_OUTPUT_SYNCHRONOUS_ARB   = DEBUG_OUTPUT_SYNCHRONOUS_ARB ,
+	DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB = DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB,
+	DEBUG_CALLBACK_FUNCTION_ARB    = DEBUG_CALLBACK_FUNCTION_ARB,
+	DEBUG_CALLBACK_USER_PARAM_ARB  = DEBUG_CALLBACK_USER_PARAM_ARB,
+	DEBUG_SOURCE_API_ARB           = DEBUG_SOURCE_API_ARB,
+	DEBUG_SOURCE_WINDOW_SYSTEM_ARB = DEBUG_SOURCE_WINDOW_SYSTEM_ARB,
+	DEBUG_SOURCE_SHADER_COMPILER_ARB = DEBUG_SOURCE_SHADER_COMPILER_ARB,
+	DEBUG_SOURCE_THIRD_PARTY_ARB   = DEBUG_SOURCE_THIRD_PARTY_ARB,
+	DEBUG_SOURCE_APPLICATION_ARB   = DEBUG_SOURCE_APPLICATION_ARB,
+	DEBUG_SOURCE_OTHER_ARB         = DEBUG_SOURCE_OTHER_ARB,
+	DEBUG_TYPE_ERROR_ARB           = DEBUG_TYPE_ERROR_ARB,
+	DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB = DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB,
+	DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB = DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB,
+	DEBUG_TYPE_PORTABILITY_ARB     = DEBUG_TYPE_PORTABILITY_ARB,
+	DEBUG_TYPE_PERFORMANCE_ARB     = DEBUG_TYPE_PERFORMANCE_ARB,
+	DEBUG_TYPE_OTHER_ARB           = DEBUG_TYPE_OTHER_ARB,
+	MAX_DEBUG_MESSAGE_LENGTH_ARB   = MAX_DEBUG_MESSAGE_LENGTH_ARB,
+	MAX_DEBUG_LOGGED_MESSAGES_ARB  = MAX_DEBUG_LOGGED_MESSAGES_ARB,
+	DEBUG_LOGGED_MESSAGES_ARB      = DEBUG_LOGGED_MESSAGES_ARB,
+	DEBUG_SEVERITY_HIGH_ARB        = DEBUG_SEVERITY_HIGH_ARB,
+	DEBUG_SEVERITY_MEDIUM_ARB      = DEBUG_SEVERITY_MEDIUM_ARB,
+	DEBUG_SEVERITY_LOW_ARB         = DEBUG_SEVERITY_LOW_ARB,
 
-	TEXTURE0 = TEXTURE0, 
-	TEXTURE1 = TEXTURE1, 
-	TEXTURE2 = TEXTURE2, 
-	TEXTURE3 = TEXTURE3, 
-	TEXTURE4 = TEXTURE4, 
-	TEXTURE5 = TEXTURE5, 
-	TEXTURE6 = TEXTURE6, 
-	TEXTURE7 = TEXTURE7, 
-	TEXTURE8 = TEXTURE8, 
-	TEXTURE9 = TEXTURE9, 
-	TEXTURE10 = TEXTURE10, 
-	TEXTURE11 = TEXTURE11, 
-	TEXTURE12 = TEXTURE12, 
-	TEXTURE13 = TEXTURE13, 
-	TEXTURE14 = TEXTURE14, 
-	TEXTURE15 = TEXTURE15, 
-	TEXTURE16 = TEXTURE16, 
-	TEXTURE17 = TEXTURE17, 
-	TEXTURE18 = TEXTURE18, 
-	TEXTURE19 = TEXTURE19, 
-	TEXTURE20 = TEXTURE20, 
-	TEXTURE21 = TEXTURE21, 
-	TEXTURE22 = TEXTURE22, 
-	TEXTURE23 = TEXTURE23, 
-	TEXTURE24 = TEXTURE24, 
-	TEXTURE25 = TEXTURE25, 
-	TEXTURE26 = TEXTURE26, 
-	TEXTURE27 = TEXTURE27, 
-	TEXTURE28 = TEXTURE28, 
-	TEXTURE29 = TEXTURE29, 
-	TEXTURE30 = TEXTURE30, 
-	TEXTURE31 = TEXTURE31, 
-	ACTIVE_TEXTURE = ACTIVE_TEXTURE, 
-	MULTISAMPLE = MULTISAMPLE, 
-	SAMPLE_ALPHA_TO_COVERAGE = SAMPLE_ALPHA_TO_COVERAGE, 
-	SAMPLE_ALPHA_TO_ONE = SAMPLE_ALPHA_TO_ONE, 
-	SAMPLE_COVERAGE = SAMPLE_COVERAGE, 
-	SAMPLE_BUFFERS = SAMPLE_BUFFERS, 
-	SAMPLES = SAMPLES, 
-	SAMPLE_COVERAGE_VALUE = SAMPLE_COVERAGE_VALUE, 
-	SAMPLE_COVERAGE_INVERT = SAMPLE_COVERAGE_INVERT, 
-	TEXTURE_CUBE_MAP = TEXTURE_CUBE_MAP, 
-	TEXTURE_BINDING_CUBE_MAP = TEXTURE_BINDING_CUBE_MAP, 
-	TEXTURE_CUBE_MAP_POSITIVE_X = TEXTURE_CUBE_MAP_POSITIVE_X, 
-	TEXTURE_CUBE_MAP_NEGATIVE_X = TEXTURE_CUBE_MAP_NEGATIVE_X, 
-	TEXTURE_CUBE_MAP_POSITIVE_Y = TEXTURE_CUBE_MAP_POSITIVE_Y, 
-	TEXTURE_CUBE_MAP_NEGATIVE_Y = TEXTURE_CUBE_MAP_NEGATIVE_Y, 
-	TEXTURE_CUBE_MAP_POSITIVE_Z = TEXTURE_CUBE_MAP_POSITIVE_Z, 
-	TEXTURE_CUBE_MAP_NEGATIVE_Z = TEXTURE_CUBE_MAP_NEGATIVE_Z, 
-	PROXY_TEXTURE_CUBE_MAP = PROXY_TEXTURE_CUBE_MAP, 
-	MAX_CUBE_MAP_TEXTURE_SIZE = MAX_CUBE_MAP_TEXTURE_SIZE, 
-	COMPRESSED_RGB = COMPRESSED_RGB, 
-	COMPRESSED_RGBA = COMPRESSED_RGBA, 
-	TEXTURE_COMPRESSION_HINT = TEXTURE_COMPRESSION_HINT, 
-	TEXTURE_COMPRESSED_IMAGE_SIZE = TEXTURE_COMPRESSED_IMAGE_SIZE, 
-	TEXTURE_COMPRESSED = TEXTURE_COMPRESSED, 
-	NUM_COMPRESSED_TEXTURE_FORMATS = NUM_COMPRESSED_TEXTURE_FORMATS, 
-	COMPRESSED_TEXTURE_FORMATS = COMPRESSED_TEXTURE_FORMATS, 
-	CLAMP_TO_BORDER = CLAMP_TO_BORDER, 
-
-	BLEND_DST_RGB = BLEND_DST_RGB, 
-	BLEND_SRC_RGB = BLEND_SRC_RGB, 
-	BLEND_DST_ALPHA = BLEND_DST_ALPHA, 
-	BLEND_SRC_ALPHA = BLEND_SRC_ALPHA, 
-	POINT_FADE_THRESHOLD_SIZE = POINT_FADE_THRESHOLD_SIZE, 
-	DEPTH_COMPONENT16 = DEPTH_COMPONENT16, 
-	DEPTH_COMPONENT24 = DEPTH_COMPONENT24, 
-	DEPTH_COMPONENT32 = DEPTH_COMPONENT32, 
-	MIRRORED_REPEAT = MIRRORED_REPEAT, 
-	MAX_TEXTURE_LOD_BIAS = MAX_TEXTURE_LOD_BIAS, 
-	TEXTURE_LOD_BIAS = TEXTURE_LOD_BIAS, 
-	INCR_WRAP = INCR_WRAP, 
-	DECR_WRAP = DECR_WRAP, 
-	TEXTURE_DEPTH_SIZE = TEXTURE_DEPTH_SIZE, 
-	TEXTURE_COMPARE_MODE = TEXTURE_COMPARE_MODE, 
-	TEXTURE_COMPARE_FUNC = TEXTURE_COMPARE_FUNC, 
-	FUNC_ADD = FUNC_ADD, 
-	FUNC_SUBTRACT = FUNC_SUBTRACT, 
-	FUNC_REVERSE_SUBTRACT = FUNC_REVERSE_SUBTRACT, 
-	MIN = MIN, 
-	MAX = MAX, 
-	CONSTANT_COLOR = CONSTANT_COLOR, 
-	ONE_MINUS_CONSTANT_COLOR = ONE_MINUS_CONSTANT_COLOR, 
-	CONSTANT_ALPHA = CONSTANT_ALPHA, 
-	ONE_MINUS_CONSTANT_ALPHA = ONE_MINUS_CONSTANT_ALPHA, 
-
-	BUFFER_SIZE = BUFFER_SIZE, 
-	BUFFER_USAGE = BUFFER_USAGE, 
-	QUERY_COUNTER_BITS = QUERY_COUNTER_BITS, 
-	CURRENT_QUERY = CURRENT_QUERY, 
-	QUERY_RESULT = QUERY_RESULT, 
-	QUERY_RESULT_AVAILABLE = QUERY_RESULT_AVAILABLE, 
-	ARRAY_BUFFER = ARRAY_BUFFER, 
-	ELEMENT_ARRAY_BUFFER = ELEMENT_ARRAY_BUFFER, 
-	ARRAY_BUFFER_BINDING = ARRAY_BUFFER_BINDING, 
-	ELEMENT_ARRAY_BUFFER_BINDING = ELEMENT_ARRAY_BUFFER_BINDING, 
-	VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, 
-	READ_ONLY = READ_ONLY, 
-	WRITE_ONLY = WRITE_ONLY, 
-	READ_WRITE = READ_WRITE, 
-	BUFFER_ACCESS = BUFFER_ACCESS, 
-	BUFFER_MAPPED = BUFFER_MAPPED, 
-	BUFFER_MAP_POINTER = BUFFER_MAP_POINTER, 
-	STREAM_DRAW = STREAM_DRAW, 
-	STREAM_READ = STREAM_READ, 
-	STREAM_COPY = STREAM_COPY, 
-	STATIC_DRAW = STATIC_DRAW, 
-	STATIC_READ = STATIC_READ, 
-	STATIC_COPY = STATIC_COPY, 
-	DYNAMIC_DRAW = DYNAMIC_DRAW, 
-	DYNAMIC_READ = DYNAMIC_READ, 
-	DYNAMIC_COPY = DYNAMIC_COPY, 
-	SAMPLES_PASSED = SAMPLES_PASSED, 
-	SRC1_ALPHA = SRC1_ALPHA, 
-
-	BLEND_EQUATION_RGB = BLEND_EQUATION_RGB, 
-	VERTEX_ATTRIB_ARRAY_ENABLED = VERTEX_ATTRIB_ARRAY_ENABLED, 
-	VERTEX_ATTRIB_ARRAY_SIZE = VERTEX_ATTRIB_ARRAY_SIZE, 
-	VERTEX_ATTRIB_ARRAY_STRIDE = VERTEX_ATTRIB_ARRAY_STRIDE, 
-	VERTEX_ATTRIB_ARRAY_TYPE = VERTEX_ATTRIB_ARRAY_TYPE, 
-	CURRENT_VERTEX_ATTRIB = CURRENT_VERTEX_ATTRIB, 
-	VERTEX_PROGRAM_POINT_SIZE = VERTEX_PROGRAM_POINT_SIZE, 
-	VERTEX_ATTRIB_ARRAY_POINTER = VERTEX_ATTRIB_ARRAY_POINTER, 
-	STENCIL_BACK_FUNC = STENCIL_BACK_FUNC, 
-	STENCIL_BACK_FAIL = STENCIL_BACK_FAIL, 
-	STENCIL_BACK_PASS_DEPTH_FAIL = STENCIL_BACK_PASS_DEPTH_FAIL, 
-	STENCIL_BACK_PASS_DEPTH_PASS = STENCIL_BACK_PASS_DEPTH_PASS, 
-	MAX_DRAW_BUFFERS = MAX_DRAW_BUFFERS, 
-	DRAW_BUFFER0 = DRAW_BUFFER0, 
-	DRAW_BUFFER1 = DRAW_BUFFER1, 
-	DRAW_BUFFER2 = DRAW_BUFFER2, 
-	DRAW_BUFFER3 = DRAW_BUFFER3, 
-	DRAW_BUFFER4 = DRAW_BUFFER4, 
-	DRAW_BUFFER5 = DRAW_BUFFER5, 
-	DRAW_BUFFER6 = DRAW_BUFFER6, 
-	DRAW_BUFFER7 = DRAW_BUFFER7, 
-	DRAW_BUFFER8 = DRAW_BUFFER8, 
-	DRAW_BUFFER9 = DRAW_BUFFER9, 
-	DRAW_BUFFER10 = DRAW_BUFFER10, 
-	DRAW_BUFFER11 = DRAW_BUFFER11, 
-	DRAW_BUFFER12 = DRAW_BUFFER12, 
-	DRAW_BUFFER13 = DRAW_BUFFER13, 
-	DRAW_BUFFER14 = DRAW_BUFFER14, 
-	DRAW_BUFFER15 = DRAW_BUFFER15, 
-	BLEND_EQUATION_ALPHA = BLEND_EQUATION_ALPHA, 
-	MAX_VERTEX_ATTRIBS = MAX_VERTEX_ATTRIBS, 
-	VERTEX_ATTRIB_ARRAY_NORMALIZED = VERTEX_ATTRIB_ARRAY_NORMALIZED, 
-	MAX_TEXTURE_IMAGE_UNITS = MAX_TEXTURE_IMAGE_UNITS, 
-	FRAGMENT_SHADER = FRAGMENT_SHADER, 
-	VERTEX_SHADER = VERTEX_SHADER, 
-	MAX_FRAGMENT_UNIFORM_COMPONENTS = MAX_FRAGMENT_UNIFORM_COMPONENTS, 
-	MAX_VERTEX_UNIFORM_COMPONENTS = MAX_VERTEX_UNIFORM_COMPONENTS, 
-	MAX_VARYING_FLOATS = MAX_VARYING_FLOATS, 
-	MAX_VERTEX_TEXTURE_IMAGE_UNITS = MAX_VERTEX_TEXTURE_IMAGE_UNITS, 
-	MAX_COMBINED_TEXTURE_IMAGE_UNITS = MAX_COMBINED_TEXTURE_IMAGE_UNITS, 
-	SHADER_TYPE = SHADER_TYPE, 
-	FLOAT_VEC2 = FLOAT_VEC2, 
-	FLOAT_VEC3 = FLOAT_VEC3, 
-	FLOAT_VEC4 = FLOAT_VEC4, 
-	INT_VEC2 = INT_VEC2, 
-	INT_VEC3 = INT_VEC3, 
-	INT_VEC4 = INT_VEC4, 
-	BOOL = BOOL, 
-	BOOL_VEC2 = BOOL_VEC2, 
-	BOOL_VEC3 = BOOL_VEC3, 
-	BOOL_VEC4 = BOOL_VEC4, 
-	FLOAT_MAT2 = FLOAT_MAT2, 
-	FLOAT_MAT3 = FLOAT_MAT3, 
-	FLOAT_MAT4 = FLOAT_MAT4, 
-	SAMPLER_1D = SAMPLER_1D, 
-	SAMPLER_2D = SAMPLER_2D, 
-	SAMPLER_3D = SAMPLER_3D, 
-	SAMPLER_CUBE = SAMPLER_CUBE, 
-	SAMPLER_1D_SHADOW = SAMPLER_1D_SHADOW, 
-	SAMPLER_2D_SHADOW = SAMPLER_2D_SHADOW, 
-	DELETE_STATUS = DELETE_STATUS, 
-	COMPILE_STATUS = COMPILE_STATUS, 
-	LINK_STATUS = LINK_STATUS, 
-	VALIDATE_STATUS = VALIDATE_STATUS, 
-	INFO_LOG_LENGTH = INFO_LOG_LENGTH, 
-	ATTACHED_SHADERS = ATTACHED_SHADERS, 
-	ACTIVE_UNIFORMS = ACTIVE_UNIFORMS, 
-	ACTIVE_UNIFORM_MAX_LENGTH = ACTIVE_UNIFORM_MAX_LENGTH, 
-	SHADER_SOURCE_LENGTH = SHADER_SOURCE_LENGTH, 
-	ACTIVE_ATTRIBUTES = ACTIVE_ATTRIBUTES, 
-	ACTIVE_ATTRIBUTE_MAX_LENGTH = ACTIVE_ATTRIBUTE_MAX_LENGTH, 
-	FRAGMENT_SHADER_DERIVATIVE_HINT = FRAGMENT_SHADER_DERIVATIVE_HINT, 
-	SHADING_LANGUAGE_VERSION = SHADING_LANGUAGE_VERSION, 
-	CURRENT_PROGRAM = CURRENT_PROGRAM, 
-	POINT_SPRITE_COORD_ORIGIN = POINT_SPRITE_COORD_ORIGIN, 
-	LOWER_LEFT = LOWER_LEFT, 
-	UPPER_LEFT = UPPER_LEFT, 
-	STENCIL_BACK_REF = STENCIL_BACK_REF, 
-	STENCIL_BACK_VALUE_MASK = STENCIL_BACK_VALUE_MASK, 
-	STENCIL_BACK_WRITEMASK = STENCIL_BACK_WRITEMASK, 
-
-	PIXEL_PACK_BUFFER = PIXEL_PACK_BUFFER, 
-	PIXEL_UNPACK_BUFFER = PIXEL_UNPACK_BUFFER, 
-	PIXEL_PACK_BUFFER_BINDING = PIXEL_PACK_BUFFER_BINDING, 
-	PIXEL_UNPACK_BUFFER_BINDING = PIXEL_UNPACK_BUFFER_BINDING, 
-	FLOAT_MAT2x3 = FLOAT_MAT2x3, 
-	FLOAT_MAT2x4 = FLOAT_MAT2x4, 
-	FLOAT_MAT3x2 = FLOAT_MAT3x2, 
-	FLOAT_MAT3x4 = FLOAT_MAT3x4, 
-	FLOAT_MAT4x2 = FLOAT_MAT4x2, 
-	FLOAT_MAT4x3 = FLOAT_MAT4x3, 
-	SRGB = SRGB, 
-	SRGB8 = SRGB8, 
-	SRGB_ALPHA = SRGB_ALPHA, 
-	SRGB8_ALPHA8 = SRGB8_ALPHA8, 
-	COMPRESSED_SRGB = COMPRESSED_SRGB, 
-	COMPRESSED_SRGB_ALPHA = COMPRESSED_SRGB_ALPHA, 
-
-	COMPARE_REF_TO_TEXTURE = COMPARE_REF_TO_TEXTURE, 
-	CLIP_DISTANCE0 = CLIP_DISTANCE0, 
-	CLIP_DISTANCE1 = CLIP_DISTANCE1, 
-	CLIP_DISTANCE2 = CLIP_DISTANCE2, 
-	CLIP_DISTANCE3 = CLIP_DISTANCE3, 
-	CLIP_DISTANCE4 = CLIP_DISTANCE4, 
-	CLIP_DISTANCE5 = CLIP_DISTANCE5, 
-	CLIP_DISTANCE6 = CLIP_DISTANCE6, 
-	CLIP_DISTANCE7 = CLIP_DISTANCE7, 
-	MAX_CLIP_DISTANCES = MAX_CLIP_DISTANCES, 
-	MAJOR_VERSION = MAJOR_VERSION, 
-	MINOR_VERSION = MINOR_VERSION, 
-	NUM_EXTENSIONS = NUM_EXTENSIONS, 
-	CONTEXT_FLAGS = CONTEXT_FLAGS, 
-	COMPRESSED_RED = COMPRESSED_RED, 
-	COMPRESSED_RG = COMPRESSED_RG, 
-	CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, 
-	RGBA32F = RGBA32F, 
-	RGB32F = RGB32F, 
-	RGBA16F = RGBA16F, 
-	RGB16F = RGB16F, 
-	VERTEX_ATTRIB_ARRAY_INTEGER = VERTEX_ATTRIB_ARRAY_INTEGER, 
-	MAX_ARRAY_TEXTURE_LAYERS = MAX_ARRAY_TEXTURE_LAYERS, 
-	MIN_PROGRAM_TEXEL_OFFSET = MIN_PROGRAM_TEXEL_OFFSET, 
-	MAX_PROGRAM_TEXEL_OFFSET = MAX_PROGRAM_TEXEL_OFFSET, 
-	CLAMP_READ_COLOR = CLAMP_READ_COLOR, 
-	FIXED_ONLY = FIXED_ONLY, 
-	MAX_VARYING_COMPONENTS = MAX_VARYING_COMPONENTS, 
-	TEXTURE_1D_ARRAY = TEXTURE_1D_ARRAY, 
-	PROXY_TEXTURE_1D_ARRAY = PROXY_TEXTURE_1D_ARRAY, 
-	TEXTURE_2D_ARRAY = TEXTURE_2D_ARRAY, 
-	PROXY_TEXTURE_2D_ARRAY = PROXY_TEXTURE_2D_ARRAY, 
-	TEXTURE_BINDING_1D_ARRAY = TEXTURE_BINDING_1D_ARRAY, 
-	TEXTURE_BINDING_2D_ARRAY = TEXTURE_BINDING_2D_ARRAY, 
-	R11F_G11F_B10F = R11F_G11F_B10F, 
-	UNSIGNED_INT_10F_11F_11F_REV = UNSIGNED_INT_10F_11F_11F_REV, 
-	RGB9_E5 = RGB9_E5, 
-	UNSIGNED_INT_5_9_9_9_REV = UNSIGNED_INT_5_9_9_9_REV, 
-	TEXTURE_SHARED_SIZE = TEXTURE_SHARED_SIZE, 
-	TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH = TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, 
-	TRANSFORM_FEEDBACK_BUFFER_MODE = TRANSFORM_FEEDBACK_BUFFER_MODE, 
-	MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, 
-	TRANSFORM_FEEDBACK_VARYINGS = TRANSFORM_FEEDBACK_VARYINGS, 
-	TRANSFORM_FEEDBACK_BUFFER_START = TRANSFORM_FEEDBACK_BUFFER_START, 
-	TRANSFORM_FEEDBACK_BUFFER_SIZE = TRANSFORM_FEEDBACK_BUFFER_SIZE, 
-	PRIMITIVES_GENERATED = PRIMITIVES_GENERATED, 
-	TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 
-	RASTERIZER_DISCARD = RASTERIZER_DISCARD, 
-	MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, 
-	MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, 
-	INTERLEAVED_ATTRIBS = INTERLEAVED_ATTRIBS, 
-	SEPARATE_ATTRIBS = SEPARATE_ATTRIBS, 
-	TRANSFORM_FEEDBACK_BUFFER = TRANSFORM_FEEDBACK_BUFFER, 
-	TRANSFORM_FEEDBACK_BUFFER_BINDING = TRANSFORM_FEEDBACK_BUFFER_BINDING, 
-	RGBA32UI = RGBA32UI, 
-	RGB32UI = RGB32UI, 
-	RGBA16UI = RGBA16UI, 
-	RGB16UI = RGB16UI, 
-	RGBA8UI = RGBA8UI, 
-	RGB8UI = RGB8UI, 
-	RGBA32I = RGBA32I, 
-	RGB32I = RGB32I, 
-	RGBA16I = RGBA16I, 
-	RGB16I = RGB16I, 
-	RGBA8I = RGBA8I, 
-	RGB8I = RGB8I, 
-	RED_INTEGER = RED_INTEGER, 
-	GREEN_INTEGER = GREEN_INTEGER, 
-	BLUE_INTEGER = BLUE_INTEGER, 
-	RGB_INTEGER = RGB_INTEGER, 
-	RGBA_INTEGER = RGBA_INTEGER, 
-	BGR_INTEGER = BGR_INTEGER, 
-	BGRA_INTEGER = BGRA_INTEGER, 
-	SAMPLER_1D_ARRAY = SAMPLER_1D_ARRAY, 
-	SAMPLER_2D_ARRAY = SAMPLER_2D_ARRAY, 
-	SAMPLER_1D_ARRAY_SHADOW = SAMPLER_1D_ARRAY_SHADOW, 
-	SAMPLER_2D_ARRAY_SHADOW = SAMPLER_2D_ARRAY_SHADOW, 
-	SAMPLER_CUBE_SHADOW = SAMPLER_CUBE_SHADOW, 
-	UNSIGNED_INT_VEC2 = UNSIGNED_INT_VEC2, 
-	UNSIGNED_INT_VEC3 = UNSIGNED_INT_VEC3, 
-	UNSIGNED_INT_VEC4 = UNSIGNED_INT_VEC4, 
-	INT_SAMPLER_1D = INT_SAMPLER_1D, 
-	INT_SAMPLER_2D = INT_SAMPLER_2D, 
-	INT_SAMPLER_3D = INT_SAMPLER_3D, 
-	INT_SAMPLER_CUBE = INT_SAMPLER_CUBE, 
-	INT_SAMPLER_1D_ARRAY = INT_SAMPLER_1D_ARRAY, 
-	INT_SAMPLER_2D_ARRAY = INT_SAMPLER_2D_ARRAY, 
-	UNSIGNED_INT_SAMPLER_1D = UNSIGNED_INT_SAMPLER_1D, 
-	UNSIGNED_INT_SAMPLER_2D = UNSIGNED_INT_SAMPLER_2D, 
-	UNSIGNED_INT_SAMPLER_3D = UNSIGNED_INT_SAMPLER_3D, 
-	UNSIGNED_INT_SAMPLER_CUBE = UNSIGNED_INT_SAMPLER_CUBE, 
-	UNSIGNED_INT_SAMPLER_1D_ARRAY = UNSIGNED_INT_SAMPLER_1D_ARRAY, 
-	UNSIGNED_INT_SAMPLER_2D_ARRAY = UNSIGNED_INT_SAMPLER_2D_ARRAY, 
-	QUERY_WAIT = QUERY_WAIT, 
-	QUERY_NO_WAIT = QUERY_NO_WAIT, 
-	QUERY_BY_REGION_WAIT = QUERY_BY_REGION_WAIT, 
-	QUERY_BY_REGION_NO_WAIT = QUERY_BY_REGION_NO_WAIT, 
-	BUFFER_ACCESS_FLAGS = BUFFER_ACCESS_FLAGS, 
-	BUFFER_MAP_LENGTH = BUFFER_MAP_LENGTH, 
-	BUFFER_MAP_OFFSET = BUFFER_MAP_OFFSET, 
-	DEPTH_COMPONENT32F = DEPTH_COMPONENT32F, 
-	DEPTH32F_STENCIL8 = DEPTH32F_STENCIL8, 
-	FLOAT_32_UNSIGNED_INT_24_8_REV = FLOAT_32_UNSIGNED_INT_24_8_REV, 
-	INVALID_FRAMEBUFFER_OPERATION = INVALID_FRAMEBUFFER_OPERATION, 
-	FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, 
-	FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE = FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, 
-	FRAMEBUFFER_ATTACHMENT_RED_SIZE = FRAMEBUFFER_ATTACHMENT_RED_SIZE, 
-	FRAMEBUFFER_ATTACHMENT_GREEN_SIZE = FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, 
-	FRAMEBUFFER_ATTACHMENT_BLUE_SIZE = FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, 
-	FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE = FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, 
-	FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE = FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, 
-	FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE = FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, 
-	FRAMEBUFFER_DEFAULT = FRAMEBUFFER_DEFAULT, 
-	FRAMEBUFFER_UNDEFINED = FRAMEBUFFER_UNDEFINED, 
-	DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL_ATTACHMENT, 
-	MAX_RENDERBUFFER_SIZE = MAX_RENDERBUFFER_SIZE, 
-	DEPTH_STENCIL = DEPTH_STENCIL, 
-	UNSIGNED_INT_24_8 = UNSIGNED_INT_24_8, 
-	DEPTH24_STENCIL8 = DEPTH24_STENCIL8, 
-	TEXTURE_STENCIL_SIZE = TEXTURE_STENCIL_SIZE, 
-	TEXTURE_RED_TYPE = TEXTURE_RED_TYPE, 
-	TEXTURE_GREEN_TYPE = TEXTURE_GREEN_TYPE, 
-	TEXTURE_BLUE_TYPE = TEXTURE_BLUE_TYPE, 
-	TEXTURE_ALPHA_TYPE = TEXTURE_ALPHA_TYPE, 
-	TEXTURE_DEPTH_TYPE = TEXTURE_DEPTH_TYPE, 
-	UNSIGNED_NORMALIZED = UNSIGNED_NORMALIZED, 
-	FRAMEBUFFER_BINDING = FRAMEBUFFER_BINDING, 
-	DRAW_FRAMEBUFFER_BINDING = DRAW_FRAMEBUFFER_BINDING, 
-	RENDERBUFFER_BINDING = RENDERBUFFER_BINDING, 
-	READ_FRAMEBUFFER = READ_FRAMEBUFFER, 
-	DRAW_FRAMEBUFFER = DRAW_FRAMEBUFFER, 
-	READ_FRAMEBUFFER_BINDING = READ_FRAMEBUFFER_BINDING, 
-	RENDERBUFFER_SAMPLES = RENDERBUFFER_SAMPLES, 
-	FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, 
-	FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, 
-	FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, 
-	FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE, 
-	FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER, 
-	FRAMEBUFFER_COMPLETE = FRAMEBUFFER_COMPLETE, 
-	FRAMEBUFFER_INCOMPLETE_ATTACHMENT = FRAMEBUFFER_INCOMPLETE_ATTACHMENT, 
-	FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT, 
-	FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER, 
-	FRAMEBUFFER_INCOMPLETE_READ_BUFFER = FRAMEBUFFER_INCOMPLETE_READ_BUFFER, 
-	FRAMEBUFFER_UNSUPPORTED = FRAMEBUFFER_UNSUPPORTED, 
-	MAX_COLOR_ATTACHMENTS = MAX_COLOR_ATTACHMENTS, 
-	COLOR_ATTACHMENT0 = COLOR_ATTACHMENT0, 
-	COLOR_ATTACHMENT1 = COLOR_ATTACHMENT1, 
-	COLOR_ATTACHMENT2 = COLOR_ATTACHMENT2, 
-	COLOR_ATTACHMENT3 = COLOR_ATTACHMENT3, 
-	COLOR_ATTACHMENT4 = COLOR_ATTACHMENT4, 
-	COLOR_ATTACHMENT5 = COLOR_ATTACHMENT5, 
-	COLOR_ATTACHMENT6 = COLOR_ATTACHMENT6, 
-	COLOR_ATTACHMENT7 = COLOR_ATTACHMENT7, 
-	COLOR_ATTACHMENT8 = COLOR_ATTACHMENT8, 
-	COLOR_ATTACHMENT9 = COLOR_ATTACHMENT9, 
-	COLOR_ATTACHMENT10 = COLOR_ATTACHMENT10, 
-	COLOR_ATTACHMENT11 = COLOR_ATTACHMENT11, 
-	COLOR_ATTACHMENT12 = COLOR_ATTACHMENT12, 
-	COLOR_ATTACHMENT13 = COLOR_ATTACHMENT13, 
-	COLOR_ATTACHMENT14 = COLOR_ATTACHMENT14, 
-	COLOR_ATTACHMENT15 = COLOR_ATTACHMENT15, 
-	COLOR_ATTACHMENT16 = COLOR_ATTACHMENT16, 
-	COLOR_ATTACHMENT17 = COLOR_ATTACHMENT17, 
-	COLOR_ATTACHMENT18 = COLOR_ATTACHMENT18, 
-	COLOR_ATTACHMENT19 = COLOR_ATTACHMENT19, 
-	COLOR_ATTACHMENT20 = COLOR_ATTACHMENT20, 
-	COLOR_ATTACHMENT21 = COLOR_ATTACHMENT21, 
-	COLOR_ATTACHMENT22 = COLOR_ATTACHMENT22, 
-	COLOR_ATTACHMENT23 = COLOR_ATTACHMENT23, 
-	COLOR_ATTACHMENT24 = COLOR_ATTACHMENT24, 
-	COLOR_ATTACHMENT25 = COLOR_ATTACHMENT25, 
-	COLOR_ATTACHMENT26 = COLOR_ATTACHMENT26, 
-	COLOR_ATTACHMENT27 = COLOR_ATTACHMENT27, 
-	COLOR_ATTACHMENT28 = COLOR_ATTACHMENT28, 
-	COLOR_ATTACHMENT29 = COLOR_ATTACHMENT29, 
-	COLOR_ATTACHMENT30 = COLOR_ATTACHMENT30, 
-	COLOR_ATTACHMENT31 = COLOR_ATTACHMENT31, 
-	DEPTH_ATTACHMENT = DEPTH_ATTACHMENT, 
-	STENCIL_ATTACHMENT = STENCIL_ATTACHMENT, 
-	FRAMEBUFFER = FRAMEBUFFER, 
-	RENDERBUFFER = RENDERBUFFER, 
-	RENDERBUFFER_WIDTH = RENDERBUFFER_WIDTH, 
-	RENDERBUFFER_HEIGHT = RENDERBUFFER_HEIGHT, 
-	RENDERBUFFER_INTERNAL_FORMAT = RENDERBUFFER_INTERNAL_FORMAT, 
-	STENCIL_INDEX1 = STENCIL_INDEX1, 
-	STENCIL_INDEX4 = STENCIL_INDEX4, 
-	STENCIL_INDEX8 = STENCIL_INDEX8, 
-	STENCIL_INDEX16 = STENCIL_INDEX16, 
-	RENDERBUFFER_RED_SIZE = RENDERBUFFER_RED_SIZE, 
-	RENDERBUFFER_GREEN_SIZE = RENDERBUFFER_GREEN_SIZE, 
-	RENDERBUFFER_BLUE_SIZE = RENDERBUFFER_BLUE_SIZE, 
-	RENDERBUFFER_ALPHA_SIZE = RENDERBUFFER_ALPHA_SIZE, 
-	RENDERBUFFER_DEPTH_SIZE = RENDERBUFFER_DEPTH_SIZE, 
-	RENDERBUFFER_STENCIL_SIZE = RENDERBUFFER_STENCIL_SIZE, 
-	FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = FRAMEBUFFER_INCOMPLETE_MULTISAMPLE, 
-	MAX_SAMPLES = MAX_SAMPLES, 
-	FRAMEBUFFER_SRGB = FRAMEBUFFER_SRGB, 
-	HALF_FLOAT = HALF_FLOAT, 
-	MAP_READ_BIT = MAP_READ_BIT, 
-	MAP_WRITE_BIT = MAP_WRITE_BIT, 
-	MAP_INVALIDATE_RANGE_BIT = MAP_INVALIDATE_RANGE_BIT, 
-	MAP_INVALIDATE_BUFFER_BIT = MAP_INVALIDATE_BUFFER_BIT, 
-	MAP_FLUSH_EXPLICIT_BIT = MAP_FLUSH_EXPLICIT_BIT, 
-	MAP_UNSYNCHRONIZED_BIT = MAP_UNSYNCHRONIZED_BIT, 
-	COMPRESSED_RED_RGTC1 = COMPRESSED_RED_RGTC1, 
-	COMPRESSED_SIGNED_RED_RGTC1 = COMPRESSED_SIGNED_RED_RGTC1, 
-	COMPRESSED_RG_RGTC2 = COMPRESSED_RG_RGTC2, 
-	COMPRESSED_SIGNED_RG_RGTC2 = COMPRESSED_SIGNED_RG_RGTC2, 
-	RG = RG, 
-	RG_INTEGER = RG_INTEGER, 
-	R8 = R8, 
-	R16 = R16, 
-	RG8 = RG8, 
-	RG16 = RG16, 
-	R16F = R16F, 
-	R32F = R32F, 
-	RG16F = RG16F, 
-	RG32F = RG32F, 
-	R8I = R8I, 
-	R8UI = R8UI, 
-	R16I = R16I, 
-	R16UI = R16UI, 
-	R32I = R32I, 
-	R32UI = R32UI, 
-	RG8I = RG8I, 
-	RG8UI = RG8UI, 
-	RG16I = RG16I, 
-	RG16UI = RG16UI, 
-	RG32I = RG32I, 
-	RG32UI = RG32UI, 
-	VERTEX_ARRAY_BINDING = VERTEX_ARRAY_BINDING, 
-
-	SAMPLER_2D_RECT = SAMPLER_2D_RECT, 
-	SAMPLER_2D_RECT_SHADOW = SAMPLER_2D_RECT_SHADOW, 
-	SAMPLER_BUFFER = SAMPLER_BUFFER, 
-	INT_SAMPLER_2D_RECT = INT_SAMPLER_2D_RECT, 
-	INT_SAMPLER_BUFFER = INT_SAMPLER_BUFFER, 
-	UNSIGNED_INT_SAMPLER_2D_RECT = UNSIGNED_INT_SAMPLER_2D_RECT, 
-	UNSIGNED_INT_SAMPLER_BUFFER = UNSIGNED_INT_SAMPLER_BUFFER, 
-	TEXTURE_BUFFER = TEXTURE_BUFFER, 
-	MAX_TEXTURE_BUFFER_SIZE = MAX_TEXTURE_BUFFER_SIZE, 
-	TEXTURE_BINDING_BUFFER = TEXTURE_BINDING_BUFFER, 
-	TEXTURE_BUFFER_DATA_STORE_BINDING = TEXTURE_BUFFER_DATA_STORE_BINDING, 
-	TEXTURE_RECTANGLE = TEXTURE_RECTANGLE, 
-	TEXTURE_BINDING_RECTANGLE = TEXTURE_BINDING_RECTANGLE, 
-	PROXY_TEXTURE_RECTANGLE = PROXY_TEXTURE_RECTANGLE, 
-	MAX_RECTANGLE_TEXTURE_SIZE = MAX_RECTANGLE_TEXTURE_SIZE, 
-	R8_SNORM = R8_SNORM, 
-	RG8_SNORM = RG8_SNORM, 
-	RGB8_SNORM = RGB8_SNORM, 
-	RGBA8_SNORM = RGBA8_SNORM, 
-	R16_SNORM = R16_SNORM, 
-	RG16_SNORM = RG16_SNORM, 
-	RGB16_SNORM = RGB16_SNORM, 
-	RGBA16_SNORM = RGBA16_SNORM, 
-	SIGNED_NORMALIZED = SIGNED_NORMALIZED, 
-	PRIMITIVE_RESTART = PRIMITIVE_RESTART, 
-	PRIMITIVE_RESTART_INDEX = PRIMITIVE_RESTART_INDEX, 
-	COPY_READ_BUFFER = COPY_READ_BUFFER, 
-	COPY_WRITE_BUFFER = COPY_WRITE_BUFFER, 
-	UNIFORM_BUFFER = UNIFORM_BUFFER, 
-	UNIFORM_BUFFER_BINDING = UNIFORM_BUFFER_BINDING, 
-	UNIFORM_BUFFER_START = UNIFORM_BUFFER_START, 
-	UNIFORM_BUFFER_SIZE = UNIFORM_BUFFER_SIZE, 
-	MAX_VERTEX_UNIFORM_BLOCKS = MAX_VERTEX_UNIFORM_BLOCKS, 
-	MAX_GEOMETRY_UNIFORM_BLOCKS = MAX_GEOMETRY_UNIFORM_BLOCKS, 
-	MAX_FRAGMENT_UNIFORM_BLOCKS = MAX_FRAGMENT_UNIFORM_BLOCKS, 
-	MAX_COMBINED_UNIFORM_BLOCKS = MAX_COMBINED_UNIFORM_BLOCKS, 
-	MAX_UNIFORM_BUFFER_BINDINGS = MAX_UNIFORM_BUFFER_BINDINGS, 
-	MAX_UNIFORM_BLOCK_SIZE = MAX_UNIFORM_BLOCK_SIZE, 
-	MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, 
-	MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS = MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS, 
-	MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, 
-	UNIFORM_BUFFER_OFFSET_ALIGNMENT = UNIFORM_BUFFER_OFFSET_ALIGNMENT, 
-	ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH = ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, 
-	ACTIVE_UNIFORM_BLOCKS = ACTIVE_UNIFORM_BLOCKS, 
-	UNIFORM_TYPE = UNIFORM_TYPE, 
-	UNIFORM_SIZE = UNIFORM_SIZE, 
-	UNIFORM_NAME_LENGTH = UNIFORM_NAME_LENGTH, 
-	UNIFORM_BLOCK_INDEX = UNIFORM_BLOCK_INDEX, 
-	UNIFORM_OFFSET = UNIFORM_OFFSET, 
-	UNIFORM_ARRAY_STRIDE = UNIFORM_ARRAY_STRIDE, 
-	UNIFORM_MATRIX_STRIDE = UNIFORM_MATRIX_STRIDE, 
-	UNIFORM_IS_ROW_MAJOR = UNIFORM_IS_ROW_MAJOR, 
-	UNIFORM_BLOCK_BINDING = UNIFORM_BLOCK_BINDING, 
-	UNIFORM_BLOCK_DATA_SIZE = UNIFORM_BLOCK_DATA_SIZE, 
-	UNIFORM_BLOCK_NAME_LENGTH = UNIFORM_BLOCK_NAME_LENGTH, 
-	UNIFORM_BLOCK_ACTIVE_UNIFORMS = UNIFORM_BLOCK_ACTIVE_UNIFORMS, 
-	UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, 
-	UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, 
-	UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER = UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, 
-	UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, 
-	INVALID_INDEX = INVALID_INDEX, 
-
-	CONTEXT_CORE_PROFILE_BIT = CONTEXT_CORE_PROFILE_BIT, 
-	CONTEXT_COMPATIBILITY_PROFILE_BIT = CONTEXT_COMPATIBILITY_PROFILE_BIT, 
-	LINES_ADJACENCY = LINES_ADJACENCY, 
-	LINE_STRIP_ADJACENCY = LINE_STRIP_ADJACENCY, 
-	TRIANGLES_ADJACENCY = TRIANGLES_ADJACENCY, 
-	TRIANGLE_STRIP_ADJACENCY = TRIANGLE_STRIP_ADJACENCY, 
-	PROGRAM_POINT_SIZE = PROGRAM_POINT_SIZE, 
-	MAX_GEOMETRY_TEXTURE_IMAGE_UNITS = MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, 
-	FRAMEBUFFER_ATTACHMENT_LAYERED = FRAMEBUFFER_ATTACHMENT_LAYERED, 
-	FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS = FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS, 
-	GEOMETRY_SHADER = GEOMETRY_SHADER, 
-	GEOMETRY_VERTICES_OUT = GEOMETRY_VERTICES_OUT, 
-	GEOMETRY_INPUT_TYPE = GEOMETRY_INPUT_TYPE, 
-	GEOMETRY_OUTPUT_TYPE = GEOMETRY_OUTPUT_TYPE, 
-	MAX_GEOMETRY_UNIFORM_COMPONENTS = MAX_GEOMETRY_UNIFORM_COMPONENTS, 
-	MAX_GEOMETRY_OUTPUT_VERTICES = MAX_GEOMETRY_OUTPUT_VERTICES, 
-	MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, 
-	MAX_VERTEX_OUTPUT_COMPONENTS = MAX_VERTEX_OUTPUT_COMPONENTS, 
-	MAX_GEOMETRY_INPUT_COMPONENTS = MAX_GEOMETRY_INPUT_COMPONENTS, 
-	MAX_GEOMETRY_OUTPUT_COMPONENTS = MAX_GEOMETRY_OUTPUT_COMPONENTS, 
-	MAX_FRAGMENT_INPUT_COMPONENTS = MAX_FRAGMENT_INPUT_COMPONENTS, 
-	CONTEXT_PROFILE_MASK = CONTEXT_PROFILE_MASK, 
-	DEPTH_CLAMP = DEPTH_CLAMP, 
-	QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION = QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION, 
-	FIRST_VERTEX_CONVENTION = FIRST_VERTEX_CONVENTION, 
-	LAST_VERTEX_CONVENTION = LAST_VERTEX_CONVENTION, 
-	PROVOKING_VERTEX = PROVOKING_VERTEX, 
-	TEXTURE_CUBE_MAP_SEAMLESS = TEXTURE_CUBE_MAP_SEAMLESS, 
-	MAX_SERVER_WAIT_TIMEOUT = MAX_SERVER_WAIT_TIMEOUT, 
-	OBJECT_TYPE = OBJECT_TYPE, 
-	SYNC_CONDITION = SYNC_CONDITION, 
-	SYNC_STATUS = SYNC_STATUS, 
-	SYNC_FLAGS = SYNC_FLAGS, 
-	SYNC_FENCE = SYNC_FENCE, 
-	SYNC_GPU_COMMANDS_COMPLETE = SYNC_GPU_COMMANDS_COMPLETE, 
-	UNSIGNALED = UNSIGNALED, 
-	SIGNALED = SIGNALED, 
-	ALREADY_SIGNALED = ALREADY_SIGNALED, 
-	TIMEOUT_EXPIRED = TIMEOUT_EXPIRED, 
-	CONDITION_SATISFIED = CONDITION_SATISFIED, 
-	WAIT_FAILED = WAIT_FAILED, 
-	TIMEOUT_IGNORED = TIMEOUT_IGNORED, 
-	SYNC_FLUSH_COMMANDS_BIT = SYNC_FLUSH_COMMANDS_BIT, 
-	SAMPLE_POSITION = SAMPLE_POSITION, 
-	SAMPLE_MASK = SAMPLE_MASK, 
-	SAMPLE_MASK_VALUE = SAMPLE_MASK_VALUE, 
-	MAX_SAMPLE_MASK_WORDS = MAX_SAMPLE_MASK_WORDS, 
-	TEXTURE_2D_MULTISAMPLE = TEXTURE_2D_MULTISAMPLE, 
-	PROXY_TEXTURE_2D_MULTISAMPLE = PROXY_TEXTURE_2D_MULTISAMPLE, 
-	TEXTURE_2D_MULTISAMPLE_ARRAY = TEXTURE_2D_MULTISAMPLE_ARRAY, 
-	PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY = PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY, 
-	TEXTURE_BINDING_2D_MULTISAMPLE = TEXTURE_BINDING_2D_MULTISAMPLE, 
-	TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY = TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY, 
-	TEXTURE_SAMPLES = TEXTURE_SAMPLES, 
-	TEXTURE_FIXED_SAMPLE_LOCATIONS = TEXTURE_FIXED_SAMPLE_LOCATIONS, 
-	SAMPLER_2D_MULTISAMPLE = SAMPLER_2D_MULTISAMPLE, 
-	INT_SAMPLER_2D_MULTISAMPLE = INT_SAMPLER_2D_MULTISAMPLE, 
-	UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, 
-	SAMPLER_2D_MULTISAMPLE_ARRAY = SAMPLER_2D_MULTISAMPLE_ARRAY, 
-	INT_SAMPLER_2D_MULTISAMPLE_ARRAY = INT_SAMPLER_2D_MULTISAMPLE_ARRAY, 
-	UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, 
-	MAX_COLOR_TEXTURE_SAMPLES = MAX_COLOR_TEXTURE_SAMPLES, 
-	MAX_DEPTH_TEXTURE_SAMPLES = MAX_DEPTH_TEXTURE_SAMPLES, 
-	MAX_INTEGER_SAMPLES = MAX_INTEGER_SAMPLES, 
-
-	VERTEX_ATTRIB_ARRAY_DIVISOR = VERTEX_ATTRIB_ARRAY_DIVISOR, 
-	SRC1_COLOR = SRC1_COLOR, 
-	ONE_MINUS_SRC1_COLOR = ONE_MINUS_SRC1_COLOR, 
-	ONE_MINUS_SRC1_ALPHA = ONE_MINUS_SRC1_ALPHA, 
-	MAX_DUAL_SOURCE_DRAW_BUFFERS = MAX_DUAL_SOURCE_DRAW_BUFFERS, 
-	ANY_SAMPLES_PASSED = ANY_SAMPLES_PASSED, 
-	SAMPLER_BINDING = SAMPLER_BINDING, 
-	RGB10_A2UI = RGB10_A2UI, 
-	TEXTURE_SWIZZLE_R = TEXTURE_SWIZZLE_R, 
-	TEXTURE_SWIZZLE_G = TEXTURE_SWIZZLE_G, 
-	TEXTURE_SWIZZLE_B = TEXTURE_SWIZZLE_B, 
-	TEXTURE_SWIZZLE_A = TEXTURE_SWIZZLE_A, 
-	TEXTURE_SWIZZLE_RGBA = TEXTURE_SWIZZLE_RGBA, 
-	TIME_ELAPSED = TIME_ELAPSED, 
-	TIMESTAMP = TIMESTAMP, 
-	INT_2_10_10_10_REV = INT_2_10_10_10_REV, 
-
-	SAMPLE_SHADING = SAMPLE_SHADING, 
-	MIN_SAMPLE_SHADING_VALUE = MIN_SAMPLE_SHADING_VALUE, 
-	MIN_PROGRAM_TEXTURE_GATHER_OFFSET = MIN_PROGRAM_TEXTURE_GATHER_OFFSET, 
-	MAX_PROGRAM_TEXTURE_GATHER_OFFSET = MAX_PROGRAM_TEXTURE_GATHER_OFFSET, 
-	TEXTURE_CUBE_MAP_ARRAY = TEXTURE_CUBE_MAP_ARRAY, 
-	TEXTURE_BINDING_CUBE_MAP_ARRAY = TEXTURE_BINDING_CUBE_MAP_ARRAY, 
-	PROXY_TEXTURE_CUBE_MAP_ARRAY = PROXY_TEXTURE_CUBE_MAP_ARRAY, 
-	SAMPLER_CUBE_MAP_ARRAY = SAMPLER_CUBE_MAP_ARRAY, 
-	SAMPLER_CUBE_MAP_ARRAY_SHADOW = SAMPLER_CUBE_MAP_ARRAY_SHADOW, 
-	INT_SAMPLER_CUBE_MAP_ARRAY = INT_SAMPLER_CUBE_MAP_ARRAY, 
-	UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY = UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY, 
-	DRAW_INDIRECT_BUFFER = DRAW_INDIRECT_BUFFER, 
-	DRAW_INDIRECT_BUFFER_BINDING = DRAW_INDIRECT_BUFFER_BINDING, 
-	GEOMETRY_SHADER_INVOCATIONS = GEOMETRY_SHADER_INVOCATIONS, 
-	MAX_GEOMETRY_SHADER_INVOCATIONS = MAX_GEOMETRY_SHADER_INVOCATIONS, 
-	MIN_FRAGMENT_INTERPOLATION_OFFSET = MIN_FRAGMENT_INTERPOLATION_OFFSET, 
-	MAX_FRAGMENT_INTERPOLATION_OFFSET = MAX_FRAGMENT_INTERPOLATION_OFFSET, 
-	FRAGMENT_INTERPOLATION_OFFSET_BITS = FRAGMENT_INTERPOLATION_OFFSET_BITS, 
-	MAX_VERTEX_STREAMS = MAX_VERTEX_STREAMS, 
-	DOUBLE_VEC2 = DOUBLE_VEC2, 
-	DOUBLE_VEC3 = DOUBLE_VEC3, 
-	DOUBLE_VEC4 = DOUBLE_VEC4, 
-	DOUBLE_MAT2 = DOUBLE_MAT2, 
-	DOUBLE_MAT3 = DOUBLE_MAT3, 
-	DOUBLE_MAT4 = DOUBLE_MAT4, 
-	DOUBLE_MAT2x3 = DOUBLE_MAT2x3, 
-	DOUBLE_MAT2x4 = DOUBLE_MAT2x4, 
-	DOUBLE_MAT3x2 = DOUBLE_MAT3x2, 
-	DOUBLE_MAT3x4 = DOUBLE_MAT3x4, 
-	DOUBLE_MAT4x2 = DOUBLE_MAT4x2, 
-	DOUBLE_MAT4x3 = DOUBLE_MAT4x3, 
-	ACTIVE_SUBROUTINES = ACTIVE_SUBROUTINES, 
-	ACTIVE_SUBROUTINE_UNIFORMS = ACTIVE_SUBROUTINE_UNIFORMS, 
-	ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS = ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, 
-	ACTIVE_SUBROUTINE_MAX_LENGTH = ACTIVE_SUBROUTINE_MAX_LENGTH, 
-	ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH = ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH, 
-	MAX_SUBROUTINES = MAX_SUBROUTINES, 
-	MAX_SUBROUTINE_UNIFORM_LOCATIONS = MAX_SUBROUTINE_UNIFORM_LOCATIONS, 
-	NUM_COMPATIBLE_SUBROUTINES = NUM_COMPATIBLE_SUBROUTINES, 
-	COMPATIBLE_SUBROUTINES = COMPATIBLE_SUBROUTINES, 
-	PATCHES = PATCHES, 
-	PATCH_VERTICES = PATCH_VERTICES, 
-	PATCH_DEFAULT_INNER_LEVEL = PATCH_DEFAULT_INNER_LEVEL, 
-	PATCH_DEFAULT_OUTER_LEVEL = PATCH_DEFAULT_OUTER_LEVEL, 
-	TESS_CONTROL_OUTPUT_VERTICES = TESS_CONTROL_OUTPUT_VERTICES, 
-	TESS_GEN_MODE = TESS_GEN_MODE, 
-	TESS_GEN_SPACING = TESS_GEN_SPACING, 
-	TESS_GEN_VERTEX_ORDER = TESS_GEN_VERTEX_ORDER, 
-	TESS_GEN_POINT_MODE = TESS_GEN_POINT_MODE, 
-	ISOLINES = ISOLINES, 
-	FRACTIONAL_ODD = FRACTIONAL_ODD, 
-	FRACTIONAL_EVEN = FRACTIONAL_EVEN, 
-	MAX_PATCH_VERTICES = MAX_PATCH_VERTICES, 
-	MAX_TESS_GEN_LEVEL = MAX_TESS_GEN_LEVEL, 
-	MAX_TESS_CONTROL_UNIFORM_COMPONENTS = MAX_TESS_CONTROL_UNIFORM_COMPONENTS, 
-	MAX_TESS_EVALUATION_UNIFORM_COMPONENTS = MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, 
-	MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS = MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, 
-	MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS = MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, 
-	MAX_TESS_CONTROL_OUTPUT_COMPONENTS = MAX_TESS_CONTROL_OUTPUT_COMPONENTS, 
-	MAX_TESS_PATCH_COMPONENTS = MAX_TESS_PATCH_COMPONENTS, 
-	MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS = MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, 
-	MAX_TESS_EVALUATION_OUTPUT_COMPONENTS = MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, 
-	MAX_TESS_CONTROL_UNIFORM_BLOCKS = MAX_TESS_CONTROL_UNIFORM_BLOCKS, 
-	MAX_TESS_EVALUATION_UNIFORM_BLOCKS = MAX_TESS_EVALUATION_UNIFORM_BLOCKS, 
-	MAX_TESS_CONTROL_INPUT_COMPONENTS = MAX_TESS_CONTROL_INPUT_COMPONENTS, 
-	MAX_TESS_EVALUATION_INPUT_COMPONENTS = MAX_TESS_EVALUATION_INPUT_COMPONENTS, 
-	MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS = MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, 
-	MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS = MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, 
-	UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER = UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER, 
-	UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER = UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER, 
-	TESS_EVALUATION_SHADER = TESS_EVALUATION_SHADER, 
-	TESS_CONTROL_SHADER = TESS_CONTROL_SHADER, 
-	TRANSFORM_FEEDBACK = TRANSFORM_FEEDBACK, 
-	TRANSFORM_FEEDBACK_BUFFER_PAUSED = TRANSFORM_FEEDBACK_BUFFER_PAUSED, 
-	TRANSFORM_FEEDBACK_BUFFER_ACTIVE = TRANSFORM_FEEDBACK_BUFFER_ACTIVE, 
-	TRANSFORM_FEEDBACK_BINDING = TRANSFORM_FEEDBACK_BINDING, 
-	MAX_TRANSFORM_FEEDBACK_BUFFERS = MAX_TRANSFORM_FEEDBACK_BUFFERS, 
-
-	FIXED = FIXED, 
-	IMPLEMENTATION_COLOR_READ_TYPE = IMPLEMENTATION_COLOR_READ_TYPE, 
-	IMPLEMENTATION_COLOR_READ_FORMAT = IMPLEMENTATION_COLOR_READ_FORMAT, 
-	LOW_FLOAT = LOW_FLOAT, 
-	MEDIUM_FLOAT = MEDIUM_FLOAT, 
-	HIGH_FLOAT = HIGH_FLOAT, 
-	LOW_INT = LOW_INT, 
-	MEDIUM_INT = MEDIUM_INT, 
-	HIGH_INT = HIGH_INT, 
-	SHADER_COMPILER = SHADER_COMPILER, 
-	SHADER_BINARY_FORMATS = SHADER_BINARY_FORMATS, 
-	NUM_SHADER_BINARY_FORMATS = NUM_SHADER_BINARY_FORMATS, 
-	MAX_VERTEX_UNIFORM_VECTORS = MAX_VERTEX_UNIFORM_VECTORS, 
-	MAX_VARYING_VECTORS = MAX_VARYING_VECTORS, 
-	MAX_FRAGMENT_UNIFORM_VECTORS = MAX_FRAGMENT_UNIFORM_VECTORS, 
-	RGB565 = RGB565, 
-	PROGRAM_BINARY_RETRIEVABLE_HINT = PROGRAM_BINARY_RETRIEVABLE_HINT, 
-	PROGRAM_BINARY_LENGTH = PROGRAM_BINARY_LENGTH, 
-	NUM_PROGRAM_BINARY_FORMATS = NUM_PROGRAM_BINARY_FORMATS, 
-	PROGRAM_BINARY_FORMATS = PROGRAM_BINARY_FORMATS, 
-	VERTEX_SHADER_BIT = VERTEX_SHADER_BIT, 
-	FRAGMENT_SHADER_BIT = FRAGMENT_SHADER_BIT, 
-	GEOMETRY_SHADER_BIT = GEOMETRY_SHADER_BIT, 
-	TESS_CONTROL_SHADER_BIT = TESS_CONTROL_SHADER_BIT, 
-	TESS_EVALUATION_SHADER_BIT = TESS_EVALUATION_SHADER_BIT, 
-	ALL_SHADER_BITS = ALL_SHADER_BITS, 
-	PROGRAM_SEPARABLE = PROGRAM_SEPARABLE, 
-	ACTIVE_PROGRAM = ACTIVE_PROGRAM, 
-	PROGRAM_PIPELINE_BINDING = PROGRAM_PIPELINE_BINDING, 
-	MAX_VIEWPORTS = MAX_VIEWPORTS, 
-	VIEWPORT_SUBPIXEL_BITS = VIEWPORT_SUBPIXEL_BITS, 
-	VIEWPORT_BOUNDS_RANGE = VIEWPORT_BOUNDS_RANGE, 
-	LAYER_PROVOKING_VERTEX = LAYER_PROVOKING_VERTEX, 
-	VIEWPORT_INDEX_PROVOKING_VERTEX = VIEWPORT_INDEX_PROVOKING_VERTEX, 
-	UNDEFINED_VERTEX = UNDEFINED_VERTEX, 
-
-	COPY_READ_BUFFER_BINDING = COPY_READ_BUFFER_BINDING, 
-	COPY_WRITE_BUFFER_BINDING = COPY_WRITE_BUFFER_BINDING, 
-	TRANSFORM_FEEDBACK_ACTIVE = TRANSFORM_FEEDBACK_ACTIVE, 
-	TRANSFORM_FEEDBACK_PAUSED = TRANSFORM_FEEDBACK_PAUSED, 
-	UNPACK_COMPRESSED_BLOCK_WIDTH = UNPACK_COMPRESSED_BLOCK_WIDTH, 
-	UNPACK_COMPRESSED_BLOCK_HEIGHT = UNPACK_COMPRESSED_BLOCK_HEIGHT, 
-	UNPACK_COMPRESSED_BLOCK_DEPTH = UNPACK_COMPRESSED_BLOCK_DEPTH, 
-	UNPACK_COMPRESSED_BLOCK_SIZE = UNPACK_COMPRESSED_BLOCK_SIZE, 
-	PACK_COMPRESSED_BLOCK_WIDTH = PACK_COMPRESSED_BLOCK_WIDTH, 
-	PACK_COMPRESSED_BLOCK_HEIGHT = PACK_COMPRESSED_BLOCK_HEIGHT, 
-	PACK_COMPRESSED_BLOCK_DEPTH = PACK_COMPRESSED_BLOCK_DEPTH, 
-	PACK_COMPRESSED_BLOCK_SIZE = PACK_COMPRESSED_BLOCK_SIZE, 
-	NUM_SAMPLE_COUNTS = NUM_SAMPLE_COUNTS, 
-	MIN_MAP_BUFFER_ALIGNMENT = MIN_MAP_BUFFER_ALIGNMENT, 
-	ATOMIC_COUNTER_BUFFER = ATOMIC_COUNTER_BUFFER, 
-	ATOMIC_COUNTER_BUFFER_BINDING = ATOMIC_COUNTER_BUFFER_BINDING, 
-	ATOMIC_COUNTER_BUFFER_START = ATOMIC_COUNTER_BUFFER_START, 
-	ATOMIC_COUNTER_BUFFER_SIZE = ATOMIC_COUNTER_BUFFER_SIZE, 
-	ATOMIC_COUNTER_BUFFER_DATA_SIZE = ATOMIC_COUNTER_BUFFER_DATA_SIZE, 
-	ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS = ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS, 
-	ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES = ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES, 
-	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER, 
-	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER, 
-	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER, 
-	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER, 
-	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER, 
-	MAX_VERTEX_ATOMIC_COUNTER_BUFFERS = MAX_VERTEX_ATOMIC_COUNTER_BUFFERS, 
-	MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS = MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, 
-	MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS = MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, 
-	MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS = MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS, 
-	MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS = MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, 
-	MAX_COMBINED_ATOMIC_COUNTER_BUFFERS = MAX_COMBINED_ATOMIC_COUNTER_BUFFERS, 
-	MAX_VERTEX_ATOMIC_COUNTERS = MAX_VERTEX_ATOMIC_COUNTERS, 
-	MAX_TESS_CONTROL_ATOMIC_COUNTERS = MAX_TESS_CONTROL_ATOMIC_COUNTERS, 
-	MAX_TESS_EVALUATION_ATOMIC_COUNTERS = MAX_TESS_EVALUATION_ATOMIC_COUNTERS, 
-	MAX_GEOMETRY_ATOMIC_COUNTERS = MAX_GEOMETRY_ATOMIC_COUNTERS, 
-	MAX_FRAGMENT_ATOMIC_COUNTERS = MAX_FRAGMENT_ATOMIC_COUNTERS, 
-	MAX_COMBINED_ATOMIC_COUNTERS = MAX_COMBINED_ATOMIC_COUNTERS, 
-	MAX_ATOMIC_COUNTER_BUFFER_SIZE = MAX_ATOMIC_COUNTER_BUFFER_SIZE, 
-	MAX_ATOMIC_COUNTER_BUFFER_BINDINGS = MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, 
-	ACTIVE_ATOMIC_COUNTER_BUFFERS = ACTIVE_ATOMIC_COUNTER_BUFFERS, 
-	UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX = UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX, 
-	UNSIGNED_INT_ATOMIC_COUNTER = UNSIGNED_INT_ATOMIC_COUNTER, 
-	VERTEX_ATTRIB_ARRAY_BARRIER_BIT = VERTEX_ATTRIB_ARRAY_BARRIER_BIT, 
-	ELEMENT_ARRAY_BARRIER_BIT = ELEMENT_ARRAY_BARRIER_BIT, 
-	UNIFORM_BARRIER_BIT = UNIFORM_BARRIER_BIT, 
-	TEXTURE_FETCH_BARRIER_BIT = TEXTURE_FETCH_BARRIER_BIT, 
-	SHADER_IMAGE_ACCESS_BARRIER_BIT = SHADER_IMAGE_ACCESS_BARRIER_BIT, 
-	COMMAND_BARRIER_BIT = COMMAND_BARRIER_BIT, 
-	PIXEL_BUFFER_BARRIER_BIT = PIXEL_BUFFER_BARRIER_BIT, 
-	TEXTURE_UPDATE_BARRIER_BIT = TEXTURE_UPDATE_BARRIER_BIT, 
-	BUFFER_UPDATE_BARRIER_BIT = BUFFER_UPDATE_BARRIER_BIT, 
-	FRAMEBUFFER_BARRIER_BIT = FRAMEBUFFER_BARRIER_BIT, 
-	TRANSFORM_FEEDBACK_BARRIER_BIT = TRANSFORM_FEEDBACK_BARRIER_BIT, 
-	ATOMIC_COUNTER_BARRIER_BIT = ATOMIC_COUNTER_BARRIER_BIT, 
-	ALL_BARRIER_BITS = ALL_BARRIER_BITS, 
-	MAX_IMAGE_UNITS = MAX_IMAGE_UNITS, 
-	MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS = MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS, 
-	IMAGE_BINDING_NAME = IMAGE_BINDING_NAME, 
-	IMAGE_BINDING_LEVEL = IMAGE_BINDING_LEVEL, 
-	IMAGE_BINDING_LAYERED = IMAGE_BINDING_LAYERED, 
-	IMAGE_BINDING_LAYER = IMAGE_BINDING_LAYER, 
-	IMAGE_BINDING_ACCESS = IMAGE_BINDING_ACCESS, 
-	IMAGE_1D = IMAGE_1D, 
-	IMAGE_2D = IMAGE_2D, 
-	IMAGE_3D = IMAGE_3D, 
-	IMAGE_2D_RECT = IMAGE_2D_RECT, 
-	IMAGE_CUBE = IMAGE_CUBE, 
-	IMAGE_BUFFER = IMAGE_BUFFER, 
-	IMAGE_1D_ARRAY = IMAGE_1D_ARRAY, 
-	IMAGE_2D_ARRAY = IMAGE_2D_ARRAY, 
-	IMAGE_CUBE_MAP_ARRAY = IMAGE_CUBE_MAP_ARRAY, 
-	IMAGE_2D_MULTISAMPLE = IMAGE_2D_MULTISAMPLE, 
-	IMAGE_2D_MULTISAMPLE_ARRAY = IMAGE_2D_MULTISAMPLE_ARRAY, 
-	INT_IMAGE_1D = INT_IMAGE_1D, 
-	INT_IMAGE_2D = INT_IMAGE_2D, 
-	INT_IMAGE_3D = INT_IMAGE_3D, 
-	INT_IMAGE_2D_RECT = INT_IMAGE_2D_RECT, 
-	INT_IMAGE_CUBE = INT_IMAGE_CUBE, 
-	INT_IMAGE_BUFFER = INT_IMAGE_BUFFER, 
-	INT_IMAGE_1D_ARRAY = INT_IMAGE_1D_ARRAY, 
-	INT_IMAGE_2D_ARRAY = INT_IMAGE_2D_ARRAY, 
-	INT_IMAGE_CUBE_MAP_ARRAY = INT_IMAGE_CUBE_MAP_ARRAY, 
-	INT_IMAGE_2D_MULTISAMPLE = INT_IMAGE_2D_MULTISAMPLE, 
-	INT_IMAGE_2D_MULTISAMPLE_ARRAY = INT_IMAGE_2D_MULTISAMPLE_ARRAY, 
-	UNSIGNED_INT_IMAGE_1D = UNSIGNED_INT_IMAGE_1D, 
-	UNSIGNED_INT_IMAGE_2D = UNSIGNED_INT_IMAGE_2D, 
-	UNSIGNED_INT_IMAGE_3D = UNSIGNED_INT_IMAGE_3D, 
-	UNSIGNED_INT_IMAGE_2D_RECT = UNSIGNED_INT_IMAGE_2D_RECT, 
-	UNSIGNED_INT_IMAGE_CUBE = UNSIGNED_INT_IMAGE_CUBE, 
-	UNSIGNED_INT_IMAGE_BUFFER = UNSIGNED_INT_IMAGE_BUFFER, 
-	UNSIGNED_INT_IMAGE_1D_ARRAY = UNSIGNED_INT_IMAGE_1D_ARRAY, 
-	UNSIGNED_INT_IMAGE_2D_ARRAY = UNSIGNED_INT_IMAGE_2D_ARRAY, 
-	UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY = UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY, 
-	UNSIGNED_INT_IMAGE_2D_MULTISAMPLE = UNSIGNED_INT_IMAGE_2D_MULTISAMPLE, 
-	UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY = UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY, 
-	MAX_IMAGE_SAMPLES = MAX_IMAGE_SAMPLES, 
-	IMAGE_BINDING_FORMAT = IMAGE_BINDING_FORMAT, 
-	IMAGE_FORMAT_COMPATIBILITY_TYPE = IMAGE_FORMAT_COMPATIBILITY_TYPE, 
-	IMAGE_FORMAT_COMPATIBILITY_BY_SIZE = IMAGE_FORMAT_COMPATIBILITY_BY_SIZE, 
-	IMAGE_FORMAT_COMPATIBILITY_BY_CLASS = IMAGE_FORMAT_COMPATIBILITY_BY_CLASS, 
-	MAX_VERTEX_IMAGE_UNIFORMS = MAX_VERTEX_IMAGE_UNIFORMS, 
-	MAX_TESS_CONTROL_IMAGE_UNIFORMS = MAX_TESS_CONTROL_IMAGE_UNIFORMS, 
-	MAX_TESS_EVALUATION_IMAGE_UNIFORMS = MAX_TESS_EVALUATION_IMAGE_UNIFORMS, 
-	MAX_GEOMETRY_IMAGE_UNIFORMS = MAX_GEOMETRY_IMAGE_UNIFORMS, 
-	MAX_FRAGMENT_IMAGE_UNIFORMS = MAX_FRAGMENT_IMAGE_UNIFORMS, 
-	MAX_COMBINED_IMAGE_UNIFORMS = MAX_COMBINED_IMAGE_UNIFORMS, 
-	COMPRESSED_RGBA_BPTC_UNORM = COMPRESSED_RGBA_BPTC_UNORM, 
-	COMPRESSED_SRGB_ALPHA_BPTC_UNORM = COMPRESSED_SRGB_ALPHA_BPTC_UNORM, 
-	COMPRESSED_RGB_BPTC_SIGNED_FLOAT = COMPRESSED_RGB_BPTC_SIGNED_FLOAT, 
-	COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, 
-	TEXTURE_IMMUTABLE_FORMAT = TEXTURE_IMMUTABLE_FORMAT, 
-
-	NUM_SHADING_LANGUAGE_VERSIONS = NUM_SHADING_LANGUAGE_VERSIONS, 
-	VERTEX_ATTRIB_ARRAY_LONG = VERTEX_ATTRIB_ARRAY_LONG, 
-	COMPRESSED_RGB8_ETC2 = COMPRESSED_RGB8_ETC2, 
-	COMPRESSED_SRGB8_ETC2 = COMPRESSED_SRGB8_ETC2, 
-	COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 
-	COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 
-	COMPRESSED_RGBA8_ETC2_EAC = COMPRESSED_RGBA8_ETC2_EAC, 
-	COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 
-	COMPRESSED_R11_EAC = COMPRESSED_R11_EAC, 
-	COMPRESSED_SIGNED_R11_EAC = COMPRESSED_SIGNED_R11_EAC, 
-	COMPRESSED_RG11_EAC = COMPRESSED_RG11_EAC, 
-	COMPRESSED_SIGNED_RG11_EAC = COMPRESSED_SIGNED_RG11_EAC, 
-	PRIMITIVE_RESTART_FIXED_INDEX = PRIMITIVE_RESTART_FIXED_INDEX, 
-	ANY_SAMPLES_PASSED_CONSERVATIVE = ANY_SAMPLES_PASSED_CONSERVATIVE, 
-	MAX_ELEMENT_INDEX = MAX_ELEMENT_INDEX, 
-	COMPUTE_SHADER = COMPUTE_SHADER, 
-	MAX_COMPUTE_UNIFORM_BLOCKS = MAX_COMPUTE_UNIFORM_BLOCKS, 
-	MAX_COMPUTE_TEXTURE_IMAGE_UNITS = MAX_COMPUTE_TEXTURE_IMAGE_UNITS, 
-	MAX_COMPUTE_IMAGE_UNIFORMS = MAX_COMPUTE_IMAGE_UNIFORMS, 
-	MAX_COMPUTE_SHARED_MEMORY_SIZE = MAX_COMPUTE_SHARED_MEMORY_SIZE, 
-	MAX_COMPUTE_UNIFORM_COMPONENTS = MAX_COMPUTE_UNIFORM_COMPONENTS, 
-	MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS = MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, 
-	MAX_COMPUTE_ATOMIC_COUNTERS = MAX_COMPUTE_ATOMIC_COUNTERS, 
-	MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS = MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS, 
-	MAX_COMPUTE_WORK_GROUP_INVOCATIONS = MAX_COMPUTE_WORK_GROUP_INVOCATIONS, 
-	MAX_COMPUTE_WORK_GROUP_COUNT = MAX_COMPUTE_WORK_GROUP_COUNT, 
-	MAX_COMPUTE_WORK_GROUP_SIZE = MAX_COMPUTE_WORK_GROUP_SIZE, 
-	COMPUTE_WORK_GROUP_SIZE = COMPUTE_WORK_GROUP_SIZE, 
-	UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER = UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER, 
-	ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER = ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER, 
-	DISPATCH_INDIRECT_BUFFER = DISPATCH_INDIRECT_BUFFER, 
-	DISPATCH_INDIRECT_BUFFER_BINDING = DISPATCH_INDIRECT_BUFFER_BINDING, 
-	COMPUTE_SHADER_BIT = COMPUTE_SHADER_BIT, 
-	DEBUG_OUTPUT_SYNCHRONOUS = DEBUG_OUTPUT_SYNCHRONOUS, 
-	DEBUG_NEXT_LOGGED_MESSAGE_LENGTH = DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, 
-	DEBUG_CALLBACK_FUNCTION = DEBUG_CALLBACK_FUNCTION, 
-	DEBUG_CALLBACK_USER_PARAM = DEBUG_CALLBACK_USER_PARAM, 
-	DEBUG_SOURCE_API = DEBUG_SOURCE_API, 
-	DEBUG_SOURCE_WINDOW_SYSTEM = DEBUG_SOURCE_WINDOW_SYSTEM, 
-	DEBUG_SOURCE_SHADER_COMPILER = DEBUG_SOURCE_SHADER_COMPILER, 
-	DEBUG_SOURCE_THIRD_PARTY = DEBUG_SOURCE_THIRD_PARTY, 
-	DEBUG_SOURCE_APPLICATION = DEBUG_SOURCE_APPLICATION, 
-	DEBUG_SOURCE_OTHER = DEBUG_SOURCE_OTHER, 
-	DEBUG_TYPE_ERROR = DEBUG_TYPE_ERROR, 
-	DEBUG_TYPE_DEPRECATED_BEHAVIOR = DEBUG_TYPE_DEPRECATED_BEHAVIOR, 
-	DEBUG_TYPE_UNDEFINED_BEHAVIOR = DEBUG_TYPE_UNDEFINED_BEHAVIOR, 
-	DEBUG_TYPE_PORTABILITY = DEBUG_TYPE_PORTABILITY, 
-	DEBUG_TYPE_PERFORMANCE = DEBUG_TYPE_PERFORMANCE, 
-	DEBUG_TYPE_OTHER = DEBUG_TYPE_OTHER, 
-	MAX_DEBUG_MESSAGE_LENGTH = MAX_DEBUG_MESSAGE_LENGTH, 
-	MAX_DEBUG_LOGGED_MESSAGES = MAX_DEBUG_LOGGED_MESSAGES, 
-	DEBUG_LOGGED_MESSAGES = DEBUG_LOGGED_MESSAGES, 
-	DEBUG_SEVERITY_HIGH = DEBUG_SEVERITY_HIGH, 
-	DEBUG_SEVERITY_MEDIUM = DEBUG_SEVERITY_MEDIUM, 
-	DEBUG_SEVERITY_LOW = DEBUG_SEVERITY_LOW, 
-	DEBUG_TYPE_MARKER = DEBUG_TYPE_MARKER, 
-	DEBUG_TYPE_PUSH_GROUP = DEBUG_TYPE_PUSH_GROUP, 
-	DEBUG_TYPE_POP_GROUP = DEBUG_TYPE_POP_GROUP, 
-	DEBUG_SEVERITY_NOTIFICATION = DEBUG_SEVERITY_NOTIFICATION, 
-	MAX_DEBUG_GROUP_STACK_DEPTH = MAX_DEBUG_GROUP_STACK_DEPTH, 
-	DEBUG_GROUP_STACK_DEPTH = DEBUG_GROUP_STACK_DEPTH, 
-	BUFFER = BUFFER, 
-	SHADER = SHADER, 
-	PROGRAM = PROGRAM, 
-	QUERY = QUERY, 
-	PROGRAM_PIPELINE = PROGRAM_PIPELINE, 
-	SAMPLER = SAMPLER, 
-	MAX_LABEL_LENGTH = MAX_LABEL_LENGTH, 
-	DEBUG_OUTPUT = DEBUG_OUTPUT, 
-	CONTEXT_FLAG_DEBUG_BIT = CONTEXT_FLAG_DEBUG_BIT, 
-	MAX_UNIFORM_LOCATIONS = MAX_UNIFORM_LOCATIONS, 
-	FRAMEBUFFER_DEFAULT_WIDTH = FRAMEBUFFER_DEFAULT_WIDTH, 
-	FRAMEBUFFER_DEFAULT_HEIGHT = FRAMEBUFFER_DEFAULT_HEIGHT, 
-	FRAMEBUFFER_DEFAULT_LAYERS = FRAMEBUFFER_DEFAULT_LAYERS, 
-	FRAMEBUFFER_DEFAULT_SAMPLES = FRAMEBUFFER_DEFAULT_SAMPLES, 
-	FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS, 
-	MAX_FRAMEBUFFER_WIDTH = MAX_FRAMEBUFFER_WIDTH, 
-	MAX_FRAMEBUFFER_HEIGHT = MAX_FRAMEBUFFER_HEIGHT, 
-	MAX_FRAMEBUFFER_LAYERS = MAX_FRAMEBUFFER_LAYERS, 
-	MAX_FRAMEBUFFER_SAMPLES = MAX_FRAMEBUFFER_SAMPLES, 
-	INTERNALFORMAT_SUPPORTED = INTERNALFORMAT_SUPPORTED, 
-	INTERNALFORMAT_PREFERRED = INTERNALFORMAT_PREFERRED, 
-	INTERNALFORMAT_RED_SIZE = INTERNALFORMAT_RED_SIZE, 
-	INTERNALFORMAT_GREEN_SIZE = INTERNALFORMAT_GREEN_SIZE, 
-	INTERNALFORMAT_BLUE_SIZE = INTERNALFORMAT_BLUE_SIZE, 
-	INTERNALFORMAT_ALPHA_SIZE = INTERNALFORMAT_ALPHA_SIZE, 
-	INTERNALFORMAT_DEPTH_SIZE = INTERNALFORMAT_DEPTH_SIZE, 
-	INTERNALFORMAT_STENCIL_SIZE = INTERNALFORMAT_STENCIL_SIZE, 
-	INTERNALFORMAT_SHARED_SIZE = INTERNALFORMAT_SHARED_SIZE, 
-	INTERNALFORMAT_RED_TYPE = INTERNALFORMAT_RED_TYPE, 
-	INTERNALFORMAT_GREEN_TYPE = INTERNALFORMAT_GREEN_TYPE, 
-	INTERNALFORMAT_BLUE_TYPE = INTERNALFORMAT_BLUE_TYPE, 
-	INTERNALFORMAT_ALPHA_TYPE = INTERNALFORMAT_ALPHA_TYPE, 
-	INTERNALFORMAT_DEPTH_TYPE = INTERNALFORMAT_DEPTH_TYPE, 
-	INTERNALFORMAT_STENCIL_TYPE = INTERNALFORMAT_STENCIL_TYPE, 
-	MAX_WIDTH = MAX_WIDTH, 
-	MAX_HEIGHT = MAX_HEIGHT, 
-	MAX_DEPTH = MAX_DEPTH, 
-	MAX_LAYERS = MAX_LAYERS, 
-	MAX_COMBINED_DIMENSIONS = MAX_COMBINED_DIMENSIONS, 
-	COLOR_COMPONENTS = COLOR_COMPONENTS, 
-	DEPTH_COMPONENTS = DEPTH_COMPONENTS, 
-	STENCIL_COMPONENTS = STENCIL_COMPONENTS, 
-	COLOR_RENDERABLE = COLOR_RENDERABLE, 
-	DEPTH_RENDERABLE = DEPTH_RENDERABLE, 
-	STENCIL_RENDERABLE = STENCIL_RENDERABLE, 
-	FRAMEBUFFER_RENDERABLE = FRAMEBUFFER_RENDERABLE, 
-	FRAMEBUFFER_RENDERABLE_LAYERED = FRAMEBUFFER_RENDERABLE_LAYERED, 
-	FRAMEBUFFER_BLEND = FRAMEBUFFER_BLEND, 
-	READ_PIXELS = READ_PIXELS, 
-	READ_PIXELS_FORMAT = READ_PIXELS_FORMAT, 
-	READ_PIXELS_TYPE = READ_PIXELS_TYPE, 
-	TEXTURE_IMAGE_FORMAT = TEXTURE_IMAGE_FORMAT, 
-	TEXTURE_IMAGE_TYPE = TEXTURE_IMAGE_TYPE, 
-	GET_TEXTURE_IMAGE_FORMAT = GET_TEXTURE_IMAGE_FORMAT, 
-	GET_TEXTURE_IMAGE_TYPE = GET_TEXTURE_IMAGE_TYPE, 
-	MIPMAP = MIPMAP, 
-	MANUAL_GENERATE_MIPMAP = MANUAL_GENERATE_MIPMAP, 
-	AUTO_GENERATE_MIPMAP = AUTO_GENERATE_MIPMAP, 
-	COLOR_ENCODING = COLOR_ENCODING, 
-	SRGB_READ = SRGB_READ, 
-	SRGB_WRITE = SRGB_WRITE, 
-	FILTER = FILTER, 
-	VERTEX_TEXTURE = VERTEX_TEXTURE, 
-	TESS_CONTROL_TEXTURE = TESS_CONTROL_TEXTURE, 
-	TESS_EVALUATION_TEXTURE = TESS_EVALUATION_TEXTURE, 
-	GEOMETRY_TEXTURE = GEOMETRY_TEXTURE, 
-	FRAGMENT_TEXTURE = FRAGMENT_TEXTURE, 
-	COMPUTE_TEXTURE = COMPUTE_TEXTURE, 
-	TEXTURE_SHADOW = TEXTURE_SHADOW, 
-	TEXTURE_GATHER = TEXTURE_GATHER, 
-	TEXTURE_GATHER_SHADOW = TEXTURE_GATHER_SHADOW, 
-	SHADER_IMAGE_LOAD = SHADER_IMAGE_LOAD, 
-	SHADER_IMAGE_STORE = SHADER_IMAGE_STORE, 
-	SHADER_IMAGE_ATOMIC = SHADER_IMAGE_ATOMIC, 
-	IMAGE_TEXEL_SIZE = IMAGE_TEXEL_SIZE, 
-	IMAGE_COMPATIBILITY_CLASS = IMAGE_COMPATIBILITY_CLASS, 
-	IMAGE_PIXEL_FORMAT = IMAGE_PIXEL_FORMAT, 
-	IMAGE_PIXEL_TYPE = IMAGE_PIXEL_TYPE, 
-	SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST = SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST, 
-	SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST = SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST, 
-	SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE = SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE, 
-	SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE = SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE, 
-	TEXTURE_COMPRESSED_BLOCK_WIDTH = TEXTURE_COMPRESSED_BLOCK_WIDTH, 
-	TEXTURE_COMPRESSED_BLOCK_HEIGHT = TEXTURE_COMPRESSED_BLOCK_HEIGHT, 
-	TEXTURE_COMPRESSED_BLOCK_SIZE = TEXTURE_COMPRESSED_BLOCK_SIZE, 
-	CLEAR_BUFFER = CLEAR_BUFFER, 
-	TEXTURE_VIEW = TEXTURE_VIEW, 
-	VIEW_COMPATIBILITY_CLASS = VIEW_COMPATIBILITY_CLASS, 
-	FULL_SUPPORT = FULL_SUPPORT, 
-	CAVEAT_SUPPORT = CAVEAT_SUPPORT, 
-	IMAGE_CLASS_4_X_32 = IMAGE_CLASS_4_X_32, 
-	IMAGE_CLASS_2_X_32 = IMAGE_CLASS_2_X_32, 
-	IMAGE_CLASS_1_X_32 = IMAGE_CLASS_1_X_32, 
-	IMAGE_CLASS_4_X_16 = IMAGE_CLASS_4_X_16, 
-	IMAGE_CLASS_2_X_16 = IMAGE_CLASS_2_X_16, 
-	IMAGE_CLASS_1_X_16 = IMAGE_CLASS_1_X_16, 
-	IMAGE_CLASS_4_X_8 = IMAGE_CLASS_4_X_8, 
-	IMAGE_CLASS_2_X_8 = IMAGE_CLASS_2_X_8, 
-	IMAGE_CLASS_1_X_8 = IMAGE_CLASS_1_X_8, 
-	IMAGE_CLASS_11_11_10 = IMAGE_CLASS_11_11_10, 
-	IMAGE_CLASS_10_10_10_2 = IMAGE_CLASS_10_10_10_2, 
-	VIEW_CLASS_128_BITS = VIEW_CLASS_128_BITS, 
-	VIEW_CLASS_96_BITS = VIEW_CLASS_96_BITS, 
-	VIEW_CLASS_64_BITS = VIEW_CLASS_64_BITS, 
-	VIEW_CLASS_48_BITS = VIEW_CLASS_48_BITS, 
-	VIEW_CLASS_32_BITS = VIEW_CLASS_32_BITS, 
-	VIEW_CLASS_24_BITS = VIEW_CLASS_24_BITS, 
-	VIEW_CLASS_16_BITS = VIEW_CLASS_16_BITS, 
-	VIEW_CLASS_8_BITS = VIEW_CLASS_8_BITS, 
-	VIEW_CLASS_S3TC_DXT1_RGB = VIEW_CLASS_S3TC_DXT1_RGB, 
-	VIEW_CLASS_S3TC_DXT1_RGBA = VIEW_CLASS_S3TC_DXT1_RGBA, 
-	VIEW_CLASS_S3TC_DXT3_RGBA = VIEW_CLASS_S3TC_DXT3_RGBA, 
-	VIEW_CLASS_S3TC_DXT5_RGBA = VIEW_CLASS_S3TC_DXT5_RGBA, 
-	VIEW_CLASS_RGTC1_RED = VIEW_CLASS_RGTC1_RED, 
-	VIEW_CLASS_RGTC2_RG = VIEW_CLASS_RGTC2_RG, 
-	VIEW_CLASS_BPTC_UNORM = VIEW_CLASS_BPTC_UNORM, 
-	VIEW_CLASS_BPTC_FLOAT = VIEW_CLASS_BPTC_FLOAT, 
-	UNIFORM = UNIFORM, 
-	UNIFORM_BLOCK = UNIFORM_BLOCK, 
-	PROGRAM_INPUT = PROGRAM_INPUT, 
-	PROGRAM_OUTPUT = PROGRAM_OUTPUT, 
-	BUFFER_VARIABLE = BUFFER_VARIABLE, 
-	SHADER_STORAGE_BLOCK = SHADER_STORAGE_BLOCK, 
-	VERTEX_SUBROUTINE = VERTEX_SUBROUTINE, 
-	TESS_CONTROL_SUBROUTINE = TESS_CONTROL_SUBROUTINE, 
-	TESS_EVALUATION_SUBROUTINE = TESS_EVALUATION_SUBROUTINE, 
-	GEOMETRY_SUBROUTINE = GEOMETRY_SUBROUTINE, 
-	FRAGMENT_SUBROUTINE = FRAGMENT_SUBROUTINE, 
-	COMPUTE_SUBROUTINE = COMPUTE_SUBROUTINE, 
-	VERTEX_SUBROUTINE_UNIFORM = VERTEX_SUBROUTINE_UNIFORM, 
-	TESS_CONTROL_SUBROUTINE_UNIFORM = TESS_CONTROL_SUBROUTINE_UNIFORM, 
-	TESS_EVALUATION_SUBROUTINE_UNIFORM = TESS_EVALUATION_SUBROUTINE_UNIFORM, 
-	GEOMETRY_SUBROUTINE_UNIFORM = GEOMETRY_SUBROUTINE_UNIFORM, 
-	FRAGMENT_SUBROUTINE_UNIFORM = FRAGMENT_SUBROUTINE_UNIFORM, 
-	COMPUTE_SUBROUTINE_UNIFORM = COMPUTE_SUBROUTINE_UNIFORM, 
-	TRANSFORM_FEEDBACK_VARYING = TRANSFORM_FEEDBACK_VARYING, 
-	ACTIVE_RESOURCES = ACTIVE_RESOURCES, 
-	MAX_NAME_LENGTH = MAX_NAME_LENGTH, 
-	MAX_NUM_ACTIVE_VARIABLES = MAX_NUM_ACTIVE_VARIABLES, 
-	MAX_NUM_COMPATIBLE_SUBROUTINES = MAX_NUM_COMPATIBLE_SUBROUTINES, 
-	NAME_LENGTH = NAME_LENGTH, 
-	TYPE = TYPE, 
-	ARRAY_SIZE = ARRAY_SIZE, 
-	OFFSET = OFFSET, 
-	BLOCK_INDEX = BLOCK_INDEX, 
-	ARRAY_STRIDE = ARRAY_STRIDE, 
-	MATRIX_STRIDE = MATRIX_STRIDE, 
-	IS_ROW_MAJOR = IS_ROW_MAJOR, 
-	ATOMIC_COUNTER_BUFFER_INDEX = ATOMIC_COUNTER_BUFFER_INDEX, 
-	BUFFER_BINDING = BUFFER_BINDING, 
-	BUFFER_DATA_SIZE = BUFFER_DATA_SIZE, 
-	NUM_ACTIVE_VARIABLES = NUM_ACTIVE_VARIABLES, 
-	ACTIVE_VARIABLES = ACTIVE_VARIABLES, 
-	REFERENCED_BY_VERTEX_SHADER = REFERENCED_BY_VERTEX_SHADER, 
-	REFERENCED_BY_TESS_CONTROL_SHADER = REFERENCED_BY_TESS_CONTROL_SHADER, 
-	REFERENCED_BY_TESS_EVALUATION_SHADER = REFERENCED_BY_TESS_EVALUATION_SHADER, 
-	REFERENCED_BY_GEOMETRY_SHADER = REFERENCED_BY_GEOMETRY_SHADER, 
-	REFERENCED_BY_FRAGMENT_SHADER = REFERENCED_BY_FRAGMENT_SHADER, 
-	REFERENCED_BY_COMPUTE_SHADER = REFERENCED_BY_COMPUTE_SHADER, 
-	TOP_LEVEL_ARRAY_SIZE = TOP_LEVEL_ARRAY_SIZE, 
-	TOP_LEVEL_ARRAY_STRIDE = TOP_LEVEL_ARRAY_STRIDE, 
-	LOCATION = LOCATION, 
-	LOCATION_INDEX = LOCATION_INDEX, 
-	IS_PER_PATCH = IS_PER_PATCH, 
-	SHADER_STORAGE_BUFFER = SHADER_STORAGE_BUFFER, 
-	SHADER_STORAGE_BUFFER_BINDING = SHADER_STORAGE_BUFFER_BINDING, 
-	SHADER_STORAGE_BUFFER_START = SHADER_STORAGE_BUFFER_START, 
-	SHADER_STORAGE_BUFFER_SIZE = SHADER_STORAGE_BUFFER_SIZE, 
-	MAX_VERTEX_SHADER_STORAGE_BLOCKS = MAX_VERTEX_SHADER_STORAGE_BLOCKS, 
-	MAX_GEOMETRY_SHADER_STORAGE_BLOCKS = MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, 
-	MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS = MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, 
-	MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS = MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, 
-	MAX_FRAGMENT_SHADER_STORAGE_BLOCKS = MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, 
-	MAX_COMPUTE_SHADER_STORAGE_BLOCKS = MAX_COMPUTE_SHADER_STORAGE_BLOCKS, 
-	MAX_COMBINED_SHADER_STORAGE_BLOCKS = MAX_COMBINED_SHADER_STORAGE_BLOCKS, 
-	MAX_SHADER_STORAGE_BUFFER_BINDINGS = MAX_SHADER_STORAGE_BUFFER_BINDINGS, 
-	MAX_SHADER_STORAGE_BLOCK_SIZE = MAX_SHADER_STORAGE_BLOCK_SIZE, 
-	SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT = SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, 
-	SHADER_STORAGE_BARRIER_BIT = SHADER_STORAGE_BARRIER_BIT, 
-	MAX_COMBINED_SHADER_OUTPUT_RESOURCES = MAX_COMBINED_SHADER_OUTPUT_RESOURCES, 
-	DEPTH_STENCIL_TEXTURE_MODE = DEPTH_STENCIL_TEXTURE_MODE, 
-	TEXTURE_BUFFER_OFFSET = TEXTURE_BUFFER_OFFSET, 
-	TEXTURE_BUFFER_SIZE = TEXTURE_BUFFER_SIZE, 
-	TEXTURE_BUFFER_OFFSET_ALIGNMENT = TEXTURE_BUFFER_OFFSET_ALIGNMENT, 
-	TEXTURE_VIEW_MIN_LEVEL = TEXTURE_VIEW_MIN_LEVEL, 
-	TEXTURE_VIEW_NUM_LEVELS = TEXTURE_VIEW_NUM_LEVELS, 
-	TEXTURE_VIEW_MIN_LAYER = TEXTURE_VIEW_MIN_LAYER, 
-	TEXTURE_VIEW_NUM_LAYERS = TEXTURE_VIEW_NUM_LAYERS, 
-	TEXTURE_IMMUTABLE_LEVELS = TEXTURE_IMMUTABLE_LEVELS, 
-	VERTEX_ATTRIB_BINDING = VERTEX_ATTRIB_BINDING, 
-	VERTEX_ATTRIB_RELATIVE_OFFSET = VERTEX_ATTRIB_RELATIVE_OFFSET, 
-	VERTEX_BINDING_DIVISOR = VERTEX_BINDING_DIVISOR, 
-	VERTEX_BINDING_OFFSET = VERTEX_BINDING_OFFSET, 
-	VERTEX_BINDING_STRIDE = VERTEX_BINDING_STRIDE, 
-	MAX_VERTEX_ATTRIB_RELATIVE_OFFSET = MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, 
-	MAX_VERTEX_ATTRIB_BINDINGS = MAX_VERTEX_ATTRIB_BINDINGS, 
-	VERTEX_BINDING_BUFFER = VERTEX_BINDING_BUFFER, 
-
-	MAX_VERTEX_ATTRIB_STRIDE = MAX_VERTEX_ATTRIB_STRIDE, 
-	PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED = PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, 
-	TEXTURE_BUFFER_BINDING = TEXTURE_BUFFER_BINDING, 
-	MAP_PERSISTENT_BIT = MAP_PERSISTENT_BIT, 
-	MAP_COHERENT_BIT = MAP_COHERENT_BIT, 
-	DYNAMIC_STORAGE_BIT = DYNAMIC_STORAGE_BIT, 
-	CLIENT_STORAGE_BIT = CLIENT_STORAGE_BIT, 
-	CLIENT_MAPPED_BUFFER_BARRIER_BIT = CLIENT_MAPPED_BUFFER_BARRIER_BIT, 
-	BUFFER_IMMUTABLE_STORAGE = BUFFER_IMMUTABLE_STORAGE, 
-	BUFFER_STORAGE_FLAGS = BUFFER_STORAGE_FLAGS, 
-	CLEAR_TEXTURE = CLEAR_TEXTURE, 
-	LOCATION_COMPONENT = LOCATION_COMPONENT, 
-	TRANSFORM_FEEDBACK_BUFFER_INDEX = TRANSFORM_FEEDBACK_BUFFER_INDEX, 
-	TRANSFORM_FEEDBACK_BUFFER_STRIDE = TRANSFORM_FEEDBACK_BUFFER_STRIDE, 
-	QUERY_BUFFER = QUERY_BUFFER, 
-	QUERY_BUFFER_BARRIER_BIT = QUERY_BUFFER_BARRIER_BIT, 
-	QUERY_BUFFER_BINDING = QUERY_BUFFER_BINDING, 
-	QUERY_RESULT_NO_WAIT = QUERY_RESULT_NO_WAIT, 
-	MIRROR_CLAMP_TO_EDGE = MIRROR_CLAMP_TO_EDGE, 
-
-	CONTEXT_LOST = CONTEXT_LOST, 
-	NEGATIVE_ONE_TO_ONE = NEGATIVE_ONE_TO_ONE, 
-	ZERO_TO_ONE = ZERO_TO_ONE, 
-	CLIP_ORIGIN = CLIP_ORIGIN, 
-	CLIP_DEPTH_MODE = CLIP_DEPTH_MODE, 
-	QUERY_WAIT_INVERTED = QUERY_WAIT_INVERTED, 
-	QUERY_NO_WAIT_INVERTED = QUERY_NO_WAIT_INVERTED, 
-	QUERY_BY_REGION_WAIT_INVERTED = QUERY_BY_REGION_WAIT_INVERTED, 
-	QUERY_BY_REGION_NO_WAIT_INVERTED = QUERY_BY_REGION_NO_WAIT_INVERTED, 
-	MAX_CULL_DISTANCES = MAX_CULL_DISTANCES, 
-	MAX_COMBINED_CLIP_AND_CULL_DISTANCES = MAX_COMBINED_CLIP_AND_CULL_DISTANCES, 
-	TEXTURE_TARGET = TEXTURE_TARGET, 
-	QUERY_TARGET = QUERY_TARGET, 
-	GUILTY_CONTEXT_RESET = GUILTY_CONTEXT_RESET, 
-	INNOCENT_CONTEXT_RESET = INNOCENT_CONTEXT_RESET, 
-	UNKNOWN_CONTEXT_RESET = UNKNOWN_CONTEXT_RESET, 
-	RESET_NOTIFICATION_STRATEGY = RESET_NOTIFICATION_STRATEGY, 
-	LOSE_CONTEXT_ON_RESET = LOSE_CONTEXT_ON_RESET, 
-	NO_RESET_NOTIFICATION = NO_RESET_NOTIFICATION, 
-	CONTEXT_FLAG_ROBUST_ACCESS_BIT = CONTEXT_FLAG_ROBUST_ACCESS_BIT, 
-	CONTEXT_RELEASE_BEHAVIOR = CONTEXT_RELEASE_BEHAVIOR, 
-	CONTEXT_RELEASE_BEHAVIOR_FLUSH = CONTEXT_RELEASE_BEHAVIOR_FLUSH, 
-
-	DEBUG_OUTPUT_SYNCHRONOUS_ARB = DEBUG_OUTPUT_SYNCHRONOUS_ARB, 
-	DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB = DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB, 
-	DEBUG_CALLBACK_FUNCTION_ARB = DEBUG_CALLBACK_FUNCTION_ARB, 
-	DEBUG_CALLBACK_USER_PARAM_ARB = DEBUG_CALLBACK_USER_PARAM_ARB, 
-	DEBUG_SOURCE_API_ARB = DEBUG_SOURCE_API_ARB, 
-	DEBUG_SOURCE_WINDOW_SYSTEM_ARB = DEBUG_SOURCE_WINDOW_SYSTEM_ARB, 
-	DEBUG_SOURCE_SHADER_COMPILER_ARB = DEBUG_SOURCE_SHADER_COMPILER_ARB, 
-	DEBUG_SOURCE_THIRD_PARTY_ARB = DEBUG_SOURCE_THIRD_PARTY_ARB, 
-	DEBUG_SOURCE_APPLICATION_ARB = DEBUG_SOURCE_APPLICATION_ARB, 
-	DEBUG_SOURCE_OTHER_ARB = DEBUG_SOURCE_OTHER_ARB, 
-	DEBUG_TYPE_ERROR_ARB = DEBUG_TYPE_ERROR_ARB, 
-	DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB = DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, 
-	DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB = DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, 
-	DEBUG_TYPE_PORTABILITY_ARB = DEBUG_TYPE_PORTABILITY_ARB, 
-	DEBUG_TYPE_PERFORMANCE_ARB = DEBUG_TYPE_PERFORMANCE_ARB, 
-	DEBUG_TYPE_OTHER_ARB = DEBUG_TYPE_OTHER_ARB, 
-	MAX_DEBUG_MESSAGE_LENGTH_ARB = MAX_DEBUG_MESSAGE_LENGTH_ARB, 
-	MAX_DEBUG_LOGGED_MESSAGES_ARB = MAX_DEBUG_LOGGED_MESSAGES_ARB, 
-	DEBUG_LOGGED_MESSAGES_ARB = DEBUG_LOGGED_MESSAGES_ARB, 
-	DEBUG_SEVERITY_HIGH_ARB = DEBUG_SEVERITY_HIGH_ARB, 
-	DEBUG_SEVERITY_MEDIUM_ARB = DEBUG_SEVERITY_MEDIUM_ARB, 
-	DEBUG_SEVERITY_LOW_ARB = DEBUG_SEVERITY_LOW_ARB, 
-
-	SHADER_BINARY_FORMAT_SPIR_V = SHADER_BINARY_FORMAT_SPIR_V, 
-	SPIR_V_BINARY = SPIR_V_BINARY, 
-	PARAMETER_BUFFER = PARAMETER_BUFFER, 
-	PARAMETER_BUFFER_BINDING = PARAMETER_BUFFER_BINDING, 
-	CONTEXT_FLAG_NO_ERROR_BIT = CONTEXT_FLAG_NO_ERROR_BIT, 
-	VERTICES_SUBMITTED = VERTICES_SUBMITTED, 
-	PRIMITIVES_SUBMITTED = PRIMITIVES_SUBMITTED, 
-	VERTEX_SHADER_INVOCATIONS = VERTEX_SHADER_INVOCATIONS, 
-	TESS_CONTROL_SHADER_PATCHES = TESS_CONTROL_SHADER_PATCHES, 
-	TESS_EVALUATION_SHADER_INVOCATIONS = TESS_EVALUATION_SHADER_INVOCATIONS, 
-	GEOMETRY_SHADER_PRIMITIVES_EMITTED = GEOMETRY_SHADER_PRIMITIVES_EMITTED, 
-	FRAGMENT_SHADER_INVOCATIONS = FRAGMENT_SHADER_INVOCATIONS, 
-	COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS, 
-	CLIPPING_INPUT_PRIMITIVES = CLIPPING_INPUT_PRIMITIVES, 
-	CLIPPING_OUTPUT_PRIMITIVES = CLIPPING_OUTPUT_PRIMITIVES, 
-	POLYGON_OFFSET_CLAMP = POLYGON_OFFSET_CLAMP, 
-	SPIR_V_EXTENSIONS = SPIR_V_EXTENSIONS, 
-	NUM_SPIR_V_EXTENSIONS = NUM_SPIR_V_EXTENSIONS, 
-	TEXTURE_MAX_ANISOTROPY = TEXTURE_MAX_ANISOTROPY, 
-	MAX_TEXTURE_MAX_ANISOTROPY = MAX_TEXTURE_MAX_ANISOTROPY, 
-	TRANSFORM_FEEDBACK_OVERFLOW = TRANSFORM_FEEDBACK_OVERFLOW, 
-	TRANSFORM_FEEDBACK_STREAM_OVERFLOW = TRANSFORM_FEEDBACK_STREAM_OVERFLOW, 
-
-	// Extensions
+	// Extensions, extended as necessary
 	DEVICE_LUID_EXT = DEVICE_LUID_EXT,
-}
+}

+ 11 - 1
vendor/README.md

@@ -148,4 +148,14 @@ Includes full bindings and Windows `.lib` and `.dll`.
 [zlib](https://github.com/madler/zlib) data compression library
 
 See also LICENSE in the `zlib` directory itself.
-Includes full bindings.
+Includes full bindings.
+
+
+## cgltf
+
+
+[cgltf](https://github.com/jkuhlmann/cgltf) is a [glTF2.0](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html) loader and writer.
+
+Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), [Unigine](https://developer.unigine.com/en/docs/2.14.1/third_party?rlang=cpp#cgltf), and more!
+
+Se also LICENCE in `cgltf` directory itself.

+ 7 - 0
vendor/cgltf/LICENSE

@@ -0,0 +1,7 @@
+Copyright (c) 2018-2021 Johannes Kuhlmann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 146 - 0
vendor/cgltf/README.md

@@ -0,0 +1,146 @@
+# :diamond_shape_with_a_dot_inside: cgltf
+**glTF loader and writer**
+(**Single-file/stb-style C glTF loader and writer**)
+
+Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf), [raylib](https://github.com/raysan5/raylib), [Unigine](https://developer.unigine.com/en/docs/2.14.1/third_party?rlang=cpp#cgltf), and more!
+
+## Usage: Loading
+Loading from file:
+```odin
+package main
+
+import "vendor:cgltf"
+
+main :: proc() {
+	options: cgltf.options
+	data, result := cgltf.parse_file(&options, "scene.gltf")
+	if result != .success {
+		/* TODO handle error */
+	}
+	defer cgltf.free(data)
+	/* TODO make awesome stuff */
+}
+```
+
+Loading from memory:
+```odin
+package main
+
+import "vendor:cgltf"
+
+main :: proc() {
+	buf: []byte = ... // data to glb or gltf file data
+
+	options: cgltf.options
+	data, result := cgltf.parse(&options, raw_data(buf), len(buf))
+	if result != .success {
+		/* TODO handle error */
+	}
+	defer cgltf.free(data)
+	/* TODO make awesome stuff */
+}
+```
+
+
+Note that cgltf does not load the contents of extra files such as buffers or images into memory by default. You'll need to read these files yourself using URIs from `data.buffers[]` or `data.images[]` respectively.
+For buffer data, you can alternatively call `cgltf.load_buffers`, which will use `^clib.FILE` APIs to open and read buffer files. This automatically decodes base64 data URIs in buffers. For data URIs in images, you will need to use `cgltf.load_buffer_base64`.
+
+**For more in-depth documentation and a description of the public interface refer to the top of the `cgltf.h` file.**
+
+## Usage: Writing
+When writing glTF data, you need a valid `cgltf.data` structure that represents a valid glTF document. You can construct such a structure yourself or load it using the loader functions described above. The writer functions do not deallocate any memory. So, you either have to do it manually or call `cgltf.free()` if you got the data by loading it from a glTF document.
+
+Writing to file:
+```odin
+package main
+
+import "vendor:cgltf"
+
+main :: proc() {
+	options: cgltf.options
+	data: ^cgltf.data = /* TODO must be valid data */
+	result := cgltf.write_file(&options, "out.gltf", data)
+	if result != .success {
+		/* TODO handle error */
+	}
+}
+```
+
+Writing to memory:
+```c
+package main
+
+import "vendor:cgltf"
+
+main :: proc() {
+	options: cgltf.options
+	data: ^cgltf.data = /* TODO must be valid data */
+
+	size := cgltf.write(&options, nil, 0, data)
+
+	buf := make([]byte, size)
+
+	written := cgltf.write(&options, raw_data(buf), size, data)
+	if written != size {
+		/* TODO handle error */
+	}
+}
+```
+
+Note that cgltf does not write the contents of extra files such as buffers or images. You'll need to write this data yourself.
+
+**For more in-depth documentation and a description of the public interface refer to the top of the `cgltf_write.h` file.**
+
+
+## Features
+cgltf supports core glTF 2.0:
+- glb (binary files) and gltf (JSON files)
+- meshes (including accessors, buffer views, buffers)
+- materials (including textures, samplers, images)
+- scenes and nodes
+- skins
+- animations
+- cameras
+- morph targets
+- extras data
+
+cgltf also supports some glTF extensions:
+- EXT_mesh_gpu_instancing
+- EXT_meshopt_compression
+- KHR_draco_mesh_compression (requires a library like [Google's Draco](https://github.com/google/draco) for decompression though)
+- KHR_lights_punctual
+- KHR_materials_clearcoat
+- KHR_materials_emissive_strength
+- KHR_materials_ior
+- KHR_materials_iridescence
+- KHR_materials_pbrSpecularGlossiness
+- KHR_materials_sheen
+- KHR_materials_specular
+- KHR_materials_transmission
+- KHR_materials_unlit
+- KHR_materials_variants
+- KHR_materials_volume
+- KHR_texture_basisu (requires a library like [Binomial Basisu](https://github.com/BinomialLLC/basis_universal) for transcoding to native compressed texture)
+- KHR_texture_transform
+
+cgltf does **not** yet support unlisted extensions. However, unlisted extensions can be accessed via "extensions" member on objects.
+
+## Contributing
+Everyone is welcome to contribute to the library. If you find any problems, you can submit them using [GitHub's issue system](https://github.com/jkuhlmann/cgltf/issues). If you want to contribute code, you should fork the project and then send a pull request.
+
+
+## Dependencies
+None.
+
+C headers being used by the implementation:
+```
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <assert.h> // If asserts are enabled.
+```
+
+Note, this library has a copy of the [JSMN JSON parser](https://github.com/zserge/jsmn) embedded in its source.

+ 689 - 0
vendor/cgltf/cgltf.odin

@@ -0,0 +1,689 @@
+//+build windows
+package cgltf
+
+when ODIN_OS == .Windows {
+	foreign import lib "lib/cgltf.lib"
+}
+
+import "core:c"
+
+
+file_type :: enum c.int {
+	invalid,
+	gltf,
+	glb,
+}
+
+result :: enum c.int {
+	success,
+	data_too_short,
+	unknown_format,
+	invalid_json,
+	invalid_gltf,
+	invalid_options,
+	file_not_found,
+	io_error,
+	out_of_memory,
+	legacy_gltf,
+}
+
+memory_options :: struct {
+	alloc_func: proc "c" (user: rawptr, size: uint) -> rawptr,
+	free_func:  proc "c" (user: rawptr, ptr: rawptr),
+	user_data:  rawptr,
+}
+
+file_options :: struct {
+	read:      proc "c" (memory_options: ^/*const*/memory_options, file_options: ^/*const*/file_options, path: cstring, size: uint, data: ^rawptr) -> result,
+	release:   proc "c" (memory_options: ^/*const*/memory_options, file_options: ^/*const*/file_options, data: rawptr),
+	user_data: rawptr,
+}
+
+options :: struct {
+	type:             file_type, /* invalid == auto detect */
+	json_token_count: uint,      /* 0 == auto */
+	memory:           memory_options,
+	file:             file_options,
+}
+
+buffer_view_type :: enum c.int {
+	invalid,
+	indices,
+	vertices,
+}
+
+attribute_type :: enum c.int {
+	invalid,
+	position,
+	normal,
+	tangent,
+	texcoord,
+	color,
+	joints,
+	weights,
+	custom,
+}
+
+component_type :: enum c.int {
+	invalid,
+	r_8,   /* BYTE */
+	r_8u,  /* UNSIGNED_BYTE */
+	r_16,  /* SHORT */
+	r_16u, /* UNSIGNED_SHORT */
+	r_32u, /* UNSIGNED_INT */
+	r_32f, /* FLOAT */
+}
+
+type :: enum c.int {
+	invalid,
+	scalar,
+	vec2,
+	vec3,
+	vec4,
+	mat2,
+	mat3,
+	mat4,
+}
+
+primitive_type :: enum c.int {
+	points,
+	lines,
+	line_loop,
+	line_strip,
+	triangles,
+	triangle_strip,
+	triangle_fan,
+}
+
+alpha_mode :: enum c.int {
+	opaque,
+	mask,
+	blend,
+}
+
+animation_path_type :: enum c.int {
+	invalid,
+	translation,
+	rotation,
+	scale,
+	weights,
+}
+
+interpolation_type :: enum c.int {
+	linear,
+	step,
+	cubic_spline,
+}
+
+camera_type :: enum c.int {
+	invalid,
+	perspective,
+	orthographic,
+}
+
+light_type :: enum c.int {
+	invalid,
+	directional,
+	point,
+	spot,
+}
+
+data_free_method :: enum c.int {
+	none,
+	file_release,
+	memory_free,
+}
+
+extras_t :: struct {
+	start_offset: uint, /* this field is deprecated and will be removed in the future; use data instead */
+	end_offset:   uint, /* this field is deprecated and will be removed in the future; use data instead */
+
+	data: [^]byte,
+}
+
+extension :: struct {
+	name: cstring,
+	data: [^]byte,
+}
+
+buffer :: struct {
+	name:             cstring,
+	size:             uint,
+	uri:              cstring,
+	data:             rawptr, /* loaded by cgltf_load_buffers */
+	data_free_method: data_free_method,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+meshopt_compression_mode :: enum c.int {
+	invalid,
+	attributes,
+	triangles,
+	indices,
+}
+
+meshopt_compression_filter :: enum c.int {
+	none,
+	octahedral,
+	quaternion,
+	exponential,
+}
+
+meshopt_compression :: struct {
+	buffer: ^buffer,
+	offset: uint,
+	size:   uint,
+	stride: uint,
+	count:  uint,
+	mode:   meshopt_compression_mode,
+	filter: meshopt_compression_filter,
+}
+
+buffer_view :: struct {
+	name:                    cstring,
+	buffer:                  ^buffer,
+	offset:                  uint,
+	size:                    uint,
+	stride:                  uint, /* 0 == automatically determined by accessor */
+	type:                    buffer_view_type,
+	data:                    rawptr, /* overrides buffer->data if present, filled by extensions */
+	has_meshopt_compression: b32,
+	meshopt_compression:     meshopt_compression,
+	extras:                  extras_t,
+	extensions_count:        uint,
+	extensions:              [^]extension,
+}
+
+accessor_sparse :: struct {
+	count:                    uint,
+	indices_buffer_view:      ^buffer_view,
+	indices_byte_offset:      uint,
+	indices_component_type:   component_type,
+	values_buffer_view:       ^buffer_view,
+	values_byte_offset:       uint,
+	extras:                   extras_t,
+	indices_extras:           extras_t,
+	values_extras:            extras_t,
+	extensions_count:         uint,
+	extensions:               [^]extension,
+	indices_extensions_count: uint,
+	indices_extensions:       [^]extension,
+	values_extensions_count:  uint,
+	values_extensions:        [^]extension,
+}
+
+accessor :: struct {
+	name:             cstring,
+	component_type:   component_type,
+	normalized:       b32,
+	type:             type,
+	offset:           uint,
+	count:            uint,
+	stride:           uint,
+	buffer_view:      ^buffer_view,
+	has_min:          b32,
+	min:              [16]f32,
+	has_max:          b32,
+	max:              [16]f32,
+	is_sparse:        b32,
+	sparse:           accessor_sparse,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+attribute :: struct {
+	name:  cstring,
+	type:  attribute_type,
+	index: c.int,
+	data:  ^accessor,
+}
+
+image :: struct {
+	name:             cstring,
+	uri:              cstring,
+	buffer_view:      ^buffer_view,
+	mime_type:        cstring,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+sampler :: struct {
+	name:             cstring,
+	mag_filter:       c.int,
+	min_filter:       c.int,
+	wrap_s:           c.int,
+	wrap_t:           c.int,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+texture :: struct {
+	name:             cstring,
+	image_:           ^image,
+	sampler:          ^sampler,
+	has_basisu:       b32 ,
+	basisu_image:     ^image,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+texture_transform :: struct {
+	offset:       [2]f32,
+	rotation:     f32,
+	scale:        [2]f32,
+	has_texcoord: b32,
+	texcoord:     c.int,
+}
+
+texture_view :: struct {
+	texture:          ^texture,
+	texcoord:         c.int,
+	scale:            f32, /* equivalent to strength for occlusion_texture */
+	has_transform:    b32,
+	transform:        texture_transform,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+pbr_metallic_roughness :: struct {
+	base_color_texture:         texture_view,
+	metallic_roughness_texture: texture_view,
+
+	base_color_factor: [4]f32,
+	metallic_factor:   f32,
+	roughness_factor:  f32,
+}
+
+pbr_specular_glossiness :: struct {
+	diffuse_texture:             texture_view,
+	specular_glossiness_texture: texture_view,
+
+	diffuse_factor:    [4]f32,
+	specular_factor:   [3]f32,
+	glossiness_factor: f32,
+}
+
+clearcoat :: struct {
+	clearcoat_texture:           texture_view,
+	clearcoat_roughness_texture: texture_view,
+	clearcoat_normal_texture:    texture_view,
+
+	clearcoat_factor:           f32,
+	clearcoat_roughness_factor: f32,
+}
+
+transmission :: struct {
+	transmission_texture: texture_view,
+	transmission_factor:  f32,
+}
+
+ior :: struct {
+	ior: f32,
+}
+
+specular :: struct {
+	specular_texture:       texture_view,
+	specular_color_texture: texture_view,
+	specular_color_factor:  [3]f32,
+	specular_factor:        f32,
+}
+
+volume :: struct {
+	thickness_texture:    texture_view,
+	thickness_factor:     f32,
+	attenuation_color:    [3]f32,
+	attenuation_distance: f32,
+}
+
+sheen :: struct {
+	sheen_color_texture:     texture_view,
+	sheen_color_factor:      [3]f32,
+	sheen_roughness_texture: texture_view,
+	sheen_roughness_factor:  f32,
+}
+
+emissive_strength :: struct {
+	emissive_strength: f32,
+}
+
+iridescence :: struct {
+	iridescence_factor:            f32,
+	iridescence_texture:           texture_view,
+	iridescence_ior:               f32,
+	iridescence_thickness_min:     f32,
+	iridescence_thickness_max:     f32,
+	iridescence_thickness_texture: texture_view,
+}
+
+material :: struct {
+	name: cstring,
+	has_pbr_metallic_roughness:  b32,
+	has_pbr_specular_glossiness: b32,
+	has_clearcoat:               b32,
+	has_transmission:            b32,
+	has_volume:                  b32,
+	has_ior:                     b32,
+	has_specular:                b32,
+	has_sheen:                   b32,
+	has_emissive_strength:       b32,
+	has_iridescence:             b32,
+	pbr_metallic_roughness:      pbr_metallic_roughness,
+	pbr_specular_glossiness:     pbr_specular_glossiness,
+	clearcoat:                   clearcoat,
+	ior:                         ior,
+	specular:                    specular,
+	sheen:                       sheen,
+	transmission:                transmission,
+	volume:                      volume,
+	emissive_strength:           emissive_strength,
+	iridescence:                 iridescence,
+	normal_texture:              texture_view,
+	occlusion_texture:           texture_view,
+	emissive_texture:            texture_view,
+	emissive_factor:             [3]f32,
+	alpha_mode:                  alpha_mode,
+	alpha_cutoff:                f32,
+	double_sided:                b32,
+	unlit:                       b32,
+	extras:                      extras_t,
+	extensions_count:            uint,
+	extensions:                  [^]extension,
+}
+
+material_mapping :: struct {
+	variant:  uint,
+	material: ^material,
+	extras:   extras_t,
+}
+
+morph_target :: struct {
+	attributes: []attribute,
+}
+
+draco_mesh_compression :: struct {
+	buffer_view: ^buffer_view,
+	attributes:  []attribute,
+}
+
+mesh_gpu_instancing :: struct {
+	buffer_view: ^buffer_view,
+	attributes:  []attribute,
+}
+
+primitive :: struct {
+	type:                       primitive_type,
+	indices:                    ^accessor,
+	material:                   ^material,
+	attributes:                 []attribute,
+	targets:                    []morph_target,
+	extras:                     extras_t,
+	has_draco_mesh_compression: b32,
+	draco_mesh_compression:     draco_mesh_compression,
+	mappings:                   []material_mapping,
+	extensions_count:           uint,
+	extensions:                 [^]extension,
+}
+
+mesh :: struct {
+	name:               cstring,
+	primitives:         []primitive,
+	weights:            []f32,
+	target_names:       []cstring,
+	extras:             extras_t,
+	extensions_count:   uint,
+	extensions:         [^]extension,
+}
+
+skin :: struct {
+	name:                  cstring,
+	joints:                []^node,
+	skeleton:              ^node,
+	inverse_bind_matrices: ^accessor,
+	extras:                extras_t,
+	extensions_count:      uint,
+	extensions:            [^]extension,
+}
+
+camera_perspective :: struct {
+	has_aspect_ratio: b32,
+	aspect_ratio:     f32,
+	yfov:             f32,
+	has_zfar:         b32,
+	zfar:             f32,
+	znear:            f32,
+	extras:           extras_t,
+}
+
+camera_orthographic :: struct {
+	xmag:   f32,
+	ymag:   f32,
+	zfar:   f32,
+	znear:  f32,
+	extras: extras_t,
+}
+
+camera :: struct {
+	name: cstring,
+	type: camera_type,
+	data: struct #raw_union {
+		perspective:  camera_perspective,
+		orthographic: camera_orthographic,
+	},
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+light :: struct {
+	name:                  cstring,
+	color:                 [3]f32,
+	intensity:             f32,
+	type:                  light_type,
+	range:                 f32,
+	spot_inner_cone_angle: f32,
+	spot_outer_cone_angle: f32,
+	extras:                extras_t,
+}
+
+node :: struct {
+	name:                    cstring,
+	parent:                  ^node,
+	children:                []^node,
+	skin:                    ^skin,
+	mesh:                    ^mesh,
+	camera:                  ^camera,
+	light:                   ^light,
+	weights:                 []f32,
+	has_translation:         b32,
+	has_rotation:            b32,
+	has_scale:               b32,
+	has_matrix:              b32,
+	translation:             [3]f32,
+	rotation:                [4]f32,
+	scale:                   [3]f32,
+	matrix_:                 [16]f32,
+	extras:                  extras_t,
+	has_mesh_gpu_instancing: b32,
+	mesh_gpu_instancing:     mesh_gpu_instancing,
+	extensions_count:        uint,
+	extensions:              [^]extension,
+}
+
+scene :: struct {
+	name:             cstring,
+	nodes:            []^node,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+animation_sampler :: struct {
+	input:            ^accessor,
+	output:           ^accessor,
+	interpolation:    interpolation_type,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+animation_channel :: struct {
+	sampler:          ^animation_sampler,
+	target_node:      ^node,
+	target_path:      animation_path_type,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+animation :: struct {
+	name:             cstring,
+	samplers:         []animation_sampler,
+	channels:         []animation_channel,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+material_variant :: struct {
+	name:   cstring,
+	extras: extras_t,
+}
+
+asset :: struct {
+	copyright:        cstring,
+	generator:        cstring,
+	version:          cstring,
+	min_version:      cstring,
+	extras:           extras_t,
+	extensions_count: uint,
+	extensions:       [^]extension,
+}
+
+data :: struct {
+	file_type: file_type,
+	file_data: rawptr,
+
+	asset: asset,
+
+	meshes:       []mesh,
+	materials:    []material,
+	accessors:    []accessor,
+	buffer_views: []buffer_view,
+	buffers:      []buffer,
+	images:       []image,
+	textures:     []texture,
+	samplers:     []sampler,
+	skins:        []skin,
+	cameras:      []camera,
+	lights:       []light,
+	nodes:        []node,
+	scenes:       []scene,
+
+	scene: ^scene,
+
+	animations: []animation,
+
+	variants: []material_variant,
+
+	extras: extras_t,
+
+	data_extensions_count: uint,
+	data_extensions:       [^]extension,
+
+	extensions_used:     []cstring,
+	extensions_required: []cstring,
+
+	json: string,
+
+	bin: []byte,
+
+	memory: memory_options,
+	file:   file_options,
+}
+
+@(require_results)
+parse :: proc "c" (#by_ptr options: options, data_ptr: rawptr, size: uint) -> (out_data: ^data, res: result) {
+	foreign lib {
+		cgltf_parse :: proc "c" (
+			#by_ptr options: options,
+			data_ptr: rawptr,
+			size: uint,
+			out_data: ^^data) -> result ---
+	}
+	res = cgltf_parse(options, data_ptr, size, &out_data)
+	return
+}
+
+@(require_results)
+parse_file :: proc "c" (#by_ptr options: options, path: cstring) -> (out_data: ^data, res: result) {
+	foreign lib {
+		cgltf_parse_file :: proc "c" (
+			#by_ptr options: options,
+			path: cstring,
+			out_data: ^^data) -> result ---
+	}
+	res = cgltf_parse_file(options, path, &out_data)
+	return
+}
+
+@(require_results)
+load_buffer_base64 :: proc "c" (#by_ptr options: options, size: uint, base64: cstring) -> (out_data: rawptr, res: result) {
+	foreign lib {
+		cgltf_load_buffer_base64 :: proc "c" (#by_ptr options: options, size: uint, base64: cstring, out_data: ^rawptr) -> result ---
+	}
+	res = cgltf_load_buffer_base64(options, size, base64, &out_data)
+	return
+}
+
+@(default_calling_convention="c")
+@(link_prefix="cgltf_")
+foreign lib {
+	@(require_results)
+	load_buffers :: proc(
+		#by_ptr options: options,
+		data: ^data,
+		gltf_path: cstring) -> result ---
+
+	@(require_results)
+	decode_string :: proc(string: [^]byte) -> uint ---
+	@(require_results)
+	decode_uri    :: proc(uri: [^]byte) -> uint ---
+
+	@(require_results)
+	validate :: proc(data: ^data) -> result ---
+
+	free :: proc(data: ^data) ---
+
+	node_transform_local :: proc(node: ^node, out_matrix: [^]f32) ---
+	node_transform_world :: proc(node: ^node, out_matrix: [^]f32) ---
+
+	@(require_results)
+	accessor_read_float :: proc(accessor: ^/*const*/accessor, index: uint, out: [^]f32,    element_size: uint) -> b32 ---
+	@(require_results)
+	accessor_read_uint  :: proc(accessor: ^/*const*/accessor, index: uint, out: [^]c.uint, element_size: uint) -> b32 ---
+	@(require_results)
+	accessor_read_index :: proc(accessor: ^/*const*/accessor, index: uint) -> uint ---
+
+	@(require_results)
+	num_components :: proc(type: type) -> uint ---
+
+	@(require_results)
+	accessor_unpack_floats :: proc(accessor: ^/*const*/accessor, out: [^]f32, float_count: uint) -> uint ---
+
+	/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
+	@(require_results)
+	copy_extras_json :: proc(data: ^data, extras: ^extras_t, dest: [^]byte, dest_size: ^uint) -> result ---
+
+	@(require_results)
+	write_file :: proc(#by_ptr options: options, path:   cstring,             data: ^data) -> result ---
+	@(require_results)
+	write      :: proc(#by_ptr options: options, buffer: [^]byte, size: uint, data: ^data) -> uint ---
+}
+

+ 8 - 0
vendor/cgltf/src/build.bat

@@ -0,0 +1,8 @@
+@echo off
+
+if not exist "..\lib" mkdir ..\lib
+
+cl -nologo -MT -TC -O2 -c cgltf.c
+lib -nologo cgltf.obj -out:..\lib\cgltf.lib
+
+del *.obj

+ 15 - 0
vendor/cgltf/src/cgltf.c

@@ -0,0 +1,15 @@
+#include <stdint.h>
+
+
+#define GlbHeaderSize      12
+#define GlbChunkHeaderSize 8
+static const uint32_t GlbVersion = 2;
+static const uint32_t GlbMagic = 0x46546C67;
+static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
+static const uint32_t GlbMagicBinChunk = 0x004E4942;
+#define CGLTF_CONSTS
+
+
+#define CGLTF_IMPLEMENTATION
+#define CGLTF_WRITE_IMPLEMENTATION
+#include "cgltf_write.h"

+ 6818 - 0
vendor/cgltf/src/cgltf.h

@@ -0,0 +1,6818 @@
+/**
+ * cgltf - a single-file glTF 2.0 parser written in C99.
+ *
+ * Version: 1.13
+ *
+ * Website: https://github.com/jkuhlmann/cgltf
+ *
+ * Distributed under the MIT License, see notice at the end of this file.
+ *
+ * Building:
+ * Include this file where you need the struct and function
+ * declarations. Have exactly one source file where you define
+ * `CGLTF_IMPLEMENTATION` before including this file to get the
+ * function definitions.
+ *
+ * Reference:
+ * `cgltf_result cgltf_parse(const cgltf_options*, const void*,
+ * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If
+ * this function returns `cgltf_result_success`, you have to call
+ * `cgltf_free()` on the created `cgltf_data*` variable.
+ * Note that contents of external files for buffers and images are not
+ * automatically loaded. You'll need to read these files yourself using
+ * URIs in the `cgltf_data` structure.
+ *
+ * `cgltf_options` is the struct passed to `cgltf_parse()` to control
+ * parts of the parsing process. You can use it to force the file type
+ * and provide memory allocation as well as file operation callbacks.
+ * Should be zero-initialized to trigger default behavior.
+ *
+ * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`.
+ * It generally mirrors the glTF format as described by the spec (see
+ * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0).
+ *
+ * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data`
+ * variable.
+ *
+ * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*,
+ * const char* gltf_path)` can be optionally called to open and read buffer
+ * files using the `FILE*` APIs. The `gltf_path` argument is the path to
+ * the original glTF file, which allows the parser to resolve the path to
+ * buffer files.
+ *
+ * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options,
+ * cgltf_size size, const char* base64, void** out_data)` decodes
+ * base64-encoded data content. Used internally by `cgltf_load_buffers()`.
+ * This is useful when decoding data URIs in images.
+ *
+ * `cgltf_result cgltf_parse_file(const cgltf_options* options, const
+ * char* path, cgltf_data** out_data)` can be used to open the given
+ * file using `FILE*` APIs and parse the data using `cgltf_parse()`.
+ *
+ * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional
+ * checks to make sure the parsed glTF data is valid.
+ *
+ * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node
+ * into a mat4.
+ *
+ * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order
+ * to compute the root-to-node transformation.
+ *
+ * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any),
+ * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called.
+ * By passing null for the output pointer, users can find out how many floats are required in the
+ * output buffer.
+ *
+ * `cgltf_num_components` is a tiny utility that tells you the dimensionality of
+ * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate
+ * the necessary amount of memory.
+ *
+ * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to
+ * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element
+ * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns
+ * false if the passed-in element_size is too small, or if the accessor is sparse.
+ *
+ * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading
+ * vector types and does not support matrix types. The passed-in element size is the number of uints
+ * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in 
+ * element_size is too small, or if the accessor is sparse.
+ *
+ * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t
+ * and only works with single-component data types.
+ *
+ * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many
+ * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using
+ * cgltf_extras::data directly instead. You can parse this data using your own JSON parser
+ * or, if you've included the cgltf implementation using the integrated JSMN JSON parser.
+ */
+#ifndef CGLTF_H_INCLUDED__
+#define CGLTF_H_INCLUDED__
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef size_t cgltf_size;
+typedef long long int cgltf_ssize;
+typedef float cgltf_float;
+typedef int cgltf_int;
+typedef unsigned int cgltf_uint;
+typedef int cgltf_bool;
+
+typedef enum cgltf_file_type
+{
+	cgltf_file_type_invalid,
+	cgltf_file_type_gltf,
+	cgltf_file_type_glb,
+	cgltf_file_type_max_enum
+} cgltf_file_type;
+
+typedef enum cgltf_result
+{
+	cgltf_result_success,
+	cgltf_result_data_too_short,
+	cgltf_result_unknown_format,
+	cgltf_result_invalid_json,
+	cgltf_result_invalid_gltf,
+	cgltf_result_invalid_options,
+	cgltf_result_file_not_found,
+	cgltf_result_io_error,
+	cgltf_result_out_of_memory,
+	cgltf_result_legacy_gltf,
+    cgltf_result_max_enum
+} cgltf_result;
+
+typedef struct cgltf_memory_options
+{
+	void* (*alloc_func)(void* user, cgltf_size size);
+	void (*free_func) (void* user, void* ptr);
+	void* user_data;
+} cgltf_memory_options;
+
+typedef struct cgltf_file_options
+{
+	cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data);
+	void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data);
+	void* user_data;
+} cgltf_file_options;
+
+typedef struct cgltf_options
+{
+	cgltf_file_type type; /* invalid == auto detect */
+	cgltf_size json_token_count; /* 0 == auto */
+	cgltf_memory_options memory;
+	cgltf_file_options file;
+} cgltf_options;
+
+typedef enum cgltf_buffer_view_type
+{
+	cgltf_buffer_view_type_invalid,
+	cgltf_buffer_view_type_indices,
+	cgltf_buffer_view_type_vertices,
+	cgltf_buffer_view_type_max_enum
+} cgltf_buffer_view_type;
+
+typedef enum cgltf_attribute_type
+{
+	cgltf_attribute_type_invalid,
+	cgltf_attribute_type_position,
+	cgltf_attribute_type_normal,
+	cgltf_attribute_type_tangent,
+	cgltf_attribute_type_texcoord,
+	cgltf_attribute_type_color,
+	cgltf_attribute_type_joints,
+	cgltf_attribute_type_weights,
+	cgltf_attribute_type_custom,
+	cgltf_attribute_type_max_enum
+} cgltf_attribute_type;
+
+typedef enum cgltf_component_type
+{
+	cgltf_component_type_invalid,
+	cgltf_component_type_r_8, /* BYTE */
+	cgltf_component_type_r_8u, /* UNSIGNED_BYTE */
+	cgltf_component_type_r_16, /* SHORT */
+	cgltf_component_type_r_16u, /* UNSIGNED_SHORT */
+	cgltf_component_type_r_32u, /* UNSIGNED_INT */
+	cgltf_component_type_r_32f, /* FLOAT */
+    cgltf_component_type_max_enum
+} cgltf_component_type;
+
+typedef enum cgltf_type
+{
+	cgltf_type_invalid,
+	cgltf_type_scalar,
+	cgltf_type_vec2,
+	cgltf_type_vec3,
+	cgltf_type_vec4,
+	cgltf_type_mat2,
+	cgltf_type_mat3,
+	cgltf_type_mat4,
+	cgltf_type_max_enum
+} cgltf_type;
+
+typedef enum cgltf_primitive_type
+{
+	cgltf_primitive_type_points,
+	cgltf_primitive_type_lines,
+	cgltf_primitive_type_line_loop,
+	cgltf_primitive_type_line_strip,
+	cgltf_primitive_type_triangles,
+	cgltf_primitive_type_triangle_strip,
+	cgltf_primitive_type_triangle_fan,
+	cgltf_primitive_type_max_enum
+} cgltf_primitive_type;
+
+typedef enum cgltf_alpha_mode
+{
+	cgltf_alpha_mode_opaque,
+	cgltf_alpha_mode_mask,
+	cgltf_alpha_mode_blend,
+	cgltf_alpha_mode_max_enum
+} cgltf_alpha_mode;
+
+typedef enum cgltf_animation_path_type {
+	cgltf_animation_path_type_invalid,
+	cgltf_animation_path_type_translation,
+	cgltf_animation_path_type_rotation,
+	cgltf_animation_path_type_scale,
+	cgltf_animation_path_type_weights,
+	cgltf_animation_path_type_max_enum
+} cgltf_animation_path_type;
+
+typedef enum cgltf_interpolation_type {
+	cgltf_interpolation_type_linear,
+	cgltf_interpolation_type_step,
+	cgltf_interpolation_type_cubic_spline,
+	cgltf_interpolation_type_max_enum
+} cgltf_interpolation_type;
+
+typedef enum cgltf_camera_type {
+	cgltf_camera_type_invalid,
+	cgltf_camera_type_perspective,
+	cgltf_camera_type_orthographic,
+	cgltf_camera_type_max_enum
+} cgltf_camera_type;
+
+typedef enum cgltf_light_type {
+	cgltf_light_type_invalid,
+	cgltf_light_type_directional,
+	cgltf_light_type_point,
+	cgltf_light_type_spot,
+	cgltf_light_type_max_enum
+} cgltf_light_type;
+
+typedef enum cgltf_data_free_method {
+	cgltf_data_free_method_none,
+	cgltf_data_free_method_file_release,
+	cgltf_data_free_method_memory_free,
+	cgltf_data_free_method_max_enum
+} cgltf_data_free_method;
+
+typedef struct cgltf_extras {
+	cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */
+	cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */
+
+	char* data;
+} cgltf_extras;
+
+typedef struct cgltf_extension {
+	char* name;
+	char* data;
+} cgltf_extension;
+
+typedef struct cgltf_buffer
+{
+	char* name;
+	cgltf_size size;
+	char* uri;
+	void* data; /* loaded by cgltf_load_buffers */
+	cgltf_data_free_method data_free_method;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_buffer;
+
+typedef enum cgltf_meshopt_compression_mode {
+	cgltf_meshopt_compression_mode_invalid,
+	cgltf_meshopt_compression_mode_attributes,
+	cgltf_meshopt_compression_mode_triangles,
+	cgltf_meshopt_compression_mode_indices,
+	cgltf_meshopt_compression_mode_max_enum
+} cgltf_meshopt_compression_mode;
+
+typedef enum cgltf_meshopt_compression_filter {
+	cgltf_meshopt_compression_filter_none,
+	cgltf_meshopt_compression_filter_octahedral,
+	cgltf_meshopt_compression_filter_quaternion,
+	cgltf_meshopt_compression_filter_exponential,
+	cgltf_meshopt_compression_filter_max_enum
+} cgltf_meshopt_compression_filter;
+
+typedef struct cgltf_meshopt_compression
+{
+	cgltf_buffer* buffer;
+	cgltf_size offset;
+	cgltf_size size;
+	cgltf_size stride;
+	cgltf_size count;
+	cgltf_meshopt_compression_mode mode;
+	cgltf_meshopt_compression_filter filter;
+} cgltf_meshopt_compression;
+
+typedef struct cgltf_buffer_view
+{
+	char *name;
+	cgltf_buffer* buffer;
+	cgltf_size offset;
+	cgltf_size size;
+	cgltf_size stride; /* 0 == automatically determined by accessor */
+	cgltf_buffer_view_type type;
+	void* data; /* overrides buffer->data if present, filled by extensions */
+	cgltf_bool has_meshopt_compression;
+	cgltf_meshopt_compression meshopt_compression;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_buffer_view;
+
+typedef struct cgltf_accessor_sparse
+{
+	cgltf_size count;
+	cgltf_buffer_view* indices_buffer_view;
+	cgltf_size indices_byte_offset;
+	cgltf_component_type indices_component_type;
+	cgltf_buffer_view* values_buffer_view;
+	cgltf_size values_byte_offset;
+	cgltf_extras extras;
+	cgltf_extras indices_extras;
+	cgltf_extras values_extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+	cgltf_size indices_extensions_count;
+	cgltf_extension* indices_extensions;
+	cgltf_size values_extensions_count;
+	cgltf_extension* values_extensions;
+} cgltf_accessor_sparse;
+
+typedef struct cgltf_accessor
+{
+	char* name;
+	cgltf_component_type component_type;
+	cgltf_bool normalized;
+	cgltf_type type;
+	cgltf_size offset;
+	cgltf_size count;
+	cgltf_size stride;
+	cgltf_buffer_view* buffer_view;
+	cgltf_bool has_min;
+	cgltf_float min[16];
+	cgltf_bool has_max;
+	cgltf_float max[16];
+	cgltf_bool is_sparse;
+	cgltf_accessor_sparse sparse;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_accessor;
+
+typedef struct cgltf_attribute
+{
+	char* name;
+	cgltf_attribute_type type;
+	cgltf_int index;
+	cgltf_accessor* data;
+} cgltf_attribute;
+
+typedef struct cgltf_image
+{
+	char* name;
+	char* uri;
+	cgltf_buffer_view* buffer_view;
+	char* mime_type;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_image;
+
+typedef struct cgltf_sampler
+{
+	char* name;
+	cgltf_int mag_filter;
+	cgltf_int min_filter;
+	cgltf_int wrap_s;
+	cgltf_int wrap_t;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_sampler;
+
+typedef struct cgltf_texture
+{
+	char* name;
+	cgltf_image* image;
+	cgltf_sampler* sampler;
+	cgltf_bool has_basisu;
+	cgltf_image* basisu_image;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_texture;
+
+typedef struct cgltf_texture_transform
+{
+	cgltf_float offset[2];
+	cgltf_float rotation;
+	cgltf_float scale[2];
+	cgltf_bool has_texcoord;
+	cgltf_int texcoord;
+} cgltf_texture_transform;
+
+typedef struct cgltf_texture_view
+{
+	cgltf_texture* texture;
+	cgltf_int texcoord;
+	cgltf_float scale; /* equivalent to strength for occlusion_texture */
+	cgltf_bool has_transform;
+	cgltf_texture_transform transform;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_texture_view;
+
+typedef struct cgltf_pbr_metallic_roughness
+{
+	cgltf_texture_view base_color_texture;
+	cgltf_texture_view metallic_roughness_texture;
+
+	cgltf_float base_color_factor[4];
+	cgltf_float metallic_factor;
+	cgltf_float roughness_factor;
+} cgltf_pbr_metallic_roughness;
+
+typedef struct cgltf_pbr_specular_glossiness
+{
+	cgltf_texture_view diffuse_texture;
+	cgltf_texture_view specular_glossiness_texture;
+
+	cgltf_float diffuse_factor[4];
+	cgltf_float specular_factor[3];
+	cgltf_float glossiness_factor;
+} cgltf_pbr_specular_glossiness;
+
+typedef struct cgltf_clearcoat
+{
+	cgltf_texture_view clearcoat_texture;
+	cgltf_texture_view clearcoat_roughness_texture;
+	cgltf_texture_view clearcoat_normal_texture;
+
+	cgltf_float clearcoat_factor;
+	cgltf_float clearcoat_roughness_factor;
+} cgltf_clearcoat;
+
+typedef struct cgltf_transmission
+{
+	cgltf_texture_view transmission_texture;
+	cgltf_float transmission_factor;
+} cgltf_transmission;
+
+typedef struct cgltf_ior
+{
+	cgltf_float ior;
+} cgltf_ior;
+
+typedef struct cgltf_specular
+{
+	cgltf_texture_view specular_texture;
+	cgltf_texture_view specular_color_texture;
+	cgltf_float specular_color_factor[3];
+	cgltf_float specular_factor;
+} cgltf_specular;
+
+typedef struct cgltf_volume
+{
+	cgltf_texture_view thickness_texture;
+	cgltf_float thickness_factor;
+	cgltf_float attenuation_color[3];
+	cgltf_float attenuation_distance;
+} cgltf_volume;
+
+typedef struct cgltf_sheen
+{
+	cgltf_texture_view sheen_color_texture;
+	cgltf_float sheen_color_factor[3];
+	cgltf_texture_view sheen_roughness_texture;
+	cgltf_float sheen_roughness_factor;
+} cgltf_sheen;
+
+typedef struct cgltf_emissive_strength
+{
+	cgltf_float emissive_strength;
+} cgltf_emissive_strength;
+
+typedef struct cgltf_iridescence
+{
+	cgltf_float iridescence_factor;
+	cgltf_texture_view iridescence_texture;
+	cgltf_float iridescence_ior;
+	cgltf_float iridescence_thickness_min;
+	cgltf_float iridescence_thickness_max;
+	cgltf_texture_view iridescence_thickness_texture;
+} cgltf_iridescence;
+
+typedef struct cgltf_material
+{
+	char* name;
+	cgltf_bool has_pbr_metallic_roughness;
+	cgltf_bool has_pbr_specular_glossiness;
+	cgltf_bool has_clearcoat;
+	cgltf_bool has_transmission;
+	cgltf_bool has_volume;
+	cgltf_bool has_ior;
+	cgltf_bool has_specular;
+	cgltf_bool has_sheen;
+	cgltf_bool has_emissive_strength;
+	cgltf_bool has_iridescence;
+	cgltf_pbr_metallic_roughness pbr_metallic_roughness;
+	cgltf_pbr_specular_glossiness pbr_specular_glossiness;
+	cgltf_clearcoat clearcoat;
+	cgltf_ior ior;
+	cgltf_specular specular;
+	cgltf_sheen sheen;
+	cgltf_transmission transmission;
+	cgltf_volume volume;
+	cgltf_emissive_strength emissive_strength;
+	cgltf_iridescence iridescence;
+	cgltf_texture_view normal_texture;
+	cgltf_texture_view occlusion_texture;
+	cgltf_texture_view emissive_texture;
+	cgltf_float emissive_factor[3];
+	cgltf_alpha_mode alpha_mode;
+	cgltf_float alpha_cutoff;
+	cgltf_bool double_sided;
+	cgltf_bool unlit;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_material;
+
+typedef struct cgltf_material_mapping
+{
+	cgltf_size variant;
+	cgltf_material* material;
+	cgltf_extras extras;
+} cgltf_material_mapping;
+
+typedef struct cgltf_morph_target {
+	cgltf_attribute* attributes;
+	cgltf_size attributes_count;
+} cgltf_morph_target;
+
+typedef struct cgltf_draco_mesh_compression {
+	cgltf_buffer_view* buffer_view;
+	cgltf_attribute* attributes;
+	cgltf_size attributes_count;
+} cgltf_draco_mesh_compression;
+
+typedef struct cgltf_mesh_gpu_instancing {
+	cgltf_buffer_view* buffer_view;
+	cgltf_attribute* attributes;
+	cgltf_size attributes_count;
+} cgltf_mesh_gpu_instancing;
+
+typedef struct cgltf_primitive {
+	cgltf_primitive_type type;
+	cgltf_accessor* indices;
+	cgltf_material* material;
+	cgltf_attribute* attributes;
+	cgltf_size attributes_count;
+	cgltf_morph_target* targets;
+	cgltf_size targets_count;
+	cgltf_extras extras;
+	cgltf_bool has_draco_mesh_compression;
+	cgltf_draco_mesh_compression draco_mesh_compression;
+	cgltf_material_mapping* mappings;
+	cgltf_size mappings_count;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_primitive;
+
+typedef struct cgltf_mesh {
+	char* name;
+	cgltf_primitive* primitives;
+	cgltf_size primitives_count;
+	cgltf_float* weights;
+	cgltf_size weights_count;
+	char** target_names;
+	cgltf_size target_names_count;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_mesh;
+
+typedef struct cgltf_node cgltf_node;
+
+typedef struct cgltf_skin {
+	char* name;
+	cgltf_node** joints;
+	cgltf_size joints_count;
+	cgltf_node* skeleton;
+	cgltf_accessor* inverse_bind_matrices;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_skin;
+
+typedef struct cgltf_camera_perspective {
+	cgltf_bool has_aspect_ratio;
+	cgltf_float aspect_ratio;
+	cgltf_float yfov;
+	cgltf_bool has_zfar;
+	cgltf_float zfar;
+	cgltf_float znear;
+	cgltf_extras extras;
+} cgltf_camera_perspective;
+
+typedef struct cgltf_camera_orthographic {
+	cgltf_float xmag;
+	cgltf_float ymag;
+	cgltf_float zfar;
+	cgltf_float znear;
+	cgltf_extras extras;
+} cgltf_camera_orthographic;
+
+typedef struct cgltf_camera {
+	char* name;
+	cgltf_camera_type type;
+	union {
+		cgltf_camera_perspective perspective;
+		cgltf_camera_orthographic orthographic;
+	} data;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_camera;
+
+typedef struct cgltf_light {
+	char* name;
+	cgltf_float color[3];
+	cgltf_float intensity;
+	cgltf_light_type type;
+	cgltf_float range;
+	cgltf_float spot_inner_cone_angle;
+	cgltf_float spot_outer_cone_angle;
+	cgltf_extras extras;
+} cgltf_light;
+
+struct cgltf_node {
+	char* name;
+	cgltf_node* parent;
+	cgltf_node** children;
+	cgltf_size children_count;
+	cgltf_skin* skin;
+	cgltf_mesh* mesh;
+	cgltf_camera* camera;
+	cgltf_light* light;
+	cgltf_float* weights;
+	cgltf_size weights_count;
+	cgltf_bool has_translation;
+	cgltf_bool has_rotation;
+	cgltf_bool has_scale;
+	cgltf_bool has_matrix;
+	cgltf_float translation[3];
+	cgltf_float rotation[4];
+	cgltf_float scale[3];
+	cgltf_float matrix[16];
+	cgltf_extras extras;
+	cgltf_bool has_mesh_gpu_instancing;
+	cgltf_mesh_gpu_instancing mesh_gpu_instancing;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+};
+
+typedef struct cgltf_scene {
+	char* name;
+	cgltf_node** nodes;
+	cgltf_size nodes_count;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_scene;
+
+typedef struct cgltf_animation_sampler {
+	cgltf_accessor* input;
+	cgltf_accessor* output;
+	cgltf_interpolation_type interpolation;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_animation_sampler;
+
+typedef struct cgltf_animation_channel {
+	cgltf_animation_sampler* sampler;
+	cgltf_node* target_node;
+	cgltf_animation_path_type target_path;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_animation_channel;
+
+typedef struct cgltf_animation {
+	char* name;
+	cgltf_animation_sampler* samplers;
+	cgltf_size samplers_count;
+	cgltf_animation_channel* channels;
+	cgltf_size channels_count;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_animation;
+
+typedef struct cgltf_material_variant
+{
+	char* name;
+	cgltf_extras extras;
+} cgltf_material_variant;
+
+typedef struct cgltf_asset {
+	char* copyright;
+	char* generator;
+	char* version;
+	char* min_version;
+	cgltf_extras extras;
+	cgltf_size extensions_count;
+	cgltf_extension* extensions;
+} cgltf_asset;
+
+typedef struct cgltf_data
+{
+	cgltf_file_type file_type;
+	void* file_data;
+
+	cgltf_asset asset;
+
+	cgltf_mesh* meshes;
+	cgltf_size meshes_count;
+
+	cgltf_material* materials;
+	cgltf_size materials_count;
+
+	cgltf_accessor* accessors;
+	cgltf_size accessors_count;
+
+	cgltf_buffer_view* buffer_views;
+	cgltf_size buffer_views_count;
+
+	cgltf_buffer* buffers;
+	cgltf_size buffers_count;
+
+	cgltf_image* images;
+	cgltf_size images_count;
+
+	cgltf_texture* textures;
+	cgltf_size textures_count;
+
+	cgltf_sampler* samplers;
+	cgltf_size samplers_count;
+
+	cgltf_skin* skins;
+	cgltf_size skins_count;
+
+	cgltf_camera* cameras;
+	cgltf_size cameras_count;
+
+	cgltf_light* lights;
+	cgltf_size lights_count;
+
+	cgltf_node* nodes;
+	cgltf_size nodes_count;
+
+	cgltf_scene* scenes;
+	cgltf_size scenes_count;
+
+	cgltf_scene* scene;
+
+	cgltf_animation* animations;
+	cgltf_size animations_count;
+
+	cgltf_material_variant* variants;
+	cgltf_size variants_count;
+
+	cgltf_extras extras;
+
+	cgltf_size data_extensions_count;
+	cgltf_extension* data_extensions;
+
+	char** extensions_used;
+	cgltf_size extensions_used_count;
+
+	char** extensions_required;
+	cgltf_size extensions_required_count;
+
+	const char* json;
+	cgltf_size json_size;
+
+	const void* bin;
+	cgltf_size bin_size;
+
+	cgltf_memory_options memory;
+	cgltf_file_options file;
+} cgltf_data;
+
+cgltf_result cgltf_parse(
+		const cgltf_options* options,
+		const void* data,
+		cgltf_size size,
+		cgltf_data** out_data);
+
+cgltf_result cgltf_parse_file(
+		const cgltf_options* options,
+		const char* path,
+		cgltf_data** out_data);
+
+cgltf_result cgltf_load_buffers(
+		const cgltf_options* options,
+		cgltf_data* data,
+		const char* gltf_path);
+
+cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data);
+
+cgltf_size cgltf_decode_string(char* string);
+cgltf_size cgltf_decode_uri(char* uri);
+
+cgltf_result cgltf_validate(cgltf_data* data);
+
+void cgltf_free(cgltf_data* data);
+
+void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix);
+void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix);
+
+cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size);
+cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size);
+cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
+
+cgltf_size cgltf_num_components(cgltf_type type);
+
+cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
+
+/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
+cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef CGLTF_H_INCLUDED__ */
+
+/*
+ *
+ * Stop now, if you are only interested in the API.
+ * Below, you find the implementation.
+ *
+ */
+
+#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
+/* This makes MSVC/CLion intellisense work. */
+#define CGLTF_IMPLEMENTATION
+#endif
+
+#ifdef CGLTF_IMPLEMENTATION
+
+#include <stdint.h> /* For uint8_t, uint32_t */
+#include <string.h> /* For strncpy */
+#include <stdio.h>  /* For fopen */
+#include <limits.h> /* For UINT_MAX etc */
+#include <float.h>  /* For FLT_MAX */
+
+#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL)
+#include <stdlib.h> /* For malloc, free, atoi, atof */
+#endif
+
+#if CGLTF_VALIDATE_ENABLE_ASSERTS
+#include <assert.h>
+#endif
+
+/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */
+#define JSMN_PARENT_LINKS
+
+/* JSMN_STRICT is necessary to reject invalid JSON documents */
+#define JSMN_STRICT
+
+/*
+ * -- jsmn.h start --
+ * Source: https://github.com/zserge/jsmn
+ * License: MIT
+ */
+typedef enum {
+	JSMN_UNDEFINED = 0,
+	JSMN_OBJECT = 1,
+	JSMN_ARRAY = 2,
+	JSMN_STRING = 3,
+	JSMN_PRIMITIVE = 4
+} jsmntype_t;
+enum jsmnerr {
+	/* Not enough tokens were provided */
+	JSMN_ERROR_NOMEM = -1,
+	/* Invalid character inside JSON string */
+	JSMN_ERROR_INVAL = -2,
+	/* The string is not a full JSON packet, more bytes expected */
+	JSMN_ERROR_PART = -3
+};
+typedef struct {
+	jsmntype_t type;
+	int start;
+	int end;
+	int size;
+#ifdef JSMN_PARENT_LINKS
+	int parent;
+#endif
+} jsmntok_t;
+typedef struct {
+	unsigned int pos; /* offset in the JSON string */
+	unsigned int toknext; /* next token to allocate */
+	int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+static void jsmn_init(jsmn_parser *parser);
+static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens);
+/*
+ * -- jsmn.h end --
+ */
+
+
+#ifndef CGLTF_CONSTS
+static const cgltf_size GlbHeaderSize = 12;
+static const cgltf_size GlbChunkHeaderSize = 8;
+static const uint32_t GlbVersion = 2;
+static const uint32_t GlbMagic = 0x46546C67;
+static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
+static const uint32_t GlbMagicBinChunk = 0x004E4942;
+#define CGLTF_CONSTS
+#endif
+
+#ifndef CGLTF_MALLOC
+#define CGLTF_MALLOC(size) malloc(size)
+#endif
+#ifndef CGLTF_FREE
+#define CGLTF_FREE(ptr) free(ptr)
+#endif
+#ifndef CGLTF_ATOI
+#define CGLTF_ATOI(str) atoi(str)
+#endif
+#ifndef CGLTF_ATOF
+#define CGLTF_ATOF(str) atof(str)
+#endif
+#ifndef CGLTF_ATOLL
+#define CGLTF_ATOLL(str) atoll(str)
+#endif
+#ifndef CGLTF_VALIDATE_ENABLE_ASSERTS
+#define CGLTF_VALIDATE_ENABLE_ASSERTS 0
+#endif
+
+static void* cgltf_default_alloc(void* user, cgltf_size size)
+{
+	(void)user;
+	return CGLTF_MALLOC(size);
+}
+
+static void cgltf_default_free(void* user, void* ptr)
+{
+	(void)user;
+	CGLTF_FREE(ptr);
+}
+
+static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count)
+{
+	if (SIZE_MAX / element_size < count)
+	{
+		return NULL;
+	}
+	void* result = options->memory.alloc_func(options->memory.user_data, element_size * count);
+	if (!result)
+	{
+		return NULL;
+	}
+	memset(result, 0, element_size * count);
+	return result;
+}
+
+static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data)
+{
+	(void)file_options;
+	void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc_func ? memory_options->alloc_func : &cgltf_default_alloc;
+	void (*memory_free)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free;
+
+	FILE* file = fopen(path, "rb");
+	if (!file)
+	{
+		return cgltf_result_file_not_found;
+	}
+
+	cgltf_size file_size = size ? *size : 0;
+
+	if (file_size == 0)
+	{
+		fseek(file, 0, SEEK_END);
+
+#ifdef _WIN32
+		__int64 length = _ftelli64(file);
+#else
+		long length = ftell(file);
+#endif
+
+		if (length < 0)
+		{
+			fclose(file);
+			return cgltf_result_io_error;
+		}
+
+		fseek(file, 0, SEEK_SET);
+		file_size = (cgltf_size)length;
+	}
+
+	char* file_data = (char*)memory_alloc(memory_options->user_data, file_size);
+	if (!file_data)
+	{
+		fclose(file);
+		return cgltf_result_out_of_memory;
+	}
+	
+	cgltf_size read_size = fread(file_data, 1, file_size, file);
+
+	fclose(file);
+
+	if (read_size != file_size)
+	{
+		memory_free(memory_options->user_data, file_data);
+		return cgltf_result_io_error;
+	}
+
+	if (size)
+	{
+		*size = file_size;
+	}
+	if (data)
+	{
+		*data = file_data;
+	}
+
+	return cgltf_result_success;
+}
+
+static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data)
+{
+	(void)file_options;
+	void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free;
+	memfree(memory_options->user_data, data);
+}
+
+static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data);
+
+cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data)
+{
+	if (size < GlbHeaderSize)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	if (options == NULL)
+	{
+		return cgltf_result_invalid_options;
+	}
+
+	cgltf_options fixed_options = *options;
+	if (fixed_options.memory.alloc_func == NULL)
+	{
+		fixed_options.memory.alloc_func = &cgltf_default_alloc;
+	}
+	if (fixed_options.memory.free_func == NULL)
+	{
+		fixed_options.memory.free_func = &cgltf_default_free;
+	}
+
+	uint32_t tmp;
+	// Magic
+	memcpy(&tmp, data, 4);
+	if (tmp != GlbMagic)
+	{
+		if (fixed_options.type == cgltf_file_type_invalid)
+		{
+			fixed_options.type = cgltf_file_type_gltf;
+		}
+		else if (fixed_options.type == cgltf_file_type_glb)
+		{
+			return cgltf_result_unknown_format;
+		}
+	}
+
+	if (fixed_options.type == cgltf_file_type_gltf)
+	{
+		cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data);
+		if (json_result != cgltf_result_success)
+		{
+			return json_result;
+		}
+
+		(*out_data)->file_type = cgltf_file_type_gltf;
+
+		return cgltf_result_success;
+	}
+
+	const uint8_t* ptr = (const uint8_t*)data;
+	// Version
+	memcpy(&tmp, ptr + 4, 4);
+	uint32_t version = tmp;
+	if (version != GlbVersion)
+	{
+		return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format;
+	}
+
+	// Total length
+	memcpy(&tmp, ptr + 8, 4);
+	if (tmp > size)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	const uint8_t* json_chunk = ptr + GlbHeaderSize;
+
+	if (GlbHeaderSize + GlbChunkHeaderSize > size)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	// JSON chunk: length
+	uint32_t json_length;
+	memcpy(&json_length, json_chunk, 4);
+	if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size)
+	{
+		return cgltf_result_data_too_short;
+	}
+
+	// JSON chunk: magic
+	memcpy(&tmp, json_chunk + 4, 4);
+	if (tmp != GlbMagicJsonChunk)
+	{
+		return cgltf_result_unknown_format;
+	}
+
+	json_chunk += GlbChunkHeaderSize;
+
+	const void* bin = 0;
+	cgltf_size bin_size = 0;
+
+	if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size)
+	{
+		// We can read another chunk
+		const uint8_t* bin_chunk = json_chunk + json_length;
+
+		// Bin chunk: length
+		uint32_t bin_length;
+		memcpy(&bin_length, bin_chunk, 4);
+		if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size)
+		{
+			return cgltf_result_data_too_short;
+		}
+
+		// Bin chunk: magic
+		memcpy(&tmp, bin_chunk + 4, 4);
+		if (tmp != GlbMagicBinChunk)
+		{
+			return cgltf_result_unknown_format;
+		}
+
+		bin_chunk += GlbChunkHeaderSize;
+
+		bin = bin_chunk;
+		bin_size = bin_length;
+	}
+
+	cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data);
+	if (json_result != cgltf_result_success)
+	{
+		return json_result;
+	}
+
+	(*out_data)->file_type = cgltf_file_type_glb;
+	(*out_data)->bin = bin;
+	(*out_data)->bin_size = bin_size;
+
+	return cgltf_result_success;
+}
+
+cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data)
+{
+	if (options == NULL)
+	{
+		return cgltf_result_invalid_options;
+	}
+
+	cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read;
+	void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release;
+
+	void* file_data = NULL;
+	cgltf_size file_size = 0;
+	cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data);
+	if (result != cgltf_result_success)
+	{
+		return result;
+	}
+
+	result = cgltf_parse(options, file_data, file_size, out_data);
+
+	if (result != cgltf_result_success)
+	{
+		file_release(&options->memory, &options->file, file_data);
+		return result;
+	}
+
+	(*out_data)->file_data = file_data;
+
+	return cgltf_result_success;
+}
+
+static void cgltf_combine_paths(char* path, const char* base, const char* uri)
+{
+	const char* s0 = strrchr(base, '/');
+	const char* s1 = strrchr(base, '\\');
+	const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1;
+
+	if (slash)
+	{
+		size_t prefix = slash - base + 1;
+
+		strncpy(path, base, prefix);
+		strcpy(path + prefix, uri);
+	}
+	else
+	{
+		strcpy(path, uri);
+	}
+}
+
+static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data)
+{
+	void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc;
+	void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free;
+	cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read;
+
+	char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1);
+	if (!path)
+	{
+		return cgltf_result_out_of_memory;
+	}
+
+	cgltf_combine_paths(path, gltf_path, uri);
+
+	// after combining, the tail of the resulting path is a uri; decode_uri converts it into path
+	cgltf_decode_uri(path + strlen(path) - strlen(uri));
+
+	void* file_data = NULL;
+	cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data);
+
+	memory_free(options->memory.user_data, path);
+
+	*out_data = (result == cgltf_result_success) ? file_data : NULL;
+
+	return result;
+}
+
+cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data)
+{
+	void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc;
+	void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free;
+
+	unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size);
+	if (!data)
+	{
+		return cgltf_result_out_of_memory;
+	}
+
+	unsigned int buffer = 0;
+	unsigned int buffer_bits = 0;
+
+	for (cgltf_size i = 0; i < size; ++i)
+	{
+		while (buffer_bits < 8)
+		{
+			char ch = *base64++;
+
+			int index =
+				(unsigned)(ch - 'A') < 26 ? (ch - 'A') :
+				(unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 :
+				(unsigned)(ch - '0') < 10 ? (ch - '0') + 52 :
+				ch == '+' ? 62 :
+				ch == '/' ? 63 :
+				-1;
+
+			if (index < 0)
+			{
+				memory_free(options->memory.user_data, data);
+				return cgltf_result_io_error;
+			}
+
+			buffer = (buffer << 6) | index;
+			buffer_bits += 6;
+		}
+
+		data[i] = (unsigned char)(buffer >> (buffer_bits - 8));
+		buffer_bits -= 8;
+	}
+
+	*out_data = data;
+
+	return cgltf_result_success;
+}
+
+static int cgltf_unhex(char ch)
+{
+	return
+		(unsigned)(ch - '0') < 10 ? (ch - '0') :
+		(unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 :
+		(unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 :
+		-1;
+}
+
+cgltf_size cgltf_decode_string(char* string)
+{
+	char* read = string + strcspn(string, "\\");
+	if (*read == 0)
+	{
+		return read - string;
+	}
+	char* write = string;
+	char* last = string;
+
+	for (;;)
+	{
+		// Copy characters since last escaped sequence
+		cgltf_size written = read - last;
+		memmove(write, last, written);
+		write += written;
+
+		if (*read++ == 0)
+		{
+			break;
+		}
+
+		// jsmn already checked that all escape sequences are valid
+		switch (*read++)
+		{
+		case '\"': *write++ = '\"'; break;
+		case '/':  *write++ = '/';  break;
+		case '\\': *write++ = '\\'; break;
+		case 'b':  *write++ = '\b'; break;
+		case 'f':  *write++ = '\f'; break;
+		case 'r':  *write++ = '\r'; break;
+		case 'n':  *write++ = '\n'; break;
+		case 't':  *write++ = '\t'; break;
+		case 'u':
+		{
+			// UCS-2 codepoint \uXXXX to UTF-8
+			int character = 0;
+			for (cgltf_size i = 0; i < 4; ++i)
+			{
+				character = (character << 4) + cgltf_unhex(*read++);
+			}
+
+			if (character <= 0x7F)
+			{
+				*write++ = character & 0xFF;
+			}
+			else if (character <= 0x7FF)
+			{
+				*write++ = 0xC0 | ((character >> 6) & 0xFF);
+				*write++ = 0x80 | (character & 0x3F);
+			}
+			else
+			{
+				*write++ = 0xE0 | ((character >> 12) & 0xFF);
+				*write++ = 0x80 | ((character >> 6) & 0x3F);
+				*write++ = 0x80 | (character & 0x3F);
+			}
+			break;
+		}
+		default:
+			break;
+		}
+
+		last = read;
+		read += strcspn(read, "\\");
+	}
+
+	*write = 0;
+	return write - string;
+}
+
+cgltf_size cgltf_decode_uri(char* uri)
+{
+	char* write = uri;
+	char* i = uri;
+
+	while (*i)
+	{
+		if (*i == '%')
+		{
+			int ch1 = cgltf_unhex(i[1]);
+
+			if (ch1 >= 0)
+			{
+				int ch2 = cgltf_unhex(i[2]);
+
+				if (ch2 >= 0)
+				{
+					*write++ = (char)(ch1 * 16 + ch2);
+					i += 3;
+					continue;
+				}
+			}
+		}
+
+		*write++ = *i++;
+	}
+
+	*write = 0;
+	return write - uri;
+}
+
+cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path)
+{
+	if (options == NULL)
+	{
+		return cgltf_result_invalid_options;
+	}
+
+	if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin)
+	{
+		if (data->bin_size < data->buffers[0].size)
+		{
+			return cgltf_result_data_too_short;
+		}
+
+		data->buffers[0].data = (void*)data->bin;
+		data->buffers[0].data_free_method = cgltf_data_free_method_none;
+	}
+
+	for (cgltf_size i = 0; i < data->buffers_count; ++i)
+	{
+		if (data->buffers[i].data)
+		{
+			continue;
+		}
+
+		const char* uri = data->buffers[i].uri;
+
+		if (uri == NULL)
+		{
+			continue;
+		}
+
+		if (strncmp(uri, "data:", 5) == 0)
+		{
+			const char* comma = strchr(uri, ',');
+
+			if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0)
+			{
+				cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data);
+				data->buffers[i].data_free_method = cgltf_data_free_method_memory_free;
+
+				if (res != cgltf_result_success)
+				{
+					return res;
+				}
+			}
+			else
+			{
+				return cgltf_result_unknown_format;
+			}
+		}
+		else if (strstr(uri, "://") == NULL && gltf_path)
+		{
+			cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data);
+			data->buffers[i].data_free_method = cgltf_data_free_method_file_release;
+
+			if (res != cgltf_result_success)
+			{
+				return res;
+			}
+		}
+		else
+		{
+			return cgltf_result_unknown_format;
+		}
+	}
+
+	return cgltf_result_success;
+}
+
+static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type);
+
+static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count)
+{
+	char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset;
+	cgltf_size bound = 0;
+
+	switch (component_type)
+	{
+	case cgltf_component_type_r_8u:
+		for (size_t i = 0; i < count; ++i)
+		{
+			cgltf_size v = ((unsigned char*)data)[i];
+			bound = bound > v ? bound : v;
+		}
+		break;
+
+	case cgltf_component_type_r_16u:
+		for (size_t i = 0; i < count; ++i)
+		{
+			cgltf_size v = ((unsigned short*)data)[i];
+			bound = bound > v ? bound : v;
+		}
+		break;
+
+	case cgltf_component_type_r_32u:
+		for (size_t i = 0; i < count; ++i)
+		{
+			cgltf_size v = ((unsigned int*)data)[i];
+			bound = bound > v ? bound : v;
+		}
+		break;
+
+	default:
+		;
+	}
+
+	return bound;
+}
+
+#if CGLTF_VALIDATE_ENABLE_ASSERTS
+#define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result;
+#else
+#define CGLTF_ASSERT_IF(cond, result) if (cond) return result;
+#endif
+
+cgltf_result cgltf_validate(cgltf_data* data)
+{
+	for (cgltf_size i = 0; i < data->accessors_count; ++i)
+	{
+		cgltf_accessor* accessor = &data->accessors[i];
+
+		cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type);
+
+		if (accessor->buffer_view)
+		{
+			cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size;
+
+			CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short);
+		}
+
+		if (accessor->is_sparse)
+		{
+			cgltf_accessor_sparse* sparse = &accessor->sparse;
+
+			cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type);
+			cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count;
+			cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count;
+
+			CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size ||
+							sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short);
+
+			CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u &&
+							sparse->indices_component_type != cgltf_component_type_r_16u &&
+							sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf);
+
+			if (sparse->indices_buffer_view->buffer->data)
+			{
+				cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count);
+
+				CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short);
+			}
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
+	{
+		cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size;
+
+		CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short);
+
+		if (data->buffer_views[i].has_meshopt_compression)
+		{
+			cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression;
+
+			CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short);
+
+			CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf);
+
+			CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf);
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->meshes_count; ++i)
+	{
+		if (data->meshes[i].weights)
+		{
+			CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf);
+		}
+
+		if (data->meshes[i].target_names)
+		{
+			CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf);
+		}
+
+		for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
+		{
+			CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf);
+
+			if (data->meshes[i].primitives[j].attributes_count)
+			{
+				cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data;
+
+				for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
+				{
+					CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf);
+				}
+
+				for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k)
+				{
+					for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m)
+					{
+						CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf);
+					}
+				}
+
+				cgltf_accessor* indices = data->meshes[i].primitives[j].indices;
+
+				CGLTF_ASSERT_IF(indices &&
+					indices->component_type != cgltf_component_type_r_8u &&
+					indices->component_type != cgltf_component_type_r_16u &&
+					indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf);
+
+				if (indices && indices->buffer_view && indices->buffer_view->buffer->data)
+				{
+					cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count);
+
+					CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short);
+				}
+
+				for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k)
+				{
+					CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf);
+				}
+			}
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->nodes_count; ++i)
+	{
+		if (data->nodes[i].weights && data->nodes[i].mesh)
+		{
+			CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf);
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->nodes_count; ++i)
+	{
+		cgltf_node* p1 = data->nodes[i].parent;
+		cgltf_node* p2 = p1 ? p1->parent : NULL;
+
+		while (p1 && p2)
+		{
+			CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf);
+
+			p1 = p1->parent;
+			p2 = p2->parent ? p2->parent->parent : NULL;
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->scenes_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j)
+		{
+			CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf);
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->animations_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
+		{
+			cgltf_animation_channel* channel = &data->animations[i].channels[j];
+
+			if (!channel->target_node)
+			{
+				continue;
+			}
+
+			cgltf_size components = 1;
+
+			if (channel->target_path == cgltf_animation_path_type_weights)
+			{
+				CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf);
+
+				components = channel->target_node->mesh->primitives[0].targets_count;
+			}
+
+			cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1;
+
+			CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_data_too_short);
+		}
+	}
+
+	return cgltf_result_success;
+}
+
+cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size)
+{
+	cgltf_size json_size = extras->end_offset - extras->start_offset;
+
+	if (!dest)
+	{
+		if (dest_size)
+		{
+			*dest_size = json_size + 1;
+			return cgltf_result_success;
+		}
+		return cgltf_result_invalid_options;
+	}
+
+	if (*dest_size + 1 < json_size)
+	{
+		strncpy(dest, data->json + extras->start_offset, *dest_size - 1);
+		dest[*dest_size - 1] = 0;
+	}
+	else
+	{
+		strncpy(dest, data->json + extras->start_offset, json_size);
+		dest[json_size] = 0;
+	}
+
+	return cgltf_result_success;
+}
+
+static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras)
+{
+	data->memory.free_func(data->memory.user_data, extras->data);
+}
+
+static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
+{
+	for (cgltf_size i = 0; i < extensions_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, extensions[i].name);
+		data->memory.free_func(data->memory.user_data, extensions[i].data);
+	}
+	data->memory.free_func(data->memory.user_data, extensions);
+}
+
+static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view)
+{
+	cgltf_free_extensions(data, view->extensions, view->extensions_count);
+	cgltf_free_extras(data, &view->extras);
+}
+
+void cgltf_free(cgltf_data* data)
+{
+	if (!data)
+	{
+		return;
+	}
+
+	void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release;
+
+	data->memory.free_func(data->memory.user_data, data->asset.copyright);
+	data->memory.free_func(data->memory.user_data, data->asset.generator);
+	data->memory.free_func(data->memory.user_data, data->asset.version);
+	data->memory.free_func(data->memory.user_data, data->asset.min_version);
+
+	cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count);
+	cgltf_free_extras(data, &data->asset.extras);
+
+	for (cgltf_size i = 0; i < data->accessors_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->accessors[i].name);
+
+		if(data->accessors[i].is_sparse)
+		{
+			cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count);
+			cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count);
+			cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count);
+			cgltf_free_extras(data, &data->accessors[i].sparse.extras);
+			cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras);
+			cgltf_free_extras(data, &data->accessors[i].sparse.values_extras);
+		}
+		cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count);
+		cgltf_free_extras(data, &data->accessors[i].extras);
+	}
+	data->memory.free_func(data->memory.user_data, data->accessors);
+
+	for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->buffer_views[i].name);
+		data->memory.free_func(data->memory.user_data, data->buffer_views[i].data);
+
+		cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count);
+		cgltf_free_extras(data, &data->buffer_views[i].extras);
+	}
+	data->memory.free_func(data->memory.user_data, data->buffer_views);
+
+	for (cgltf_size i = 0; i < data->buffers_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->buffers[i].name);
+
+		if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release)
+		{
+			file_release(&data->memory, &data->file, data->buffers[i].data);
+		}
+		else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free)
+		{
+			data->memory.free_func(data->memory.user_data, data->buffers[i].data);
+		}
+
+		data->memory.free_func(data->memory.user_data, data->buffers[i].uri);
+
+		cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count);
+		cgltf_free_extras(data, &data->buffers[i].extras);
+	}
+	data->memory.free_func(data->memory.user_data, data->buffers);
+
+	for (cgltf_size i = 0; i < data->meshes_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->meshes[i].name);
+
+		for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
+		{
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
+			{
+				data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name);
+			}
+
+			data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes);
+
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k)
+			{
+				for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m)
+				{
+					data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name);
+				}
+
+				data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes);
+			}
+
+			data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets);
+
+			if (data->meshes[i].primitives[j].has_draco_mesh_compression)
+			{
+				for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k)
+				{
+					data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name);
+				}
+
+				data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes);
+			}
+
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k)
+			{
+				cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras);
+			}
+
+			data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings);
+
+			cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count);
+			cgltf_free_extras(data, &data->meshes[i].primitives[j].extras);
+		}
+
+		data->memory.free_func(data->memory.user_data, data->meshes[i].primitives);
+		data->memory.free_func(data->memory.user_data, data->meshes[i].weights);
+
+		for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j)
+		{
+			data->memory.free_func(data->memory.user_data, data->meshes[i].target_names[j]);
+		}
+
+		cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count);
+		cgltf_free_extras(data, &data->meshes[i].extras);
+
+		data->memory.free_func(data->memory.user_data, data->meshes[i].target_names);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->meshes);
+
+	for (cgltf_size i = 0; i < data->materials_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->materials[i].name);
+
+		if(data->materials[i].has_pbr_metallic_roughness)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture);
+			cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture);
+		}
+		if(data->materials[i].has_pbr_specular_glossiness)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture);
+			cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture);
+		}
+		if(data->materials[i].has_clearcoat)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture);
+			cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture);
+			cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture);
+		}
+		if(data->materials[i].has_specular)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture);
+			cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture);
+		}
+		if(data->materials[i].has_transmission)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture);
+		}
+		if (data->materials[i].has_volume)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture);
+		}
+		if(data->materials[i].has_sheen)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture);
+			cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture);
+		}
+		if(data->materials[i].has_iridescence)
+		{
+			cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture);
+			cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture);
+		}
+
+		cgltf_free_texture_view(data, &data->materials[i].normal_texture);
+		cgltf_free_texture_view(data, &data->materials[i].occlusion_texture);
+		cgltf_free_texture_view(data, &data->materials[i].emissive_texture);
+
+		cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count);
+		cgltf_free_extras(data, &data->materials[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->materials);
+
+	for (cgltf_size i = 0; i < data->images_count; ++i) 
+	{
+		data->memory.free_func(data->memory.user_data, data->images[i].name);
+		data->memory.free_func(data->memory.user_data, data->images[i].uri);
+		data->memory.free_func(data->memory.user_data, data->images[i].mime_type);
+
+		cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count);
+		cgltf_free_extras(data, &data->images[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->images);
+
+	for (cgltf_size i = 0; i < data->textures_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->textures[i].name);
+
+		cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count);
+		cgltf_free_extras(data, &data->textures[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->textures);
+
+	for (cgltf_size i = 0; i < data->samplers_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->samplers[i].name);
+
+		cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count);
+		cgltf_free_extras(data, &data->samplers[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->samplers);
+
+	for (cgltf_size i = 0; i < data->skins_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->skins[i].name);
+		data->memory.free_func(data->memory.user_data, data->skins[i].joints);
+
+		cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count);
+		cgltf_free_extras(data, &data->skins[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->skins);
+
+	for (cgltf_size i = 0; i < data->cameras_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->cameras[i].name);
+
+		if (data->cameras[i].type == cgltf_camera_type_perspective)
+		{
+			cgltf_free_extras(data, &data->cameras[i].data.perspective.extras);
+		}
+		else if (data->cameras[i].type == cgltf_camera_type_orthographic)
+		{
+			cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras);
+		}
+
+		cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count);
+		cgltf_free_extras(data, &data->cameras[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->cameras);
+
+	for (cgltf_size i = 0; i < data->lights_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->lights[i].name);
+
+		cgltf_free_extras(data, &data->lights[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->lights);
+
+	for (cgltf_size i = 0; i < data->nodes_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->nodes[i].name);
+		data->memory.free_func(data->memory.user_data, data->nodes[i].children);
+		data->memory.free_func(data->memory.user_data, data->nodes[i].weights);
+
+		if (data->nodes[i].has_mesh_gpu_instancing)
+		{
+			for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j)
+			{
+				data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name);
+			}
+
+			data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes);
+		}
+
+		cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count);
+		cgltf_free_extras(data, &data->nodes[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->nodes);
+
+	for (cgltf_size i = 0; i < data->scenes_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->scenes[i].name);
+		data->memory.free_func(data->memory.user_data, data->scenes[i].nodes);
+
+		cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count);
+		cgltf_free_extras(data, &data->scenes[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->scenes);
+
+	for (cgltf_size i = 0; i < data->animations_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->animations[i].name);
+		for (cgltf_size j = 0; j <  data->animations[i].samplers_count; ++j)
+		{
+			cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count);
+			cgltf_free_extras(data, &data->animations[i].samplers[j].extras);
+		}
+		data->memory.free_func(data->memory.user_data, data->animations[i].samplers);
+
+		for (cgltf_size j = 0; j <  data->animations[i].channels_count; ++j)
+		{
+			cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count);
+			cgltf_free_extras(data, &data->animations[i].channels[j].extras);
+		}
+		data->memory.free_func(data->memory.user_data, data->animations[i].channels);
+
+		cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count);
+		cgltf_free_extras(data, &data->animations[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->animations);
+
+	for (cgltf_size i = 0; i < data->variants_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->variants[i].name);
+
+		cgltf_free_extras(data, &data->variants[i].extras);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->variants);
+
+	cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count);
+	cgltf_free_extras(data, &data->extras);
+
+	for (cgltf_size i = 0; i < data->extensions_used_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->extensions_used[i]);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->extensions_used);
+
+	for (cgltf_size i = 0; i < data->extensions_required_count; ++i)
+	{
+		data->memory.free_func(data->memory.user_data, data->extensions_required[i]);
+	}
+
+	data->memory.free_func(data->memory.user_data, data->extensions_required);
+
+	file_release(&data->memory, &data->file, data->file_data);
+
+	data->memory.free_func(data->memory.user_data, data);
+}
+
+void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix)
+{
+	cgltf_float* lm = out_matrix;
+
+	if (node->has_matrix)
+	{
+		memcpy(lm, node->matrix, sizeof(float) * 16);
+	}
+	else
+	{
+		float tx = node->translation[0];
+		float ty = node->translation[1];
+		float tz = node->translation[2];
+
+		float qx = node->rotation[0];
+		float qy = node->rotation[1];
+		float qz = node->rotation[2];
+		float qw = node->rotation[3];
+
+		float sx = node->scale[0];
+		float sy = node->scale[1];
+		float sz = node->scale[2];
+
+		lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx;
+		lm[1] = (2 * qx*qy + 2 * qz*qw) * sx;
+		lm[2] = (2 * qx*qz - 2 * qy*qw) * sx;
+		lm[3] = 0.f;
+
+		lm[4] = (2 * qx*qy - 2 * qz*qw) * sy;
+		lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy;
+		lm[6] = (2 * qy*qz + 2 * qx*qw) * sy;
+		lm[7] = 0.f;
+
+		lm[8] = (2 * qx*qz + 2 * qy*qw) * sz;
+		lm[9] = (2 * qy*qz - 2 * qx*qw) * sz;
+		lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz;
+		lm[11] = 0.f;
+
+		lm[12] = tx;
+		lm[13] = ty;
+		lm[14] = tz;
+		lm[15] = 1.f;
+	}
+}
+
+void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix)
+{
+	cgltf_float* lm = out_matrix;
+	cgltf_node_transform_local(node, lm);
+
+	const cgltf_node* parent = node->parent;
+
+	while (parent)
+	{
+		float pm[16];
+		cgltf_node_transform_local(parent, pm);
+
+		for (int i = 0; i < 4; ++i)
+		{
+			float l0 = lm[i * 4 + 0];
+			float l1 = lm[i * 4 + 1];
+			float l2 = lm[i * 4 + 2];
+
+			float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8];
+			float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9];
+			float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10];
+
+			lm[i * 4 + 0] = r0;
+			lm[i * 4 + 1] = r1;
+			lm[i * 4 + 2] = r2;
+		}
+
+		lm[12] += pm[12];
+		lm[13] += pm[13];
+		lm[14] += pm[14];
+
+		parent = parent->parent;
+	}
+}
+
+static cgltf_ssize cgltf_component_read_integer(const void* in, cgltf_component_type component_type)
+{
+	switch (component_type)
+	{
+		case cgltf_component_type_r_16:
+			return *((const int16_t*) in);
+		case cgltf_component_type_r_16u:
+			return *((const uint16_t*) in);
+		case cgltf_component_type_r_32u:
+			return *((const uint32_t*) in);
+		case cgltf_component_type_r_32f:
+			return (cgltf_ssize)*((const float*) in);
+		case cgltf_component_type_r_8:
+			return *((const int8_t*) in);
+		case cgltf_component_type_r_8u:
+			return *((const uint8_t*) in);
+		default:
+			return 0;
+	}
+}
+
+static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type)
+{
+	switch (component_type)
+	{
+		case cgltf_component_type_r_16u:
+			return *((const uint16_t*) in);
+		case cgltf_component_type_r_32u:
+			return *((const uint32_t*) in);
+		case cgltf_component_type_r_32f:
+			return (cgltf_size)*((const float*) in);
+		case cgltf_component_type_r_8u:
+			return *((const uint8_t*) in);
+		default:
+			return 0;
+	}
+}
+
+static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized)
+{
+	if (component_type == cgltf_component_type_r_32f)
+	{
+		return *((const float*) in);
+	}
+
+	if (normalized)
+	{
+		switch (component_type)
+		{
+			// note: glTF spec doesn't currently define normalized conversions for 32-bit integers
+			case cgltf_component_type_r_16:
+				return *((const int16_t*) in) / (cgltf_float)32767;
+			case cgltf_component_type_r_16u:
+				return *((const uint16_t*) in) / (cgltf_float)65535;
+			case cgltf_component_type_r_8:
+				return *((const int8_t*) in) / (cgltf_float)127;
+			case cgltf_component_type_r_8u:
+				return *((const uint8_t*) in) / (cgltf_float)255;
+			default:
+				return 0;
+		}
+	}
+
+	return (cgltf_float)cgltf_component_read_integer(in, component_type);
+}
+
+static cgltf_size cgltf_component_size(cgltf_component_type component_type);
+
+static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size)
+{
+	cgltf_size num_components = cgltf_num_components(type);
+
+	if (element_size < num_components) {
+		return 0;
+	}
+
+	// There are three special cases for component extraction, see #data-alignment in the 2.0 spec.
+
+	cgltf_size component_size = cgltf_component_size(component_type);
+
+	if (type == cgltf_type_mat2 && component_size == 1)
+	{
+		out[0] = cgltf_component_read_float(element, component_type, normalized);
+		out[1] = cgltf_component_read_float(element + 1, component_type, normalized);
+		out[2] = cgltf_component_read_float(element + 4, component_type, normalized);
+		out[3] = cgltf_component_read_float(element + 5, component_type, normalized);
+		return 1;
+	}
+
+	if (type == cgltf_type_mat3 && component_size == 1)
+	{
+		out[0] = cgltf_component_read_float(element, component_type, normalized);
+		out[1] = cgltf_component_read_float(element + 1, component_type, normalized);
+		out[2] = cgltf_component_read_float(element + 2, component_type, normalized);
+		out[3] = cgltf_component_read_float(element + 4, component_type, normalized);
+		out[4] = cgltf_component_read_float(element + 5, component_type, normalized);
+		out[5] = cgltf_component_read_float(element + 6, component_type, normalized);
+		out[6] = cgltf_component_read_float(element + 8, component_type, normalized);
+		out[7] = cgltf_component_read_float(element + 9, component_type, normalized);
+		out[8] = cgltf_component_read_float(element + 10, component_type, normalized);
+		return 1;
+	}
+
+	if (type == cgltf_type_mat3 && component_size == 2)
+	{
+		out[0] = cgltf_component_read_float(element, component_type, normalized);
+		out[1] = cgltf_component_read_float(element + 2, component_type, normalized);
+		out[2] = cgltf_component_read_float(element + 4, component_type, normalized);
+		out[3] = cgltf_component_read_float(element + 8, component_type, normalized);
+		out[4] = cgltf_component_read_float(element + 10, component_type, normalized);
+		out[5] = cgltf_component_read_float(element + 12, component_type, normalized);
+		out[6] = cgltf_component_read_float(element + 16, component_type, normalized);
+		out[7] = cgltf_component_read_float(element + 18, component_type, normalized);
+		out[8] = cgltf_component_read_float(element + 20, component_type, normalized);
+		return 1;
+	}
+
+	for (cgltf_size i = 0; i < num_components; ++i)
+	{
+		out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized);
+	}
+	return 1;
+}
+
+const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view)
+{
+	if (view->data)
+		return (const uint8_t*)view->data;
+
+	if (!view->buffer->data)
+		return NULL;
+
+	const uint8_t* result = (const uint8_t*)view->buffer->data;
+	result += view->offset;
+	return result;
+}
+
+cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size)
+{
+	if (accessor->is_sparse)
+	{
+		return 0;
+	}
+	if (accessor->buffer_view == NULL)
+	{
+		memset(out, 0, element_size * sizeof(cgltf_float));
+		return 1;
+	}
+	const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view);
+	if (element == NULL)
+	{
+		return 0;
+	}
+	element += accessor->offset + accessor->stride * index;
+	return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size);
+}
+
+cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count)
+{
+	cgltf_size floats_per_element = cgltf_num_components(accessor->type);
+	cgltf_size available_floats = accessor->count * floats_per_element;
+	if (out == NULL)
+	{
+		return available_floats;
+	}
+
+	float_count = available_floats < float_count ? available_floats : float_count;
+	cgltf_size element_count = float_count / floats_per_element;
+
+	// First pass: convert each element in the base accessor.
+	cgltf_float* dest = out;
+	cgltf_accessor dense = *accessor;
+	dense.is_sparse = 0;
+	for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element)
+	{
+		if (!cgltf_accessor_read_float(&dense, index, dest, floats_per_element))
+		{
+			return 0;
+		}
+	}
+
+	// Second pass: write out each element in the sparse accessor.
+	if (accessor->is_sparse)
+	{
+		const cgltf_accessor_sparse* sparse = &dense.sparse;
+
+		const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view);
+		const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view);
+
+		if (index_data == NULL || reader_head == NULL)
+		{
+			return 0;
+		}
+
+		index_data += sparse->indices_byte_offset;
+		reader_head += sparse->values_byte_offset;
+
+		cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type);
+		for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride)
+		{
+			size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type);
+			float* writer_head = out + writer_index * floats_per_element;
+
+			if (!cgltf_element_read_float(reader_head, dense.type, dense.component_type, dense.normalized, writer_head, floats_per_element))
+			{
+				return 0;
+			}
+
+			reader_head += dense.stride;
+		}
+	}
+
+	return element_count * floats_per_element;
+}
+
+static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type)
+{
+	switch (component_type)
+	{
+		case cgltf_component_type_r_8:
+			return *((const int8_t*) in);
+
+		case cgltf_component_type_r_8u:
+			return *((const uint8_t*) in);
+
+		case cgltf_component_type_r_16:
+			return *((const int16_t*) in);
+
+		case cgltf_component_type_r_16u:
+			return *((const uint16_t*) in);
+
+		case cgltf_component_type_r_32u:
+			return *((const uint32_t*) in);
+
+		default:
+			return 0;
+	}
+}
+
+static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size)
+{
+	cgltf_size num_components = cgltf_num_components(type);
+
+	if (element_size < num_components)
+	{
+		return 0;
+	}
+
+	// Reading integer matrices is not a valid use case
+	if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4)
+	{
+		return 0;
+	}
+
+	cgltf_size component_size = cgltf_component_size(component_type);
+
+	for (cgltf_size i = 0; i < num_components; ++i)
+	{
+		out[i] = cgltf_component_read_uint(element + component_size * i, component_type);
+	}
+	return 1;
+}
+
+cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size)
+{
+	if (accessor->is_sparse)
+	{
+		return 0;
+	}
+	if (accessor->buffer_view == NULL)
+	{
+		memset(out, 0, element_size * sizeof( cgltf_uint ));
+		return 1;
+	}
+	const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view);
+	if (element == NULL)
+	{
+		return 0;
+	}
+	element += accessor->offset + accessor->stride * index;
+	return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size);
+}
+
+cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index)
+{
+	if (accessor->is_sparse)
+	{
+		return 0; // This is an error case, but we can't communicate the error with existing interface.
+	}
+	if (accessor->buffer_view == NULL)
+	{
+		return 0;
+	}
+	const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view);
+	if (element == NULL)
+	{
+		return 0; // This is an error case, but we can't communicate the error with existing interface.
+	}
+	element += accessor->offset + accessor->stride * index;
+	return cgltf_component_read_index(element, accessor->component_type);
+}
+
+#define CGLTF_ERROR_JSON -1
+#define CGLTF_ERROR_NOMEM -2
+#define CGLTF_ERROR_LEGACY -3
+
+#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; }
+#define CGLTF_CHECK_TOKTYPE_RETTYPE(tok_, type_, ret_) if ((tok_).type != (type_)) { return (ret_)CGLTF_ERROR_JSON; }
+#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */
+
+#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1)
+#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; }
+#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1];
+
+static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str)
+{
+	CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING);
+	size_t const str_len = strlen(str);
+	size_t const name_length = tok->end - tok->start;
+	return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128;
+}
+
+static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk)
+{
+	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
+	char tmp[128];
+	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
+	strncpy(tmp, (const char*)json_chunk + tok->start, size);
+	tmp[size] = 0;
+	return CGLTF_ATOI(tmp);
+}
+
+static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk)
+{
+	CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size);
+	char tmp[128];
+	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
+	strncpy(tmp, (const char*)json_chunk + tok->start, size);
+	tmp[size] = 0;
+	return (cgltf_size)CGLTF_ATOLL(tmp);
+}
+
+static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk)
+{
+	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
+	char tmp[128];
+	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
+	strncpy(tmp, (const char*)json_chunk + tok->start, size);
+	tmp[size] = 0;
+	return (cgltf_float)CGLTF_ATOF(tmp);
+}
+
+static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk)
+{
+	int size = tok->end - tok->start;
+	return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0;
+}
+
+static int cgltf_skip_json(jsmntok_t const* tokens, int i)
+{
+	int end = i + 1;
+
+	while (i < end)
+	{
+		switch (tokens[i].type)
+		{
+		case JSMN_OBJECT:
+			end += tokens[i].size * 2;
+			break;
+
+		case JSMN_ARRAY:
+			end += tokens[i].size;
+			break;
+
+		case JSMN_PRIMITIVE:
+		case JSMN_STRING:
+			break;
+
+		default:
+			return -1;
+		}
+
+		i++;
+	}
+
+	return i;
+}
+
+static void cgltf_fill_float_array(float* out_array, int size, float value)
+{
+	for (int j = 0; j < size; ++j)
+	{
+		out_array[j] = value;
+	}
+}
+
+static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	if (tokens[i].size != size)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+	++i;
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+		out_array[j] = cgltf_json_to_float(tokens + i, json_chunk);
+		++i;
+	}
+	return i;
+}
+
+static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING);
+	if (*out_string)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+	int size = tokens[i].end - tokens[i].start;
+	char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
+	if (!result)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+	strncpy(result, (const char*)json_chunk + tokens[i].start, size);
+	result[size] = 0;
+	*out_string = result;
+	return i + 1;
+}
+
+static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size)
+{
+	(void)json_chunk;
+	if (tokens[i].type != JSMN_ARRAY)
+	{
+		return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON;
+	}
+	if (*out_array)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+	int size = tokens[i].size;
+	void* result = cgltf_calloc(options, element_size, size);
+	if (!result)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+	*out_array = result;
+	*out_size = size;
+	return i + 1;
+}
+
+static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < *out_size; ++j)
+	{
+		i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array));
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index)
+{
+	if (*name == '_')
+	{
+		*out_type = cgltf_attribute_type_custom;
+		return;
+	}
+
+	const char* us = strchr(name, '_');
+	size_t len = us ? (size_t)(us - name) : strlen(name);
+
+	if (len == 8 && strncmp(name, "POSITION", 8) == 0)
+	{
+		*out_type = cgltf_attribute_type_position;
+	}
+	else if (len == 6 && strncmp(name, "NORMAL", 6) == 0)
+	{
+		*out_type = cgltf_attribute_type_normal;
+	}
+	else if (len == 7 && strncmp(name, "TANGENT", 7) == 0)
+	{
+		*out_type = cgltf_attribute_type_tangent;
+	}
+	else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0)
+	{
+		*out_type = cgltf_attribute_type_texcoord;
+	}
+	else if (len == 5 && strncmp(name, "COLOR", 5) == 0)
+	{
+		*out_type = cgltf_attribute_type_color;
+	}
+	else if (len == 6 && strncmp(name, "JOINTS", 6) == 0)
+	{
+		*out_type = cgltf_attribute_type_joints;
+	}
+	else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0)
+	{
+		*out_type = cgltf_attribute_type_weights;
+	}
+	else
+	{
+		*out_type = cgltf_attribute_type_invalid;
+	}
+
+	if (us && *out_type != cgltf_attribute_type_invalid)
+	{
+		*out_index = CGLTF_ATOI(us + 1);
+	}
+}
+
+static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	if (*out_attributes)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+
+	*out_attributes_count = tokens[i].size;
+	*out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count);
+	++i;
+
+	if (!*out_attributes)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+
+	for (cgltf_size j = 0; j < *out_attributes_count; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name);
+		if (i < 0)
+		{
+			return CGLTF_ERROR_JSON;
+		}
+
+		cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index);
+
+		(*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
+		++i;
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
+{
+	if (out_extras->data)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+
+	/* fill deprecated fields for now, this will be removed in the future */
+	out_extras->start_offset = tokens[i].start;
+	out_extras->end_offset = tokens[i].end;
+
+	size_t start = tokens[i].start;
+	size_t size = tokens[i].end - start;
+	out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
+	if (!out_extras->data)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+	strncpy(out_extras->data, (const char*)json_chunk + start, size);
+	out_extras->data[size] = '\0';
+
+	i = cgltf_skip_json(tokens, i);
+	return i;
+}
+
+static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING);
+	CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT);
+	if (out_extension->name)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+
+	cgltf_size name_length = tokens[i].end - tokens[i].start;
+	out_extension->name = (char*)options->memory.alloc_func(options->memory.user_data, name_length + 1);
+	if (!out_extension->name)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+	strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length);
+	out_extension->name[name_length] = 0;
+	i++;
+
+	size_t start = tokens[i].start;
+	size_t size = tokens[i].end - start;
+	out_extension->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
+	if (!out_extension->data)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+	strncpy(out_extension->data, (const char*)json_chunk + start, size);
+	out_extension->data[size] = '\0';
+
+	i = cgltf_skip_json(tokens, i);
+
+	return i;
+}
+
+static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions)
+{
+	++i;
+
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	if(*out_extensions)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+
+	int extensions_size = tokens[i].size;
+	*out_extensions_count = 0;
+	*out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+
+	if (!*out_extensions)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+
+	++i;
+
+	for (int j = 0; j < extensions_size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		cgltf_size extension_index = (*out_extensions_count)++;
+		cgltf_extension* extension = &((*out_extensions)[extension_index]);
+		i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension);
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0)
+		{
+			i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0)
+		{
+			++i;
+			out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_mesh_gpu_instancing(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh_gpu_instancing* out_mesh_gpu_instancing)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0)
+		{
+			i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_mesh_gpu_instancing->attributes, &out_mesh_gpu_instancing->attributes_count);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0)
+		{
+			++i;
+			out_mesh_gpu_instancing->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset)
+{
+	(void)options;
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+		int obj_size = tokens[i].size;
+		++i;
+
+		int material = -1;
+		int variants_tok = -1;
+		int extras_tok = -1;
+
+		for (int k = 0; k < obj_size; ++k)
+		{
+			CGLTF_CHECK_KEY(tokens[i]);
+
+			if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0)
+			{
+				++i;
+				material = cgltf_json_to_int(tokens + i, json_chunk);
+				++i;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0)
+			{
+				variants_tok = i+1;
+				CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY);
+
+				i = cgltf_skip_json(tokens, i+1);
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+			{
+				extras_tok = i + 1;
+				i = cgltf_skip_json(tokens, extras_tok);
+			}
+			else
+			{
+				i = cgltf_skip_json(tokens, i+1);
+			}
+
+			if (i < 0)
+			{
+				return i;
+			}
+		}
+
+		if (material < 0 || variants_tok < 0)
+		{
+			return CGLTF_ERROR_JSON;
+		}
+
+		if (out_mappings)
+		{
+			for (int k = 0; k < tokens[variants_tok].size; ++k)
+			{
+				int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk);
+				if (variant < 0)
+					return variant;
+
+				out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material);
+				out_mappings[*offset].variant = variant;
+
+				if (extras_tok >= 0)
+				{
+					int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras);
+					if (e < 0)
+						return e;
+				}
+
+				(*offset)++;
+			}
+		}
+		else
+		{
+			(*offset) += tokens[variants_tok].size;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0)
+		{
+			if (out_prim->mappings)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			cgltf_size mappings_offset = 0;
+			int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset);
+			if (k < 0)
+			{
+				return k;
+			}
+
+			out_prim->mappings_count = mappings_offset;
+			out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count);
+
+			mappings_offset = 0;
+			i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_prim->type = cgltf_primitive_type_triangles;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0)
+		{
+			++i;
+			out_prim->type
+					= (cgltf_primitive_type)
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
+		{
+			++i;
+			out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0)
+		{
+			++i;
+			out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0)
+		{
+			i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size k = 0; k < out_prim->targets_count; ++k)
+			{
+				i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count);
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if(out_prim->extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			out_prim->extensions_count = 0;
+			out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+
+			if (!out_prim->extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			++i;
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0)
+				{
+					out_prim->has_draco_mesh_compression = 1;
+					i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0)
+				{
+					i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim);
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index)
+			{
+				i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]);
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			++i;
+
+			out_mesh->extras.start_offset = tokens[i].start;
+			out_mesh->extras.end_offset = tokens[i].end;
+
+			if (tokens[i].type == JSMN_OBJECT)
+			{
+				int extras_size = tokens[i].size;
+				++i;
+
+				for (int k = 0; k < extras_size; ++k)
+				{
+					CGLTF_CHECK_KEY(tokens[i]);
+
+					if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY)
+					{
+						i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count);
+					}
+					else
+					{
+						i = cgltf_skip_json(tokens, i+1);
+					}
+
+					if (i < 0)
+					{
+						return i;
+					}
+				}
+			}
+			else
+			{
+				i = cgltf_skip_json(tokens, i);
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->meshes_count; ++j)
+	{
+		i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk)
+{
+	int type = cgltf_json_to_int(tok, json_chunk);
+
+	switch (type)
+	{
+	case 5120:
+		return cgltf_component_type_r_8;
+	case 5121:
+		return cgltf_component_type_r_8u;
+	case 5122:
+		return cgltf_component_type_r_16;
+	case 5123:
+		return cgltf_component_type_r_16u;
+	case 5125:
+		return cgltf_component_type_r_32u;
+	case 5126:
+		return cgltf_component_type_r_32f;
+	default:
+		return cgltf_component_type_invalid;
+	}
+}
+
+static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
+		{
+			++i;
+			out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+			int indices_size = tokens[i].size;
+			++i;
+
+			for (int k = 0; k < indices_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
+				{
+					++i;
+					out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+				{
+					++i;
+					out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0)
+				{
+					++i;
+					out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+				{
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras);
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+				{
+					i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->indices_extensions_count, &out_sparse->indices_extensions);
+				}
+				else
+				{
+					i = cgltf_skip_json(tokens, i+1);
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+			int values_size = tokens[i].size;
+			++i;
+
+			for (int k = 0; k < values_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
+				{
+					++i;
+					out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+				{
+					++i;
+					out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+				{
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras);
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+				{
+					i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->values_extensions_count, &out_sparse->values_extensions);
+				}
+				else
+				{
+					i = cgltf_skip_json(tokens, i+1);
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->extensions_count, &out_sparse->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
+		{
+			++i;
+			out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+		{
+			++i;
+			out_accessor->offset =
+					cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0)
+		{
+			++i;
+			out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0)
+		{
+			++i;
+			out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
+		{
+			++i;
+			out_accessor->count =
+					cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0)
+			{
+				out_accessor->type = cgltf_type_scalar;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0)
+			{
+				out_accessor->type = cgltf_type_vec2;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0)
+			{
+				out_accessor->type = cgltf_type_vec3;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0)
+			{
+				out_accessor->type = cgltf_type_vec4;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0)
+			{
+				out_accessor->type = cgltf_type_mat2;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0)
+			{
+				out_accessor->type = cgltf_type_mat3;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0)
+			{
+				out_accessor->type = cgltf_type_mat4;
+			}
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0)
+		{
+			++i;
+			out_accessor->has_min = 1;
+			// note: we can't parse the precise number of elements since type may not have been computed yet
+			int min_size = tokens[i].size > 16 ? 16 : tokens[i].size;
+			i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0)
+		{
+			++i;
+			out_accessor->has_max = 1;
+			// note: we can't parse the precise number of elements since type may not have been computed yet
+			int max_size = tokens[i].size > 16 ? 16 : tokens[i].size;
+			i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0)
+		{
+			out_accessor->is_sparse = 1;
+			i = cgltf_parse_json_accessor_sparse(options, tokens, i + 1, json_chunk, &out_accessor->sparse);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0)
+		{
+			++i;
+			out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0)
+		{
+			++i;
+			out_texture_transform->has_texcoord = 1;
+			out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_texture_view->scale = 1.0f;
+	cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0)
+		{
+			++i;
+			out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0)
+		{
+			++i;
+			out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) 
+		{
+			++i;
+			out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0)
+		{
+			++i;
+			out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if(out_texture_view->extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			out_texture_view->extensions_count = 0;
+			out_texture_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+
+			if (!out_texture_view->extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			++i;
+
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0)
+				{
+					out_texture_view->has_transform = 1;
+					i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform);
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture_view->extensions[out_texture_view->extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0)
+		{
+			++i;
+			out_pbr->metallic_factor = 
+				cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) 
+		{
+			++i;
+			out_pbr->roughness_factor =
+				cgltf_json_to_float(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
+				&out_pbr->base_color_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
+				&out_pbr->metallic_roughness_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0)
+		{
+			++i;
+			out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0)
+		{
+			++i;
+			out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0)
+		{
+			++i;
+			out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	// Default values
+	out_ior->ior = 1.5f;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0)
+		{
+			++i;
+			out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	// Default values
+	out_specular->specular_factor = 1.0f;
+	cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f);
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0)
+		{
+			++i;
+			out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0)
+		{
+			++i;
+			out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0)
+		{
+			++i;
+			out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0)
+		{
+			++i;
+			out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0)
+		{
+			++i;
+			out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	// Default
+	out_emissive_strength->emissive_strength = 1.f;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0)
+		{
+			++i;
+			out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_iridescence* out_iridescence)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+	int size = tokens[i].size;
+	++i;
+
+	// Default
+	out_iridescence->iridescence_ior = 1.3f;
+	out_iridescence->iridescence_thickness_min = 100.f;
+	out_iridescence->iridescence_thickness_max = 400.f;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceFactor") == 0)
+		{
+			++i;
+			out_iridescence->iridescence_factor = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceIor") == 0)
+		{
+			++i;
+			out_iridescence->iridescence_ior = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMinimum") == 0)
+		{
+			++i;
+			out_iridescence->iridescence_thickness_min = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMaximum") == 0)
+		{
+			++i;
+			out_iridescence->iridescence_thickness_max = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_thickness_texture);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j) 
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) 
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
+		{
+			++i;
+			out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler)
+{
+	(void)options;
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_sampler->wrap_s = 10497;
+	out_sampler->wrap_t = 10497;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0)
+		{
+			++i;
+			out_sampler->mag_filter
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0)
+		{
+			++i;
+			out_sampler->min_filter
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0)
+		{
+			++i;
+			out_sampler->wrap_s
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) 
+		{
+			++i;
+			out_sampler->wrap_t
+				= cgltf_json_to_int(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0)
+		{
+			++i;
+			out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) 
+		{
+			++i;
+			out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if (out_texture->extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			++i;
+			out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+			out_texture->extensions_count = 0;
+
+			if (!out_texture->extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0)
+				{
+					out_texture->has_basisu = 1;
+					++i;
+					CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+					int num_properties = tokens[i].size;
+					++i;
+
+					for (int t = 0; t < num_properties; ++t)
+					{
+						CGLTF_CHECK_KEY(tokens[i]);
+
+						if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0)
+						{
+							++i;
+							out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk));
+							++i;
+						}
+						else
+						{
+							i = cgltf_skip_json(tokens, i + 1);
+						}
+						if (i < 0)
+						{
+							return i;
+						}
+					}
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f);
+	out_material->pbr_metallic_roughness.metallic_factor = 1.0f;
+	out_material->pbr_metallic_roughness.roughness_factor = 1.0f;
+
+	cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f);
+	cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f);
+	out_material->pbr_specular_glossiness.glossiness_factor = 1.0f;
+
+	cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f);
+	out_material->volume.attenuation_distance = FLT_MAX;
+
+	out_material->alpha_cutoff = 0.5f;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0)
+		{
+			out_material->has_pbr_metallic_roughness = 1;
+			i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
+				&out_material->normal_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
+				&out_material->occlusion_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0)
+		{
+			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
+				&out_material->emissive_texture);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0)
+			{
+				out_material->alpha_mode = cgltf_alpha_mode_opaque;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0)
+			{
+				out_material->alpha_mode = cgltf_alpha_mode_mask;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0)
+			{
+				out_material->alpha_mode = cgltf_alpha_mode_blend;
+			}
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0)
+		{
+			++i;
+			out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0)
+		{
+			++i;
+			out_material->double_sided =
+				cgltf_json_to_bool(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if(out_material->extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			++i;
+			out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+			out_material->extensions_count= 0;
+
+			if (!out_material->extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0)
+				{
+					out_material->has_pbr_specular_glossiness = 1;
+					i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0)
+				{
+					out_material->unlit = 1;
+					i = cgltf_skip_json(tokens, i+1);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0)
+				{
+					out_material->has_clearcoat = 1;
+					i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0)
+				{
+					out_material->has_ior = 1;
+					i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0)
+				{
+					out_material->has_specular = 1;
+					i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0)
+				{
+					out_material->has_transmission = 1;
+					i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission);
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0)
+				{
+					out_material->has_volume = 1;
+					i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume);
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0)
+				{
+					out_material->has_sheen = 1;
+					i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen);
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0)
+				{
+					out_material->has_emissive_strength = 1;
+					i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength);
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_iridescence") == 0)
+				{
+					out_material->has_iridescence = 1;
+					i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence);
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->accessors_count; ++j)
+	{
+		i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->materials_count; ++j)
+	{
+		i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->images_count; ++j)
+	{
+		i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->textures_count; ++j)
+	{
+		i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->samplers_count; ++j)
+	{
+		i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression)
+{
+	(void)options;
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0)
+		{
+			++i;
+			out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+		{
+			++i;
+			out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
+		{
+			++i;
+			out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0)
+		{
+			++i;
+			out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
+		{
+			++i;
+			out_meshopt_compression->count = cgltf_json_to_int(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0)
+			{
+				out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0)
+			{
+				out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0)
+			{
+				out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices;
+			}
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0)
+			{
+				out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0)
+			{
+				out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0)
+			{
+				out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion;
+			}
+			else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0)
+			{
+				out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential;
+			}
+			++i;
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0)
+		{
+			++i;
+			out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
+		{
+			++i;
+			out_buffer_view->offset =
+					cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
+		{
+			++i;
+			out_buffer_view->size =
+					cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0)
+		{
+			++i;
+			out_buffer_view->stride =
+					cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0)
+		{
+			++i;
+			int type = cgltf_json_to_int(tokens+i, json_chunk);
+			switch (type)
+			{
+			case 34962:
+				type = cgltf_buffer_view_type_vertices;
+				break;
+			case 34963:
+				type = cgltf_buffer_view_type_indices;
+				break;
+			default:
+				type = cgltf_buffer_view_type_invalid;
+				break;
+			}
+			out_buffer_view->type = (cgltf_buffer_view_type)type;
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if(out_buffer_view->extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			out_buffer_view->extensions_count = 0;
+			out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+
+			if (!out_buffer_view->extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			++i;
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0)
+				{
+					out_buffer_view->has_meshopt_compression = 1;
+					i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression);
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j)
+	{
+		i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
+		{
+			++i;
+			out_buffer->size =
+					cgltf_json_to_size(tokens+i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->buffers_count; ++j)
+	{
+		i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size k = 0; k < out_skin->joints_count; ++k)
+			{
+				out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
+				++i;
+			}
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+			out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+			out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->skins_count; ++j)
+	{
+		i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+			int data_size = tokens[i].size;
+			++i;
+
+			if (out_camera->type != cgltf_camera_type_invalid)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			out_camera->type = cgltf_camera_type_perspective;
+
+			for (int k = 0; k < data_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0)
+				{
+					++i;
+					out_camera->data.perspective.has_aspect_ratio = 1;
+					out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0)
+				{
+					++i;
+					out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
+				{
+					++i;
+					out_camera->data.perspective.has_zfar = 1;
+					out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
+				{
+					++i;
+					out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+				{
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
+				}
+				else
+				{
+					i = cgltf_skip_json(tokens, i+1);
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+			int data_size = tokens[i].size;
+			++i;
+
+			if (out_camera->type != cgltf_camera_type_invalid)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			out_camera->type = cgltf_camera_type_orthographic;
+
+			for (int k = 0; k < data_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0)
+				{
+					++i;
+					out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0)
+				{
+					++i;
+					out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
+				{
+					++i;
+					out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
+				{
+					++i;
+					out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+				{
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
+				}
+				else
+				{
+					i = cgltf_skip_json(tokens, i+1);
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->cameras_count; ++j)
+	{
+		i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_light->color[0] = 1.f;
+	out_light->color[1] = 1.f;
+	out_light->color[2] = 1.f;
+	out_light->intensity = 1.f;
+
+	out_light->spot_inner_cone_angle = 0.f;
+	out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0)
+		{
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0)
+		{
+			++i;
+			out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0)
+			{
+				out_light->type = cgltf_light_type_directional;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0)
+			{
+				out_light->type = cgltf_light_type_point;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0)
+			{
+				out_light->type = cgltf_light_type_spot;
+			}
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0)
+		{
+			++i;
+			out_light->range = cgltf_json_to_float(tokens + i, json_chunk);
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+			int data_size = tokens[i].size;
+			++i;
+
+			for (int k = 0; k < data_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0)
+				{
+					++i;
+					out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0)
+				{
+					++i;
+					out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk);
+					++i;
+				}
+				else
+				{
+					i = cgltf_skip_json(tokens, i+1);
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->lights_count; ++j)
+	{
+		i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	out_node->rotation[3] = 1.0f;
+	out_node->scale[0] = 1.0f;
+	out_node->scale[1] = 1.0f;
+	out_node->scale[2] = 1.0f;
+	out_node->matrix[0] = 1.0f;
+	out_node->matrix[5] = 1.0f;
+	out_node->matrix[10] = 1.0f;
+	out_node->matrix[15] = 1.0f;
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size k = 0; k < out_node->children_count; ++k)
+			{
+				out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
+				++i;
+			}
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+			out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+			out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0)
+		{
+			++i;
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+			out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0)
+		{
+			out_node->has_translation = 1;
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0)
+		{
+			out_node->has_rotation = 1;
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0)
+		{
+			out_node->has_scale = 1;
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0)
+		{
+			out_node->has_matrix = 1;
+			i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if(out_node->extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			out_node->extensions_count= 0;
+			out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+
+			if (!out_node->extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			++i;
+
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0)
+				{
+					++i;
+
+					CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+					int data_size = tokens[i].size;
+					++i;
+
+					for (int m = 0; m < data_size; ++m)
+					{
+						CGLTF_CHECK_KEY(tokens[i]);
+
+						if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0)
+						{
+							++i;
+							CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
+							out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk));
+							++i;
+						}
+						else
+						{
+							i = cgltf_skip_json(tokens, i + 1);
+						}
+
+						if (i < 0)
+						{
+							return i;
+						}
+					}
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_mesh_gpu_instancing") == 0)
+				{
+					out_node->has_mesh_gpu_instancing = 1;
+					i = cgltf_parse_json_mesh_gpu_instancing(options, tokens, i + 1, json_chunk, &out_node->mesh_gpu_instancing);
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->nodes_count; ++j)
+	{
+		i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size k = 0; k < out_scene->nodes_count; ++k)
+			{
+				out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
+				++i;
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->scenes_count; ++j)
+	{
+		i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler)
+{
+	(void)options;
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0)
+		{
+			++i;
+			out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0)
+		{
+			++i;
+			out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0)
+		{
+			++i;
+			if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0)
+			{
+				out_sampler->interpolation = cgltf_interpolation_type_linear;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0)
+			{
+				out_sampler->interpolation = cgltf_interpolation_type_step;
+			}
+			else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0)
+			{
+				out_sampler->interpolation = cgltf_interpolation_type_cubic_spline;
+			}
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel)
+{
+	(void)options;
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0)
+		{
+			++i;
+			out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+			int target_size = tokens[i].size;
+			++i;
+
+			for (int k = 0; k < target_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0)
+				{
+					++i;
+					out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0)
+				{
+					++i;
+					if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0)
+					{
+						out_channel->target_path = cgltf_animation_path_type_translation;
+					}
+					else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0)
+					{
+						out_channel->target_path = cgltf_animation_path_type_rotation;
+					}
+					else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0)
+					{
+						out_channel->target_path = cgltf_animation_path_type_scale;
+					}
+					else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0)
+					{
+						out_channel->target_path = cgltf_animation_path_type_weights;
+					}
+					++i;
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+				{
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras);
+				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+				{
+					i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions);
+				}
+				else
+				{
+					i = cgltf_skip_json(tokens, i+1);
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size k = 0; k < out_animation->samplers_count; ++k)
+			{
+				i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]);
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0)
+		{
+			i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count);
+			if (i < 0)
+			{
+				return i;
+			}
+
+			for (cgltf_size k = 0; k < out_animation->channels_count; ++k)
+			{
+				i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]);
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->animations_count; ++j)
+	{
+		i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count);
+	if (i < 0)
+	{
+		return i;
+	}
+
+	for (cgltf_size j = 0; j < out_data->variants_count; ++j)
+	{
+		i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]);
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+	return i;
+}
+
+static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0)
+		{
+			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i+1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2)
+	{
+		return CGLTF_ERROR_LEGACY;
+	}
+
+	return i;
+}
+
+cgltf_size cgltf_num_components(cgltf_type type) {
+	switch (type)
+	{
+	case cgltf_type_vec2:
+		return 2;
+	case cgltf_type_vec3:
+		return 3;
+	case cgltf_type_vec4:
+		return 4;
+	case cgltf_type_mat2:
+		return 4;
+	case cgltf_type_mat3:
+		return 9;
+	case cgltf_type_mat4:
+		return 16;
+	case cgltf_type_invalid:
+	case cgltf_type_scalar:
+	default:
+		return 1;
+	}
+}
+
+static cgltf_size cgltf_component_size(cgltf_component_type component_type) {
+	switch (component_type)
+	{
+	case cgltf_component_type_r_8:
+	case cgltf_component_type_r_8u:
+		return 1;
+	case cgltf_component_type_r_16:
+	case cgltf_component_type_r_16u:
+		return 2;
+	case cgltf_component_type_r_32u:
+	case cgltf_component_type_r_32f:
+		return 4;
+	case cgltf_component_type_invalid:
+	default:
+		return 0;
+	}
+}
+
+static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type)
+{
+	cgltf_size component_size = cgltf_component_size(component_type);
+	if (type == cgltf_type_mat2 && component_size == 1)
+	{
+		return 8 * component_size;
+	}
+	else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2))
+	{
+		return 12 * component_size;
+	}
+	return component_size * cgltf_num_components(type);
+}
+
+static int cgltf_fixup_pointers(cgltf_data* out_data);
+
+static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
+{
+	CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+	int size = tokens[i].size;
+	++i;
+
+	for (int j = 0; j < size; ++j)
+	{
+		CGLTF_CHECK_KEY(tokens[i]);
+
+		if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0)
+		{
+			i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0)
+		{
+			i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0)
+		{
+			i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0)
+		{
+			i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0)
+		{
+			i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0)
+		{
+			i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0)
+		{
+			i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0)
+		{
+			i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0)
+		{
+			i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0)
+		{
+			i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0)
+		{
+			i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0)
+		{
+			i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0)
+		{
+			i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0)
+		{
+			++i;
+			out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk));
+			++i;
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0)
+		{
+			i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data);
+		}
+		else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0)
+		{
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
+		{
+			++i;
+
+			CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+			if(out_data->data_extensions)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			int extensions_size = tokens[i].size;
+			out_data->data_extensions_count = 0;
+			out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
+
+			if (!out_data->data_extensions)
+			{
+				return CGLTF_ERROR_NOMEM;
+			}
+
+			++i;
+
+			for (int k = 0; k < extensions_size; ++k)
+			{
+				CGLTF_CHECK_KEY(tokens[i]);
+
+				if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0)
+				{
+					++i;
+
+					CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+					int data_size = tokens[i].size;
+					++i;
+
+					for (int m = 0; m < data_size; ++m)
+					{
+						CGLTF_CHECK_KEY(tokens[i]);
+
+						if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0)
+						{
+							i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data);
+						}
+						else
+						{
+							i = cgltf_skip_json(tokens, i + 1);
+						}
+
+						if (i < 0)
+						{
+							return i;
+						}
+					}
+				}
+				else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0)
+				{
+					++i;
+
+					CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+
+					int data_size = tokens[i].size;
+					++i;
+
+					for (int m = 0; m < data_size; ++m)
+					{
+						CGLTF_CHECK_KEY(tokens[i]);
+
+						if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0)
+						{
+							i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data);
+						}
+						else
+						{
+							i = cgltf_skip_json(tokens, i + 1);
+						}
+
+						if (i < 0)
+						{
+							return i;
+						}
+					}
+				}
+				else
+				{
+					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++]));
+				}
+
+				if (i < 0)
+				{
+					return i;
+				}
+			}
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0)
+		{
+			i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count);
+		}
+		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0)
+		{
+			i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count);
+		}
+		else
+		{
+			i = cgltf_skip_json(tokens, i + 1);
+		}
+
+		if (i < 0)
+		{
+			return i;
+		}
+	}
+
+	return i;
+}
+
+cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data)
+{
+	jsmn_parser parser = { 0, 0, 0 };
+
+	if (options->json_token_count == 0)
+	{
+		int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0);
+
+		if (token_count <= 0)
+		{
+			return cgltf_result_invalid_json;
+		}
+
+		options->json_token_count = token_count;
+	}
+
+	jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc_func(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1));
+
+	if (!tokens)
+	{
+		return cgltf_result_out_of_memory;
+	}
+
+	jsmn_init(&parser);
+
+	int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count);
+
+	if (token_count <= 0)
+	{
+		options->memory.free_func(options->memory.user_data, tokens);
+		return cgltf_result_invalid_json;
+	}
+
+	// this makes sure that we always have an UNDEFINED token at the end of the stream
+	// for invalid JSON inputs this makes sure we don't perform out of bound reads of token data
+	tokens[token_count].type = JSMN_UNDEFINED;
+
+	cgltf_data* data = (cgltf_data*)options->memory.alloc_func(options->memory.user_data, sizeof(cgltf_data));
+
+	if (!data)
+	{
+		options->memory.free_func(options->memory.user_data, tokens);
+		return cgltf_result_out_of_memory;
+	}
+
+	memset(data, 0, sizeof(cgltf_data));
+	data->memory = options->memory;
+	data->file = options->file;
+
+	int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data);
+
+	options->memory.free_func(options->memory.user_data, tokens);
+
+	if (i < 0)
+	{
+		cgltf_free(data);
+
+		switch (i)
+		{
+		case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory;
+		case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf;
+		default: return cgltf_result_invalid_gltf;
+		}
+	}
+
+	if (cgltf_fixup_pointers(data) < 0)
+	{
+		cgltf_free(data);
+		return cgltf_result_invalid_gltf;
+	}
+
+	data->json = (const char*)json_chunk;
+	data->json_size = size;
+
+	*out_data = data;
+
+	return cgltf_result_success;
+}
+
+static int cgltf_fixup_pointers(cgltf_data* data)
+{
+	for (cgltf_size i = 0; i < data->meshes_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
+		{
+			CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count);
+			CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count);
+
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
+			{
+				CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count);
+			}
+
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k)
+			{
+				for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m)
+				{
+					CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count);
+				}
+			}
+
+			if (data->meshes[i].primitives[j].has_draco_mesh_compression)
+			{
+				CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count);
+				for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m)
+				{
+					CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count);
+				}
+			}
+
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k)
+			{
+				CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count);
+			}
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->accessors_count; ++i)
+	{
+		CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count);
+
+		if (data->accessors[i].is_sparse)
+		{
+			CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count);
+			CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count);
+		}
+
+		if (data->accessors[i].buffer_view)
+		{
+			data->accessors[i].stride = data->accessors[i].buffer_view->stride;
+		}
+
+		if (data->accessors[i].stride == 0)
+		{
+			data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type);
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->textures_count; ++i)
+	{
+		CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count);
+		CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count);
+		CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count);
+	}
+
+	for (cgltf_size i = 0; i < data->images_count; ++i)
+	{
+		CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count);
+	}
+
+	for (cgltf_size i = 0; i < data->materials_count; ++i)
+	{
+		CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count);
+
+		CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count);
+		CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count);
+	}
+
+	for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
+	{
+		CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count);
+
+		if (data->buffer_views[i].has_meshopt_compression)
+		{
+			CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count);
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->skins_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j)
+		{
+			CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count);
+		}
+
+		CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count);
+		CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count);
+	}
+
+	for (cgltf_size i = 0; i < data->nodes_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j)
+		{
+			CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count);
+
+			if (data->nodes[i].children[j]->parent)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
+			data->nodes[i].children[j]->parent = &data->nodes[i];
+		}
+
+		CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count);
+		CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count);
+		CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count);
+		CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count);
+
+		if (data->nodes[i].has_mesh_gpu_instancing)
+		{
+			CGLTF_PTRFIXUP_REQ(data->nodes[i].mesh_gpu_instancing.buffer_view, data->buffer_views, data->buffer_views_count);
+			for (cgltf_size m = 0; m < data->nodes[i].mesh_gpu_instancing.attributes_count; ++m)
+			{
+				CGLTF_PTRFIXUP_REQ(data->nodes[i].mesh_gpu_instancing.attributes[m].data, data->accessors, data->accessors_count);
+			}
+		}
+	}
+
+	for (cgltf_size i = 0; i < data->scenes_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j)
+		{
+			CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count);
+
+			if (data->scenes[i].nodes[j]->parent)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+		}
+	}
+
+	CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count);
+
+	for (cgltf_size i = 0; i < data->animations_count; ++i)
+	{
+		for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j)
+		{
+			CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count);
+			CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count);
+		}
+
+		for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
+		{
+			CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count);
+			CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * -- jsmn.c start --
+ * Source: https://github.com/zserge/jsmn
+ * License: MIT
+ *
+ * Copyright (c) 2010 Serge A. Zaitsev
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+				   jsmntok_t *tokens, size_t num_tokens) {
+	jsmntok_t *tok;
+	if (parser->toknext >= num_tokens) {
+		return NULL;
+	}
+	tok = &tokens[parser->toknext++];
+	tok->start = tok->end = -1;
+	tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+	tok->parent = -1;
+#endif
+	return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+				int start, int end) {
+	token->type = type;
+	token->start = start;
+	token->end = end;
+	token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+				size_t len, jsmntok_t *tokens, size_t num_tokens) {
+	jsmntok_t *token;
+	int start;
+
+	start = parser->pos;
+
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+		switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+		/* In strict mode primitive must be followed by "," or "}" or "]" */
+		case ':':
+#endif
+		case '\t' : case '\r' : case '\n' : case ' ' :
+		case ','  : case ']'  : case '}' :
+			goto found;
+		}
+		if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+			parser->pos = start;
+			return JSMN_ERROR_INVAL;
+		}
+	}
+#ifdef JSMN_STRICT
+	/* In strict mode primitive must be followed by a comma/object/array */
+	parser->pos = start;
+	return JSMN_ERROR_PART;
+#endif
+
+found:
+	if (tokens == NULL) {
+		parser->pos--;
+		return 0;
+	}
+	token = jsmn_alloc_token(parser, tokens, num_tokens);
+	if (token == NULL) {
+		parser->pos = start;
+		return JSMN_ERROR_NOMEM;
+	}
+	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+	token->parent = parser->toksuper;
+#endif
+	parser->pos--;
+	return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+				 size_t len, jsmntok_t *tokens, size_t num_tokens) {
+	jsmntok_t *token;
+
+	int start = parser->pos;
+
+	parser->pos++;
+
+	/* Skip starting quote */
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+		char c = js[parser->pos];
+
+		/* Quote: end of string */
+		if (c == '\"') {
+			if (tokens == NULL) {
+				return 0;
+			}
+			token = jsmn_alloc_token(parser, tokens, num_tokens);
+			if (token == NULL) {
+				parser->pos = start;
+				return JSMN_ERROR_NOMEM;
+			}
+			jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+			token->parent = parser->toksuper;
+#endif
+			return 0;
+		}
+
+		/* Backslash: Quoted symbol expected */
+		if (c == '\\' && parser->pos + 1 < len) {
+			int i;
+			parser->pos++;
+			switch (js[parser->pos]) {
+			/* Allowed escaped symbols */
+			case '\"': case '/' : case '\\' : case 'b' :
+			case 'f' : case 'r' : case 'n'  : case 't' :
+				break;
+				/* Allows escaped symbol \uXXXX */
+			case 'u':
+				parser->pos++;
+				for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
+					/* If it isn't a hex character we have an error */
+					if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+						 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+						 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+						parser->pos = start;
+						return JSMN_ERROR_INVAL;
+					}
+					parser->pos++;
+				}
+				parser->pos--;
+				break;
+				/* Unexpected symbol */
+			default:
+				parser->pos = start;
+				return JSMN_ERROR_INVAL;
+			}
+		}
+	}
+	parser->pos = start;
+	return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+		   jsmntok_t *tokens, size_t num_tokens) {
+	int r;
+	int i;
+	jsmntok_t *token;
+	int count = parser->toknext;
+
+	for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+		char c;
+		jsmntype_t type;
+
+		c = js[parser->pos];
+		switch (c) {
+		case '{': case '[':
+			count++;
+			if (tokens == NULL) {
+				break;
+			}
+			token = jsmn_alloc_token(parser, tokens, num_tokens);
+			if (token == NULL)
+				return JSMN_ERROR_NOMEM;
+			if (parser->toksuper != -1) {
+				tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+				token->parent = parser->toksuper;
+#endif
+			}
+			token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+			token->start = parser->pos;
+			parser->toksuper = parser->toknext - 1;
+			break;
+		case '}': case ']':
+			if (tokens == NULL)
+				break;
+			type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+			if (parser->toknext < 1) {
+				return JSMN_ERROR_INVAL;
+			}
+			token = &tokens[parser->toknext - 1];
+			for (;;) {
+				if (token->start != -1 && token->end == -1) {
+					if (token->type != type) {
+						return JSMN_ERROR_INVAL;
+					}
+					token->end = parser->pos + 1;
+					parser->toksuper = token->parent;
+					break;
+				}
+				if (token->parent == -1) {
+					if(token->type != type || parser->toksuper == -1) {
+						return JSMN_ERROR_INVAL;
+					}
+					break;
+				}
+				token = &tokens[token->parent];
+			}
+#else
+			for (i = parser->toknext - 1; i >= 0; i--) {
+				token = &tokens[i];
+				if (token->start != -1 && token->end == -1) {
+					if (token->type != type) {
+						return JSMN_ERROR_INVAL;
+					}
+					parser->toksuper = -1;
+					token->end = parser->pos + 1;
+					break;
+				}
+			}
+			/* Error if unmatched closing bracket */
+			if (i == -1) return JSMN_ERROR_INVAL;
+			for (; i >= 0; i--) {
+				token = &tokens[i];
+				if (token->start != -1 && token->end == -1) {
+					parser->toksuper = i;
+					break;
+				}
+			}
+#endif
+			break;
+		case '\"':
+			r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+			if (r < 0) return r;
+			count++;
+			if (parser->toksuper != -1 && tokens != NULL)
+				tokens[parser->toksuper].size++;
+			break;
+		case '\t' : case '\r' : case '\n' : case ' ':
+			break;
+		case ':':
+			parser->toksuper = parser->toknext - 1;
+			break;
+		case ',':
+			if (tokens != NULL && parser->toksuper != -1 &&
+					tokens[parser->toksuper].type != JSMN_ARRAY &&
+					tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+				parser->toksuper = tokens[parser->toksuper].parent;
+#else
+				for (i = parser->toknext - 1; i >= 0; i--) {
+					if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+						if (tokens[i].start != -1 && tokens[i].end == -1) {
+							parser->toksuper = i;
+							break;
+						}
+					}
+				}
+#endif
+			}
+			break;
+#ifdef JSMN_STRICT
+			/* In strict mode primitives are: numbers and booleans */
+		case '-': case '0': case '1' : case '2': case '3' : case '4':
+		case '5': case '6': case '7' : case '8': case '9':
+		case 't': case 'f': case 'n' :
+			/* And they must not be keys of the object */
+			if (tokens != NULL && parser->toksuper != -1) {
+				jsmntok_t *t = &tokens[parser->toksuper];
+				if (t->type == JSMN_OBJECT ||
+						(t->type == JSMN_STRING && t->size != 0)) {
+					return JSMN_ERROR_INVAL;
+				}
+			}
+#else
+			/* In non-strict mode every unquoted value is a primitive */
+		default:
+#endif
+			r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+			if (r < 0) return r;
+			count++;
+			if (parser->toksuper != -1 && tokens != NULL)
+				tokens[parser->toksuper].size++;
+			break;
+
+#ifdef JSMN_STRICT
+			/* Unexpected char in strict mode */
+		default:
+			return JSMN_ERROR_INVAL;
+#endif
+		}
+	}
+
+	if (tokens != NULL) {
+		for (i = parser->toknext - 1; i >= 0; i--) {
+			/* Unmatched opened object or array */
+			if (tokens[i].start != -1 && tokens[i].end == -1) {
+				return JSMN_ERROR_PART;
+			}
+		}
+	}
+
+	return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens
+ * available.
+ */
+static void jsmn_init(jsmn_parser *parser) {
+	parser->pos = 0;
+	parser->toknext = 0;
+	parser->toksuper = -1;
+}
+/*
+ * -- jsmn.c end --
+ */
+
+#endif /* #ifdef CGLTF_IMPLEMENTATION */
+
+/* cgltf is distributed under MIT license:
+ *
+ * Copyright (c) 2018-2021 Johannes Kuhlmann
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */

+ 1488 - 0
vendor/cgltf/src/cgltf_write.h

@@ -0,0 +1,1488 @@
+/**
+ * cgltf_write - a single-file glTF 2.0 writer written in C99.
+ *
+ * Version: 1.13
+ *
+ * Website: https://github.com/jkuhlmann/cgltf
+ *
+ * Distributed under the MIT License, see notice at the end of this file.
+ *
+ * Building:
+ * Include this file where you need the struct and function
+ * declarations. Have exactly one source file where you define
+ * `CGLTF_WRITE_IMPLEMENTATION` before including this file to get the
+ * function definitions.
+ *
+ * Reference:
+ * `cgltf_result cgltf_write_file(const cgltf_options* options, const char*
+ * path, const cgltf_data* data)` writes a glTF data to the given file path.
+ * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary
+ * buffer of the given glTF data will be written in a GLB format.
+ * Otherwise, only the JSON part will be written.
+ * External buffers and images are not written out. `data` is not deallocated.
+ *
+ * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer,
+ * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory
+ * buffer. Returns the number of bytes written to `buffer`, including a null
+ * terminator. If buffer is null, returns the number of bytes that would have
+ * been written. `data` is not deallocated.
+ */
+#ifndef CGLTF_WRITE_H_INCLUDED__
+#define CGLTF_WRITE_H_INCLUDED__
+
+#include "cgltf.h"
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data);
+cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef CGLTF_WRITE_H_INCLUDED__ */
+
+/*
+ *
+ * Stop now, if you are only interested in the API.
+ * Below, you find the implementation.
+ *
+ */
+
+#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
+/* This makes MSVC/CLion intellisense work. */
+#define CGLTF_WRITE_IMPLEMENTATION
+#endif
+
+#ifdef CGLTF_WRITE_IMPLEMENTATION
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <float.h>
+
+#define CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM      (1 << 0)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT        (1 << 1)
+#define CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS    (1 << 2)
+#define CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL        (1 << 3)
+#define CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION (1 << 4)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT    (1 << 5)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_IOR          (1 << 6)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR     (1 << 7)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION (1 << 8)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN        (1 << 9)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS     (1 << 10)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME       (1 << 11)
+#define CGLTF_EXTENSION_FLAG_TEXTURE_BASISU        (1 << 12)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH (1 << 13)
+#define CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING (1 << 14)
+#define CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE (1 << 15)
+
+typedef struct {
+	char* buffer;
+	cgltf_size buffer_size;
+	cgltf_size remaining;
+	char* cursor;
+	cgltf_size tmp;
+	cgltf_size chars_written;
+	const cgltf_data* data;
+	int depth;
+	const char* indent;
+	int needs_comma;
+	uint32_t extension_flags;
+	uint32_t required_extension_flags;
+} cgltf_write_context;
+
+#define CGLTF_MIN(a, b) (a < b ? a : b)
+
+#ifdef FLT_DECIMAL_DIG
+	// FLT_DECIMAL_DIG is C11
+	#define CGLTF_DECIMAL_DIG (FLT_DECIMAL_DIG)
+#else
+	#define CGLTF_DECIMAL_DIG 9
+#endif
+
+#define CGLTF_SPRINTF(...) { \
+		assert(context->cursor || (!context->cursor && context->remaining == 0)); \
+		context->tmp = snprintf ( context->cursor, context->remaining, __VA_ARGS__ ); \
+		context->chars_written += context->tmp; \
+		if (context->cursor) { \
+			context->cursor += context->tmp; \
+			context->remaining -= context->tmp; \
+		} }
+
+#define CGLTF_SNPRINTF(length, ...) { \
+		assert(context->cursor || (!context->cursor && context->remaining == 0)); \
+		context->tmp = snprintf ( context->cursor, CGLTF_MIN(length + 1, context->remaining), __VA_ARGS__ ); \
+		context->chars_written += length; \
+		if (context->cursor) { \
+			context->cursor += length; \
+			context->remaining -= length; \
+		} }
+
+#define CGLTF_WRITE_IDXPROP(label, val, start) if (val) { \
+		cgltf_write_indent(context); \
+		CGLTF_SPRINTF("\"%s\": %d", label, (int) (val - start)); \
+		context->needs_comma = 1; }
+
+#define CGLTF_WRITE_IDXARRPROP(label, dim, vals, start) if (vals) { \
+		cgltf_write_indent(context); \
+		CGLTF_SPRINTF("\"%s\": [", label); \
+		for (int i = 0; i < (int)(dim); ++i) { \
+			int idx = (int) (vals[i] - start); \
+			if (i != 0) CGLTF_SPRINTF(","); \
+			CGLTF_SPRINTF(" %d", idx); \
+		} \
+		CGLTF_SPRINTF(" ]"); \
+		context->needs_comma = 1; }
+
+#define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \
+		cgltf_write_line(context, "\"" label "\": {"); \
+		CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
+		cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
+		if (info.has_transform) { \
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
+			cgltf_write_texture_transform(context, &info.transform); \
+		} \
+		cgltf_write_extras(context, &info.extras); \
+		cgltf_write_line(context, "}"); }
+
+#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \
+		cgltf_write_line(context, "\"" label "\": {"); \
+		CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
+		cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
+		cgltf_write_floatprop(context, "scale", info.scale, 1.0f); \
+		if (info.has_transform) { \
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
+			cgltf_write_texture_transform(context, &info.transform); \
+		} \
+		cgltf_write_extras(context, &info.extras); \
+		cgltf_write_line(context, "}"); }
+
+#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \
+		cgltf_write_line(context, "\"" label "\": {"); \
+		CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
+		cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
+		cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \
+		if (info.has_transform) { \
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
+			cgltf_write_texture_transform(context, &info.transform); \
+		} \
+		cgltf_write_extras(context, &info.extras); \
+		cgltf_write_line(context, "}"); }
+
+#ifndef CGLTF_CONSTS
+static const cgltf_size GlbHeaderSize = 12;
+static const cgltf_size GlbChunkHeaderSize = 8;
+static const uint32_t GlbVersion = 2;
+static const uint32_t GlbMagic = 0x46546C67;
+static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
+static const uint32_t GlbMagicBinChunk = 0x004E4942;
+#define CGLTF_CONSTS
+#endif
+
+static void cgltf_write_indent(cgltf_write_context* context)
+{
+	if (context->needs_comma)
+	{
+		CGLTF_SPRINTF(",\n");
+		context->needs_comma = 0;
+	}
+	else
+	{
+		CGLTF_SPRINTF("\n");
+	}
+	for (int i = 0; i < context->depth; ++i)
+	{
+		CGLTF_SPRINTF("%s", context->indent);
+	}
+}
+
+static void cgltf_write_line(cgltf_write_context* context, const char* line)
+{
+	if (line[0] == ']' || line[0] == '}')
+	{
+		--context->depth;
+		context->needs_comma = 0;
+	}
+	cgltf_write_indent(context);
+	CGLTF_SPRINTF("%s", line);
+	cgltf_size last = (cgltf_size)(strlen(line) - 1);
+	if (line[0] == ']' || line[0] == '}')
+	{
+		context->needs_comma = 1;
+	}
+	if (line[last] == '[' || line[last] == '{')
+	{
+		++context->depth;
+		context->needs_comma = 0;
+	}
+}
+
+static void cgltf_write_strprop(cgltf_write_context* context, const char* label, const char* val)
+{
+	if (val)
+	{
+		cgltf_write_indent(context);
+		CGLTF_SPRINTF("\"%s\": \"%s\"", label, val);
+		context->needs_comma = 1;
+	}
+}
+
+static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras)
+{
+	if (extras->data)
+	{
+		cgltf_write_indent(context);
+		CGLTF_SPRINTF("\"extras\": %s", extras->data);
+		context->needs_comma = 1;
+	}
+	else
+	{
+		cgltf_size length = extras->end_offset - extras->start_offset;
+		if (length > 0 && context->data->json)
+		{
+			char* json_string = ((char*) context->data->json) + extras->start_offset;
+			cgltf_write_indent(context);
+			CGLTF_SPRINTF("%s", "\"extras\": ");
+			CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string);
+			context->needs_comma = 1;
+		}
+	}
+}
+
+static void cgltf_write_stritem(cgltf_write_context* context, const char* item)
+{
+	cgltf_write_indent(context);
+	CGLTF_SPRINTF("\"%s\"", item);
+	context->needs_comma = 1;
+}
+
+static void cgltf_write_intprop(cgltf_write_context* context, const char* label, int val, int def)
+{
+	if (val != def)
+	{
+		cgltf_write_indent(context);
+		CGLTF_SPRINTF("\"%s\": %d", label, val);
+		context->needs_comma = 1;
+	}
+}
+
+static void cgltf_write_sizeprop(cgltf_write_context* context, const char* label, cgltf_size val, cgltf_size def)
+{
+	if (val != def)
+	{
+		cgltf_write_indent(context);
+		CGLTF_SPRINTF("\"%s\": %zu", label, val);
+		context->needs_comma = 1;
+	}
+}
+
+static void cgltf_write_floatprop(cgltf_write_context* context, const char* label, float val, float def)
+{
+	if (val != def)
+	{
+		cgltf_write_indent(context);
+		CGLTF_SPRINTF("\"%s\": ", label);
+		CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, val);
+		context->needs_comma = 1;
+
+		if (context->cursor)
+		{
+			char *decimal_comma = strchr(context->cursor - context->tmp, ',');
+			if (decimal_comma)
+			{
+				*decimal_comma = '.';
+			}
+		}
+	}
+}
+
+static void cgltf_write_boolprop_optional(cgltf_write_context* context, const char* label, bool val, bool def)
+{
+	if (val != def)
+	{
+		cgltf_write_indent(context);
+		CGLTF_SPRINTF("\"%s\": %s", label, val ? "true" : "false");
+		context->needs_comma = 1;
+	}
+}
+
+static void cgltf_write_floatarrayprop(cgltf_write_context* context, const char* label, const cgltf_float* vals, cgltf_size dim)
+{
+	cgltf_write_indent(context);
+	CGLTF_SPRINTF("\"%s\": [", label);
+	for (cgltf_size i = 0; i < dim; ++i)
+	{
+		if (i != 0)
+		{
+			CGLTF_SPRINTF(", %.*g", CGLTF_DECIMAL_DIG, vals[i]);
+		}
+		else
+		{
+			CGLTF_SPRINTF("%.*g", CGLTF_DECIMAL_DIG, vals[i]);
+		}
+	}
+	CGLTF_SPRINTF("]");
+	context->needs_comma = 1;
+}
+
+static bool cgltf_check_floatarray(const float* vals, int dim, float val) {
+	while (dim--)
+	{
+		if (vals[dim] != val)
+		{
+			return true;
+		}
+	}
+	return false;
+}
+
+static int cgltf_int_from_component_type(cgltf_component_type ctype)
+{
+	switch (ctype)
+	{
+		case cgltf_component_type_r_8: return 5120;
+		case cgltf_component_type_r_8u: return 5121;
+		case cgltf_component_type_r_16: return 5122;
+		case cgltf_component_type_r_16u: return 5123;
+		case cgltf_component_type_r_32u: return 5125;
+		case cgltf_component_type_r_32f: return 5126;
+		default: return 0;
+	}
+}
+
+static const char* cgltf_str_from_alpha_mode(cgltf_alpha_mode alpha_mode)
+{
+	switch (alpha_mode)
+	{
+		case cgltf_alpha_mode_mask: return "MASK";
+		case cgltf_alpha_mode_blend: return "BLEND";
+		default: return NULL;
+	}
+}
+
+static const char* cgltf_str_from_type(cgltf_type type)
+{
+	switch (type)
+	{
+		case cgltf_type_scalar: return "SCALAR";
+		case cgltf_type_vec2: return "VEC2";
+		case cgltf_type_vec3: return "VEC3";
+		case cgltf_type_vec4: return "VEC4";
+		case cgltf_type_mat2: return "MAT2";
+		case cgltf_type_mat3: return "MAT3";
+		case cgltf_type_mat4: return "MAT4";
+		default: return NULL;
+	}
+}
+
+static cgltf_size cgltf_dim_from_type(cgltf_type type)
+{
+	switch (type)
+	{
+		case cgltf_type_scalar: return 1;
+		case cgltf_type_vec2: return 2;
+		case cgltf_type_vec3: return 3;
+		case cgltf_type_vec4: return 4;
+		case cgltf_type_mat2: return 4;
+		case cgltf_type_mat3: return 9;
+		case cgltf_type_mat4: return 16;
+		default: return 0;
+	}
+}
+
+static const char* cgltf_str_from_camera_type(cgltf_camera_type camera_type)
+{
+	switch (camera_type)
+	{
+		case cgltf_camera_type_perspective: return "perspective";
+		case cgltf_camera_type_orthographic: return "orthographic";
+		default: return NULL;
+	}
+}
+
+static const char* cgltf_str_from_light_type(cgltf_light_type light_type)
+{
+	switch (light_type)
+	{
+		case cgltf_light_type_directional: return "directional";
+		case cgltf_light_type_point: return "point";
+		case cgltf_light_type_spot: return "spot";
+		default: return NULL;
+	}
+}
+
+static void cgltf_write_texture_transform(cgltf_write_context* context, const cgltf_texture_transform* transform)
+{
+	cgltf_write_line(context, "\"extensions\": {");
+	cgltf_write_line(context, "\"KHR_texture_transform\": {");
+	if (cgltf_check_floatarray(transform->offset, 2, 0.0f))
+	{
+		cgltf_write_floatarrayprop(context, "offset", transform->offset, 2);
+	}
+	cgltf_write_floatprop(context, "rotation", transform->rotation, 0.0f);
+	if (cgltf_check_floatarray(transform->scale, 2, 1.0f))
+	{
+		cgltf_write_floatarrayprop(context, "scale", transform->scale, 2);
+	}
+	if (transform->has_texcoord)
+	{
+		cgltf_write_intprop(context, "texCoord", transform->texcoord, -1);
+	}
+	cgltf_write_line(context, "}");
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_asset(cgltf_write_context* context, const cgltf_asset* asset)
+{
+	cgltf_write_line(context, "\"asset\": {");
+	cgltf_write_strprop(context, "copyright", asset->copyright);
+	cgltf_write_strprop(context, "generator", asset->generator);
+	cgltf_write_strprop(context, "version", asset->version);
+	cgltf_write_strprop(context, "min_version", asset->min_version);
+	cgltf_write_extras(context, &asset->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_primitive(cgltf_write_context* context, const cgltf_primitive* prim)
+{
+	cgltf_write_intprop(context, "mode", (int) prim->type, 4);
+	CGLTF_WRITE_IDXPROP("indices", prim->indices, context->data->accessors);
+	CGLTF_WRITE_IDXPROP("material", prim->material, context->data->materials);
+	cgltf_write_line(context, "\"attributes\": {");
+	for (cgltf_size i = 0; i < prim->attributes_count; ++i)
+	{
+		const cgltf_attribute* attr = prim->attributes + i;
+		CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
+	}
+	cgltf_write_line(context, "}");
+
+	if (prim->targets_count)
+	{
+		cgltf_write_line(context, "\"targets\": [");
+		for (cgltf_size i = 0; i < prim->targets_count; ++i)
+		{
+			cgltf_write_line(context, "{");
+			for (cgltf_size j = 0; j < prim->targets[i].attributes_count; ++j)
+			{
+				const cgltf_attribute* attr = prim->targets[i].attributes + j;
+				CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
+			}
+			cgltf_write_line(context, "}");
+		}
+		cgltf_write_line(context, "]");
+	}
+	cgltf_write_extras(context, &prim->extras);
+
+	if (prim->has_draco_mesh_compression || prim->mappings_count > 0)
+	{
+		cgltf_write_line(context, "\"extensions\": {");
+
+		if (prim->has_draco_mesh_compression)
+		{
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION;
+			if (prim->attributes_count == 0 || prim->indices == 0)
+			{
+				context->required_extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION;				 
+			}
+
+			cgltf_write_line(context, "\"KHR_draco_mesh_compression\": {");
+			CGLTF_WRITE_IDXPROP("bufferView", prim->draco_mesh_compression.buffer_view, context->data->buffer_views);
+			cgltf_write_line(context, "\"attributes\": {");
+			for (cgltf_size i = 0; i < prim->draco_mesh_compression.attributes_count; ++i)
+			{
+				const cgltf_attribute* attr = prim->draco_mesh_compression.attributes + i;
+				CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
+			}
+			cgltf_write_line(context, "}");
+			cgltf_write_line(context, "}");
+		}
+
+		if (prim->mappings_count > 0)
+		{
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS;
+			cgltf_write_line(context, "\"KHR_materials_variants\": {");
+			cgltf_write_line(context, "\"mappings\": [");
+			for (cgltf_size i = 0; i < prim->mappings_count; ++i)
+			{
+				const cgltf_material_mapping* map = prim->mappings + i;
+				cgltf_write_line(context, "{");
+				CGLTF_WRITE_IDXPROP("material", map->material, context->data->materials);
+
+				cgltf_write_indent(context);
+				CGLTF_SPRINTF("\"variants\": [%d]", (int)map->variant);
+				context->needs_comma = 1;
+
+				cgltf_write_extras(context, &map->extras);
+				cgltf_write_line(context, "}");
+			}
+			cgltf_write_line(context, "]");
+			cgltf_write_line(context, "}");
+		}
+
+		cgltf_write_line(context, "}");
+	}
+}
+
+static void cgltf_write_mesh(cgltf_write_context* context, const cgltf_mesh* mesh)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", mesh->name);
+
+	cgltf_write_line(context, "\"primitives\": [");
+	for (cgltf_size i = 0; i < mesh->primitives_count; ++i)
+	{
+		cgltf_write_line(context, "{");
+		cgltf_write_primitive(context, mesh->primitives + i);
+		cgltf_write_line(context, "}");
+	}
+	cgltf_write_line(context, "]");
+
+	if (mesh->weights_count > 0)
+	{
+		cgltf_write_floatarrayprop(context, "weights", mesh->weights, mesh->weights_count);
+	}
+
+	cgltf_write_extras(context, &mesh->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_buffer_view(cgltf_write_context* context, const cgltf_buffer_view* view)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", view->name);
+	CGLTF_WRITE_IDXPROP("buffer", view->buffer, context->data->buffers);
+	cgltf_write_sizeprop(context, "byteLength", view->size, (cgltf_size)-1);
+	cgltf_write_sizeprop(context, "byteOffset", view->offset, 0);
+	cgltf_write_sizeprop(context, "byteStride", view->stride, 0);
+	// NOTE: We skip writing "target" because the spec says its usage can be inferred.
+	cgltf_write_extras(context, &view->extras);
+	cgltf_write_line(context, "}");
+}
+
+
+static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer* buffer)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", buffer->name);
+	cgltf_write_strprop(context, "uri", buffer->uri);
+	cgltf_write_sizeprop(context, "byteLength", buffer->size, (cgltf_size)-1);
+	cgltf_write_extras(context, &buffer->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_material(cgltf_write_context* context, const cgltf_material* material)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", material->name);
+	if (material->alpha_mode == cgltf_alpha_mode_mask)
+	{
+		cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f);
+	}
+	cgltf_write_boolprop_optional(context, "doubleSided", (bool)material->double_sided, false);
+	// cgltf_write_boolprop_optional(context, "unlit", material->unlit, false);
+
+	if (material->unlit)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT;
+	}
+
+	if (material->has_pbr_specular_glossiness)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS;
+	}
+
+	if (material->has_clearcoat)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT;
+	}
+
+	if (material->has_transmission)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION;
+	}
+
+	if (material->has_volume)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME;
+	}
+
+	if (material->has_ior)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IOR;
+	}
+
+	if (material->has_specular)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR;
+	}
+
+	if (material->has_sheen)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN;
+	}
+
+	if (material->has_emissive_strength)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH;
+	}
+
+	if (material->has_iridescence)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE;
+	}
+
+	if (material->has_pbr_metallic_roughness)
+	{
+		const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness;
+		cgltf_write_line(context, "\"pbrMetallicRoughness\": {");
+		CGLTF_WRITE_TEXTURE_INFO("baseColorTexture", params->base_color_texture);
+		CGLTF_WRITE_TEXTURE_INFO("metallicRoughnessTexture", params->metallic_roughness_texture);
+		cgltf_write_floatprop(context, "metallicFactor", params->metallic_factor, 1.0f);
+		cgltf_write_floatprop(context, "roughnessFactor", params->roughness_factor, 1.0f);
+		if (cgltf_check_floatarray(params->base_color_factor, 4, 1.0f))
+		{
+			cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4);
+		}
+		cgltf_write_line(context, "}");
+	}
+
+	if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission || material->has_sheen || material->has_volume || material->has_emissive_strength || material->has_iridescence)
+	{
+		cgltf_write_line(context, "\"extensions\": {");
+		if (material->has_clearcoat)
+		{
+			const cgltf_clearcoat* params = &material->clearcoat;
+			cgltf_write_line(context, "\"KHR_materials_clearcoat\": {");
+			CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture);
+			CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture);
+			CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
+			cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f);
+			cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f);
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_ior)
+		{
+			const cgltf_ior* params = &material->ior;
+			cgltf_write_line(context, "\"KHR_materials_ior\": {");
+			cgltf_write_floatprop(context, "ior", params->ior, 1.5f);
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_specular)
+		{
+			const cgltf_specular* params = &material->specular;
+			cgltf_write_line(context, "\"KHR_materials_specular\": {");
+			CGLTF_WRITE_TEXTURE_INFO("specularTexture", params->specular_texture);
+			CGLTF_WRITE_TEXTURE_INFO("specularColorTexture", params->specular_color_texture);
+			cgltf_write_floatprop(context, "specularFactor", params->specular_factor, 1.0f);
+			if (cgltf_check_floatarray(params->specular_color_factor, 3, 1.0f))
+			{
+				cgltf_write_floatarrayprop(context, "specularColorFactor", params->specular_color_factor, 3);
+			}
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_transmission)
+		{
+			const cgltf_transmission* params = &material->transmission;
+			cgltf_write_line(context, "\"KHR_materials_transmission\": {");
+			CGLTF_WRITE_TEXTURE_INFO("transmissionTexture", params->transmission_texture);
+			cgltf_write_floatprop(context, "transmissionFactor", params->transmission_factor, 0.0f);
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_volume)
+		{
+			const cgltf_volume* params = &material->volume;
+			cgltf_write_line(context, "\"KHR_materials_volume\": {");
+			CGLTF_WRITE_TEXTURE_INFO("thicknessTexture", params->thickness_texture);
+			cgltf_write_floatprop(context, "thicknessFactor", params->thickness_factor, 0.0f);
+			if (cgltf_check_floatarray(params->attenuation_color, 3, 1.0f))
+			{
+				cgltf_write_floatarrayprop(context, "attenuationColor", params->attenuation_color, 3);
+			}
+			if (params->attenuation_distance < FLT_MAX) 
+			{
+				cgltf_write_floatprop(context, "attenuationDistance", params->attenuation_distance, FLT_MAX);
+			}
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_sheen)
+		{
+			const cgltf_sheen* params = &material->sheen;
+			cgltf_write_line(context, "\"KHR_materials_sheen\": {");
+			CGLTF_WRITE_TEXTURE_INFO("sheenColorTexture", params->sheen_color_texture);
+			CGLTF_WRITE_TEXTURE_INFO("sheenRoughnessTexture", params->sheen_roughness_texture);
+			if (cgltf_check_floatarray(params->sheen_color_factor, 3, 0.0f))
+			{
+				cgltf_write_floatarrayprop(context, "sheenColorFactor", params->sheen_color_factor, 3);
+			}
+			cgltf_write_floatprop(context, "sheenRoughnessFactor", params->sheen_roughness_factor, 0.0f);
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_pbr_specular_glossiness)
+		{
+			const cgltf_pbr_specular_glossiness* params = &material->pbr_specular_glossiness;
+			cgltf_write_line(context, "\"KHR_materials_pbrSpecularGlossiness\": {");
+			CGLTF_WRITE_TEXTURE_INFO("diffuseTexture", params->diffuse_texture);
+			CGLTF_WRITE_TEXTURE_INFO("specularGlossinessTexture", params->specular_glossiness_texture);
+			if (cgltf_check_floatarray(params->diffuse_factor, 4, 1.0f))
+			{
+				cgltf_write_floatarrayprop(context, "diffuseFactor", params->diffuse_factor, 4);
+			}
+			if (cgltf_check_floatarray(params->specular_factor, 3, 1.0f))
+			{
+				cgltf_write_floatarrayprop(context, "specularFactor", params->specular_factor, 3);
+			}
+			cgltf_write_floatprop(context, "glossinessFactor", params->glossiness_factor, 1.0f);
+			cgltf_write_line(context, "}");
+		}
+		if (material->unlit)
+		{
+			cgltf_write_line(context, "\"KHR_materials_unlit\": {}");
+		}
+		if (material->has_emissive_strength)
+		{
+			cgltf_write_line(context, "\"KHR_materials_emissive_strength\": {");
+			const cgltf_emissive_strength* params = &material->emissive_strength;
+			cgltf_write_floatprop(context, "emissiveStrength", params->emissive_strength, 1.f);
+			cgltf_write_line(context, "}");
+		}
+		if (material->has_iridescence)
+		{
+			cgltf_write_line(context, "\"KHR_materials_iridescence\": {");
+			const cgltf_iridescence* params = &material->iridescence;
+			cgltf_write_floatprop(context, "iridescenceFactor", params->iridescence_factor, 0.f);
+			CGLTF_WRITE_TEXTURE_INFO("iridescenceTexture", params->iridescence_texture);
+			cgltf_write_floatprop(context, "iridescenceIor", params->iridescence_ior, 1.3f);
+			cgltf_write_floatprop(context, "iridescenceThicknessMinimum", params->iridescence_thickness_min, 100.f);
+			cgltf_write_floatprop(context, "iridescenceThicknessMaximum", params->iridescence_thickness_max, 400.f);
+			CGLTF_WRITE_TEXTURE_INFO("iridescenceThicknessTexture", params->iridescence_thickness_texture);
+			cgltf_write_line(context, "}");
+		}
+		cgltf_write_line(context, "}");
+	}
+
+	CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture);
+	CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
+	CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture);
+	if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f))
+	{
+		cgltf_write_floatarrayprop(context, "emissiveFactor", material->emissive_factor, 3);
+	}
+	cgltf_write_strprop(context, "alphaMode", cgltf_str_from_alpha_mode(material->alpha_mode));
+	cgltf_write_extras(context, &material->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_image(cgltf_write_context* context, const cgltf_image* image)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", image->name);
+	cgltf_write_strprop(context, "uri", image->uri);
+	CGLTF_WRITE_IDXPROP("bufferView", image->buffer_view, context->data->buffer_views);
+	cgltf_write_strprop(context, "mimeType", image->mime_type);
+	cgltf_write_extras(context, &image->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_texture(cgltf_write_context* context, const cgltf_texture* texture)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", texture->name);
+	CGLTF_WRITE_IDXPROP("source", texture->image, context->data->images);
+	CGLTF_WRITE_IDXPROP("sampler", texture->sampler, context->data->samplers);
+
+	if (texture->has_basisu)
+	{
+		cgltf_write_line(context, "\"extensions\": {");
+		{
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_BASISU;
+			cgltf_write_line(context, "\"KHR_texture_basisu\": {");
+			CGLTF_WRITE_IDXPROP("source", texture->basisu_image, context->data->images);
+			cgltf_write_line(context, "}");
+		}
+		cgltf_write_line(context, "}");
+	}
+	cgltf_write_extras(context, &texture->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_skin(cgltf_write_context* context, const cgltf_skin* skin)
+{
+	cgltf_write_line(context, "{");
+	CGLTF_WRITE_IDXPROP("skeleton", skin->skeleton, context->data->nodes);
+	CGLTF_WRITE_IDXPROP("inverseBindMatrices", skin->inverse_bind_matrices, context->data->accessors);
+	CGLTF_WRITE_IDXARRPROP("joints", skin->joints_count, skin->joints, context->data->nodes);
+	cgltf_write_strprop(context, "name", skin->name);
+	cgltf_write_extras(context, &skin->extras);
+	cgltf_write_line(context, "}");
+}
+
+static const char* cgltf_write_str_path_type(cgltf_animation_path_type path_type)
+{
+	switch (path_type)
+	{
+	case cgltf_animation_path_type_translation:
+		return "translation";
+	case cgltf_animation_path_type_rotation:
+		return "rotation";
+	case cgltf_animation_path_type_scale:
+		return "scale";
+	case cgltf_animation_path_type_weights:
+		return "weights";
+	default:
+		break;
+	}
+	return "invalid";
+}
+
+static const char* cgltf_write_str_interpolation_type(cgltf_interpolation_type interpolation_type)
+{
+	switch (interpolation_type)
+	{
+	case cgltf_interpolation_type_linear:
+		return "LINEAR";
+	case cgltf_interpolation_type_step:
+		return "STEP";
+	case cgltf_interpolation_type_cubic_spline:
+		return "CUBICSPLINE";
+	default:
+		break;
+	}
+	return "invalid";
+}
+
+static void cgltf_write_path_type(cgltf_write_context* context, const char *label, cgltf_animation_path_type path_type)
+{
+	cgltf_write_strprop(context, label, cgltf_write_str_path_type(path_type));
+}
+
+static void cgltf_write_interpolation_type(cgltf_write_context* context, const char *label, cgltf_interpolation_type interpolation_type)
+{
+	cgltf_write_strprop(context, label, cgltf_write_str_interpolation_type(interpolation_type));
+}
+
+static void cgltf_write_animation_sampler(cgltf_write_context* context, const cgltf_animation_sampler* animation_sampler)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_interpolation_type(context, "interpolation", animation_sampler->interpolation);
+	CGLTF_WRITE_IDXPROP("input", animation_sampler->input, context->data->accessors);
+	CGLTF_WRITE_IDXPROP("output", animation_sampler->output, context->data->accessors);
+	cgltf_write_extras(context, &animation_sampler->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_animation_channel(cgltf_write_context* context, const cgltf_animation* animation, const cgltf_animation_channel* animation_channel)
+{
+	cgltf_write_line(context, "{");
+	CGLTF_WRITE_IDXPROP("sampler", animation_channel->sampler, animation->samplers);
+	cgltf_write_line(context, "\"target\": {");
+	CGLTF_WRITE_IDXPROP("node", animation_channel->target_node, context->data->nodes);
+	cgltf_write_path_type(context, "path", animation_channel->target_path);
+	cgltf_write_line(context, "}");
+	cgltf_write_extras(context, &animation_channel->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_animation(cgltf_write_context* context, const cgltf_animation* animation)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", animation->name);
+
+	if (animation->samplers_count > 0)
+	{
+		cgltf_write_line(context, "\"samplers\": [");
+		for (cgltf_size i = 0; i < animation->samplers_count; ++i)
+		{
+			cgltf_write_animation_sampler(context, animation->samplers + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+	if (animation->channels_count > 0)
+	{
+		cgltf_write_line(context, "\"channels\": [");
+		for (cgltf_size i = 0; i < animation->channels_count; ++i)
+		{
+			cgltf_write_animation_channel(context, animation, animation->channels + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+	cgltf_write_extras(context, &animation->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_sampler(cgltf_write_context* context, const cgltf_sampler* sampler)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", sampler->name);
+	cgltf_write_intprop(context, "magFilter", sampler->mag_filter, 0);
+	cgltf_write_intprop(context, "minFilter", sampler->min_filter, 0);
+	cgltf_write_intprop(context, "wrapS", sampler->wrap_s, 10497);
+	cgltf_write_intprop(context, "wrapT", sampler->wrap_t, 10497);
+	cgltf_write_extras(context, &sampler->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_node(cgltf_write_context* context, const cgltf_node* node)
+{
+	cgltf_write_line(context, "{");
+	CGLTF_WRITE_IDXARRPROP("children", node->children_count, node->children, context->data->nodes);
+	CGLTF_WRITE_IDXPROP("mesh", node->mesh, context->data->meshes);
+	cgltf_write_strprop(context, "name", node->name);
+	if (node->has_matrix)
+	{
+		cgltf_write_floatarrayprop(context, "matrix", node->matrix, 16);
+	}
+	if (node->has_translation)
+	{
+		cgltf_write_floatarrayprop(context, "translation", node->translation, 3);
+	}
+	if (node->has_rotation)
+	{
+		cgltf_write_floatarrayprop(context, "rotation", node->rotation, 4);
+	}
+	if (node->has_scale)
+	{
+		cgltf_write_floatarrayprop(context, "scale", node->scale, 3);
+	}
+	if (node->skin)
+	{
+		CGLTF_WRITE_IDXPROP("skin", node->skin, context->data->skins);
+	}
+
+	bool has_extension = node->light || (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0);
+	if(has_extension)
+		cgltf_write_line(context, "\"extensions\": {");
+
+	if (node->light)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL;
+		cgltf_write_line(context, "\"KHR_lights_punctual\": {");
+		CGLTF_WRITE_IDXPROP("light", node->light, context->data->lights);
+		cgltf_write_line(context, "}");
+	}
+
+	if (node->has_mesh_gpu_instancing && node->mesh_gpu_instancing.attributes_count > 0)
+	{
+		context->extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING;
+		context->required_extension_flags |= CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING;
+
+		cgltf_write_line(context, "\"EXT_mesh_gpu_instancing\": {");
+		{
+			CGLTF_WRITE_IDXPROP("bufferView", node->mesh_gpu_instancing.buffer_view, context->data->buffer_views);
+			cgltf_write_line(context, "\"attributes\": {");
+			{
+				for (cgltf_size i = 0; i < node->mesh_gpu_instancing.attributes_count; ++i)
+				{
+					const cgltf_attribute* attr = node->mesh_gpu_instancing.attributes + i;
+					CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
+				}
+			}
+			cgltf_write_line(context, "}");
+		}
+		cgltf_write_line(context, "}");
+	}
+
+	if (has_extension)
+		cgltf_write_line(context, "}");
+
+	if (node->weights_count > 0)
+	{
+		cgltf_write_floatarrayprop(context, "weights", node->weights, node->weights_count);
+	}
+
+	if (node->camera)
+	{
+		CGLTF_WRITE_IDXPROP("camera", node->camera, context->data->cameras);
+	}
+
+	cgltf_write_extras(context, &node->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_scene(cgltf_write_context* context, const cgltf_scene* scene)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", scene->name);
+	CGLTF_WRITE_IDXARRPROP("nodes", scene->nodes_count, scene->nodes, context->data->nodes);
+	cgltf_write_extras(context, &scene->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_accessor* accessor)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", accessor->name);
+	CGLTF_WRITE_IDXPROP("bufferView", accessor->buffer_view, context->data->buffer_views);
+	cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->component_type), 0);
+	cgltf_write_strprop(context, "type", cgltf_str_from_type(accessor->type));
+	cgltf_size dim = cgltf_dim_from_type(accessor->type);
+	cgltf_write_boolprop_optional(context, "normalized", (bool)accessor->normalized, false);
+	cgltf_write_sizeprop(context, "byteOffset", (int)accessor->offset, 0);
+	cgltf_write_intprop(context, "count", (int)accessor->count, -1);
+	if (accessor->has_min)
+	{
+		cgltf_write_floatarrayprop(context, "min", accessor->min, dim);
+	}
+	if (accessor->has_max)
+	{
+		cgltf_write_floatarrayprop(context, "max", accessor->max, dim);
+	}
+	if (accessor->is_sparse)
+	{
+		cgltf_write_line(context, "\"sparse\": {");
+		cgltf_write_intprop(context, "count", (int)accessor->sparse.count, 0);
+		cgltf_write_line(context, "\"indices\": {");
+		cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0);
+		CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views);
+		cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0);
+		cgltf_write_extras(context, &accessor->sparse.indices_extras);
+		cgltf_write_line(context, "}");
+		cgltf_write_line(context, "\"values\": {");
+		cgltf_write_sizeprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0);
+		CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views);
+		cgltf_write_extras(context, &accessor->sparse.values_extras);
+		cgltf_write_line(context, "}");
+		cgltf_write_extras(context, &accessor->sparse.extras);
+		cgltf_write_line(context, "}");
+	}
+	cgltf_write_extras(context, &accessor->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_camera(cgltf_write_context* context, const cgltf_camera* camera)
+{
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "type", cgltf_str_from_camera_type(camera->type));
+	if (camera->name)
+	{
+		cgltf_write_strprop(context, "name", camera->name);
+	}
+
+	if (camera->type == cgltf_camera_type_orthographic)
+	{
+		cgltf_write_line(context, "\"orthographic\": {");
+		cgltf_write_floatprop(context, "xmag", camera->data.orthographic.xmag, -1.0f);
+		cgltf_write_floatprop(context, "ymag", camera->data.orthographic.ymag, -1.0f);
+		cgltf_write_floatprop(context, "zfar", camera->data.orthographic.zfar, -1.0f);
+		cgltf_write_floatprop(context, "znear", camera->data.orthographic.znear, -1.0f);
+		cgltf_write_extras(context, &camera->data.orthographic.extras);
+		cgltf_write_line(context, "}");
+	}
+	else if (camera->type == cgltf_camera_type_perspective)
+	{
+		cgltf_write_line(context, "\"perspective\": {");
+
+		if (camera->data.perspective.has_aspect_ratio) {
+			cgltf_write_floatprop(context, "aspectRatio", camera->data.perspective.aspect_ratio, -1.0f);
+		}
+
+		cgltf_write_floatprop(context, "yfov", camera->data.perspective.yfov, -1.0f);
+
+		if (camera->data.perspective.has_zfar) {
+			cgltf_write_floatprop(context, "zfar", camera->data.perspective.zfar, -1.0f);
+		}
+
+		cgltf_write_floatprop(context, "znear", camera->data.perspective.znear, -1.0f);
+		cgltf_write_extras(context, &camera->data.perspective.extras);
+		cgltf_write_line(context, "}");
+	}
+	cgltf_write_extras(context, &camera->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_light(cgltf_write_context* context, const cgltf_light* light)
+{
+	context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL;
+
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "type", cgltf_str_from_light_type(light->type));
+	if (light->name)
+	{
+		cgltf_write_strprop(context, "name", light->name);
+	}
+	if (cgltf_check_floatarray(light->color, 3, 1.0f))
+	{
+		cgltf_write_floatarrayprop(context, "color", light->color, 3);
+	}
+	cgltf_write_floatprop(context, "intensity", light->intensity, 1.0f);
+	cgltf_write_floatprop(context, "range", light->range, 0.0f);
+
+	if (light->type == cgltf_light_type_spot)
+	{
+		cgltf_write_line(context, "\"spot\": {");
+		cgltf_write_floatprop(context, "innerConeAngle", light->spot_inner_cone_angle, 0.0f);
+		cgltf_write_floatprop(context, "outerConeAngle", light->spot_outer_cone_angle, 3.14159265358979323846f/4.0f);
+		cgltf_write_line(context, "}");
+	}
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_variant(cgltf_write_context* context, const cgltf_material_variant* variant)
+{
+	context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS;
+
+	cgltf_write_line(context, "{");
+	cgltf_write_strprop(context, "name", variant->name);
+	cgltf_write_extras(context, &variant->extras);
+	cgltf_write_line(context, "}");
+}
+
+static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size)
+{
+	char header[GlbHeaderSize];
+	char chunk_header[GlbChunkHeaderSize];
+	char json_pad[3] = { 0x20, 0x20, 0x20 };
+	char bin_pad[3] = { 0, 0, 0 };
+
+	cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0;
+	cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0;
+	cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize;
+	if (bin_buf != NULL && bin_size > 0) {
+		total_size += GlbChunkHeaderSize + bin_size + bin_padsize;
+	}
+
+	// Write a GLB header
+	memcpy(header, &GlbMagic, 4);
+	memcpy(header + 4, &GlbVersion, 4);
+	memcpy(header + 8, &total_size, 4);
+	fwrite(header, 1, GlbHeaderSize, file);
+
+	// Write a JSON chunk (header & data)
+	uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize);
+	memcpy(chunk_header, &json_chunk_size, 4);
+	memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4);
+	fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
+
+	fwrite(json_buf, 1, json_size, file);
+	fwrite(json_pad, 1, json_padsize, file);
+
+	if (bin_buf != NULL && bin_size > 0) {
+		// Write a binary chunk (header & data)
+		uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize);
+		memcpy(chunk_header, &bin_chunk_size, 4);
+		memcpy(chunk_header + 4, &GlbMagicBinChunk, 4);
+		fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
+
+		fwrite(bin_buf, 1, bin_size, file);
+		fwrite(bin_pad, 1, bin_padsize, file);
+	}
+}
+
+cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data)
+{
+	cgltf_size expected = cgltf_write(options, NULL, 0, data);
+	char* buffer = (char*) malloc(expected);
+	cgltf_size actual = cgltf_write(options, buffer, expected, data);
+	if (expected != actual) {
+		fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual);
+	}
+	FILE* file = fopen(path, "wb");
+	if (!file)
+	{
+		return cgltf_result_file_not_found;
+	}
+	// Note that cgltf_write() includes a null terminator, which we omit from the file content.
+	if (options->type == cgltf_file_type_glb) {
+		cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
+	} else {
+		// Write a plain JSON file.
+		fwrite(buffer, actual - 1, 1, file);
+	}
+	fclose(file);
+	free(buffer);
+	return cgltf_result_success;
+}
+
+static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extension_flags)
+{
+	if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM) {
+		cgltf_write_stritem(context, "KHR_texture_transform");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT) {
+		cgltf_write_stritem(context, "KHR_materials_unlit");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS) {
+		cgltf_write_stritem(context, "KHR_materials_pbrSpecularGlossiness");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL) {
+		cgltf_write_stritem(context, "KHR_lights_punctual");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION) {
+		cgltf_write_stritem(context, "KHR_draco_mesh_compression");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT) {
+		cgltf_write_stritem(context, "KHR_materials_clearcoat");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IOR) {
+		cgltf_write_stritem(context, "KHR_materials_ior");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR) {
+		cgltf_write_stritem(context, "KHR_materials_specular");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION) {
+		cgltf_write_stritem(context, "KHR_materials_transmission");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SHEEN) {
+		cgltf_write_stritem(context, "KHR_materials_sheen");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VARIANTS) {
+		cgltf_write_stritem(context, "KHR_materials_variants");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_VOLUME) {
+		cgltf_write_stritem(context, "KHR_materials_volume");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_BASISU) {
+		cgltf_write_stritem(context, "KHR_texture_basisu");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_EMISSIVE_STRENGTH) {
+		cgltf_write_stritem(context, "KHR_materials_emissive_strength");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IRIDESCENCE) {
+		cgltf_write_stritem(context, "KHR_materials_iridescence");
+	}
+	if (extension_flags & CGLTF_EXTENSION_FLAG_MESH_GPU_INSTANCING) {
+		cgltf_write_stritem(context, "EXT_mesh_gpu_instancing");
+	}
+}
+
+cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data)
+{
+	(void)options;
+	cgltf_write_context ctx;
+	ctx.buffer = buffer;
+	ctx.buffer_size = size;
+	ctx.remaining = size;
+	ctx.cursor = buffer;
+	ctx.chars_written = 0;
+	ctx.data = data;
+	ctx.depth = 1;
+	ctx.indent = "  ";
+	ctx.needs_comma = 0;
+	ctx.extension_flags = 0;
+	ctx.required_extension_flags = 0;
+
+	cgltf_write_context* context = &ctx;
+
+	CGLTF_SPRINTF("{");
+
+	if (data->accessors_count > 0)
+	{
+		cgltf_write_line(context, "\"accessors\": [");
+		for (cgltf_size i = 0; i < data->accessors_count; ++i)
+		{
+			cgltf_write_accessor(context, data->accessors + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	cgltf_write_asset(context, &data->asset);
+
+	if (data->buffer_views_count > 0)
+	{
+		cgltf_write_line(context, "\"bufferViews\": [");
+		for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
+		{
+			cgltf_write_buffer_view(context, data->buffer_views + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->buffers_count > 0)
+	{
+		cgltf_write_line(context, "\"buffers\": [");
+		for (cgltf_size i = 0; i < data->buffers_count; ++i)
+		{
+			cgltf_write_buffer(context, data->buffers + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->images_count > 0)
+	{
+		cgltf_write_line(context, "\"images\": [");
+		for (cgltf_size i = 0; i < data->images_count; ++i)
+		{
+			cgltf_write_image(context, data->images + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->meshes_count > 0)
+	{
+		cgltf_write_line(context, "\"meshes\": [");
+		for (cgltf_size i = 0; i < data->meshes_count; ++i)
+		{
+			cgltf_write_mesh(context, data->meshes + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->materials_count > 0)
+	{
+		cgltf_write_line(context, "\"materials\": [");
+		for (cgltf_size i = 0; i < data->materials_count; ++i)
+		{
+			cgltf_write_material(context, data->materials + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->nodes_count > 0)
+	{
+		cgltf_write_line(context, "\"nodes\": [");
+		for (cgltf_size i = 0; i < data->nodes_count; ++i)
+		{
+			cgltf_write_node(context, data->nodes + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->samplers_count > 0)
+	{
+		cgltf_write_line(context, "\"samplers\": [");
+		for (cgltf_size i = 0; i < data->samplers_count; ++i)
+		{
+			cgltf_write_sampler(context, data->samplers + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	CGLTF_WRITE_IDXPROP("scene", data->scene, data->scenes);
+
+	if (data->scenes_count > 0)
+	{
+		cgltf_write_line(context, "\"scenes\": [");
+		for (cgltf_size i = 0; i < data->scenes_count; ++i)
+		{
+			cgltf_write_scene(context, data->scenes + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->textures_count > 0)
+	{
+		cgltf_write_line(context, "\"textures\": [");
+		for (cgltf_size i = 0; i < data->textures_count; ++i)
+		{
+			cgltf_write_texture(context, data->textures + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->skins_count > 0)
+	{
+		cgltf_write_line(context, "\"skins\": [");
+		for (cgltf_size i = 0; i < data->skins_count; ++i)
+		{
+			cgltf_write_skin(context, data->skins + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->animations_count > 0)
+	{
+		cgltf_write_line(context, "\"animations\": [");
+		for (cgltf_size i = 0; i < data->animations_count; ++i)
+		{
+			cgltf_write_animation(context, data->animations + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->cameras_count > 0)
+	{
+		cgltf_write_line(context, "\"cameras\": [");
+		for (cgltf_size i = 0; i < data->cameras_count; ++i)
+		{
+			cgltf_write_camera(context, data->cameras + i);
+		}
+		cgltf_write_line(context, "]");
+	}
+
+	if (data->lights_count > 0 || data->variants_count > 0)
+	{
+		cgltf_write_line(context, "\"extensions\": {");
+
+		if (data->lights_count > 0)
+		{
+			cgltf_write_line(context, "\"KHR_lights_punctual\": {");
+			cgltf_write_line(context, "\"lights\": [");
+			for (cgltf_size i = 0; i < data->lights_count; ++i)
+			{
+				cgltf_write_light(context, data->lights + i);
+			}
+			cgltf_write_line(context, "]");
+			cgltf_write_line(context, "}");
+		}
+
+		if (data->variants_count)
+		{
+			cgltf_write_line(context, "\"KHR_materials_variants\": {");
+			cgltf_write_line(context, "\"variants\": [");
+			for (cgltf_size i = 0; i < data->variants_count; ++i)
+			{
+				cgltf_write_variant(context, data->variants + i);
+			}
+			cgltf_write_line(context, "]");
+			cgltf_write_line(context, "}");
+		}
+
+		cgltf_write_line(context, "}");
+	}
+
+	if (context->extension_flags != 0)
+	{
+		cgltf_write_line(context, "\"extensionsUsed\": [");
+		cgltf_write_extensions(context, context->extension_flags);
+		cgltf_write_line(context, "]");
+	}
+
+	if (context->required_extension_flags != 0)
+	{
+		cgltf_write_line(context, "\"extensionsRequired\": [");
+		cgltf_write_extensions(context, context->required_extension_flags);
+		cgltf_write_line(context, "]");
+	}
+
+	cgltf_write_extras(context, &data->extras);
+
+	CGLTF_SPRINTF("\n}\n");
+
+	// snprintf does not include the null terminator in its return value, so be sure to include it
+	// in the returned byte count.
+	return 1 + ctx.chars_written;
+}
+
+#endif /* #ifdef CGLTF_WRITE_IMPLEMENTATION */
+
+/* cgltf is distributed under MIT license:
+ *
+ * Copyright (c) 2019-2021 Philip Rideout
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */

+ 6 - 3
vendor/commonmark/cmark.odin

@@ -489,10 +489,13 @@ cmark_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mod
 
 	cmark_alloc := cast(^Allocator)allocator_data
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		ptr := cmark_alloc.calloc(1, c.size_t(size))
-		res  = transmute([]byte)runtime.Raw_Slice{ptr, size}
-		return res, nil
+		res = ([^]byte)(ptr)[:size]
+		if ptr == nil {
+			err = .Out_Of_Memory
+		}
+		return
 
 	case .Free:
 		cmark_alloc.free(old_memory)

+ 1 - 1
vendor/commonmark/doc.odin

@@ -1,4 +1,4 @@
-// +ignore
+//+build ignore
 /*
 	Bindings against CMark (https://github.com/commonmark/cmark)
 

+ 12 - 11
vendor/directx/d3d11/d3d11.odin

@@ -53,11 +53,13 @@ foreign d3d11 {
 	) -> HRESULT ---
 }
 
+WKPDID_D3DDebugObjectNameW_UUID_STRING :: "4CCA5FD8-921F-42C8-8566-70CAF2A9B741"
+WKPDID_D3DDebugObjectNameW_UUID        := &IID{0x4cca5fd8, 0x921f, 0x42c8, {0x85, 0x66, 0x70, 0xca, 0xf2, 0xa9, 0xb7, 0x41}}
+
+// TODO(bill): Convert these to actual internal UUID
 foreign d3d11 {
-	WKPDID_D3DDebugObjectNameW: GUID
-	WKPDID_CommentStringW:      GUID
+	WKPDID_CommentStringW: GUID
 }
-
 @(link_prefix="D3D_")
 foreign d3d11 {
 	TEXTURE_LAYOUT_ROW_MAJOR:             GUID
@@ -1242,11 +1244,10 @@ COLOR_WRITE_ENABLE_ALPHA :: COLOR_WRITE_ENABLE_MASK{.ALPHA}
 COLOR_WRITE_ENABLE_ALL   :: COLOR_WRITE_ENABLE_MASK{.RED, .GREEN, .BLUE, .ALPHA}
 
 COLOR_WRITE_ENABLE :: enum i32 {
-	RED   = 1,
-	GREEN = 2,
-	BLUE  = 4,
-	ALPHA = 8,
-	ALL   = 15,
+	RED   = 0,
+	GREEN = 1,
+	BLUE  = 2,
+	ALPHA = 3,
 }
 
 RENDER_TARGET_BLEND_DESC :: struct {
@@ -2401,7 +2402,7 @@ IDeviceContext_VTable :: struct {
 	ClearRenderTargetView:                     proc "stdcall" (this: ^IDeviceContext, pRenderTargetView: ^IRenderTargetView, ColorRGBA: ^[4]f32),
 	ClearUnorderedAccessViewUint:              proc "stdcall" (this: ^IDeviceContext, pUnorderedAccessView: ^IUnorderedAccessView, Values: ^[4]u32),
 	ClearUnorderedAccessViewFloat:             proc "stdcall" (this: ^IDeviceContext, pUnorderedAccessView: ^IUnorderedAccessView, Values: ^[4]f32),
-	ClearDepthStencilView:                     proc "stdcall" (this: ^IDeviceContext, pDepthStencilView: ^IDepthStencilView, ClearFlags: CLEAR_FLAG, Depth: f32, Stencil: u8),
+	ClearDepthStencilView:                     proc "stdcall" (this: ^IDeviceContext, pDepthStencilView: ^IDepthStencilView, ClearFlags: CLEAR_FLAGS, Depth: f32, Stencil: u8),
 	GenerateMips:                              proc "stdcall" (this: ^IDeviceContext, pShaderResourceView: ^IShaderResourceView),
 	SetResourceMinLOD:                         proc "stdcall" (this: ^IDeviceContext, pResource: ^IResource, MinLOD: f32),
 	GetResourceMinLOD:                         proc "stdcall" (this: ^IDeviceContext, pResource: ^IResource) -> f32,
@@ -3734,10 +3735,10 @@ MESSAGE_CATEGORY :: enum u32 {
 INFO_QUEUE_FILTER_DESC :: struct {
 	NumCategories:    u32,
 	pCategoryList:    ^MESSAGE_CATEGORY,
-	
+
 	NumSeverities:    u32,
 	pSeverityList:    ^MESSAGE_SEVERITY,
-	
+
 	NumIDs:           u32,
 	pIDList:          ^MESSAGE_ID,
 }

+ 339 - 311
vendor/directx/d3d12/d3d12.odin

@@ -212,11 +212,12 @@ SHADER_VARIABLE_CLASS :: enum i32 {
 	INTERFACE_POINTER     = 7,
 }
 
-SHADER_VARIABLE_FLAGS :: enum u32 { // TODO: make bit_set
-	USERPACKED              = 0x1,
-	USED                    = 0x2,
-	INTERFACE_POINTER       = 0x4,
-	INTERFACE_PARAMETER     = 0x8,
+SHADER_VARIABLE_FLAGS :: distinct bit_set[SHADER_VARIABLE_FLAG; u32]
+SHADER_VARIABLE_FLAG :: enum u32 {
+	USERPACKED              = 0,
+	USED                    = 1,
+	INTERFACE_POINTER       = 2,
+	INTERFACE_PARAMETER     = 3,
 }
 
 SHADER_VARIABLE_TYPE :: enum i32 {
@@ -280,15 +281,16 @@ SHADER_VARIABLE_TYPE :: enum i32 {
 	MIN16UINT                     = 57,
 }
 
-SHADER_INPUT_FLAGS :: enum u32 { // TODO: make bit_set
-	USERPACKED              = 0x1,
-	COMPARISON_SAMPLER      = 0x2,
-	TEXTURE_COMPONENT_0     = 0x4,
-	TEXTURE_COMPONENT_1     = 0x8,
-	TEXTURE_COMPONENTS      = 0xc,
-	UNUSED                  = 0x10,
+SHADER_INPUT_FLAGS :: distinct bit_set[SHADER_INPUT_FLAG; u32]
+SHADER_INPUT_FLAG :: enum u32 {
+	USERPACKED              = 0,
+	COMPARISON_SAMPLER      = 1,
+	TEXTURE_COMPONENT_0     = 2,
+	TEXTURE_COMPONENT_1     = 3,
+	UNUSED                  = 4,
 }
 
+
 SHADER_INPUT_TYPE :: enum i32 {
 	CBUFFER                        = 0,
 	TBUFFER                        = 1,
@@ -306,8 +308,9 @@ SHADER_INPUT_TYPE :: enum i32 {
 	UAV_FEEDBACKTEXTURE            = 13,
 }
 
-SHADER_CBUFFER_FLAGS :: enum u32 { // TODO: make bit_set
-	USERPACKED = 0x1,
+SHADER_CBUFFER_FLAGS :: distinct bit_set[SHADER_CBUFFER_FLAG; u32]
+SHADER_CBUFFER_FLAG :: enum u32 {
+	USERPACKED = 0,
 }
 
 CBUFFER_TYPE :: enum i32 {
@@ -410,13 +413,12 @@ INTERPOLATION_MODE :: enum i32 {
 	LINEAR_NOPERSPECTIVE_SAMPLE   = 7,
 }
 
-PARAMETER_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE = 0x0,
-	IN   = 0x1,
-	OUT  = 0x2,
+PARAMETER_FLAGS :: distinct bit_set[PARAMETER_FLAG; u32]
+PARAMETER_FLAG :: enum u32 {
+	IN   = 0,
+	OUT  = 1,
 }
 
-
 GPU_VIRTUAL_ADDRESS :: u64
 
 COMMAND_LIST_TYPE :: enum i32 {
@@ -429,9 +431,9 @@ COMMAND_LIST_TYPE :: enum i32 {
 	VIDEO_ENCODE  = 6,
 }
 
-COMMAND_QUEUE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                = 0x0,
-	DISABLE_GPU_TIMEOUT = 0x1,
+COMMAND_QUEUE_FLAGS :: distinct bit_set[COMMAND_QUEUE_FLAG; u32]
+COMMAND_QUEUE_FLAG :: enum u32 {
+	DISABLE_GPU_TIMEOUT = 0,
 }
 
 COMMAND_QUEUE_PRIORITY :: enum i32 {
@@ -593,12 +595,19 @@ BLEND_OP :: enum i32 {
 	MAX          = 5,
 }
 
-COLOR_WRITE_ENABLE :: enum i32 { // TODO: make bit_set
-	RED   = 1,
-	GREEN = 2,
-	BLUE  = 4,
-	ALPHA = 8,
-	ALL   = 15,
+COLOR_WRITE_ENABLE_MASK   :: distinct bit_set[COLOR_WRITE_ENABLE; u32]
+
+COLOR_WRITE_ENABLE_RED   :: COLOR_WRITE_ENABLE_MASK{.RED}
+COLOR_WRITE_ENABLE_GREEN :: COLOR_WRITE_ENABLE_MASK{.GREEN}
+COLOR_WRITE_ENABLE_BLUE  :: COLOR_WRITE_ENABLE_MASK{.BLUE}
+COLOR_WRITE_ENABLE_ALPHA :: COLOR_WRITE_ENABLE_MASK{.ALPHA}
+COLOR_WRITE_ENABLE_ALL   :: COLOR_WRITE_ENABLE_MASK{.RED, .GREEN, .BLUE, .ALPHA}
+
+COLOR_WRITE_ENABLE :: enum i32 {
+	RED   = 0,
+	GREEN = 1,
+	BLUE  = 2,
+	ALPHA = 3,
 }
 
 LOGIC_OP :: enum i32 {
@@ -721,9 +730,9 @@ CACHED_PIPELINE_STATE :: struct {
 	CachedBlobSizeInBytes: SIZE_T,
 }
 
-PIPELINE_STATE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE       = 0x0,
-	TOOL_DEBUG = 0x1,
+PIPELINE_STATE_FLAGS :: distinct bit_set[PIPELINE_STATE_FLAG; u32]
+PIPELINE_STATE_FLAG :: enum u32 {
+	TOOL_DEBUG = 0,
 }
 
 GRAPHICS_PIPELINE_STATE_DESC :: struct {
@@ -853,58 +862,61 @@ CONSERVATIVE_RASTERIZATION_TIER :: enum i32 {
 	_3            = 3,
 }
 
-FORMAT_SUPPORT1 :: enum i32 { // TODO: make bit_set
-	NONE                        = 0,
-	BUFFER                      = 1,
-	IA_VERTEX_BUFFER            = 2,
-	IA_INDEX_BUFFER             = 4,
-	SO_BUFFER                   = 8,
-	TEXTURE1D                   = 16,
-	TEXTURE2D                   = 32,
-	TEXTURE3D                   = 64,
-	TEXTURECUBE                 = 128,
-	SHADER_LOAD                 = 256,
-	SHADER_SAMPLE               = 512,
-	SHADER_SAMPLE_COMPARISON    = 1024,
-	SHADER_SAMPLE_MONO_TEXT     = 2048,
-	MIP                         = 4096,
-	RENDER_TARGET               = 16384,
-	BLENDABLE                   = 32768,
-	DEPTH_STENCIL               = 65536,
-	MULTISAMPLE_RESOLVE         = 262144,
-	DISPLAY                     = 524288,
-	CAST_WITHIN_BIT_LAYOUT      = 1048576,
-	MULTISAMPLE_RENDERTARGET    = 2097152,
-	MULTISAMPLE_LOAD            = 4194304,
-	SHADER_GATHER               = 8388608,
-	BACK_BUFFER_CAST            = 16777216,
-	TYPED_UNORDERED_ACCESS_VIEW = 33554432,
-	SHADER_GATHER_COMPARISON    = 67108864,
-	DECODER_OUTPUT              = 134217728,
-	VIDEO_PROCESSOR_OUTPUT      = 268435456,
-	VIDEO_PROCESSOR_INPUT       = 536870912,
-	VIDEO_ENCODER               = 1073741824,
-}
-
-FORMAT_SUPPORT2 :: enum i32 { // TODO: make bit_set
-	NONE                                         = 0,
-	UAV_ATOMIC_ADD                               = 1,
-	UAV_ATOMIC_BITWISE_OPS                       = 2,
-	UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE = 4,
-	UAV_ATOMIC_EXCHANGE                          = 8,
-	UAV_ATOMIC_SIGNED_MIN_OR_MAX                 = 16,
-	UAV_ATOMIC_UNSIGNED_MIN_OR_MAX               = 32,
-	UAV_TYPED_LOAD                               = 64,
-	UAV_TYPED_STORE                              = 128,
-	OUTPUT_MERGER_LOGIC_OP                       = 256,
-	TILED                                        = 512,
-	MULTIPLANE_OVERLAY                           = 16384,
-	SAMPLER_FEEDBACK                             = 32768,
-}
-
-MULTISAMPLE_QUALITY_LEVEL_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE           = 0x0,
-	TILED_RESOURCE = 0x1,
+FORMAT_SUPPORT1 :: distinct bit_set[FORMAT_SUPPORT1_FLAG; u32]
+FORMAT_SUPPORT1_FLAG :: enum i32 {
+	BUFFER                      = 0,
+	IA_VERTEX_BUFFER            = 1,
+	IA_INDEX_BUFFER             = 2,
+	SO_BUFFER                   = 3,
+	TEXTURE1D                   = 4,
+	TEXTURE2D                   = 5,
+	TEXTURE3D                   = 6,
+	TEXTURECUBE                 = 7,
+	SHADER_LOAD                 = 8,
+	SHADER_SAMPLE               = 9,
+	SHADER_SAMPLE_COMPARISON    = 10,
+	SHADER_SAMPLE_MONO_TEXT     = 11,
+	MIP                         = 12,
+
+	RENDER_TARGET               = 14,
+	BLENDABLE                   = 15,
+	DEPTH_STENCIL               = 16,
+
+	MULTISAMPLE_RESOLVE         = 18,
+	DISPLAY                     = 19,
+	CAST_WITHIN_BIT_LAYOUT      = 20,
+	MULTISAMPLE_RENDERTARGET    = 21,
+	MULTISAMPLE_LOAD            = 22,
+	SHADER_GATHER               = 23,
+	BACK_BUFFER_CAST            = 24,
+	TYPED_UNORDERED_ACCESS_VIEW = 25,
+	SHADER_GATHER_COMPARISON    = 26,
+	DECODER_OUTPUT              = 27,
+	VIDEO_PROCESSOR_OUTPUT      = 28,
+	VIDEO_PROCESSOR_INPUT       = 29,
+	VIDEO_ENCODER               = 30,
+}
+
+FORMAT_SUPPORT2 :: distinct bit_set[FORMAT_SUPPORT2_FLAG; u32]
+FORMAT_SUPPORT2_FLAG :: enum i32 {
+	UAV_ATOMIC_ADD                               = 0,
+	UAV_ATOMIC_BITWISE_OPS                       = 1,
+	UAV_ATOMIC_COMPARE_STORE_OR_COMPARE_EXCHANGE = 2,
+	UAV_ATOMIC_EXCHANGE                          = 3,
+	UAV_ATOMIC_SIGNED_MIN_OR_MAX                 = 4,
+	UAV_ATOMIC_UNSIGNED_MIN_OR_MAX               = 5,
+	UAV_TYPED_LOAD                               = 6,
+	UAV_TYPED_STORE                              = 7,
+	OUTPUT_MERGER_LOGIC_OP                       = 8,
+	TILED                                        = 9,
+
+	MULTIPLANE_OVERLAY                           = 14,
+	SAMPLER_FEEDBACK                             = 15,
+}
+
+MULTISAMPLE_QUALITY_LEVEL_FLAGS :: distinct bit_set[MULTISAMPLE_QUALITY_LEVEL_FLAG; u32]
+MULTISAMPLE_QUALITY_LEVEL_FLAG :: enum u32 {
+	TILED_RESOURCE = 0,
 }
 
 CROSS_NODE_SHARING_TIER :: enum i32 {
@@ -1034,12 +1046,12 @@ FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT :: struct {
 	MaxGPUVirtualAddressBitsPerProcess:  u32,
 }
 
-SHADER_CACHE_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                   = 0x0,
-	SINGLE_PSO             = 0x1,
-	LIBRARY                = 0x2,
-	AUTOMATIC_INPROC_CACHE = 0x4,
-	AUTOMATIC_DISK_CACHE   = 0x8,
+SHADER_CACHE_SUPPORT_FLAGS :: distinct bit_set[SHADER_CACHE_SUPPORT_FLAG; u32]
+SHADER_CACHE_SUPPORT_FLAG :: enum u32 {
+	SINGLE_PSO             = 0,
+	LIBRARY                = 1,
+	AUTOMATIC_INPROC_CACHE = 2,
+	AUTOMATIC_DISK_CACHE   = 3,
 }
 
 FEATURE_DATA_SHADER_CACHE :: struct {
@@ -1052,15 +1064,15 @@ FEATURE_DATA_COMMAND_QUEUE_PRIORITY :: struct {
 	PriorityForTypeIsSupported: BOOL,
 }
 
-COMMAND_LIST_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE          = 0x0,
-	DIRECT        = 0x1,
-	BUNDLE        = 0x2,
-	COMPUTE       = 0x4,
-	COPY          = 0x8,
-	VIDEO_DECODE  = 0x10,
-	VIDEO_PROCESS = 0x20,
-	VIDEO_ENCODE  = 0x40,
+COMMAND_LIST_SUPPORT_FLAGS :: distinct bit_set[COMMAND_LIST_SUPPORT_FLAG; u32]
+COMMAND_LIST_SUPPORT_FLAG :: enum u32 {
+	DIRECT        = 0,
+	BUNDLE        = 1,
+	COMPUTE       = 2,
+	COPY          = 3,
+	VIDEO_DECODE  = 4,
+	VIDEO_PROCESS = 5,
+	VIDEO_ENCODE  = 6,
 }
 
 FEATURE_DATA_OPTIONS3 :: struct {
@@ -1198,25 +1210,26 @@ HEAP_PROPERTIES :: struct {
 	VisibleNodeMask:      u32,
 }
 
-HEAP_FLAGS :: enum u32 { // TODO: make bit_set ???
-	NONE                           = 0x0,
-	SHARED                         = 0x1,
-	DENY_BUFFERS                   = 0x4,
-	ALLOW_DISPLAY                  = 0x8,
-	SHARED_CROSS_ADAPTER           = 0x20,
-	DENY_RT_DS_TEXTURES            = 0x40,
-	DENY_NON_RT_DS_TEXTURES        = 0x80,
-	HARDWARE_PROTECTED             = 0x100,
-	ALLOW_WRITE_WATCH              = 0x200,
-	ALLOW_SHADER_ATOMICS           = 0x400,
-	CREATE_NOT_RESIDENT            = 0x800,
-	CREATE_NOT_ZEROED              = 0x1000,
-	ALLOW_ALL_BUFFERS_AND_TEXTURES = 0x0,
-	ALLOW_ONLY_BUFFERS             = 0xc0,
-	ALLOW_ONLY_NON_RT_DS_TEXTURES  = 0x44,
-	ALLOW_ONLY_RT_DS_TEXTURES      = 0x84,
+HEAP_FLAGS :: distinct bit_set[HEAP_FLAG; u32]
+HEAP_FLAG :: enum u32 {
+	SHARED                         = 0,
+	DENY_BUFFERS                   = 2,
+	ALLOW_DISPLAY                  = 3,
+	SHARED_CROSS_ADAPTER           = 5,
+	DENY_RT_DS_TEXTURES            = 6,
+	DENY_NON_RT_DS_TEXTURES        = 7,
+	HARDWARE_PROTECTED             = 8,
+	ALLOW_WRITE_WATCH              = 9,
+	ALLOW_SHADER_ATOMICS           = 10,
+	CREATE_NOT_RESIDENT            = 11,
+	CREATE_NOT_ZEROED              = 12,
 }
 
+HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES :: HEAP_FLAGS{}
+HEAP_FLAG_ALLOW_ONLY_BUFFERS             :: HEAP_FLAGS{.DENY_BUFFERS, .ALLOW_DISPLAY}
+HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES  :: HEAP_FLAGS{.DENY_BUFFERS, .DENY_RT_DS_TEXTURES}
+HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES      :: HEAP_FLAGS{.DENY_BUFFERS, .DENY_NON_RT_DS_TEXTURES}
+
 HEAP_DESC :: struct {
 	SizeInBytes: u64,
 	Properties:  HEAP_PROPERTIES,
@@ -1239,15 +1252,15 @@ TEXTURE_LAYOUT :: enum i32 {
 	_64KB_STANDARD_SWIZZLE  = 3,
 }
 
-RESOURCE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                        = 0x0,
-	ALLOW_RENDER_TARGET         = 0x1,
-	ALLOW_DEPTH_STENCIL         = 0x2,
-	ALLOW_UNORDERED_ACCESS      = 0x4,
-	DENY_SHADER_RESOURCE        = 0x8,
-	ALLOW_CROSS_ADAPTER         = 0x10,
-	ALLOW_SIMULTANEOUS_ACCESS   = 0x20,
-	VIDEO_DECODE_REFERENCE_ONLY = 0x40,
+RESOURCE_FLAGS :: distinct bit_set[RESOURCE_FLAG; u32]
+RESOURCE_FLAG :: enum u32 {
+	ALLOW_RENDER_TARGET         = 0,
+	ALLOW_DEPTH_STENCIL         = 1,
+	ALLOW_UNORDERED_ACCESS      = 2,
+	DENY_SHADER_RESOURCE        = 3,
+	ALLOW_CROSS_ADAPTER         = 4,
+	ALLOW_SIMULTANEOUS_ACCESS   = 5,
+	VIDEO_DECODE_REFERENCE_ONLY = 6,
 }
 
 MIP_REGION :: struct {
@@ -1332,11 +1345,11 @@ TILE_REGION_SIZE :: struct {
 	Depth:    u16,
 }
 
-TILE_RANGE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE              = 0x0,
-	NULL              = 0x1,
-	SKIP              = 0x2,
-	REUSE_SINGLE_TILE = 0x4,
+TILE_RANGE_FLAGS :: distinct bit_set[TILE_RANGE_FLAG; u32]
+TILE_RANGE_FLAG :: enum u32 {
+	NULL              = 0,
+	SKIP              = 1,
+	REUSE_SINGLE_TILE = 2,
 }
 
 SUBRESOURCE_TILING :: struct {
@@ -1359,45 +1372,52 @@ PACKED_MIP_INFO :: struct {
 	StartTileIndexInOverallResource: u32,
 }
 
-TILE_MAPPING_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE      = 0x0,
-	NO_HAZARD = 0x1,
-}
-
-TILE_COPY_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                                     = 0x0,
-	NO_HAZARD                                = 0x1,
-	LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE = 0x2,
-	SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER = 0x4,
-}
-
-RESOURCE_STATES :: enum i32 { // TODO: make bit_set
-	COMMON                            = 0,
-	VERTEX_AND_CONSTANT_BUFFER        = 1,
-	INDEX_BUFFER                      = 2,
-	RENDER_TARGET                     = 4,
-	UNORDERED_ACCESS                  = 8,
-	DEPTH_WRITE                       = 16,
-	DEPTH_READ                        = 32,
-	NON_PIXEL_SHADER_RESOURCE         = 64,
-	PIXEL_SHADER_RESOURCE             = 128,
-	STREAM_OUT                        = 256,
-	INDIRECT_ARGUMENT                 = 512,
-	COPY_DEST                         = 1024,
-	COPY_SOURCE                       = 2048,
-	RESOLVE_DEST                      = 4096,
-	RESOLVE_SOURCE                    = 8192,
-	RAYTRACING_ACCELERATION_STRUCTURE = 4194304,
-	SHADING_RATE_SOURCE               = 16777216,
-	GENERIC_READ                      = 2755,
-	PRESENT                           = 0,
-	PREDICATION                       = 512,
-	VIDEO_DECODE_READ                 = 65536,
-	VIDEO_DECODE_WRITE                = 131072,
-	VIDEO_PROCESS_READ                = 262144,
-	VIDEO_PROCESS_WRITE               = 524288,
-	VIDEO_ENCODE_READ                 = 2097152,
-	VIDEO_ENCODE_WRITE                = 8388608,
+TILE_MAPPING_FLAGS :: distinct bit_set[TILE_MAPPING_FLAG; u32]
+TILE_MAPPING_FLAG :: enum u32 {
+	NO_HAZARD = 0,
+}
+
+TILE_COPY_FLAGS :: distinct bit_set[TILE_COPY_FLAG; u32]
+TILE_COPY_FLAG :: enum u32 {
+	NO_HAZARD                                = 0,
+	LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE = 1,
+	SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER = 2,
+}
+
+RESOURCE_STATES :: distinct bit_set[RESOURCE_STATE; u32]
+RESOURCE_STATE :: enum i32 {
+	VERTEX_AND_CONSTANT_BUFFER        = 0,
+	INDEX_BUFFER                      = 1,
+	RENDER_TARGET                     = 2,
+	UNORDERED_ACCESS                  = 3,
+	DEPTH_WRITE                       = 4,
+	DEPTH_READ                        = 5,
+	NON_PIXEL_SHADER_RESOURCE         = 6,
+	PIXEL_SHADER_RESOURCE             = 7,
+	STREAM_OUT                        = 8,
+	INDIRECT_ARGUMENT                 = 9,
+	COPY_DEST                         = 10,
+	COPY_SOURCE                       = 11,
+	RESOLVE_DEST                      = 12,
+	RESOLVE_SOURCE                    = 13,
+	RAYTRACING_ACCELERATION_STRUCTURE = 22,
+	SHADING_RATE_SOURCE               = 24,
+	PREDICATION                       = 9,
+	VIDEO_DECODE_READ                 = 16,
+	VIDEO_DECODE_WRITE                = 17,
+	VIDEO_PROCESS_READ                = 18,
+	VIDEO_PROCESS_WRITE               = 19,
+	VIDEO_ENCODE_READ                 = 21,
+	VIDEO_ENCODE_WRITE                = 23,
+}
+
+RESOURCE_STATE_COMMON :: RESOURCE_STATES{}
+RESOURCE_STATE_PRESENT :: RESOURCE_STATES{}
+RESOURCE_STATE_GENERIC_READ :: RESOURCE_STATES{
+	.VERTEX_AND_CONSTANT_BUFFER, .INDEX_BUFFER, .NON_PIXEL_SHADER_RESOURCE, .PIXEL_SHADER_RESOURCE, .INDIRECT_ARGUMENT, .COPY_SOURCE,
+}
+RESOURCE_STATE_ALL_SHADER_RESOURCE :: RESOURCE_STATES{
+	.SHADING_RATE_SOURCE, .INDEX_BUFFER,
 }
 
 RESOURCE_BARRIER_TYPE :: enum i32 {
@@ -1422,10 +1442,10 @@ RESOURCE_UAV_BARRIER :: struct {
 	pResource: ^IResource,
 }
 
-RESOURCE_BARRIER_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE       = 0x0,
-	BEGIN_ONLY = 0x1,
-	END_ONLY   = 0x2,
+RESOURCE_BARRIER_FLAGS :: distinct bit_set[RESOURCE_BARRIER_FLAG; u32]
+RESOURCE_BARRIER_FLAG :: enum u32 {
+	BEGIN_ONLY = 0,
+	END_ONLY   = 1,
 }
 
 RESOURCE_BARRIER :: struct {
@@ -1484,9 +1504,9 @@ VIEW_INSTANCE_LOCATION :: struct {
 	RenderTargetArrayIndex: u32,
 }
 
-VIEW_INSTANCING_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                         = 0x0,
-	ENABLE_VIEW_INSTANCE_MASKING = 0x1,
+VIEW_INSTANCING_FLAGS :: distinct bit_set[VIEW_INSTANCING_FLAG; u32]
+VIEW_INSTANCING_FLAG :: enum u32 {
+	ENABLE_VIEW_INSTANCE_MASKING = 0,
 }
 
 VIEW_INSTANCING_DESC :: struct {
@@ -1504,9 +1524,9 @@ SHADER_COMPONENT_MAPPING :: enum i32 {
 	FORCE_VALUE_1           = 5,
 }
 
-BUFFER_SRV_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE = 0x0,
-	RAW  = 0x1,
+BUFFER_SRV_FLAGS :: distinct bit_set[BUFFER_SRV_FLAG; u32]
+BUFFER_SRV_FLAG :: enum u32 {
+	RAW = 0,
 }
 
 BUFFER_SRV :: struct {
@@ -1675,9 +1695,9 @@ SAMPLER_DESC :: struct {
 	MaxLOD:         f32,
 }
 
-BUFFER_UAV_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE = 0x0,
-	RAW  = 0x1,
+BUFFER_UAV_FLAGS :: distinct bit_set[BUFFER_UAV_FLAG; u32]
+BUFFER_UAV_FLAG :: enum u32 {
+	RAW = 0,
 }
 
 BUFFER_UAV :: struct {
@@ -1837,10 +1857,10 @@ TEX2DMS_ARRAY_DSV :: struct {
 	ArraySize:       u32,
 }
 
-DSV_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE              = 0x0,
-	READ_ONLY_DEPTH   = 0x1,
-	READ_ONLY_STENCIL = 0x2,
+DSV_FLAGS :: distinct bit_set[DSV_FLAG; u32]
+DSV_FLAG :: enum u32 {
+	READ_ONLY_DEPTH   = 0,
+	READ_ONLY_STENCIL = 1,
 }
 
 DSV_DIMENSION :: enum i32 {
@@ -1867,16 +1887,17 @@ DEPTH_STENCIL_VIEW_DESC :: struct {
 	},
 }
 
-CLEAR_FLAGS :: enum u32 { // TODO: make bit_set
-	DEPTH   = 0x1,
-	STENCIL = 0x2,
+CLEAR_FLAGS :: distinct bit_set[CLEAR_FLAG; u32]
+CLEAR_FLAG :: enum u32 {
+	DEPTH   = 0,
+	STENCIL = 1,
 }
 
-FENCE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                 = 0x0,
-	SHARED               = 0x1,
-	SHARED_CROSS_ADAPTER = 0x2,
-	NON_MONITORED        = 0x4,
+FENCE_FLAGS :: distinct bit_set[FENCE_FLAG; u32]
+FENCE_FLAG :: enum u32 {
+	SHARED               = 0,
+	SHARED_CROSS_ADAPTER = 1,
+	NON_MONITORED        = 2,
 }
 
 DESCRIPTOR_HEAP_TYPE :: enum i32 {
@@ -1887,9 +1908,9 @@ DESCRIPTOR_HEAP_TYPE :: enum i32 {
 	NUM_TYPES   = 4,
 }
 
-DESCRIPTOR_HEAP_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE           = 0x0,
-	SHADER_VISIBLE = 0x1,
+DESCRIPTOR_HEAP_FLAGS :: distinct bit_set[DESCRIPTOR_HEAP_FLAG; u32]
+DESCRIPTOR_HEAP_FLAG :: enum u32 {
+	SHADER_VISIBLE = 0,
 }
 
 DESCRIPTOR_HEAP_DESC :: struct {
@@ -1959,18 +1980,18 @@ ROOT_PARAMETER :: struct {
 	ShaderVisibility: SHADER_VISIBILITY,
 }
 
-ROOT_SIGNATURE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                                  = 0x0,
-	ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT    = 0x1,
-	DENY_VERTEX_SHADER_ROOT_ACCESS        = 0x2,
-	DENY_HULL_SHADER_ROOT_ACCESS          = 0x4,
-	DENY_DOMAIN_SHADER_ROOT_ACCESS        = 0x8,
-	DENY_GEOMETRY_SHADER_ROOT_ACCESS      = 0x10,
-	DENY_PIXEL_SHADER_ROOT_ACCESS         = 0x20,
-	ALLOW_STREAM_OUTPUT                   = 0x40,
-	LOCAL_ROOT_SIGNATURE                  = 0x80,
-	DENY_AMPLIFICATION_SHADER_ROOT_ACCESS = 0x100,
-	DENY_MESH_SHADER_ROOT_ACCESS          = 0x200,
+ROOT_SIGNATURE_FLAGS :: distinct bit_set[ROOT_SIGNATURE_FLAG; u32]
+ROOT_SIGNATURE_FLAG :: enum u32 {
+	ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT    = 0,
+	DENY_VERTEX_SHADER_ROOT_ACCESS        = 1,
+	DENY_HULL_SHADER_ROOT_ACCESS          = 2,
+	DENY_DOMAIN_SHADER_ROOT_ACCESS        = 3,
+	DENY_GEOMETRY_SHADER_ROOT_ACCESS      = 4,
+	DENY_PIXEL_SHADER_ROOT_ACCESS         = 5,
+	ALLOW_STREAM_OUTPUT                   = 6,
+	LOCAL_ROOT_SIGNATURE                  = 7,
+	DENY_AMPLIFICATION_SHADER_ROOT_ACCESS = 8,
+	DENY_MESH_SHADER_ROOT_ACCESS          = 9,
 }
 
 STATIC_BORDER_COLOR :: enum i32 {
@@ -2003,13 +2024,13 @@ ROOT_SIGNATURE_DESC :: struct {
 	Flags:             ROOT_SIGNATURE_FLAGS,
 }
 
-DESCRIPTOR_RANGE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                                            = 0x0,
-	DESCRIPTORS_VOLATILE                            = 0x1,
-	DATA_VOLATILE                                   = 0x2,
-	DATA_STATIC_WHILE_SET_AT_EXECUTE                = 0x4,
-	DATA_STATIC                                     = 0x8,
-	DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS = 0x10000,
+DESCRIPTOR_RANGE_FLAGS :: distinct bit_set[DESCRIPTOR_RANGE_FLAG; u32]
+DESCRIPTOR_RANGE_FLAG :: enum u32 {
+	DESCRIPTORS_VOLATILE                            = 0,
+	DATA_VOLATILE                                   = 1,
+	DATA_STATIC_WHILE_SET_AT_EXECUTE                = 2,
+	DATA_STATIC                                     = 3,
+	DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS = 16,
 }
 
 DESCRIPTOR_RANGE1 :: struct {
@@ -2026,11 +2047,11 @@ ROOT_DESCRIPTOR_TABLE1 :: struct {
 	pDescriptorRanges:   ^DESCRIPTOR_RANGE1,
 }
 
-ROOT_DESCRIPTOR_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                             = 0x0,
-	DATA_VOLATILE                    = 0x2,
-	DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x4,
-	DATA_STATIC                      = 0x8,
+ROOT_DESCRIPTOR_FLAGS :: distinct bit_set[ROOT_DESCRIPTOR_FLAG; u32]
+ROOT_DESCRIPTOR_FLAG :: enum u32 {
+	DATA_VOLATILE                    = 2,
+	DATA_STATIC_WHILE_SET_AT_EXECUTE = 3,
+	DATA_STATIC                      = 4,
 }
 
 ROOT_DESCRIPTOR1 :: struct {
@@ -2571,11 +2592,13 @@ IPipelineLibrary1_VTable :: struct {
 	LoadPipeline: proc "stdcall" (this: ^IPipelineLibrary1, pName: [^]u16, pDesc: ^PIPELINE_STATE_STREAM_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT,
 }
 
-MULTIPLE_FENCE_WAIT_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE = 0x0,
-	ANY  = 0x1,
-	ALL  = 0x0,
+MULTIPLE_FENCE_WAIT_FLAGS :: distinct bit_set[MULTIPLE_FENCE_WAIT_FLAG; u32]
+MULTIPLE_FENCE_WAIT_FLAG :: enum u32 {
+	ANY = 0,
 }
+MULTIPLE_FENCE_WAIT_FLAG_NONE :: MULTIPLE_FENCE_WAIT_FLAGS{}
+MULTIPLE_FENCE_WAIT_FLAG_ANY  :: MULTIPLE_FENCE_WAIT_FLAGS{.ANY}
+MULTIPLE_FENCE_WAIT_FLAG_ALL  :: MULTIPLE_FENCE_WAIT_FLAGS{}
 
 RESIDENCY_PRIORITY :: enum i32 {
 	MINIMUM = 671088640,
@@ -2611,9 +2634,9 @@ IDevice2_VTable :: struct {
 	CreatePipelineState: proc "stdcall" (this: ^IDevice2, pDesc: ^PIPELINE_STATE_STREAM_DESC, riid: ^IID, ppPipelineState: ^rawptr) -> HRESULT,
 }
 
-RESIDENCY_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE            = 0x0,
-	DENY_OVERBUDGET = 0x1,
+RESIDENCY_FLAGS :: distinct bit_set[RESIDENCY_FLAG; u32]
+RESIDENCY_FLAG :: enum u32 {
+	DENY_OVERBUDGET = 0,
 }
 
 
@@ -2630,16 +2653,16 @@ IDevice3_VTable :: struct {
 	EnqueueMakeResident:             proc "stdcall" (this: ^IDevice3, Flags: RESIDENCY_FLAGS, NumObjects: u32, ppObjects: ^^IPageable, pFenceToSignal: ^IFence, FenceValueToSignal: u64) -> HRESULT,
 }
 
-COMMAND_LIST_FLAGS :: enum u32 { // TODO: make bit_set
-	COMMAND_LIST_FLAG_NONE = 0x0,
+COMMAND_LIST_FLAGS :: distinct bit_set[COMMAND_LIST_FLAG; u32]
+COMMAND_LIST_FLAG :: enum u32 {
 }
 
-COMMAND_POOL_FLAGS :: enum u32 { // TODO: make bit_set
-	COMMAND_POOL_FLAG_NONE = 0x0,
+COMMAND_POOL_FLAGS :: distinct bit_set[COMMAND_POOL_FLAG; u32]
+COMMAND_POOL_FLAG :: enum u32 {
 }
 
-COMMAND_RECORDER_FLAGS :: enum u32 { // TODO: make bit_set
-	COMMAND_RECORDER_FLAG_NONE = 0x0,
+COMMAND_RECORDER_FLAGS :: distinct bit_set[COMMAND_RECORDER_FLAG; u32]
+COMMAND_RECORDER_FLAG :: enum u32 {
 }
 
 PROTECTED_SESSION_STATUS :: enum i32 {
@@ -2660,9 +2683,9 @@ IProtectedSession_VTable :: struct {
 	GetSessionStatus: proc "stdcall" (this: ^IProtectedSession) -> PROTECTED_SESSION_STATUS,
 }
 
-PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE      = 0x0,
-	SUPPORTED = 0x1,
+PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS :: distinct bit_set[PROTECTED_RESOURCE_SESSION_SUPPORT_FLAG; u32]
+PROTECTED_RESOURCE_SESSION_SUPPORT_FLAG :: enum u32 {
+	SUPPORTED = 0,
 }
 
 FEATURE_DATA_PROTECTED_RESOURCE_SESSION_SUPPORT :: struct {
@@ -2670,8 +2693,8 @@ FEATURE_DATA_PROTECTED_RESOURCE_SESSION_SUPPORT :: struct {
 	Support:   PROTECTED_RESOURCE_SESSION_SUPPORT_FLAGS,
 }
 
-PROTECTED_RESOURCE_SESSION_FLAGS :: enum u32 { // TODO: make bit_set
-	PROTECTED_RESOURCE_SESSION_FLAG_NONE = 0x0,
+PROTECTED_RESOURCE_SESSION_FLAGS :: distinct bit_set[PROTECTED_RESOURCE_SESSION_FLAG; u32]
+PROTECTED_RESOURCE_SESSION_FLAG :: enum u32 {
 }
 
 PROTECTED_RESOURCE_SESSION_DESC :: struct {
@@ -2760,9 +2783,10 @@ META_COMMAND_PARAMETER_TYPE :: enum i32 {
 	GPU_DESCRIPTOR_HANDLE_HEAP_TYPE_CBV_SRV_UAV = 4,
 }
 
-META_COMMAND_PARAMETER_FLAGS :: enum u32 { // TODO: make bit_set
-	INPUT  = 0x1,
-	OUTPUT = 0x2,
+META_COMMAND_PARAMETER_FLAGS :: distinct bit_set[META_COMMAND_PARAMETER_FLAG; u32]
+META_COMMAND_PARAMETER_FLAG :: enum u32 {
+	INPUT  = 0,
+	OUTPUT = 1,
 }
 
 META_COMMAND_PARAMETER_STAGE :: enum i32 {
@@ -2850,11 +2874,11 @@ STATE_SUBOBJECT :: struct {
 	pDesc: rawptr,
 }
 
-STATE_OBJECT_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                                             = 0x0,
-	ALLOW_LOCAL_DEPENDENCIES_ON_EXTERNAL_DEFINITIONS = 0x1,
-	ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS = 0x2,
-	ALLOW_STATE_OBJECT_ADDITIONS                     = 0x4,
+STATE_OBJECT_FLAGS :: distinct bit_set[STATE_OBJECT_FLAG; u32]
+STATE_OBJECT_FLAG :: enum u32 {
+	ALLOW_LOCAL_DEPENDENCIES_ON_EXTERNAL_DEFINITIONS = 0,
+	ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS = 1,
+	ALLOW_STATE_OBJECT_ADDITIONS                     = 2,
 }
 
 STATE_OBJECT_CONFIG :: struct {
@@ -2873,8 +2897,8 @@ NODE_MASK :: struct {
 	NodeMask: u32,
 }
 
-EXPORT_FLAGS :: enum u32 { // TODO: make bit_set
-	EXPORT_FLAG_NONE = 0x0,
+EXPORT_FLAGS :: distinct bit_set[EXPORT_FLAG; u32]
+EXPORT_FLAG :: enum u32 {
 }
 
 EXPORT_DESC :: struct {
@@ -2929,10 +2953,10 @@ RAYTRACING_PIPELINE_CONFIG :: struct {
 	MaxTraceRecursionDepth: u32,
 }
 
-RAYTRACING_PIPELINE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                       = 0x0,
-	SKIP_TRIANGLES             = 0x100,
-	SKIP_PROCEDURAL_PRIMITIVES = 0x200,
+RAYTRACING_PIPELINE_FLAGS :: distinct bit_set[RAYTRACING_PIPELINE_FLAG; u32]
+RAYTRACING_PIPELINE_FLAG :: enum u32 {
+	SKIP_TRIANGLES             = 8,
+	SKIP_PROCEDURAL_PRIMITIVES = 9,
 }
 
 RAYTRACING_PIPELINE_CONFIG1 :: struct {
@@ -2951,10 +2975,10 @@ STATE_OBJECT_DESC :: struct {
 	pSubobjects:   ^STATE_SUBOBJECT,
 }
 
-RAYTRACING_GEOMETRY_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                           = 0x0,
-	OPAQUE                         = 0x1,
-	NO_DUPLICATE_ANYHIT_INVOCATION = 0x2,
+RAYTRACING_GEOMETRY_FLAGS :: distinct bit_set[RAYTRACING_GEOMETRY_FLAG; u32]
+RAYTRACING_GEOMETRY_FLAG :: enum u32 {
+	OPAQUE                         = 0,
+	NO_DUPLICATE_ANYHIT_INVOCATION = 1,
 }
 
 RAYTRACING_GEOMETRY_TYPE :: enum i32 {
@@ -2962,12 +2986,12 @@ RAYTRACING_GEOMETRY_TYPE :: enum i32 {
 	PROCEDURAL_PRIMITIVE_AABBS = 1,
 }
 
-RAYTRACING_INSTANCE_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                            = 0x0,
-	TRIANGLE_CULL_DISABLE           = 0x1,
-	TRIANGLE_FRONT_COUNTERCLOCKWISE = 0x2,
-	FORCE_OPAQUE                    = 0x4,
-	FORCE_NON_OPAQUE                = 0x8,
+RAYTRACING_INSTANCE_FLAGS :: distinct bit_set[RAYTRACING_INSTANCE_FLAG; u32]
+RAYTRACING_INSTANCE_FLAG :: enum u32 {
+	TRIANGLE_CULL_DISABLE           = 0,
+	TRIANGLE_FRONT_COUNTERCLOCKWISE = 1,
+	FORCE_OPAQUE                    = 2,
+	FORCE_NON_OPAQUE                = 3,
 }
 
 GPU_VIRTUAL_ADDRESS_AND_STRIDE :: struct {
@@ -3010,14 +3034,14 @@ RAYTRACING_GEOMETRY_AABBS_DESC :: struct {
 	AABBs:     GPU_VIRTUAL_ADDRESS_AND_STRIDE,
 }
 
-RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE              = 0x0,
-	ALLOW_UPDATE      = 0x1,
-	ALLOW_COMPACTION  = 0x2,
-	PREFER_FAST_TRACE = 0x4,
-	PREFER_FAST_BUILD = 0x8,
-	MINIMIZE_MEMORY   = 0x10,
-	PERFORM_UPDATE    = 0x20,
+RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS :: distinct bit_set[RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG; u32]
+RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG :: enum u32 {
+	ALLOW_UPDATE      = 0,
+	ALLOW_COMPACTION  = 1,
+	PREFER_FAST_TRACE = 2,
+	PREFER_FAST_BUILD = 3,
+	MINIMIZE_MEMORY   = 4,
+	PERFORM_UPDATE    = 5,
 }
 
 RAYTRACING_ACCELERATION_STRUCTURE_COPY_MODE :: enum i32 {
@@ -3137,18 +3161,18 @@ RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO :: struct {
 	UpdateScratchDataSizeInBytes: u64,
 }
 
-RAY_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                            = 0x0,
-	FORCE_OPAQUE                    = 0x1,
-	FORCE_NON_OPAQUE                = 0x2,
-	ACCEPT_FIRST_HIT_AND_END_SEARCH = 0x4,
-	SKIP_CLOSEST_HIT_SHADER         = 0x8,
-	CULL_BACK_FACING_TRIANGLES      = 0x10,
-	CULL_FRONT_FACING_TRIANGLES     = 0x20,
-	CULL_OPAQUE                     = 0x40,
-	CULL_NON_OPAQUE                 = 0x80,
-	SKIP_TRIANGLES                  = 0x100,
-	SKIP_PROCEDURAL_PRIMITIVES      = 0x200,
+RAY_FLAGS :: distinct bit_set[RAY_FLAG; u32]
+RAY_FLAG :: enum u32 {
+	FORCE_OPAQUE                    = 0,
+	FORCE_NON_OPAQUE                = 1,
+	ACCEPT_FIRST_HIT_AND_END_SEARCH = 2,
+	SKIP_CLOSEST_HIT_SHADER         = 3,
+	CULL_BACK_FACING_TRIANGLES      = 4,
+	CULL_FRONT_FACING_TRIANGLES     = 5,
+	CULL_OPAQUE                     = 6,
+	CULL_NON_OPAQUE                 = 7,
+	SKIP_TRIANGLES                  = 8,
+	SKIP_PROCEDURAL_PRIMITIVES      = 9,
 }
 
 HIT_KIND :: enum i32 {
@@ -3260,10 +3284,10 @@ DRED_VERSION :: enum i32 {
 	_1_2 = 3,
 }
 
-DRED_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                    = 0x0,
-	FORCE_ENABLE            = 0x1,
-	DISABLE_AUTOBREADCRUMBS = 0x2,
+DRED_FLAGS :: distinct bit_set[DRED_FLAG; u32]
+DRED_FLAG :: enum u32 {
+	FORCE_ENABLE            = 0,
+	DISABLE_AUTOBREADCRUMBS = 1,
 }
 
 DRED_ENABLEMENT :: enum i32 {
@@ -3611,11 +3635,11 @@ RENDER_PASS_DEPTH_STENCIL_DESC :: struct {
 	StencilEndingAccess:    RENDER_PASS_ENDING_ACCESS,
 }
 
-RENDER_PASS_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE             = 0x0,
-	ALLOW_UAV_WRITES = 0x1,
-	SUSPENDING_PASS  = 0x2,
-	RESUMING_PASS    = 0x4,
+RENDER_PASS_FLAGS :: distinct bit_set[RENDER_PASS_FLAG; u32]
+RENDER_PASS_FLAG :: enum u32 {
+	ALLOW_UAV_WRITES = 0,
+	SUSPENDING_PASS  = 1,
+	RESUMING_PASS    = 2,
 }
 
 
@@ -3697,9 +3721,9 @@ IDebug_VTable :: struct {
 	EnableDebugLayer: proc "stdcall" (this: ^IDebug),
 }
 
-GPU_BASED_VALIDATION_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE                   = 0x0,
-	DISABLE_STATE_TRACKING = 0x1,
+GPU_BASED_VALIDATION_FLAGS :: distinct bit_set[GPU_BASED_VALIDATION_FLAG; u32]
+GPU_BASED_VALIDATION_FLAG :: enum u32 {
+	DISABLE_STATE_TRACKING = 0,
 }
 
 
@@ -3741,11 +3765,11 @@ IDebug3_VTable :: struct {
 	SetGPUBasedValidationFlags:                  proc "stdcall" (this: ^IDebug3, Flags: GPU_BASED_VALIDATION_FLAGS),
 }
 
-RLDO_FLAGS :: enum u32 { // TODO: make bit_set
-	NONE            = 0x0,
-	SUMMARY         = 0x1,
-	DETAIL          = 0x2,
-	IGNORE_INTERNAL = 0x4,
+RLDO_FLAGS :: distinct bit_set[RLDO_FLAG; u32]
+RLDO_FLAG :: enum u32 {
+	SUMMARY         = 0,
+	DETAIL          = 1,
+	IGNORE_INTERNAL = 2,
 }
 
 DEBUG_DEVICE_PARAMETER_TYPE :: enum i32 {
@@ -3754,12 +3778,12 @@ DEBUG_DEVICE_PARAMETER_TYPE :: enum i32 {
 	GPU_SLOWDOWN_PERFORMANCE_FACTOR = 2,
 }
 
-DEBUG_FEATURE :: enum i32 { // TODO: make bit_set
-	NONE                                   = 0,
-	ALLOW_BEHAVIOR_CHANGING_DEBUG_AIDS     = 1,
-	CONSERVATIVE_RESOURCE_STATE_TRACKING   = 2,
-	DISABLE_VIRTUALIZED_BUNDLES_VALIDATION = 4,
-	EMULATE_WINDOWS7                       = 8,
+DEBUG_FEATURE :: distinct bit_set[DEBUG_FEATURE_FLAG; u32]
+DEBUG_FEATURE_FLAG :: enum i32 {
+	ALLOW_BEHAVIOR_CHANGING_DEBUG_AIDS     = 0,
+	CONSERVATIVE_RESOURCE_STATE_TRACKING   = 1,
+	DISABLE_VIRTUALIZED_BUNDLES_VALIDATION = 2,
+	EMULATE_WINDOWS7                       = 3,
 }
 
 GPU_BASED_VALIDATION_SHADER_PATCH_MODE :: enum i32 {
@@ -3770,12 +3794,16 @@ GPU_BASED_VALIDATION_SHADER_PATCH_MODE :: enum i32 {
 	NUM_GPU_BASED_VALIDATION_SHADER_PATCH_MODES = 4,
 }
 
-GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAGS :: enum u32 { // TODO: make bit_set
-	GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_NONE                                           = 0x0,
-	GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_FRONT_LOAD_CREATE_TRACKING_ONLY_SHADERS        = 0x1,
-	GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_FRONT_LOAD_CREATE_UNGUARDED_VALIDATION_SHADERS = 0x2,
-	GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_FRONT_LOAD_CREATE_GUARDED_VALIDATION_SHADERS   = 0x4,
-	VALID_MASK                                                                                           = 0x7,
+GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAGS :: distinct bit_set[GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG; u32]
+GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG :: enum u32 {
+	FRONT_LOAD_CREATE_TRACKING_ONLY_SHADERS        = 0,
+	FRONT_LOAD_CREATE_UNGUARDED_VALIDATION_SHADERS = 1,
+	FRONT_LOAD_CREATE_GUARDED_VALIDATION_SHADERS   = 2,
+}
+GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAG_VALID_MASK :: GPU_BASED_VALIDATION_PIPELINE_STATE_CREATE_FLAGS{
+	.FRONT_LOAD_CREATE_TRACKING_ONLY_SHADERS,
+	.FRONT_LOAD_CREATE_UNGUARDED_VALIDATION_SHADERS,
+	.FRONT_LOAD_CREATE_GUARDED_VALIDATION_SHADERS,
 }
 
 DEBUG_DEVICE_GPU_BASED_VALIDATION_SETTINGS :: struct {

+ 53 - 46
vendor/directx/d3d_compiler/d3d_compiler.odin

@@ -45,50 +45,57 @@ foreign d3dcompiler {
 
 
 
-D3DCOMPILE :: enum u32 { // TODO: make bit_field
-	DEBUG                              = 1 << 0,
-	SKIP_VALIDATION                    = 1 << 1,
-	SKIP_OPTIMIZATION                  = 1 << 2,
-	PACK_MATRIX_ROW_MAJOR              = 1 << 3,
-	PACK_MATRIX_COLUMN_MAJOR           = 1 << 4,
-	PARTIAL_PRECISION                  = 1 << 5,
-	FORCE_VS_SOFTWARE_NO_OPT           = 1 << 6,
-	FORCE_PS_SOFTWARE_NO_OPT           = 1 << 7,
-	NO_PRESHADER                       = 1 << 8,
-	AVOID_FLOW_CONTROL                 = 1 << 9,
-	PREFER_FLOW_CONTROL                = 1 << 10,
-	ENABLE_STRICTNESS                  = 1 << 11,
-	ENABLE_BACKWARDS_COMPATIBILITY     = 1 << 12,
-	IEEE_STRICTNESS                    = 1 << 13,
-	OPTIMIZATION_LEVEL0                = 1 << 14,
-	OPTIMIZATION_LEVEL1                = 0,
-	OPTIMIZATION_LEVEL2                = (1 << 14)|(1 << 15), // Added manually
-	OPTIMIZATION_LEVEL3                = 1 << 15,
-	RESERVED16                         = 1 << 16,
-	RESERVED17                         = 1 << 17,
-	WARNINGS_ARE_ERRORS                = 1 << 18,
-	RESOURCES_MAY_ALIAS                = 1 << 19,
-	ENABLE_UNBOUNDED_DESCRIPTOR_TABLES = 1 << 20,
-	ALL_RESOURCES_BOUND                = 1 << 21,
-	DEBUG_NAME_FOR_SOURCE              = 1 << 22,
-	DEBUG_NAME_FOR_BINARY              = 1 << 23,
-}
-
-EFFECT :: enum u32 { // TODO: make bit_field
-	CHILD_EFFECT   = 1 << 0,
-	ALLOW_SLOW_OPS = 1 << 1,
-}
-
-FLAGS2 :: enum u32 { // TODO: make bit_field
+D3DCOMPILE :: distinct bit_set[D3DCOMPILE_FLAG; u32]
+D3DCOMPILE_FLAG :: enum u32 {
+	DEBUG                              = 0,
+	SKIP_VALIDATION                    = 1,
+	SKIP_OPTIMIZATION                  = 2,
+	PACK_MATRIX_ROW_MAJOR              = 3,
+	PACK_MATRIX_COLUMN_MAJOR           = 4,
+	PARTIAL_PRECISION                  = 5,
+	FORCE_VS_SOFTWARE_NO_OPT           = 6,
+	FORCE_PS_SOFTWARE_NO_OPT           = 7,
+	NO_PRESHADER                       = 8,
+	AVOID_FLOW_CONTROL                 = 9,
+	PREFER_FLOW_CONTROL                = 10,
+	ENABLE_STRICTNESS                  = 11,
+	ENABLE_BACKWARDS_COMPATIBILITY     = 12,
+	IEEE_STRICTNESS                    = 13,
+	OPTIMIZATION_LEVEL0                = 14,
+	OPTIMIZATION_LEVEL3                = 15,
+	RESERVED16                         = 16,
+	RESERVED17                         = 17,
+	WARNINGS_ARE_ERRORS                = 18,
+	RESOURCES_MAY_ALIAS                = 19,
+	ENABLE_UNBOUNDED_DESCRIPTOR_TABLES = 20,
+	ALL_RESOURCES_BOUND                = 21,
+	DEBUG_NAME_FOR_SOURCE              = 22,
+	DEBUG_NAME_FOR_BINARY              = 23,
+}
+
+D3DCOMPILE_OPTIMIZATION_LEVEL0 :: D3DCOMPILE{.OPTIMIZATION_LEVEL0}
+D3DCOMPILE_OPTIMIZATION_LEVEL1 :: D3DCOMPILE{}
+D3DCOMPILE_OPTIMIZATION_LEVEL2 :: D3DCOMPILE{.IEEE_STRICTNESS, .OPTIMIZATION_LEVEL3}
+D3DCOMPILE_OPTIMIZATION_LEVEL3 :: D3DCOMPILE{.OPTIMIZATION_LEVEL3}
+
+
+EFFECT :: distinct bit_set[EFFECT_FLAG; u32]
+EFFECT_FLAG :: enum u32 {
+	CHILD_EFFECT   = 0,
+	ALLOW_SLOW_OPS = 1,
+}
+
+FLAGS2 :: enum u32 {
 	FORCE_ROOT_SIGNATURE_LATEST = 0,
 	FORCE_ROOT_SIGNATURE_1_0    = 1 << 4,
 	FORCE_ROOT_SIGNATURE_1_1    = 1 << 5,
 }
 
-SECDATA :: enum u32 { // TODO: make bit_field
-	MERGE_UAV_SLOTS         = 0x00000001,
-	PRESERVE_TEMPLATE_SLOTS = 0x00000002,
-	REQUIRE_TEMPLATE_MATCH  = 0x00000004,
+SECDATA :: distinct bit_set[SECDATA_FLAG; u32]
+SECDATA_FLAG :: enum u32 {
+	MERGE_UAV_SLOTS         = 0,
+	PRESERVE_TEMPLATE_SLOTS = 1,
+	REQUIRE_TEMPLATE_MATCH  = 2,
 }
 
 DISASM_ENABLE_COLOR_CODE            :: 0x00000001
@@ -188,13 +195,13 @@ pD3DCompile     :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: cstring, a3: ^SHA
 pD3DPreprocess  :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: cstring, a3: ^SHADER_MACRO, a4: ^ID3DInclude, a5: ^^ID3DBlob, a6: ^^ID3DBlob) -> HRESULT
 pD3DDisassemble :: #type proc "c" (a0: rawptr, a1: SIZE_T, a2: u32, a3: cstring, a4: ^^ID3DBlob) -> HRESULT
 
-D3DCOMPILER_STRIP_FLAGS :: enum u32 { // TODO: make bit_field
-	REFLECTION_DATA = 0x1,
-	DEBUG_INFO      = 0x2,
-	TEST_BLOBS      = 0x4,
-	PRIVATE_DATA    = 0x8,
-	ROOT_SIGNATURE  = 0x10,
-	FORCE_DWORD     = 0x7fffffff,
+D3DCOMPILER_STRIP_FLAGS :: distinct bit_set[D3DCOMPILER_STRIP_FLAG; u32]
+D3DCOMPILER_STRIP_FLAG :: enum u32 {
+	REFLECTION_DATA = 0,
+	DEBUG_INFO      = 1,
+	TEST_BLOBS      = 2,
+	PRIVATE_DATA    = 3,
+	ROOT_SIGNATURE  = 4,
 }
 
 BLOB_PART :: enum i32 {

+ 40 - 31
vendor/directx/dxgi/dxgi.odin

@@ -57,14 +57,15 @@ CPU_ACCESS :: enum u32 {
 	FIELD      = 15,
 }
 
-USAGE :: enum u32 { // TODO: convert to bit_set
-	SHADER_INPUT         = 0x00000010,
-	RENDER_TARGET_OUTPUT = 0x00000020,
-	BACK_BUFFER          = 0x00000040,
-	SHARED               = 0x00000080,
-	READ_ONLY            = 0x00000100,
-	DISCARD_ON_PRESENT   = 0x00000200,
-	UNORDERED_ACCESS     = 0x00000400,
+USAGE :: distinct bit_set[USAGE_FLAG; u32]
+USAGE_FLAG :: enum u32 {
+	SHADER_INPUT         =  4,
+	RENDER_TARGET_OUTPUT =  5,
+	BACK_BUFFER          =  6,
+	SHARED               =  7,
+	READ_ONLY            =  8,
+	DISCARD_ON_PRESENT   =  9,
+	UNORDERED_ACCESS     = 10,
 }
 
 RESOURCE_PRIORITY :: enum u32 {
@@ -75,37 +76,45 @@ RESOURCE_PRIORITY :: enum u32 {
 	MAXIMUM = 0xc8000000,
 }
 
-MAP :: enum u32 { // TODO: convert to bit_set
+MAP :: enum u32 {
 	READ    = 1,
 	WRITE   = 2,
 	DISCARD = 4,
 }
 
-ENUM_MODES :: enum u32 { // TODO: convert to bit_set
-	INTERLACED      = 1,
-	SCALING         = 2,
-	STEREO          = 4,
-	DISABLED_STEREO = 8,
+ENUM_MODES :: distinct bit_set[ENUM_MODE; u32]
+ENUM_MODE :: enum u32 {
+	INTERLACED      = 0,
+	SCALING         = 1,
+	STEREO          = 2,
+	DISABLED_STEREO = 3,
 }
 
 MAX_SWAP_CHAIN_BUFFERS :: 16
-PRESENT :: enum u32 { // TODO: convert to bit_set
-	TEST                  = 0x00000001,
-	DO_NOT_SEQUENCE       = 0x00000002,
-	RESTART               = 0x00000004,
-	DO_NOT_WAIT           = 0x00000008,
-	STEREO_PREFER_RIGHT   = 0x00000010,
-	STEREO_TEMPORARY_MONO = 0x00000020,
-	RESTRICT_TO_OUTPUT    = 0x00000040,
-	USE_DURATION          = 0x00000100,
-	ALLOW_TEARING         = 0x00000200,
-}
-
-MWA :: enum u32 { // TODO: convert to bit_set
-	NO_WINDOW_CHANGES = 1 << 0,
-	NO_ALT_ENTER      = 1 << 1,
-	NO_PRINT_SCREEN   = 1 << 2,
-	VALID             = 0x7,
+PRESENT :: distinct bit_set[PRESENT_FLAG; u32]
+PRESENT_FLAG :: enum u32 {
+	TEST                  = 0,
+	DO_NOT_SEQUENCE       = 1,
+	RESTART               = 2,
+	DO_NOT_WAIT           = 3,
+	STEREO_PREFER_RIGHT   = 4,
+	STEREO_TEMPORARY_MONO = 5,
+	RESTRICT_TO_OUTPUT    = 6,
+
+	USE_DURATION          = 8,
+	ALLOW_TEARING         = 9,
+}
+
+MWA :: distinct bit_set[MWA_FLAG; u32]
+MWA_FLAG :: enum u32 {
+	NO_WINDOW_CHANGES = 0,
+	NO_ALT_ENTER      = 1,
+	NO_PRINT_SCREEN   = 2,
+}
+MWA_VALID :: MWA{
+	.NO_WINDOW_CHANGES,
+	.NO_ALT_ENTER,
+	.NO_PRINT_SCREEN,
 }
 
 SHARED_RESOURCE_READ  :: 0x80000000

+ 1 - 1
vendor/raylib/raylib.odin

@@ -1564,7 +1564,7 @@ MemAllocatorProc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
                          size, alignment: int,
                          old_memory: rawptr, old_size: int, location := #caller_location) -> (data: []byte, err: mem.Allocator_Error)  {
 	switch mode {
-	case .Alloc:
+	case .Alloc, .Alloc_Non_Zeroed:
 		ptr := MemAlloc(c.int(size))
 		if ptr == nil {
 			err = .Out_Of_Memory

+ 1 - 1
vendor/stb/rect_pack/stb_rect_pack.odin

@@ -6,7 +6,7 @@ import c "core:c/libc"
 
 when ODIN_OS == .Windows { foreign import lib "../lib/stb_rect_pack.lib" }
 when ODIN_OS == .Linux   { foreign import lib "../lib/stb_rect_pack.a"   }
-when ODIN_OS == .Darwin  { foreign import lib "../lib/stb_rect_pack.a"   }
+when ODIN_OS == .Darwin  { foreign import lib "../lib/darwin/stb_rect_pack.a"   }
 
 Coord :: distinct c.int
 _MAXVAL :: max(Coord)

+ 1 - 1
vendor/stb/truetype/stb_truetype.odin

@@ -5,7 +5,7 @@ import stbrp "vendor:stb/rect_pack"
 
 when ODIN_OS == .Windows { foreign import stbtt "../lib/stb_truetype.lib" }
 when ODIN_OS == .Linux   { foreign import stbtt "../lib/stb_truetype.a"   }
-when ODIN_OS == .Darwin  { foreign import stbtt "../lib/stb_truetype.a"   }
+when ODIN_OS == .Darwin  { foreign import stbtt "../lib/darwin/stb_truetype.a"   }
 
 
 ///////////////////////////////////////////////////////////////////////////////

+ 1 - 1
vendor/stb/vorbis/stb_vorbis.odin

@@ -5,7 +5,7 @@ import c "core:c/libc"
 
 when ODIN_OS == .Windows { foreign import lib "../lib/stb_vorbis.lib" }
 when ODIN_OS == .Linux   { foreign import lib "../lib/stb_vorbis.a"   }
-when ODIN_OS == .Darwin  { foreign import lib "../lib/stb_vorbis.a"   }
+when ODIN_OS == .Darwin  { foreign import lib "../lib/darwin/stb_vorbis.a"   }
 
 
 

部分文件因为文件数量过多而无法显示