-
-
Notifications
You must be signed in to change notification settings - Fork 651
Loading function pointers
In the following we will use the process of grabbing OpenGL functions from the graphics driver as an example, but the same process can be generalized to any other function (or, procedure) pointer loading.
We have three options on how to fetch an OpenGL function pointer from the driver. If I want to use the procedure glfw.GetProcAddress
, then
// assigning directly, but casting the result of a glfw.GetProcAddressprocedure call
CullFace = cast(proc"c"(u32))glfw.GetProcAddress("glCullFace")
// passing a pointer to the procedure variable to a set_proc_address procedure, but interpreting it as a rawptr.
set_proc_address :: proc(p: rawptr, name: cstring) {
(cast(^rawptr)p)^ = glfw.GetProcAddress(name)
}
set_proc_address(&CullFace, "glCullFace")
// passing a pointer to the procedure variable to a set_proc_address procedure, but interpreting it as a ^rawptr
set_proc_address :: proc(p: ^rawptr, name: cstring) {
p^ = glfw.GetProcAddress(name)
}
set_proc_address(cast(^rawptr)&CullFace, "glCullFace")
Modern OpenGL functions has to be loaded at runtime from the dynamic/shared library supplied by your driver (opengl32.dll
in Windows and libGL.so
in Linux). To use them we need to declare all OpenGL functions we want to use as function pointers, then we call the appropriate OS API to fetch the function pointers from driver at runtime. The last part is typically done by calling a function like wglGetProcAddress
(in Windows), or glXGetProcAddress
(in Linux), or an OS wrapper function like glfwGetProcAddress
with the function name as a string for input.
Consider the first function listed in gl.odin, glCullFace
. It is declared as:
CullFace: proc "c" (mode: u32)
and is simply just a procedure variable (equivalent to a function pointer in C).
Say I wanted to use glfw.GetProcAddress
to grab the function pointers from the driver. I could then assign to the CullFace
variable like
CullFace = cast(proc"c"(u32))glfw.GetProcAddress("glCullFace")
But this is a bit unwieldy to handle for a huge library such as gl.odin (a lot of extra typing for the explicit casting). Instead we can pass a pointer to the procedure variable as a parameter to a set_proc_address
procedure that sets it for us
set_proc_address :: proc(p: rawptr, name: cstring) {
(cast(^rawptr)p)^ = glfw.GetProcAddress(name)
}
set_proc_address(&CullFace, "glCullFace")
where the casting of p
to a pointer reflects that the input to the set_proc_address
is a pointer to a function pointer!
The reason one makes the p
parameter a rawptr
instead of a pointer to a rawptr
is that in that case one would have to cast upon calling set_proc_address
, which looks worse.
set_proc_address :: proc(p: ^rawptr, name: cstring) {
p^ = glfw.GetProcAddress(name)
}
set_proc_address(cast(^rawptr)&CullFace, "glCullFace")
Every pointer type can be assigned to a rawptr
variable, but not a ^rawptr
(which is not a rawptr
!).
Depending on what you want to do, you might prefer one approach over the other. If you want to fetch pointers in bulk, you might prefer the second option. If you want to only fetch a select few you might prefer the first option.