|
Am 27.04.2014 08:46 schröbte David Demelier:
On 26/04/2014 16:41, Philipp Janda wrote:myTexture:with_lock( function( modifypixels ) -- code using the modifypixels function ... end ) but in the end I simply didn't implement direct access to pixel data in my binding (it has a rather narrow focus). `with_lock` would `pcall` (not `pcallk`![*]) the given function and pass a closure that has access to the pixel data and the pitch as argument. The Lua code would only access the pixel data via that function. After the `pcall` returns the upvalues of `modifypixels` get nil'ed using `lua_setupvalue` (for safety), and `SDL_UnlockTexture` is called before `with_lock` returns ...That's a really interesting way of doing it :-).
It should solve the problem of giving Lua code safe access to a void pointer with non-contiguous memory, the problem that a reference to the void pointer (or the `modifypixels` function) might escape, and the problem to make sure that `SDL_UnlockTexture` is called in any case.
I don't know if that's intended, but your binding is rather low-level: It can crash in certain situations (e.g. if a renderer is collected before its textures, or window before renderer, etc.), and it would be easy to call `SDL_Quit` automatically via garbage-collection so that the binding doesn't leak if there is a non-local exit (e.g. an error, or a yielded coroutine that gets collected). Another minor point: If `lua_newuserdata` ever raises an error you will leak the corresponding SDL object.Ah I didn't know that the program crash is a renderer is destroyed before textures. But I can easily fix that by keeping references until the renderer is destroyed it "owns" a reference to the texture and may not be collected. (It's also done in the SDL_net Set object which "owns" sockets).
Yes, that's what I did (I have an extra function in my Lua helper toolkit that constructs an object with an env table and puts the required reference in there). The SDL2 documentation is not very clear about this and I usually had to look at the source code, but I can remember the following: * a renderer accesses its window in `SDL_DestroyRenderer`, so the window has to live longer. * a renderer destroys all its remaining textures in `SDL_DestroyRenderer`, so it has to live longer than any of its textures (or else you get double frees and/or dangling pointers). * a window should not be collected before the surface returned by `SDL_GetWindowSurface`, or you have a dangling pointer. * `SDL_Quit` destroys all remaining windows (and probably other required data behind the scenes), so exposing this function may leave dangling references in Lua code.
There may be others, my binding isn't nearly as comprehensive as yours (in particular I left out most of the surface stuff), and I only looked up what I needed ...
Yes, the binding is rather low-level, because I wanted it to be a SDL binding and not a game engine :-). However it is still a bit higher level than C with the object orientation and some other convenient stuff.
And it needs to be. Lua has features that more or less require automatic resource cleanup (e.g. closures, errors, yields). Therefore, doing manual management correctly is harder in Lua than it is in C (which is why I flinch every time when someone uses lightuserdata instead of full userdata for object references). So you really should call `SDL_Quit` and the other cleanup functions automatically instead of requiring your users to keep track of the control flow. I usually put an `int` userdata with a custom `__gc` function in the registry. The `__gc` calls the cleanup function when the `int` is >= 1. After successful initialization, I set that userdata to 1, and forget about it ...
Philipp