1. sdl2 and ffi
- Posted by ChrisB (moderator) Nov 14, 2022
- 3069 views
Hi Andy
What are you doing here?
18 object event = allocate_struct(event)
Cheers
Chris
2. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 14, 2022
- 3002 views
Whats the ffi.e equivalent of allocate, as in
atom rect = allocate(16)
poke4(rect,{50,50,100,100})
Cheers
Chris
3. Re: sdl2 and ffi
- Posted by ghaberek (admin) Nov 14, 2022
- 2988 views
What are you doing here?
18 object event = allocate_struct(event)
I'm not sure that going to work. I had discussed the current "correct" approach here: https://openeuphoria.org/forum/137153.wc
Whats the ffi.e equivalent of allocate, as in
atom rect = allocate(16)
poke4(rect,{50,50,100,100})
The prototype currently works like this:
-- RECT structure (windef.h) -- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect constant C_RECT = define_c_type({ C_LONG, -- left C_LONG, -- top C_LONG, -- right C_LONG -- bottom }) atom rect = allocate_struct( C_RECT ) poke_type( rect, C_RECT, {50,50,100,100} )
But I'd like to rename define_c_type() to define_c_struct() and then add define_c_union(), and then re-implement define_c_type() for "raw" types (like C_STRING as a copy of C_POINTER).
-- RECT structure (windef.h) -- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect constant C_RECT = define_c_struct({ {C_LONG,"left"}, {C_LONG,"top"}, {C_LONG,"right"}, {C_LONG,"bottom"} }, "RECT") atom rect = allocate_struct( C_RECT, {50,50,100,100} ) ? peek_member( rect, "RECT.left" ) -- prints 50 ? peek_member( rect, "RECT" ) -- prints {50,50,100,100}
Thoughts/opinions on this design are welcome.
-Greg
4. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 14, 2022
- 2961 views
What are you doing here?
18 object event = allocate_struct(event)
I'm not sure that going to work. I had discussed the current "correct" approach here: https://openeuphoria.org/forum/137153.wc
Whats the ffi.e equivalent of allocate, as in
atom rect = allocate(16)
poke4(rect,{50,50,100,100})
The prototype currently works like this:
-- RECT structure (windef.h) -- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect constant C_RECT = define_c_type({ C_LONG, -- left C_LONG, -- top C_LONG, -- right C_LONG -- bottom }) atom rect = allocate_struct( C_RECT ) poke_type( rect, C_RECT, {50,50,100,100} )
But I'd like to rename define_c_type() to define_c_struct() and then add define_c_union(), and then re-implement define_c_type() for "raw" types (like C_STRING as a copy of C_POINTER).
-- RECT structure (windef.h) -- https://learn.microsoft.com/en-us/windows/win32/api/windef/ns-windef-rect constant C_RECT = define_c_struct({ {C_LONG,"left"}, {C_LONG,"top"}, {C_LONG,"right"}, {C_LONG,"bottom"} }, "RECT") atom rect = allocate_struct( C_RECT, {50,50,100,100} ) ? peek_member( rect, "RECT.left" ) -- prints 50 ? peek_member( rect, "RECT" ) -- prints {50,50,100,100}
Thoughts/opinions on this design are welcome.
-Greg
Hmm, that'll take some digesting, but my initial thought is that it needs to be as simple as possible to fit the eu ethos, so that someone like me can make stuff with the simpler stuff, and someone like you can do the more brain strangling spaghetti stuff.
so,
rect = allocate(16) allocates a block of memory of 16 bytes poke4(rect,{50,50,100,100}) - poke 4 4-byte integers into memory whose location begins at rect
replace by
create a structure containing the following memory elements
constant C_RECT = define_c_type({ --this c_type is made up of 4 C_LONGs C_LONG, -- left --could be any other mix of c_types C_LONG, -- top --even other pointers to strings C_LONG, -- right C_LONG -- bottom })\\
then
atom rect = allocate_struct( C_RECT ) --allocate a memory block of the size required to hold the c_type C_RECT poke_type( rect, C_RECT, {50,50,100,100} ) poke type( rect - pointer to where the memory is located C_RECT - the definition of the structure elements {50,50,100,100} - what to poke into each of those memory elements.
and you could access that structure in the same way with peek as before
peek4u(rect) (= 50) peek4u(rect + 12) (= 100) peek4u({rect,4}) (= {50,50,100,100} )
or is there a newer way to do that too?
Then you add define_c_struct to short circuit the above process
constant C_RECT = define_c_struct({ C_RECT is the structure defined {C_LONG,"left"}, these are the elements of the structure {C_LONG,"top"}, they are named for convenience of access {C_LONG,"right"}, {C_LONG,"bottom"} }, "RECT") this is the variable we are defining with }}} the structure of C_RECT Now we are going to create a block of memory that will allow the above structure to live in it {{{ atom rect = allocate_struct( C_RECT, {50,50,100,100} )which we could still access with
peek4u(rect + 12) (= 100) peek4u({rect,4}) (= {50,50,100,100} )
or
? peek_member( rect, "RECT.left" ) -- prints 50 ? peek_member( rect, "RECT" ) -- prints {50,50,100,100}
I can see the advantages, even though it looks more messy to me, and may not be intermediate level concept wise. However, I also like the simpler version, so
define_c_type
allocate structure
is simpler than
define_c_struct
allocate_struct
Both methods produce the same end results, the second method has more frills, which IMHO increase the complexity. It's a shame you have to wrap the definitions in quotes.
If a union is an amalgamation of single items and structures, I take it it would look something like this (in simple form)
C_RECT already defined as above constant C_THING = define_c_union { C_LONG, C_LONG, C_POINTER, C_RECT, --pointer to the C_RECT structure } thing = allocate_union(C_THING)
Just my confused thoughts.
Chris
5. Re: sdl2 and ffi
- Posted by ghaberek (admin) Nov 14, 2022
- 2930 views
Hmm, that'll take some digesting, but my initial thought is that it needs to be as simple as possible to fit the eu ethos, so that someone like me can make stuff with the simpler stuff, and someone like you can do the more brain strangling spaghetti stuff.
so,
rect = allocate(16) allocates a block of memory of 16 bytes poke4(rect,{50,50,100,100}) - poke 4 4-byte integers into memory whose location begins at rect
replace by
create a structure containing the following memory elements
constant C_RECT = define_c_type({ C_LONG, -- left C_LONG, -- top C_LONG, -- right C_LONG -- bottom })\\
then
atom rect = allocate_struct( C_RECT ) --allocate a memory block of the size required to hold the c_type C_RECT poke_type( rect, C_RECT, {50,50,100,100} ) poke type( rect - pointer to where the memory is located C_RECT - the definition of the structure elements {50,50,100,100} - what to poke into each of those memory elements.
Yes that's what I'm going for. I originally called it define_c_type() because that's basically what I want: to define additional C types, be they "int" or "float" or structures and unions. That way we're extending the existing design of define_c_func/proc() and the new structure/union "types" will work there as well, almost transparently.
and you could access that structure in the same way with peek as before
peek4u(rect) (= 50) peek4u(rect + 12) (= 100) peek4u({rect,4}) (= {50,50,100,100} )
You're not wrong; you can certainly do that. But there's a lot of potential for error here, especially with the addition of 64-bit Euphoria and many more peek/poke routines like peek8u() and peek_pointer(). IMHO the ethos of Euphoria is to help reduce this kind of confusion. Why should you have to worry about the size of those structure members?
or is there a newer way to do that too?
That's where peek_type() and poke_type() come in: these routines will handle the nonsense of "is it four or eight bytes? signed? floating-point?" and just dispatch to the correct peek/poke for you, and it will do so correctly based on the platform (not all C_LONG are four bytes).
Then you add define_c_struct to short circuit the above process
constant C_RECT = define_c_struct({ {C_LONG,"left"}, {C_LONG,"top"}, {C_LONG,"right"}, {C_LONG,"bottom"} }, "RECT")
Now we are going to create a block of memory that will allow the above structure to live in it
atom rect = allocate_struct( C_RECT, {50,50,100,100} )which we could still access with
peek4u(rect + 12) (= 100) peek4u({rect,4}) (= {50,50,100,100} )
That's the gist of it. Although minding all of those "+ 12" offsets gets very messy when we have to support 32-bit and 64-bit Intel/AMD architecture, let alone that ARM processors are more common and those could introduce differences as well. Using libffi to help do some of this heavy lifting means we're building on literal decades of experience.
or
? peek_member( rect, "RECT.left" ) -- prints 50 ? peek_member( rect, "RECT" ) -- prints {50,50,100,100}
I can see the advantages, even though it looks more messy to me, and may not be intermediate level concept wise. However, I also like the simpler version, so
define_c_type
allocate structure
is simpler than
define_c_struct
allocate_struct
Both methods produce the same end results, the second method has more frills, which IMHO increase the complexity. It's a shame you have to wrap the definitions in quotes.
This is where feedback helps the most. I'm trying to reduce complexity, not add to it. Is peek_type( ptr, C_TYPE ) or peek_member( ptr, "TYPE.name" ) really more complex than peek4u( ptr + 12 )? and what does 12 mean? it might have to be 16 or 20 on 64-bit, right? who knows! So I'm basically going back to the drawing board on what Matt had original mostly-built for memstruct. He was doing all of these sizes-and-offsets by hand. Plus it seems a bit out of order to implement memstruct now with out a better plan for how classes will work later, since they might utilize a lot of the same type-sniffing code.
If a union is an amalgamation of single items and structures, I take it it would look something like this (in simple form)
C_RECT already defined as above constant C_THING = define_c_union { C_LONG, C_LONG, C_POINTER, C_RECT, --pointer to the C_RECT structure } thing = allocate_union(C_THING)
This is probably where having named members helps the most. Otherwise you're really shooting in the dark with magic numbers. Basically a union lets you perform bit-twiddling by converting a chunk of memory into different types. I don't know about you but to me, remembering what something like peek_type( ptr, C_THING, 1 ) does is going to be hard if not impossible.
Just my confused thoughts.
I think you're doing great, Chris.
-Greg
6. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 14, 2022
- 2911 views
Already helped with converting more demos to SDL2FFI
sdl_render.e
line 387
public function SDL_RenderFillRect(atom ren,sequence rect)
should be
public function SDL_RenderFillRect(atom ren, atom rect)
Cheers
Chris
7. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 14, 2022
- 2933 views
Includes missing in sdl.e
public include SDL_keyboard.e
public include SDL_scancode.e
Cheers
Chris
8. Re: sdl2 and ffi
- Posted by Icy_Viking Nov 14, 2022
- 2918 views
I like the idea of having define_c_struct() and define_c_union(), I think that would help simplify things.
Would it be possible to do something like this, eventually:
public constant MY_STRUCT = define_c_struct({ C_INT, --x C_INT, --y C_INT, --w C_INT --h, MY_UNION = define_c_union({ C_FLOAT, --xx C_FLOAT, --yy }) --end of union }) --end of struct
The SDL 2 wrapper using FFI is still a work in progress and can be fixed and updated. It works for the most part, but it could use some tidying up here and there.
9. Re: sdl2 and ffi
- Posted by SDPringle Nov 16, 2022
- 2701 views
You guys are making cool stuff! The data structures that must exist could open the door to making validating data as well.
-- you could write a function like this: is_struct(RECT, x) -- returns true iff x has four members and each member falls within the range of the respective c types type rect(object x) return is_struct(RECT, x) end type
10. Re: sdl2 and ffi
- Posted by ghaberek (admin) Nov 16, 2022
- 2670 views
You guys are making cool stuff! The data structures that must exist could open the door to making validating data as well.
-- you could write a function like this: is_struct(RECT, x) -- returns true iff x has four members and each member falls within the range of the respective c types type rect(object x) return is_struct(RECT, x) end type
That would help when storing structure values in a sequence but we can't do much about inferring a structure's type while in memory. A structure of int[4] could look the same as short[8] or char[16] and we wouldn't be able to tell by just looking at it. (This is how unions work and it's what allows for things like type punning). I'm still considering how to go about this for implementing structs and classes later. Suggestions are welcome.
-Greg
11. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 17, 2022
- 2476 views
FAO Andy
Hi
You've defined SDL_surface in SDL_surface.e as a define_c_type, and then
export constant xSDL_GetWindowSurface = define_c_func(sdl,"+SDL_GetWindowSurface",{C_POINTER},SDL_Surface) public function SDL_GetWindowSurface(atom win) return c_func(xSDL_GetWindowSurface,{win}) end function
the function SDL_GetWindowSurface returns a pointer to the SDL_surface, not the actual structure itself, so eu is throwing type errors.
atom surf = SDL_GetWindowSurface(win)
D:\EuProgramming\Euphoria4_1\SDL2Wrapper\Examples\WinImage.exw:28 type_check failure, surf is {7195552,{36'$',{1952061344,{298874067,2684419766,...
So, am I missing a step somewhere?
Cheers
Chris
12. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 17, 2022
- 2465 views
FAO Greg
Andy's simplewin demo works when you do this
--object event = allocate_struct(event) --this is the missing allocate_mem object event = machine_func(16, 1024) --16 is M_ALLOC
I know that event is a union, and you can't access the union part of the event (not without lots of pointers referincing pointers spaghetti at least), but the first part is a structure, and you can access those.
Could I request an allocate_mem procedure in ffi, to allow for those rare events when users might want to allocate there own blocks of memory that don't fit into other types of memory allocation?
And....make your M_ALLOC, M_FREE etc public
Cheers Chris
13. Re: sdl2 and ffi
- Posted by Icy_Viking Nov 17, 2022
- 2400 views
FAO Andy
Hi
You've defined SDL_surface in SDL_surface.e as a define_c_type, and then
export constant xSDL_GetWindowSurface = define_c_func(sdl,"+SDL_GetWindowSurface",{C_POINTER},SDL_Surface) public function SDL_GetWindowSurface(atom win) return c_func(xSDL_GetWindowSurface,{win}) end function
the function SDL_GetWindowSurface returns a pointer to the SDL_surface, not the actual structure itself, so eu is throwing type errors.
atom surf = SDL_GetWindowSurface(win)
D:\EuProgramming\Euphoria4_1\SDL2Wrapper\Examples\WinImage.exw:28 type_check failure, surf is {7195552,{36'$',{1952061344,{298874067,2684419766,...
So, am I missing a step somewhere?
Cheers
Chris
I've changed it from SDL_Surface to C_POINTER. I've uploaded the change to github. I also like the allocate_mem feature that could be added to the FFI wrapper/library.
14. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 17, 2022
- 2419 views
Thanks
slightly change allocate_string in ffi
public function allocate_string( sequence s, integer cleanup = 0 ) atom mem = machine_func( M_ALLOC, length(s) + 1 ) if mem then poke( mem, s ) poke( mem+length(s), 0 ) end if if cleanup then end if return mem end function
This brings it more in line with eu's machin.e allocate_string(). You'll note the cleanup is basically just a stub.
Note, I'm just getting them to work atm, don't know if this is the best way.
Cheers
Chris
15. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 17, 2022
- 2390 views
SDL_render.e
line 116
public function SDL_CreateTextureFromSurface(atom ren, sequence surf)
->
public function SDL_CreateTextureFromSurface(atom ren, atom surf)
Cheers Chris
16. Re: sdl2 and ffi
- Posted by Icy_Viking Nov 17, 2022
- 2365 views
SDL_render.e
line 116
public function SDL_CreateTextureFromSurface(atom ren, sequence surf)
->
public function SDL_CreateTextureFromSurface(atom ren, atom surf)
Cheers Chris
I have fixed this and uploaded it to Github.
17. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 18, 2022
- 2304 views
Hi Andy
Did you just replace that one instance? There are quite a few others. All surf s and rect s need to be atoms, not sequences, because all of the sdl C functions, afaict, need to have pointers sent to them, and you are trying to send a type. I may be missing something of course.
I've just recloned the repository, and it's wiped all the changes and test files I made on my local folder. Ho hum, back to the drawing board.
Cheers
Chris
edit - I see that define_c_type returns an atom, which I imagine is a pointer to the block of memory allocated for the structure. The structure may need to be filled to be sent, or may be filled by the C function, but on the eu side the function still need to recieve pointers (atoms) to send to the C function of the block of memory allocated.
Cheers
Chris
18. Re: sdl2 and ffi
- Posted by ghaberek (admin) Nov 18, 2022
- 2281 views
I've just recloned the repository, and it's wiped all the changes and test files I made on my local folder. Ho hum, back to the drawing board.
Oh no! Here's nice guide from the R community on how to avoid that: https://happygitwithr.com/pull-tricky.html Basically you use git stash to save your changes then git pull to pull updates then git stash pop to put back your changes.
I see that define_c_type returns an atom, which I imagine is a pointer to the block of memory allocated for the structure. The structure may need to be filled to be sent, or may be filled by the C function, but on the eu side the function still need to recieve pointers (atoms) to send to the C function of the block of memory allocated.
This is correct. The value returned from define_c_type() are pointers to ffi_type structures. When using std/ffi.e the values of C_INT, C_POINTER, etc. are also pointers. The only difference being the built-in values from std/ffi.e are pulled directly from libffi using define_c_var().
The crucial point being that you still need to allocate memory and then peek/poke values accordingly. Not only should you avoid passing the values you made with define_c_type() to c_func(), but doing so could overwrite the memory at that location which could cause any number of weird problems later.
-Greg
19. Re: sdl2 and ffi
- Posted by Icy_Viking Nov 18, 2022
- 2203 views
Hi Andy
Did you just replace that one instance? There are quite a few others. All surf s and rect s need to be atoms, not sequences, because all of the sdl C functions, afaict, need to have pointers sent to them, and you are trying to send a type. I may be missing something of course.
I've just recloned the repository, and it's wiped all the changes and test files I made on my local folder. Ho hum, back to the drawing board.
Cheers
Chris
edit - I see that define_c_type returns an atom, which I imagine is a pointer to the block of memory allocated for the structure. The structure may need to be filled to be sent, or may be filled by the C function, but on the eu side the function still need to recieve pointers (atoms) to send to the C function of the block of memory allocated.
Cheers
Chris
I'll try and go through the code and update it, this weekend. I think my inital thought process must have been from looking at how Greg wrapped Raylib using FFI.
--Raylib Wrapper (FFI) public constant RL_VECTOR2 = define_c_type({ C_FLOAT, -- x // Vector x component C_FLOAT -- y // Vector y component }) export constant raylib = open_dll( "raylib.dll" ), xInitWindow = define_c_proc( raylib, "+InitWindow", {C_INT,C_INT,C_STRING} ), xWindowShouldClose = define_c_func( raylib, "+WindowShouldClose", {}, C_BOOL ), xCloseWindow = define_c_proc( raylib, "+CloseWindow", {} ), xGetWindowPosition = define_c_func( raylib, "+GetWindowPosition", {}, RL_VECTOR2 ), xGetScreenToWorld2D = define_c_func( raylib, "+GetScreenToWorld2D", {RL_VECTOR2,RL_CAMERA2D}, RL_VECTOR2 ) --Only copying what is relevant to my thought process public function GetScreenToWorld2D( sequence position, sequence camera ) return c_func( xGetScreenToWorld2D, {position,camera} ) end function
My thought process being that if a struct or so is defined as define_c_type() using FFI, then it must be called as a sequence in Euphoria. However that it is clear that is not always the case. SDL must not pass all structs by value as Raylib does.
public constant SDL_Rect = define_c_type({ --Part of My SDL Wrapper --From SDL_Rect.e C_INT, --x C_INT, --y C_INT, --w C_INT --h }) export constant xSDL_HasIntersection = define_c_func(sdl,"+SDL_HasIntersection",{SDL_Rect,SDL_Rect},C_BOOL) public function SDL_HasIntersection(sequence a,sequence b) return c_func(xSDL_HasIntersection,{a,b}) end function --My thought being you could use it as such: sequence myRect = SDL_Rect() myRect[1] = 5 --x myRect[2] = 5 --y And so forth. })
My thought process being that sequence is the closest thing Euphoria has to struct in its current form.
EDIT: I have changed most of the sequences to atoms. Functions that returned a define_c_type() should now return a pointer as I changed it from I.E. SDL_Rect to C_POINTER.
20. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 19, 2022
- 2138 views
Thanks Andy and Gregg.
This is a good place to put thought processes.
Chris
21. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Nov 19, 2022
- 2181 views
- Last edited Nov 22, 2022
Hi
There's a rule in there somewhere isn't there.
When the C library accepts pass by value you can pass the parameter as a sequence, eg
public constant RL_VECTOR2 = define_c_type({ C_FLOAT, -- x // Vector x component C_FLOAT -- y // Vector y component }) constant xGetScreenToWorld2D = define_c_func( raylib, "+GetScreenToWorld2D", {RL_VECTOR2,RL_CAMERA2D}, RL_VECTOR2 ) --Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position public function GetScreenToWorld2D( sequence position, sequence camera ) return c_func( xGetScreenToWorld2D, {position,camera} ) end function
However, when the C library accepts pass by reference, you must use the pointer to the structure as a parameter, eg
public constant SDL_Rect = define_c_type({ C_INT, --x C_INT, --y C_INT, --w C_INT --h }) --to illustrate the 2 ways of filling the structure a = allocate_struct(SDL_Rect) b = allocate_struct(SDL_Rect, {30,70,10,10}) poke4(a, {20,30,50,50} ) export constant xSDL_HasIntersection = define_c_func(sdl,"+SDL_HasIntersection",{SDL_Rect,SDL_Rect},C_BOOL) --SDL_bool SDL_HasIntersection(const SDL_Rect * A, const SDL_Rect * B); public function SDL_HasIntersection(atom a,atom b) --a and b pointers to the SDL_RECT structures, already filled with values poked into the allocated memory blocks return c_func(xSDL_HasIntersection,{a,b}) end function
Does that look ok to add to some documentation?
Chris
22. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Dec 04, 2022
- 1380 views
Hi Greg
running this :
--Show CPU Info Demo include std/ffi.e include sdl.e puts(1,"1 Means True, 0 Means False\n") printf(1,"CPU Count: %d\n",{SDL_GetCPUCount() }) printf(1,"Cache Line Size: %d\n",{SDL_GetCPUCacheLineSize() }) printf(1,"Has RDTSC: %d\n",{SDL_HasRDTSC() }) printf(1,"Has AtltiVec: %d\n",{SDL_HasAltiVec() }) printf(1,"Has MMX:%d\n",{SDL_HasMMX() }) printf(1,"Has 3D Now:%d\n",{SDL_Has3DNow() }) printf(1,"Has SSE:%d\n",{SDL_HasSSE() }) printf(1,"Has SSE2:%d\n",{SDL_HasSSE2() }) printf(1,"Has SSE3:%d\n",{SDL_HasSSE3() }) printf(1,"Has SSE41:%d\n",{SDL_HasSSE41() }) printf(1,"Has SSE42:%d\n",{SDL_HasSSE42() }) printf(1,"Has AVX:%d\n",{SDL_HasAVX() }) printf(1,"Has AVX2:%d\n",{SDL_HasAVX2() }) printf(1,"Has AVX512F:%d\n",{SDL_HasAVX512F() }) printf(1,"Has ARMSIMD:%d\n",{SDL_HasARMSIMD() }) printf(1,"Has NEON:%d\n",{SDL_HasNEON() }) printf(1,"Has LSX:%d\n",{SDL_HasLSX() }) printf(1,"Has LASX:%d\n",{SDL_HasLASX() }) printf(1,"RAM: %d\n",{SDL_GetSystemRAM() })
produces this
C:\EuProgramming\Euphoria4_1\libffi-euphoria\include\std\ffi.e:910 in function c_func() attempt to subscript an atom (reading from it) - in assignment to 'fn' rid = 1
because, in ffi.e
override function c_func( integer rid, sequence args ) if rid > 0 then return eu:c_func( rid, args ) end if rid = rid * -1 atom fn = m_funcs[rid][1] --<-- rid was passed as an integer atom cif = m_funcs[rid][2] sequence name = m_funcs[rid][3]
Cheers, Chris
23. Re: sdl2 and ffi
- Posted by Icy_Viking Dec 04, 2022
- 1352 views
In the example program. I only have include sdl.e, I didn't include ffi.e in the example program.
So my example program as it is currently written
--Show CPU Info Demo include sdl.e puts(1,"1 Means True, 0 Means False\n") printf(1,"CPU Count: %d\n",{SDL_GetCPUCount() }) printf(1,"Cache Line Size: %d\n",{SDL_GetCPUCacheLineSize() }) printf(1,"Has RDTSC: %d\n",{SDL_HasRDTSC() }) printf(1,"Has AtltiVec: %d\n",{SDL_HasAltiVec() }) printf(1,"Has MMX:%d\n",{SDL_HasMMX() }) printf(1,"Has 3D Now:%d\n",{SDL_Has3DNow() }) printf(1,"Has SSE:%d\n",{SDL_HasSSE() }) printf(1,"Has SSE2:%d\n",{SDL_HasSSE2() }) printf(1,"Has SSE3:%d\n",{SDL_HasSSE3() }) printf(1,"Has SSE41:%d\n",{SDL_HasSSE41() }) printf(1,"Has SSE42:%d\n",{SDL_HasSSE42() }) printf(1,"Has AVX:%d\n",{SDL_HasAVX() }) printf(1,"Has AVX2:%d\n",{SDL_HasAVX2() }) printf(1,"Has AVX512F:%d\n",{SDL_HasAVX512F() }) printf(1,"Has ARMSIMD:%d\n",{SDL_HasARMSIMD() }) printf(1,"Has NEON:%d\n",{SDL_HasNEON() }) printf(1,"Has LSX:%d\n",{SDL_HasLSX() }) printf(1,"Has LASX:%d\n",{SDL_HasLASX() }) printf(1,"RAM: %d\n",{SDL_GetSystemRAM() }) printf(1,"OS: %s\n",{SDL_GetPlatform() })
Its a very simple program. It just uses the SDL functions to show what your OS is, how much RAM you have and what features your CPU has.
24. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Dec 04, 2022
- 1302 views
- Last edited Dec 05, 2022
Hi, That's not the point, I realise that, however it also tests ffi.e - included as this is going to be the new dll.e
in that an integer is passed to c_func, and an attempt was made to subscript it.
This post is about ffi, rather than sdl, should have made that more clear.
Cheers
Chris
25. Re: sdl2 and ffi
- Posted by ghaberek (admin) Dec 05, 2022
- 1235 views
produces this
C:\EuProgramming\Euphoria4_1\libffi-euphoria\include\std\ffi.e:910 in function c_func() attempt to subscript an atom (reading from it) - in assignment to 'fn' rid = 1
The problem is that define_c_func() returned -1 for something and c_func() doesn't have good error handling. The first value of m_funcs is an atom because nothing is supposed to exist there. You can add a quick check in c_func() to help find the problem:
override function c_func( integer rid, sequence args ) if rid > 0 then return eu:c_func( rid, args ) end if -- add this: if rid = -1 then machine_proc( M_CRASH, "Invalid C function id: %d", {rid} ) end if rid = rid * -1 atom fn = m_funcs[rid][1] atom cif = m_funcs[rid][2] sequence name = m_funcs[rid][3]
-Greg
26. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Dec 05, 2022
- 1220 views
Thanks Greg, my mistake, fixed it. I was using version 2.0 of the SDL DLL instead of version 2.26. Still leaves that atttempt to subscript atom waiting to rear it's ugly head.
[edit] - ah, sorry, mfuncs is the sequence, in this instance rid is an index to it.
These are the fixes I made
in SDl_version.e
--export constant xSDL_GetVersion = define_c_proc(sdl,"+SDL_GetVersion",{SDL_version}) export constant xSDL_GetVersion = define_c_proc(sdl,"+SDL_GetVersion",{C_POINTER}) --public procedure SDL_GetVersion(sequence ver) public procedure SDL_GetVersion(atom ver_ptr) c_proc(xSDL_GetVersion,{ver_ptr}) end procedure
in cpu_demo.ex
--Show CPU Info Demo include std/ffi.e include sdl.e printf(1, "System RAM %d Gb\n", {SDL_GetSystemRAM()}) --get the dll version we are using atom DLL_vers DLL_vers = allocate_struct(SDL_version, {0,0,0}) --allocate the memory, and return the pointer to that memory SDL_GetVersion(DLL_vers) printf(1, "DLL versio : %d.%d.%d\n)", { peek(DLL_vers), --single byte, 8 bit numbers peek(DLL_vers + 1), peek(DLL_vers + 2) }) puts(1,"1 Means True, 0 Means False\n") printf(1,"CPU Count: %d\n",{SDL_GetCPUCount() }) printf(1,"Cache Line Size: %d\n",{SDL_GetCPUCacheLineSize() }) printf(1,"Has RDTSC: %d\n",{SDL_HasRDTSC() }) printf(1,"Has AtltiVec: %d\n",{SDL_HasAltiVec() }) printf(1,"Has MMX:%d\n",{SDL_HasMMX() }) printf(1,"Has 3D Now:%d\n",{SDL_Has3DNow() }) printf(1,"Has SSE:%d\n",{SDL_HasSSE() }) printf(1,"Has SSE2:%d\n",{SDL_HasSSE2() }) printf(1,"Has SSE3:%d\n",{SDL_HasSSE3() }) printf(1,"Has SSE41:%d\n",{SDL_HasSSE41() }) printf(1,"Has SSE42:%d\n",{SDL_HasSSE42() }) printf(1,"Has AVX:%d\n",{SDL_HasAVX() }) printf(1,"Has AVX2:%d\n",{SDL_HasAVX2() }) printf(1,"Has AVX512F:%d\n",{SDL_HasAVX512F() }) printf(1,"Has ARMSIMD:%d\n",{SDL_HasARMSIMD() }) printf(1,"Has NEON:%d\n",{SDL_HasNEON() }) printf(1,"Has LSX:%d\n",{SDL_HasLSX() }) printf(1,"Has LASX:%d\n",{SDL_HasLASX() }) printf(1,"RAM: %d\n",{SDL_GetSystemRAM() })
I'm afraid these types being squirted at the C functions are a big issue a the moment, as far as I can tell, all the SDL_ routines need pointers of structures as parameters, not types, or actual structures, and the SDL library is littered with these, you can't send the ffi types - yet.
Cheers
Chris
27. Re: sdl2 and ffi
- Posted by ghaberek (admin) Dec 05, 2022
- 1201 views
I'm afraid these types being squirted at the C functions are a big issue a the moment, as far as I can tell, all the SDL_ routines need pointers of structures as parameters, not types, or actual structures, and the SDL library is littered with these, you can't send the ffi types - yet.
I not sure I understand the problem. What do you mean by "you can't send the ffi types yet?" Maybe you're misunderstanding my intentions for using structures with FFI? If a function expects a pointer to a structure, then it expects a pointer to a structure, period. There's no changing that and I've no intention to change that.
What we need to keep in mind is that, while I'm trying to make wrapping C libraries easier, that doesn't mean it'll ever be easy. Such is the nature of interfacing from another language. When wrapping any C library, it's important to reflect on what parts should work "as is" and what parts should be made more "Euphorian."
Let's take a look at SDL_GetVersion() to start with. For reference, here's how you'd call the function in C. The first is more "traditional" but the second is more "Euphorian."
#include "SDL.h" // // Method 1 - Using the stack // This is typically what you'll see when calling a function directly from C code. // int main( int argc, char *argv[] ) { // Declaring a structure this way creates it on "the stack" not in memory ("the heap") SDL_Version ver; // Using "&" gives us the pointer to the structure, which the function will fill for us SDL_GetVersion( &ver ); // Since this is structure is "local" (on the stack) we use dots to access the members printf( "major: %d\n", ver.major ); printf( "minor: %d\n", ver.minor ); printf( "patch: %d\n", ver.patch ); // No free() here, the structure is cleared from the stack when it goes out of scope return 0; } // // Method 2 - Using the heap // Manually allocate the memory. This is typically avoided and is technically slower. // int main( int argc, char *argv[] ) { // Allocate a pointer to some memory (on "the heap") the size of our structure SDL_Version *ver = malloc( sizeof(SDL_Version) ); // Here we pass "ver" directly since it is already a pointer to some memory SDL_GetVersion( ver ); // Since this is a pointer to a structure, we use "->" to access the members printf( "major: %d\n", ver->major ); printf( "minor: %d\n", ver->minor ); printf( "patch: %d\n", ver->patch ); // Free the memory from the heap otherwise we could cause a memory leak free( ver ); return 0; }
And the naive approach would be to wrap it like this:
-- SDL_version.e include std/ffi.e constant xSDL_GetVersion = define_c_proc( libsdl, "+SDL_GetVersion", {C_POINTER} ) public constant SDL_Version = define_c_struct({ C_UINT8, -- major C_UINT8, -- minor C_UINT8 -- patch }) public procedure SDL_GetVersion( atom ver ) c_proc( xSDL_GetVersion, {ver} ) end procedure
Which would require the user to allocate the structure, call the function, peek the result, and then free the memory, like this:
include std/ffi.e include SDL.e atom ver = allocate_struct( SDL_Version ) SDL_GetVersion( ver ) printf( 1, "major: %d\n", ver[1] ) printf( 1, "minor: %d\n", ver[2] ) printf( 1, "patch: %d\n", ver[3] ) free( ver )
Sure, that code works but it's messy and error-prone and I doubt anyone wants to spend much time writing such tedious boilerplate code.
Instead, we wrap these functions in such a way that we're doing all the boilerplate and heavy lifting behind the scenes. This is where FFI works best.
-- SDL_version.e include std/ffi.e constant xSDL_GetVersion = define_c_proc( libsdl, "+SDL_GetVersion", {C_POINTER} ) -- notice I made this this all-caps and removed the scope, since only -- the wrapper is concerned with dealing with the structure internally. constant SDL_VERSION = define_c_struct({ C_UINT8, -- major C_UINT8, -- minor C_UINT8 -- patch }) public function SDL_GetVersion() atom ver = allocate_struct( SDL_VERSION ) c_proc( xSDL_GetVersion, {ver} ) sequence result = peek_struct( ver, SDL_VERSION ) free( ver ) return result end function
And now the end-user code is much cleaner and "feels" more like Euphoria code.
include SDL.e sequence ver = SDL_GetVersion() printf( 1, "major: %d\n", ver[1] ) printf( 1, "minor: %d\n", ver[2] ) printf( 1, "patch: %d\n", ver[3] )
This is the Way.
-Greg
P.S. Here's a good quick write-up on "stack" vs "heap" in C: https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap#80113
28. Re: sdl2 and ffi
- Posted by ChrisB (moderator) Dec 05, 2022
- 1187 views
Hi
At the risk of sounding like a troll (seriously not my intention, Andy has always done great work on all the libraries he has created, and wrapping all those functions must be some sort of tedium), but I think you've missed my point Gregg.
Let's also take SDL_GetVersion as an example. This is how Andy originally presented it
--SDL_Version is a type set up in sdl_version.e public constant SDL_version = define_c_type({ C_UINT, --major C_UINT, --minor C_UINT --patch }) --and then in sdl_version.e export constant xSDL_GetVersion = define_c_proc(sdl,"+SDL_GetVersion",{SDL_version}) public procedure SDL_GetVersion(sequence ver) c_proc(xSDL_GetVersion,{ver}) end procedure
As you can see, and is passing a type to the c function, not a pointer, and the eu procedure is expecting a sequence
The correct way is
export constant xSDL_GetVersion = define_c_proc(sdl,"+SDL_GetVersion",{C_POINTER}) public procedure SDL_GetVersion(atom ver_ptr) c_proc(xSDL_GetVersion,{ver_ptr}) end procedure
Now the c function gets a pointer value, and the eu procedure gets an atom, created with
atom DLL_vers DLL_vers = allocate_struct(SDL_version, {0,0,0}) --allocate the memory, and return the pointer to that memory SDL_GetVersion(DLL_vers) --an atom, a pointer, is passed to the eu function printf(1, "DLL version : %d.%d.%d\n)", { peek(DLL_vers), --single byte, 8 bit numbers peek(DLL_vers + 1), peek(DLL_vers + 2) }) --the more ''ffi' way printf(1, "DLL version : %d.%d.%d\n", peek_struct( DLL_vers, SDL_VERSION )) --just tried this - doesn't work, peek_struct is 'out'
My point is that all sdl functions need pointers to structures, as far as I can tell, and passing a type, created using allocate structure, does not contain the pointer to that allocated memory
I don't doubt that ffi will make it easier to manipulate structures, it already has done so for me, and I know I'm a pest, but the SDL library as Andy has presented just doesn't work. Now, correct me if I'm wrong, and I'm misunderstanding something here, but is passing a type the same as passing pointer?
Cheers, Chris
ps - see the code above - peek_struct isn't returning the correct values - is C_UINT in euphoria 1 byte, 8 bits only, as that's what SDL_GetVersion returns, and that's why the peek version returns the correct values. Change the SDL_version type C_UINTs to C_UINT8s, and it works.
Cheers
Chris
29. Re: sdl2 and ffi
- Posted by Icy_Viking Dec 05, 2022
- 1202 views
OK, I've changed the code in SDL_Version.e following Greg's advice. It works for me. I've changed from C_UINT to C_UINT8, and rewrote the SDL_GetVersion() function as Greg has shown. I tested it and it works for me. I've also pushed these changes on github. I had forgotten to update the values for the current SDL version in the SDL_Version.e code, but that has been fixed too.
include std/ffi.e include std/machine.e --I got "free" not found if I don't include machine.e include sdl.e public constant SDL_VERSION = define_c_type({ C_UINT8, --major C_UINT8, --minor C_UINT8 --patch }) public constant SDL_MAJOR_VERSION = 2, SDL_MINOR_VERSION = 26, SDL_PATCHLEVEL = 1 export constant xSDL_GetVersion = define_c_proc(sdl,"+SDL_GetVersion",{C_POINTER}) public function SDL_GetVersion() atom ver = allocate_struct(SDL_VERSION) c_proc(xSDL_GetVersion,{ver}) sequence res = peek_struct(ver,SDL_VERSION) free(ver) return res end function export constant xSDL_GetRevision = define_c_func(sdl,"+SDL_GetRevision",{},C_STRING) public function SDL_GetRevision() return c_func(xSDL_GetRevision,{}) end function export constant xSDL_GetRevisionNumber = define_c_func(sdl,"+SDL_GetRevisionNumber",{},C_INT) public function SDL_GetRevisionNumber() return c_func(xSDL_GetRevisionNumber,{}) end function
--Show CPU Info Demo include sdl.e puts(1,"1 Means True, 0 Means False\n") printf(1,"CPU Count: %d\n",{SDL_GetCPUCount() }) printf(1,"Cache Line Size: %d\n",{SDL_GetCPUCacheLineSize() }) printf(1,"Has RDTSC: %d\n",{SDL_HasRDTSC() }) printf(1,"Has AtltiVec: %d\n",{SDL_HasAltiVec() }) printf(1,"Has MMX:%d\n",{SDL_HasMMX() }) printf(1,"Has 3D Now:%d\n",{SDL_Has3DNow() }) printf(1,"Has SSE:%d\n",{SDL_HasSSE() }) printf(1,"Has SSE2:%d\n",{SDL_HasSSE2() }) printf(1,"Has SSE3:%d\n",{SDL_HasSSE3() }) printf(1,"Has SSE41:%d\n",{SDL_HasSSE41() }) printf(1,"Has SSE42:%d\n",{SDL_HasSSE42() }) printf(1,"Has AVX:%d\n",{SDL_HasAVX() }) printf(1,"Has AVX2:%d\n",{SDL_HasAVX2() }) printf(1,"Has AVX512F:%d\n",{SDL_HasAVX512F() }) printf(1,"Has ARMSIMD:%d\n",{SDL_HasARMSIMD() }) printf(1,"Has NEON:%d\n",{SDL_HasNEON() }) printf(1,"Has LSX:%d\n",{SDL_HasLSX() }) printf(1,"Has LASX:%d\n",{SDL_HasLASX() }) printf(1,"RAM: %d\n",{SDL_GetSystemRAM() }) printf(1,"OS: %s\n",{SDL_GetPlatform() }) sequence ver = SDL_GetVersion() printf(1,"SDL Version:\n") printf(1,"Major: %d\n",ver[1]) printf(1,"Minor: %d\n",ver[2]) printf(1,"Patch: %d\n",ver[3])
30. Re: sdl2 and ffi
- Posted by ghaberek (admin) Dec 05, 2022
- 1180 views
At the risk of sounding like a troll (seriously not my intention, Andy has always done great work on all the libraries he has created, and wrapping all those functions must be some sort of tedium), but I think you've missed my point Gregg.
I don't think you're trolling. This is a very complex topic and sometimes it's hard to tell who's understanding what and by how much. I'm happy to iterate over this until we're all in understanding.
My point is that all sdl functions need pointers to structures, as far as I can tell, and passing a type, created using allocate structure, does not contain the pointer to that allocated memory
It looks like Andy misunderstood how to use the types created by FFI, but I hadn't realized that yet so you and I were talking about slightly different things. Sorry about that. Once Andy corrects the wrapper to use C_POINTER wherever there are types like SDL_Event *event (mind the asterisk!) then we should all be on the same conceptual page again.
I don't doubt that ffi will make it easier to manipulate structures, it already has done so for me, and I know I'm a pest, but the SDL library as Andy has presented just doesn't work. Now, correct me if I'm wrong, and I'm misunderstanding something here, but is passing a type the same as passing pointer?
No, passing a type is not the same as passing a pointer. Internally it's much different. Each combination of platform, architecture, and calling convention presents a new way of handling values and they all involve registers and stack space. The Wikipedia x86 calling conventions page helps explain this but it's all very messy. This is why I opted to use libffi instead of trying to extend Euphoria's "legacy" backend functions. I'd only be reinventing the wheel and likely creating all sorts of issues due to the many-to-many-to-many complexity involved, and libffi has already spent decades addressing and correcting these issues.
ps - see the code above - peek_struct isn't returning the correct values - is C_UINT in euphoria 1 byte, 8 bits only, as that's what SDL_GetVersion returns, and that's why the peek version returns the correct values. Change the SDL_version type C_UINTs to C_UINT8s, and it works.
C_UINT is an unsigned int which is "at least 16 bits" which is weird, right? C types are loosely defined and weren't really intended for use outside the compiler. C99 created "fixed width" types which is what gives us things like uint8_t which is an unsigned int that is always 8-bits like you would expect. Effectively unsigned int would be the same as uint32_t but since it's loosely defined that makes figuring these things out very challenging. The Wikipedia C data types page helps explain this, and at least it seems more straight-forward than the calling conventions concept.
-Greg
31. Re: sdl2 and ffi
- Posted by Icy_Viking Dec 05, 2022
- 1151 views
At the risk of sounding like a troll (seriously not my intention, Andy has always done great work on all the libraries he has created, and wrapping all those functions must be some sort of tedium), but I think you've missed my point Gregg.
I don't think you're trolling. This is a very complex topic and sometimes it's hard to tell who's understanding what and by how much. I'm happy to iterate over this until we're all in understanding.
My point is that all sdl functions need pointers to structures, as far as I can tell, and passing a type, created using allocate structure, does not contain the pointer to that allocated memory
It looks like Andy misunderstood how to use the types created by FFI, but I hadn't realized that yet so you and I were talking about slightly different things. Sorry about that. Once Andy corrects the wrapper to use C_POINTER wherever there are types like SDL_Event *event (mind the asterisk!) then we should all be on the same conceptual page again.
I don't doubt that ffi will make it easier to manipulate structures, it already has done so for me, and I know I'm a pest, but the SDL library as Andy has presented just doesn't work. Now, correct me if I'm wrong, and I'm misunderstanding something here, but is passing a type the same as passing pointer?
No, passing a type is not the same as passing a pointer. Internally it's much different. Each combination of platform, architecture, and calling convention presents a new way of handling values and they all involve registers and stack space. The Wikipedia x86 calling conventions page helps explain this but it's all very messy. This is why I opted to use libffi instead of trying to extend Euphoria's "legacy" backend functions. I'd only be reinventing the wheel and likely creating all sorts of issues due to the many-to-many-to-many complexity involved, and libffi has already spent decades addressing and correcting these issues.
ps - see the code above - peek_struct isn't returning the correct values - is C_UINT in euphoria 1 byte, 8 bits only, as that's what SDL_GetVersion returns, and that's why the peek version returns the correct values. Change the SDL_version type C_UINTs to C_UINT8s, and it works.
C_UINT is an unsigned int which is "at least 16 bits" which is weird, right? C types are loosely defined and weren't really intended for use outside the compiler. C99 created "fixed width" types which is what gives us things like uint8_t which is an unsigned int that is always 8-bits like you would expect. Effectively unsigned int would be the same as uint32_t but since it's loosely defined that makes figuring these things out very challenging. The Wikipedia C data types page helps explain this, and at least it seems more straight-forward than the calling conventions concept.
-Greg
I've fixed the SDL_events.e file. I've changed the SDL_Event to a C_POINTER.