Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide low-level API to raylib without automatic freeing (WindowResources) #55

Closed
oshyshko opened this issue May 15, 2024 · 5 comments
Closed

Comments

@oshyshko
Copy link

oshyshko commented May 15, 2024

It's enjoyable to have a raylib API in Haskell that takes care about Foreign stuff (pointers and marshaling), so one doesn't have to.

However, at the moment h-raylib enforces a particular memory management approach that may be not good for certain cases or favorable by some programmers. It would be nice for a library user programmer have a choice whether to use raylib without WindowResources facility.

Here is a great article on different levels of APIs Haskell (please, note that it's HTTP, you might need to instruct the browser to render it):
http://blog.haskell-exists.com/yuras/posts/a-begginers-guide-to-over-engineering.html

Note, how the API of the lowest level allows building higher level APIs on top of it, but not vice versa.

Please, note that raylib philosophy is about simplicity and not enforcing things, but rather leaving a door open to have them.

Low-level API with Closeable

Here's an example of a low API that allows automatic resource freeing, but can be opted out if not wanted:

with :: Closeable a => IO a -> (a -> IO b) -> IO b
with = flip bracket close

class                     Closeable a       where close :: a -> IO ()
instance                  Closeable Image   where close = ...
instance                  Closeable Texture where close = ...
instance                  Closeable Font    where close = ...
instance (Closeable c) => Closeable [c]     where close = mapM_ close

User programmers can decide how they want to write:

t <- openTexture "1.png"
...
close t

...or...

with (openText "1.png") $ \t -> do
  ...

...or something else.

With this design, user programmers can opt-out from using bracket/with and use something like resourcet or managed, if they want (but let's not focus on these approaches here).

Another important thing: user programmers are not forced to pass WindowsResources to every API call -- especially if they favor other resource-handling techniques such as with/close.

If a user programmer want, he can still implement WindowResources approach in their code on top of low-level API and track all the resources (e.g. this can be useful for runtime introspection). Also, he may go even further and push the WindowResources to a Reader monad, so he doesn't have to carry it around -- that's up to the user programmer.

@Anut-py
Copy link
Owner

Anut-py commented May 15, 2024

h-raylib exposes a native version of every function, just prepend it with c' (e.g. c'initWindow). This uses raw Ptrs and no automatic memory management, I think this is what you're looking for. To free the resources from CPU but not GPU, you can use rlFree from the Freeable typeclass. To free the resources from both CPU and GPU, you can use c'unload* (e.g. c'unloadImage), then Foreign.Marshall.Alloc.free.

@oshyshko
Copy link
Author

oshyshko commented May 15, 2024

Thank you for your answer.

What I would really like to have (as a user programmer) is these functions without WindowResources arguments and add* tracking functionality in them.
https://github.com/Anut-py/h-raylib/blob/master/src/Raylib/Core/Textures.hs#L650-L673

...and have a single close function to free things instead of 2 or 3 calls (which is error-prone and context-switching for user programmer -- I would very much prefer not to keep in my head which ones should freed from CPU, GPU or something else -- just closing/freeing with one call).

Sure, it could be doable just to copy-paste all such functions (there's around 100 of them) into my module and then remove WindowResources and addX from all. Not quite good, but doable.

But it's also not possible, because these fns depend on helpers from module Raylib.Internal.Foreign such as withFreeable, pop, popCArray, popCString -- they are hidden.

Is there a better way?

@Anut-py
Copy link
Owner

Anut-py commented May 15, 2024

I'll have to think of a good solution for this. I might make a Closeable typeclass as you suggested.

@oshyshko
Copy link
Author

oshyshko commented May 15, 2024

Great! It would be really great to stay away from pointers and Foreign.

It seems like an extract refactoring of from the existing functions (the ones tracking via WindowResources) -- so it should possible to re-use new fns back from where they extracted + to have a new API.

Things come to my mind such as Raylib.Closeable module or, of if without a module, fns like openImageCloseable, openFontCloseable next to existing ones.

@Anut-py
Copy link
Owner

Anut-py commented Jul 12, 2024

Done in 5.5.0.0, reopen this issue if there are any problems

@Anut-py Anut-py closed this as completed Jul 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants