|
| 1 | +# zigglgen |
| 2 | + |
| 3 | +The only Zig OpenGL binding generator you need. |
| 4 | + |
| 5 | +## Installation and usage |
| 6 | + |
| 7 | +zigglgen requires a recent-ish master build of the Zig compiler. The oldest known supported version is |
| 8 | +`0.12.0-dev.2063+804cee3b9` (identical to [`2024.1.0-mach`](https://machengine.org/about/nominated-zig/#202410-mach)), |
| 9 | +but more recent versions may also work. |
| 10 | + |
| 11 | +1\. Run `zig fetch` to add the zigglgen package to your `build.zig.zon` manifest: |
| 12 | + |
| 13 | +```sh |
| 14 | +zig fetch https://github.com/castholm/zigglgen/releases/download/v0.1.0/zigglgen.tar.gz |
| 15 | +``` |
| 16 | + |
| 17 | +2\. Generate a set of OpenGL bindings in your `build.zig` build script: |
| 18 | + |
| 19 | +```zig |
| 20 | +const std = @import("std"); |
| 21 | +const zigglgen = @import("zigglgen"); |
| 22 | +
|
| 23 | +pub fn build(b: *std.Build) void { |
| 24 | + const exe = b.addExecutable(...); |
| 25 | +
|
| 26 | + // Choose the OpenGL API, version, profile and extensions you want to generate bindings for. |
| 27 | + const gl_bindings = zigglgen.generateBindingsModule(b, .{ |
| 28 | + .api = .gl, |
| 29 | + .version = .@"4.1", |
| 30 | + .profile = .core, |
| 31 | + .extensions = &.{ .ARB_clip_control, .NV_scissor_exclusive }, |
| 32 | + }); |
| 33 | +
|
| 34 | + // Import the generated module. |
| 35 | + exe.root_module.addImport("gl", gl_bindings); |
| 36 | +
|
| 37 | + b.installArtifact(exe); |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +3\. Initialize OpenGL and start issuing commands: |
| 42 | + |
| 43 | +```zig |
| 44 | +const windowing = @import(...); |
| 45 | +const gl = @import("gl"); |
| 46 | +
|
| 47 | +// Procedure table that will hold OpenGL functions loaded at runtime. |
| 48 | +var procs: gl.ProcTable = undefined; |
| 49 | +
|
| 50 | +pub fn main() !void { |
| 51 | + // Create an OpenGL context using a windowing system of your choice. |
| 52 | + var context = windowing.createContext(...); |
| 53 | + defer context.destroy(); |
| 54 | +
|
| 55 | + // Make the OpenGL context current on the calling thread. |
| 56 | + windowing.makeContextCurrent(context); |
| 57 | + defer windowing.makeContextCurrent(null); |
| 58 | +
|
| 59 | + // Initialize the procedure table. |
| 60 | + if (!procs.init(windowing.getProcAddress)) return error.InitFailed; |
| 61 | +
|
| 62 | + // Make the procedure table current on the calling thread. |
| 63 | + gl.makeProcTableCurrent(&procs); |
| 64 | + defer gl.makeProcTableCurrent(null); |
| 65 | +
|
| 66 | + // Issue OpenGL commands to your heart's content! |
| 67 | + const alpha: gl.float = 1; |
| 68 | + gl.ClearColor(1, 1, 1, alpha); |
| 69 | + gl.Clear(gl.COLOR_BUFFER_BIT); |
| 70 | + } |
| 71 | +``` |
| 72 | + |
| 73 | +See [`zigglgen-example/`](zigglgen-example/) for a complete example project that uses |
| 74 | +[mach-glfw](https://machengine.org/pkg/mach-glfw/) as the windowing system. |
| 75 | + |
| 76 | +## API |
| 77 | + |
| 78 | +### OpenGL symbols |
| 79 | + |
| 80 | +zigglgen generates declarations for OpenGL functions, constants, types and extensions using the original names as |
| 81 | +defined in the various OpenGL specifications (as opposed to the prefixed names used in C): |
| 82 | + |
| 83 | +| | C | Zig | |
| 84 | +|-----------|:----------------------|:-------------------| |
| 85 | +| Command | `glClearColor` | `ClearColor` | |
| 86 | +| Constant | `GL_TRIANGLES` | `TRIANGLES` | |
| 87 | +| Type | `GLfloat` | `float` | |
| 88 | +| Extension | `GL_ARB_clip_control` | `ARB_clip_control` | |
| 89 | + |
| 90 | +### `info` |
| 91 | + |
| 92 | +```zig |
| 93 | +pub const info = struct {}; |
| 94 | +``` |
| 95 | + |
| 96 | +Contains information about the generated set of OpenGL bindings, such as the OpenGL API, version and profile the |
| 97 | +bindings were generated for. |
| 98 | + |
| 99 | +### `ProcTable` |
| 100 | + |
| 101 | +```zig |
| 102 | +pub const ProcTable = struct {}; |
| 103 | +``` |
| 104 | + |
| 105 | +Holds pointers to OpenGL functions loaded at runtime. |
| 106 | + |
| 107 | +This struct is very large, so you should avoid storing instances of it on the stack. Use global variables or allocate |
| 108 | +them on the heap instead. |
| 109 | + |
| 110 | +### `ProcTable.init` |
| 111 | + |
| 112 | +```zig |
| 113 | +pub fn init(procs: *ProcTable, locator: anytype) bool {} |
| 114 | +``` |
| 115 | + |
| 116 | +Initializes the specified procedure table and returns `true` if successful, `false` otherwise. |
| 117 | + |
| 118 | +A procedure table must be successfully initialized before passing it to `makeProcTableCurrent` or accessing any of |
| 119 | +its fields. |
| 120 | + |
| 121 | +`locator` is duck-typed. Given the prefixed name of an OpenGL command (e.g. `"glClear"`), it should return a pointer to |
| 122 | +the corresponding function. It should be able to be used in one of the following two ways: |
| 123 | + |
| 124 | +- `@as(?PROC, locator(@as([*:0]const u8, prefixed_name)))` |
| 125 | +- `@as(?PROC, locator.getProcAddress(@as([*:0]const u8, prefixed_name)))` |
| 126 | + |
| 127 | +If your windowing system has a "get procedure address" function, it is usually enough to simply pass that function as |
| 128 | +the `locator` argument. |
| 129 | + |
| 130 | +No references to `locator` are retained after this function returns. |
| 131 | + |
| 132 | +There is no corresponding `deinit` function. |
| 133 | + |
| 134 | +### `makeProcTableCurrent` |
| 135 | + |
| 136 | +```zig |
| 137 | +pub fn makeProcTableCurrent(procs: ?*const ProcTable) void {} |
| 138 | +``` |
| 139 | + |
| 140 | +Makes the specified procedure table current on the calling thread. |
| 141 | + |
| 142 | +A valid procedure table must be made current on a thread before issuing any OpenGL commands from that same thread. |
| 143 | + |
| 144 | +### `getCurrentProcTable` |
| 145 | + |
| 146 | +```zig |
| 147 | +pub fn getCurrentProcTable() ?*const ProcTable {} |
| 148 | +``` |
| 149 | + |
| 150 | +Returns the procedure table that is current on the calling thread. |
| 151 | + |
| 152 | +### `extensionSupported` |
| 153 | + |
| 154 | +(Only generated if at least one extension is specified.) |
| 155 | + |
| 156 | +```zig |
| 157 | +pub fn extensionSupported(comptime extension: Extension) bool {} |
| 158 | +``` |
| 159 | + |
| 160 | +Returns `true` if the specified OpenGL extension is supported by the procedure table that is current on the calling |
| 161 | +thread, `false` otherwise. |
| 162 | + |
| 163 | +## FAQ |
| 164 | + |
| 165 | +### Which OpenGL APIs are supported? |
| 166 | + |
| 167 | +Any APIs, versions, profiles and extensions included in Khronos's [OpenGL XML API |
| 168 | +Registry](https://github.com/KhronosGroup/OpenGL-Registry/tree/main/xml) are supported. These include: |
| 169 | + |
| 170 | +- OpenGL 1.0 through 3.1 |
| 171 | +- OpenGL 3.2 through 4.6 (Compatibility/Core profile) |
| 172 | +- OpenGL ES 1.1 (Common/Common-Lite profile) |
| 173 | +- OpenGL ES 2.0 through 3.2 |
| 174 | +- OpenGL SC 2.0 |
| 175 | + |
| 176 | +The [`zigglgen/updateApiRegistry.ps1`](zigglgen/updateApiRegistry.ps1) PowerShell script is used to fetch the API |
| 177 | +registry and convert it to a set of Zig source files that are comitted to revision control and used by the generator. |
| 178 | + |
| 179 | +### Why is a thread-local procedure table required? |
| 180 | + |
| 181 | +Per the OpenGL spec, OpenGL function pointers loaded when one OpenGL context is current are not guaranteed to remain |
| 182 | +valid when a different context becomes current. This means that it would be incorrect to load a single set of function |
| 183 | +pointers to global memory just once at application startup and then have them be shared by all current and future |
| 184 | +OpenGL contexts. |
| 185 | + |
| 186 | +In order to support portable multi-threaded multi-context OpenGL applications, it must be possible to load multiple sets |
| 187 | +of function pointers. Because OpenGL contexts are already thread-local, it makes a lot of sense to handle function |
| 188 | +pointers in a similar manner. |
| 189 | + |
| 190 | +### Why aren't OpenGL constants represented as Zig enums? |
| 191 | + |
| 192 | +The short answer is that it's simply not possible to represent groups of OpenGL constants as Zig enums in a |
| 193 | +satisfying manner: |
| 194 | + |
| 195 | +- The API registry currently specifies some of these groups, but far from all of them, and the groups are not guaranteed |
| 196 | + to be complete. Groups can be extended by extensions, so Zig enums structs would need to be defined as non-exhaustive, |
| 197 | + and using constants not specified as part of a group would require casting. |
| 198 | +- Some commands like *GetIntegerv* that can return constants will return them as plain integers. Comparing the returned |
| 199 | + values against Zig enum fields would require casting. |
| 200 | +- Some constants in the same group are aliases for the same value, which makes them impossible to represent as |
| 201 | + Zig enums. |
| 202 | + |
| 203 | +### Why did calling a supported extension function result in a null pointer dereference? |
| 204 | + |
| 205 | +Certain OpenGL extension add features that are only conditionally available under certain OpenGL versions/profiles or |
| 206 | +when certain other extensions are also supported; for example, the *VertexWeighthNV* command from the *NV_half_float* |
| 207 | +extension is only available when the *EXT_vertex_weighting* extension is also supported. Unfortunately, the API registry |
| 208 | +does not specify these interactions in a consistent manner, so it's not possible for zigglgen to generate code that |
| 209 | +ensures that calls to supported extension functions are always safe. |
| 210 | + |
| 211 | +If you use OpenGL extensions it is your responsibility to read the extension specifications carefully and understand |
| 212 | +under which conditions their features are available. |
| 213 | + |
| 214 | +## Contributing |
| 215 | + |
| 216 | +If you have any issues or suggestions, please open an issue or a pull request. |
| 217 | + |
| 218 | +### Help us define overrides for function parameters and return types! |
| 219 | + |
| 220 | +Due to the nature of the API Registry being designed for C, zigglgen currently generates most pointers types as `[*c]` |
| 221 | +pointers, which is less than ideal. A long-term goal for zigglgen is for every single pointer type to be correctly |
| 222 | +annotated. There are approximately 3300 commands defined in the API registry and if we work together, we can achieve |
| 223 | +that goal sooner. Even fixing up just a few commands would mean a lot! |
| 224 | + |
| 225 | +Overriding parameters/return types is very easy; all you need to do is add additional entries to the |
| 226 | +`paramOverride`/`returnTypeOverride` functions in [`zigglgen/generator.zig`](`zigglgen/generator.zig`), then open a pull |
| 227 | +request with your changes (bonus points if you also reference relevant OpenGL references page or specifications in the |
| 228 | +description of your pull request). |
| 229 | + |
| 230 | +## License |
| 231 | + |
| 232 | +zigglgen is licensed under the [MIT License](LICENSE.md). |
| 233 | + |
| 234 | +See [`zigglgen/THIRD-PARTY-NOTICES.txt`](zigglgen/THIRD-PARTY-NOTICES.txt) for third-party license notices. |
0 commit comments