glfw: use comptime magic to remove InternalUserPointer and associated overhead #162

Merged
leecannon merged 1 commit from no_hidden_allocation into main 2022-02-11 22:13:58 +00:00
leecannon commented 2022-02-09 23:56:26 +00:00 (Migrated from github.com)

This PR changes the way glfw callback functions are implemented.

Improvements

Before
Inside Window.zig from there is an allocation of a InternalUserPointer with an allocator not explicitly provided by the caller.

After
No hidden allocation anywhere. (atleast in the zig wrapper's code)

Before
Due to using the user pointer functionality provided by glfw to carry around function callbacks, the API to work with the end users actual user pointer generates more code.

After
getUserPointer & setUserPointer are trivial passthrough functions.

Before
When an event occurs that the user has set a callback for the below takes place before the users callback code is reached:

  • call through a runtime function pointer from glfw to a callback wrapper
  • call back into glfw to get the userPointer and check if its null (Window.zig from)
  • null check on the user provided function pointer
  • cast/transform the c callbacks parameters into nice zig ones
  • call through a runtime function pointer from the callback wrapper to the users callback function

After
When an event occurs that the user has set a callback for the below takes place before the users callback code is reached:

  • call through a runtime function pointer from glfw to the users callback function
  • cast/transform the c callbacks parameters into nice zig ones

There is no second call through a runtime function pointer due to a very nice thing about functions that are taken as comptime arguments, they are statically determined at compile time and calling them results in the same codegen as calling a function directly. Due to this we can even force the function to be fully inlined essentially turning the c wrapper function into a prelude of the users callback function with no additional function call overhead.

API Change

The change to take the functions as comptime arguments does prevent runtime known funcion pointers from being provided, however in almost every situation a truly runtime function pointer is rare.
The most common reason a function pointer is runtime known is due to deciding between different implementations based on a runtime value, however this can easily work with the new API:

switch (runtime_value) {
    .one => window.setPosCallback(posCallbackOne),
    .two => window.setPosCallback(posCallbackTwo),
    .three => window.setPosCallback(posCallbackThree),
}

Or the user could decide to install a callback that calls a runtime known function pointer, this would be very similar to how the current API is implemented, however the choice to do this would be in the users hands.

  • By selecting this checkbox, I agree to license my contributions to this project under the license(s) described in the LICENSE file, and I have the right to do so or have received permission to do so by an employer or client I am producing work for whom has this right.
This PR changes the way glfw callback functions are implemented. ## Improvements **Before** Inside Window.zig `from` there is an allocation of a `InternalUserPointer` with an allocator not explicitly provided by the caller. **After** No hidden allocation anywhere. (atleast in the zig wrapper's code) **Before** Due to using the user pointer functionality provided by glfw to carry around function callbacks, the API to work with the end users actual user pointer generates more code. **After** `getUserPointer` & `setUserPointer` are trivial passthrough functions. **Before** When an event occurs that the user has set a callback for the below takes place before the users callback code is reached: - call through a runtime function pointer from glfw to a callback wrapper - call back into glfw to get the userPointer and check if its null (Window.zig `from`) - null check on the user provided function pointer - cast/transform the c callbacks parameters into nice zig ones - call through a runtime function pointer from the callback wrapper to the users callback function **After** When an event occurs that the user has set a callback for the below takes place before the users callback code is reached: - call through a runtime function pointer from glfw to the users callback function - cast/transform the c callbacks parameters into nice zig ones There is no second call through a runtime function pointer due to a very nice thing about functions that are taken as comptime arguments, they are statically determined at compile time and calling them results in the same codegen as calling a function directly. Due to this we can even force the function to be fully inlined essentially turning the c wrapper function into a prelude of the users callback function with no additional function call overhead. ## API Change The change to take the functions as comptime arguments does prevent runtime known funcion pointers from being provided, however in almost every situation a truly runtime function pointer is rare. The most common reason a function pointer is runtime known is due to deciding between different implementations based on a runtime value, however this can easily work with the new API: ```zig switch (runtime_value) { .one => window.setPosCallback(posCallbackOne), .two => window.setPosCallback(posCallbackTwo), .three => window.setPosCallback(posCallbackThree), } ``` Or the user could decide to install a callback that calls a runtime known function pointer, this would be very similar to how the current API is implemented, however the choice to do this would be in the users hands. - [X] By selecting this checkbox, I agree to license my contributions to this project under the license(s) described in the LICENSE file, and I have the right to do so or have received permission to do so by an employer or client I am producing work for whom has this right.
InKryption commented 2022-02-10 00:00:14 +00:00 (Migrated from github.com)

I like this change. I squinted at this part of the library for a bit, but never thought of a solution like this, aside from maybe allowing the user to provide an allocator.

I like this change. I squinted at this part of the library for a bit, but never thought of a solution like this, aside from maybe allowing the user to provide an allocator.
emidoots (Migrated from github.com) approved these changes 2022-02-11 22:13:47 +00:00
emidoots (Migrated from github.com) left a comment

Thanks so much for this, this looks incredible. Super nice solution! I wouldn't have thought of this :)

Thanks so much for this, this looks incredible. Super nice solution! I wouldn't have thought of this :)
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
hexops/mach!162
No description provided.