Skip to content

Commit a05e7a5

Browse files
committed
Initial commit
0 parents  commit a05e7a5

17 files changed

+69079
-0
lines changed

.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 4
6+
tab_width = 8
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
zig-cache/
2+
zig-out/

LICENSE.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# MIT License
2+
3+
Copyright © 2024 Carl Åstholm
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8+
persons to whom the Software is furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
11+
the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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.

zigglgen-example/build.zig

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const std = @import("std");
2+
const zigglgen = @import("zigglgen");
3+
4+
pub fn build(b: *std.Build) void {
5+
const target = b.standardTargetOptions(.{});
6+
const optimize = b.standardOptimizeOption(.{});
7+
8+
const mach_glfw_dep = b.dependency("mach-glfw", .{
9+
.target = target,
10+
.optimize = optimize,
11+
});
12+
13+
const exe = b.addExecutable(.{
14+
.name = "zigglgen-example",
15+
.root_source_file = .{ .path = "main.zig" },
16+
.target = target,
17+
.optimize = optimize,
18+
});
19+
exe.subsystem = .Windows;
20+
21+
exe.root_module.addImport("glfw", mach_glfw_dep.module("mach-glfw"));
22+
exe.root_module.addImport("gl", zigglgen.generateBindingsModule(b, .{
23+
.api = .gl,
24+
.version = .@"4.1",
25+
.profile = .core,
26+
.extensions = &.{ .ARB_clip_control, .NV_scissor_exclusive },
27+
}));
28+
29+
b.installArtifact(exe);
30+
31+
const run_exe = b.addRunArtifact(exe);
32+
run_exe.step.dependOn(b.getInstallStep());
33+
34+
const run_step = b.step("run", "Run the app");
35+
run_step.dependOn(&run_exe.step);
36+
}

zigglgen-example/build.zig.zon

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.{
2+
.name = "zigglgen-example",
3+
.version = "0.0.0",
4+
.dependencies = .{
5+
.@"mach-glfw" = .{
6+
.url = "https://pkg.machengine.org/mach-glfw/63da35e57c3695a787b927e69e4140eb74ffc234.tar.gz",
7+
.hash = "1220fda87b59df6353864651cdd3943f1159d8d517a45cd67bed493fe6c0a0c78c17",
8+
},
9+
.zigglgen = .{
10+
.path = "../zigglgen",
11+
},
12+
},
13+
.paths = .{
14+
"build.zig",
15+
"build.zig.zon",
16+
"main.zig",
17+
},
18+
}

zigglgen-example/main.zig

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const std = @import("std");
2+
const glfw = @import("glfw");
3+
const gl = @import("gl");
4+
5+
var gl_procs: gl.ProcTable = undefined;
6+
7+
pub fn main() !void {
8+
if (!glfw.init(.{})) return error.InitFailed;
9+
defer glfw.terminate();
10+
11+
const window = glfw.Window.create(640, 480, "OpenGL is a art", null, null, .{
12+
.context_version_major = gl.info.version_major,
13+
.context_version_minor = gl.info.version_minor,
14+
.opengl_profile = .opengl_core_profile,
15+
.opengl_forward_compat = true,
16+
}) orelse return error.InitFailed;
17+
defer window.destroy();
18+
19+
glfw.makeContextCurrent(window);
20+
defer glfw.makeContextCurrent(null);
21+
22+
if (!gl_procs.init(glfw.getProcAddress)) return error.InitFailed;
23+
24+
gl.makeProcTableCurrent(&gl_procs);
25+
defer gl.makeProcTableCurrent(null);
26+
27+
glfw.swapInterval(1);
28+
29+
main_loop: while (true) {
30+
glfw.pollEvents();
31+
if (window.shouldClose()) break :main_loop;
32+
33+
gl.Disable(gl.SCISSOR_TEST);
34+
if (gl.extensionSupported(.NV_scissor_exclusive)) {
35+
gl.Disable(gl.SCISSOR_TEST_EXCLUSIVE_NV);
36+
gl.ClearColor(1, 0.8, 0.2, 1);
37+
gl.Clear(gl.COLOR_BUFFER_BIT);
38+
gl.Enable(gl.SCISSOR_TEST_EXCLUSIVE_NV);
39+
gl.ScissorExclusiveNV(72, 56, 8, 8);
40+
}
41+
gl.ClearColor(1, 1, 1, 1);
42+
gl.Clear(gl.COLOR_BUFFER_BIT);
43+
gl.Enable(gl.SCISSOR_TEST);
44+
const magic: u256 = 0x1FF8200446024F3A8071E321B0EDAC0A9BFA56AA4BFA26AA13F20802060401F8;
45+
var i: gl.int = 0;
46+
while (i < 256) : (i += 1) {
47+
if (magic >> @intCast(i) & 1 != 0) {
48+
gl.Scissor(@rem(i, 16) * 8 + 8, @divTrunc(i, 16) * 8 + 8, 8, 8);
49+
gl.ClearColor(0, 0, 0, 1);
50+
gl.Clear(gl.COLOR_BUFFER_BIT);
51+
}
52+
}
53+
54+
window.swapBuffers();
55+
}
56+
}

zigglgen/LICENSE.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# MIT License
2+
3+
Copyright © 2024 Carl Åstholm
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8+
persons to whom the Software is furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
11+
the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0 commit comments

Comments
 (0)