1. sdl2 and ffi

Hi Andy

SimpleWin

What are you doing here?

18 object event = allocate_struct(event)

Cheers

Chris

new topic     » topic index » view message » categorize

2. Re: sdl2 and ffi

Whats the ffi.e equivalent of allocate, as in

atom rect = allocate(16)
poke4(rect,{50,50,100,100})

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

3. Re: sdl2 and ffi

ChrisB said...

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

ChrisB said...

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

new topic     » goto parent     » topic index » view message » categorize

4. Re: sdl2 and ffi

ghaberek said...
ChrisB said...

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

ChrisB said...

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

new topic     » goto parent     » topic index » view message » categorize

5. Re: sdl2 and ffi

ChrisB said...

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.

ChrisB said...

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?

ChrisB said...

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).

ChrisB said...

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.

ChrisB said...

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.

ChrisB said...

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.

ChrisB said...

Just my confused thoughts.

I think you're doing great, Chris.

-Greg

new topic     » goto parent     » topic index » view message » categorize

6. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

7. Re: sdl2 and ffi

Includes missing in sdl.e

public include SDL_keyboard.e
public include SDL_scancode.e

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

8. Re: sdl2 and ffi

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.

new topic     » goto parent     » topic index » view message » categorize

9. Re: sdl2 and ffi

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 
 
new topic     » goto parent     » topic index » view message » categorize

10. Re: sdl2 and ffi

SDPringle said...

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

new topic     » goto parent     » topic index » view message » categorize

11. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

12. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

13. Re: sdl2 and ffi

ChrisB said...

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.

new topic     » goto parent     » topic index » view message » categorize

14. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

15. Re: sdl2 and ffi

SDL_render.e
line 116
public function SDL_CreateTextureFromSurface(atom ren, sequence surf)
->
public function SDL_CreateTextureFromSurface(atom ren, atom surf)

Cheers Chris

new topic     » goto parent     » topic index » view message » categorize

16. Re: sdl2 and ffi

ChrisB said...

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.

new topic     » goto parent     » topic index » view message » categorize

17. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

18. Re: sdl2 and ffi

ChrisB said...

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.

ChrisB said...

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

new topic     » goto parent     » topic index » view message » categorize

19. Re: sdl2 and ffi

ChrisB said...

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.

new topic     » goto parent     » topic index » view message » categorize

20. Re: sdl2 and ffi

Thanks Andy and Gregg.

This is a good place to put thought processes.

Chris

new topic     » goto parent     » topic index » view message » categorize

21. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

22. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

23. Re: sdl2 and ffi

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.

new topic     » goto parent     » topic index » view message » categorize

24. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

25. Re: sdl2 and ffi

ChrisB said...

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

new topic     » goto parent     » topic index » view message » categorize

26. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

27. Re: sdl2 and ffi

ChrisB said...

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

new topic     » goto parent     » topic index » view message » categorize

28. Re: sdl2 and ffi

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

new topic     » goto parent     » topic index » view message » categorize

29. Re: sdl2 and ffi

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]) 
new topic     » goto parent     » topic index » view message » categorize

30. Re: sdl2 and ffi

ChrisB said...

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.

ChrisB said...

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.

ChrisB said...

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.

ChrisB said...

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

new topic     » goto parent     » topic index » view message » categorize

31. Re: sdl2 and ffi

ghaberek said...
ChrisB said...

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.

ChrisB said...

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.

ChrisB said...

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.

ChrisB said...

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.

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu