1. Orx Wrapper Hurdles
- Posted by Icy_Viking Aug 21, 2021
- 811 views
Hello all,
As I was writing the wrapper for Orx I came across the most obvious hurdle. The init and execute functions needed for Orx are not in the Orx DLL as they are called as Inline functions and not exported to the DLL. So without those, Orx won't start even with other commands that have init functions. There may be a way with ifdef statements, but I haven't tried yet. If anyone could help or has a solution, please let me know. Otherwise I'm not sure how feasible this wrapper may be to continue.
https://orx-project.org/orx/doc/html/orx_8h_source.html
static orxINLINE void orx_Execute(orxU32 _u32NbParams, orxSTRING _azParams[], const orxMODULE_INIT_FUNCTION _pfnInit, const orxMODULE_RUN_FUNCTION _pfnRun, const orxMODULE_EXIT_FUNCTION _pfnExit)
ifdef orx_Eexcute then --I'm not sure how to trasnalte this to eu code end ifdef
I already have some functions already wrapped.
public constant xorxClock_Setup = define_c_proc(orx,"+orxClock_Setup",{}), xorxClock_Init = define_c_func(orx,"+orxClock_Init",{},C_UINT), xorxClock_Exit = define_c_proc(orx,"+orxClock_Exit",{}) public procedure orxClock_Setup() c_proc(xorxClock_Setup,{}) end procedure public function orxClock_Init() return c_func(xorxClock_Init,{}) end function public procedure orxClock_Exit() c_proc(xorxClock_Exit,{}) end procedure
2. Re: Orx Wrapper Hurdles
- Posted by ghaberek (admin) Aug 23, 2021
- 776 views
Hello all,
As I was writing the wrapper for Orx I came across the most obvious hurdle. The init and execute functions needed for Orx are not in the Orx DLL as they are called as Inline functions and not exported to the DLL. So without those, Orx won't start even with other commands that have init functions. There may be a way with ifdef statements, but I haven't tried yet. If anyone could help or has a solution, please let me know. Otherwise I'm not sure how feasible this wrapper may be to continue.
I mentioned this in your original thread: https://openeuphoria.org/forum/136281.wc#136281
Two major hurdles I'm seeing here:
- Calling convention: Most of the library functions are declared using the fastcall convention, which Euphoria isn't currently built to handle (we only support stdcall and cdecl). It's possible this might only be the case when compiling the library statically so that it's embedded into the resulting executable and the shared library might still be using stdcall or cdecl. I haven't dug through the code enough to tell yet. The good news is that all those different conventions only affect x86 and on x64 there's just "the x64 calling convention" so it should work fine if you stick to 64-bit Euphoria (which you should!).
- Inline functions: Some functions of the library are declared inline which means they're always compiled directly into the executable and don't get exported (or even compiled into) the shared library. You could either unmark these as inline and rebuild the library or reconstruct them directly in Euphoria. Unfortunately the orx_Execute function is marked inline, and it's literally the first function you need to bootstrap a program.
Obviously you've solved the calling convention issue, great job! It looks like these functions are declared with both static and orxINLINE which is a bit problematic. The orxINLINE is guarded in an ifndef block so defining that as blank (-DorxINLINE) should stop it from inserting inline everywhere, but there's no corresponding orxSTATIC and the static keyword is just used directly in the code. So if you wanted to fix this you'd have to copy the ifndef block for orxINLINE to orxSTATIC and then do the same trick of defining it as blank.
My recommendation would be to just rewrite these functions directly in Euphoria. It seems like they're marked static and inline because they're small, so doing so shouldn't be difficult. Doing this will also give you the benefit of allowing the interpreter to inline the routines, keeping with the intent of the original code. The default behavior is to inline routines of 30 IL bytes. You can override this with with inline. So if you can figure out how many IL bytes these routines use (using dis.ex maybe?) then you can manually tweak that value in the wrapper file.
-Greg
3. Re: Orx Wrapper Hurdles
- Posted by Icy_Viking Aug 23, 2021
- 780 views
Hello all,
As I was writing the wrapper for Orx I came across the most obvious hurdle. The init and execute functions needed for Orx are not in the Orx DLL as they are called as Inline functions and not exported to the DLL. So without those, Orx won't start even with other commands that have init functions. There may be a way with ifdef statements, but I haven't tried yet. If anyone could help or has a solution, please let me know. Otherwise I'm not sure how feasible this wrapper may be to continue.
I mentioned this in your original thread: https://openeuphoria.org/forum/136281.wc#136281
Two major hurdles I'm seeing here:
- Calling convention: Most of the library functions are declared using the fastcall convention, which Euphoria isn't currently built to handle (we only support stdcall and cdecl). It's possible this might only be the case when compiling the library statically so that it's embedded into the resulting executable and the shared library might still be using stdcall or cdecl. I haven't dug through the code enough to tell yet. The good news is that all those different conventions only affect x86 and on x64 there's just "the x64 calling convention" so it should work fine if you stick to 64-bit Euphoria (which you should!).
- Inline functions: Some functions of the library are declared inline which means they're always compiled directly into the executable and don't get exported (or even compiled into) the shared library. You could either unmark these as inline and rebuild the library or reconstruct them directly in Euphoria. Unfortunately the orx_Execute function is marked inline, and it's literally the first function you need to bootstrap a program.
Obviously you've solved the calling convention issue, great job! It looks like these functions are declared with both static and orxINLINE which is a bit problematic. The orxINLINE is guarded in an ifndef block so defining that as blank (-DorxINLINE) should stop it from inserting inline everywhere, but there's no corresponding orxSTATIC and the static keyword is just used directly in the code. So if you wanted to fix this you'd have to copy the ifndef block for orxINLINE to orxSTATIC and then do the same trick of defining it as blank.
My recommendation would be to just rewrite these functions directly in Euphoria. It seems like they're marked static and inline because they're small, so doing so shouldn't be difficult. Doing this will also give you the benefit of allowing the interpreter to inline the routines, keeping with the intent of the original code. The default behavior is to inline routines of 30 IL bytes. You can override this with with inline. So if you can figure out how many IL bytes these routines use (using dis.ex maybe?) then you can manually tweak that value in the wrapper file.
-Greg
Hi Greg, thanks for the input. This is where the problem lies. I'm not sure how to translate the inline functions into Eu code. I have vague ideas, but I'm really stuck as to where to go from there.
4. Re: Orx Wrapper Hurdles
- Posted by ghaberek (admin) Aug 24, 2021
- 744 views
Hi Greg, thanks for the input. This is where the problem lies. I'm not sure how to translate the inline functions into Eu code. I have vague ideas, but I'm really stuck as to where to go from there.
Here's a quick conversion of 01_Object.c.
include "orx.e" /** Inits the tutorial */ function Init() /* Displays a small hint in console */ orxLOG("\n* This tutorial creates a viewport/camera couple and an object" & "\n* You can play with the config parameters in ../01_Object.ini" & "\n* After changing them, relaunch the tutorial to see their effects") /* Creates viewport */ orxViewport_CreateFromConfig("Viewport") /* Creates object */ orxObject_CreateFromConfig("Object") /* Done! */ return orxSTATUS_SUCCESS end function /** Run function */ function Run() integer eResult = orxSTATUS_SUCCESS /* Should quit? */ if orxInput_IsActive("Quit") then /* Updates result */ eResult = orxSTATUS_FAILURE end if /* Done! */ return eResult end function /** Exit function */ function Exit() /* We're a bit lazy here so we let orx clean all our mess! :) */ return 0 end function /** Main function */ procedure main() sequence argv = command_line() integer argc = length(argv) /* Executes a new instance of tutorial */ orx_Execute(argc, argv, "Init", "Run", "Exit") end procedure main()
Most of this hinges on orx_Execute which is of course inline, so here's how I'd (roughly) convert it:
/** Should stop execution by default event handling? */ integer sbStopByEvent = orxFALSE /** Orx main execution function * @param[in] _u32NbParams Main function parameters number (argc) * @param[in] _azParams Main function parameter list (argv) * @param[in] _pfnInit Main init function (should init all the main stuff and register the main event handler to override the default one) * @param[in] _pfnRun Main run function (will be called once per frame, should return orxSTATUS_SUCCESS to continue processing) * @param[in] _pfnExit Main exit function (should clean all the main stuff) */ public procedure orx_Execute(atom _u32NbParams, sequence _azParams, sequence _zInit, sequence _zRun, sequence _zExit, integer _fnInit=routine_id(_zInit), integer _fnRun=routine_id(_zRun), integer _fnExit=routine_id(_zExit)) /* Inits the Debug System */ orxDEBUG_INIT() /* Checks */ orxASSERT(_u32NbParams > 0) orxASSERT(_azParams != orxNULL) orxASSERT(_fnRun != orxNULL) /* Registers main module */ orxModule_Register(orxMODULE_ID_MAIN, "MAIN", routine_id("orx_MainSetup"), _fnInit, _fnExit) /* Sends the command line arguments to orxParam module */ if orxParam_SetArgs(_u32NbParams, _azParams) != orxSTATUS_FAILURE then /* Inits the engine */ if orxModule_Init(orxMODULE_ID_MAIN) != orxSTATUS_FAILURE then atom stPayload = allocate_data( SIZEOF_ORXSYSTEM_EVENT_PAYLOAD ) integer eClockStatus, eMainStatus integer bStop = orxFALSE atom u32FrameCount /* Registers default event handler */ orxEvent_AddHandler(orxEVENT_TYPE_SYSTEM, orx_DefaultEventHandler) orxEvent_SetHandlerIDFlags(orx_DefaultEventHandler, orxEVENT_TYPE_SYSTEM, orxNULL, orxEVENT_GET_FLAG(orxSYSTEM_EVENT_CLOSE), orxEVENT_KU32_MASK_ID_ALL) /* Clears payload */ orxMemory_Zero(stPayload, SIZEOF_ORXSYSTEM_EVENT_PAYLOAD) /* Main loop */ while bStop = orxFALSE do /* Sends frame start event */ orxEVENT_SEND(orxEVENT_TYPE_SYSTEM, orxSYSTEM_EVENT_GAME_LOOP_START, orxNULL, orxNULL, stPayload) /* Runs game specific code */ eMainStatus = call_func( _fnRun, {} ) /* Updates clock system */ eClockStatus = orxClock_Update() /* Sends frame stop event */ orxEVENT_SEND(orxEVENT_TYPE_SYSTEM, orxSYSTEM_EVENT_GAME_LOOP_STOP, orxNULL, orxNULL, stPayload) /* Updates frame count */ u32FrameCount = peek4u( stPayload + orxSYSTEM_EVENT_PAYLOAD_u32FrameCount ) poke4( stPayload + orxSYSTEM_EVENT_PAYLOAD_u32FrameCount, u32FrameCount + 1 ) bStop = ((sbStopByEvent != orxFALSE) or (eMainStatus = orxSTATUS_FAILURE) or (eClockStatus = orxSTATUS_FAILURE)) end while /* Removes event handler */ orxEvent_RemoveHandler(orxEVENT_TYPE_SYSTEM, orx_DefaultEventHandler) /* Exits from the engine */ orxModule_Exit(orxMODULE_ID_MAIN) free( stPayload ) end if end if /* Exits from the Debug system */ orxDEBUG_EXIT() end procedure
-Greg
5. Re: Orx Wrapper Hurdles
- Posted by Icy_Viking Aug 24, 2021
- 729 views
Thanks Greg, this has made it much more clear.
Here's what I have so far. Note that orxASSERT is not wrapped yet, orxDefaultEventHandler and orxEventPayloadSize are structs, so I haven't got around to wrapping/converting to them to Eu code yet. I guess I could release what I have wrapped so far and EuOrx can be a Euphoria community project kinda similar to how Win32lib was. Not sure how many would be interested in helping though.
--Memory Zero is an inline functions public procedure orxMemory_Zero(atom d,atom size) d = 0 size = allocate_data(1000) end procedure --Orx inline Functions converted to Eu integer sbStopByEvent = orxFALSE public procedure orx_Execute(atom _u32NbParams,sequence _azParams,sequence _zInit,sequence _zRun,sequence _zExit,integer _fnInit=routine_id(_zInit),integer _fnRun=routine_id(_zRun),integer _fnExit=routine_id(_zExit)) orxDebug_Init() --orxASSERT not wrapped yet orxModule_Register(orxMODULE_ID_MAIN,"MAIN",routine_id("orx_MainSetup"),_fnInit,_fnExit) if orxParam_SetArgs(_u32NbParams, _azParams) != orxSTATUS_FAILURE then if orxModule_Init(orxMODULE_ID_MAIN) != orxSTATUS_FAILURE then atom stPayload = allocate_data(1000) integer eClockStatus,eMainStatus integer bStop = orxFALSE atom u32FrameCount orxEvent_AddHandler(orxEVENT_TYPE_SYSTEM, 1) orxEvent_SetHandlerIDFlags(1,orxEVENT_TYPE_SYSTEM,orxNULL,orxEVENT_GET_FLAG=orxSYSTEM_EVENT_CLOSE, orxEVENT_KU32_MASK_ID_ALL) orxMemory_Zero(stPayload,allocate_data(1000)) while bStop = orxFALSE do orxEvent_Send(orxEVENT_TYPE_SYSTEM+orxSYSTEM_EVENT_GAME_LOOP_START+orxNULL+orxNULL+stPayload) eMainStatus = call_func(_fnRun,{}) eClockStatus = orxClock_Update() orxEvent_Send(orxEVENT_TYPE_SYSTEM+orxSYSTEM_EVENT_GAME_LOOP_STOP+orxNULL+orxNULL+stPayload) u32FrameCount = peek4u(stPayload + 1) poke4(stPayload + orxEVENT_TYPE_SYSTEM,u32FrameCount + 1) bStop = ((sbStopByEvent != orxFALSE) or (eMainStatus = orxSTATUS_FAILURE) or (eClockStatus = orxSTATUS_FAILURE)) end while orxEvent_RemoveHandler(orxEVENT_TYPE_SYSTEM,1) orxModule_Exit(orxMODULE_ID_MAIN) free(stPayload) end if end if orxDebug_Exit() end procedure
6. Re: Orx Wrapper Hurdles
- Posted by ghaberek (admin) Aug 24, 2021
- 719 views
Here's what I have so far. Note that orxASSERT is not wrapped yet,
I've developed a few tricks when making Euphoria MVC's logging library that can contribute which should very similar behavior to how these debug macros work in Orx.
orxDefaultEventHandler and orxEventPayloadSize are structs, so I haven't got around to wrapping/converting to them to Eu code yet.
I haven't looked, but if there aren't too many structs in Orx then wrapping them the traditional way (a bunch of constants of field offsets) should be fine.
But if Orx makes heavy use of structs then using the new memstruct feature should prove helpful here. I'm getting pretty close on having a full build ready for 4.2. (Although that might still be "months" close not "days" close.)
I guess I could release what I have wrapped so far and EuOrx can be a Euphoria community project kinda similar to how Win32lib was. Not sure how many would be interested in helping though.
I would be happy to contribute. I already started exploring and semi-wrapping Orx to get a feel for how the library comes together. I can contribute the work I've done so far if you to share access to your repo.
-Greg
7. Re: Orx Wrapper Hurdles
- Posted by Icy_Viking Aug 24, 2021
- 769 views
Here's what I have so far. Note that orxASSERT is not wrapped yet,
I've developed a few tricks when making Euphoria MVC's logging library that can contribute which should very similar behavior to how these debug macros work in Orx.
orxDefaultEventHandler and orxEventPayloadSize are structs, so I haven't got around to wrapping/converting to them to Eu code yet.
I haven't looked, but if there aren't too many structs in Orx then wrapping them the traditional way (a bunch of constants of field offsets) should be fine.
But if Orx makes heavy use of structs then using the new memstruct feature should prove helpful here. I'm getting pretty close on having a full build ready for 4.2. (Although that might still be "months" close not "days" close.)
I guess I could release what I have wrapped so far and EuOrx can be a Euphoria community project kinda similar to how Win32lib was. Not sure how many would be interested in helping though.
I would be happy to contribute. I already started exploring and semi-wrapping Orx to get a feel for how the library comes together. I can contribute the work I've done so far if you to share access to your repo.
-Greg
That sounds great Greg! Also, excited to hear that Eu 4.2 is coming along nicely. Hopefully it will release before the end of this year. There's a handful of structs and unions in Orx, but it might be possible to convert them into Eu code using constants and such. I have started a repo for Orx on my current wrapper. So far left to do is finish wrapping the flags and functions from The I/O section of the game engine, starting with the file functions. I have commented in the code. I of course will be continung to work on this in the meantime. Anyone else who would like to help, please contact or say something in this thread. Let me know what section of the wrapper you'd like to work on and such.
The memstruct feature will help a ton when writing wrappers! I really hope you will be able to get it out before the end of this year.
What I do is go into the Orx help files and find the .h file for what I need to wrap and wrap the functions going down the .h file. For example I'd go into orxFile.h to continue wrapping the file functions for Orx. And then go from there.
@Greg, I've given you access/invited you to the EuOrx repo. Anyone else who wants to help, please contact me on here if you'd like access to the repo.