• FEATURED
• FEATURED
• FEATURED
• FEATURED
• FEATURED

View more

View more

View more

Image of the Day Submit

IOTD | Top Screenshots

The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

Angelscript on Raspberry Pi

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

131 replies to this topic

#101Tzarls  Members

Posted 12 December 2012 - 01:12 PM

Ok, more disassembling and trial-error is in order then.

About the refactoring: I can test when needed, and of course you can log into my RPi when you feel necessary, I have no problems with that.

I was also thinking about a way to speed up all this parameter passing functions at runtime, maybe at least for functions with "simple" arguments (primitives? int, double, etc) - objects might be more complicated (or not?). In those kind of functions maybe AS, at compile time, can build a "map" that outlines where (in the stack, in register, etc) each argument should be placed. At runtime, the CallSystemFunctionNative(...) function would only have to copy the arguments to the paramBuffer based on what the map says - less decisions to make at runtime. What do you think?

#102Andreas Jonsson  Moderators

Posted 12 December 2012 - 01:50 PM

Yes. I have had thoughts on similar paths before. I definitely think it is possible to do something like this, and it ought to significantly reduce the overhead of calling native functions, especially when the ABI is as complex as the Linux ARM ABI has proven to be.

The question is if it is really worth it. The overhead of taking all these runtime decisions haven't shown to be causing any major impact in any of the applications that use AngelScript so far.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#103Tzarls  Members

Posted 12 December 2012 - 02:29 PM

Well, in my app I use AS for DSP, so from the audio thread one can call native functions 44100 times per second. Even with the JIT the CPU gets busy pretty fast with complex scripts, so any optimization that ends up freeing a couple of CPU cycles is always welcome!

#104Andreas Jonsson  Moderators

Posted 12 December 2012 - 02:43 PM

I would have to agree in that in this case it may well prove worth it.

Though, you may want to experiment with using the generic calling convention first. When the ABI is as complex as this, the generic calling convention often ends up winning in terms of performance.

You should also review the functions you call the most frequently to make sure the overhead with the parameters is as small as possible. Definitely avoid passing objects by value, and try to use primitives where possible.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#105Tzarls  Members

Posted 19 December 2012 - 04:11 PM

Just found some time to test the ABI and find how object with all-floats are passed by value. This are the results:

- If the object has any member that is NOT a float (int, double, etc) the the whole object is passed using the stack.
- If the object is all-floats but has more floats than available "s" registers, then the object is passed using the stack.
- The object doesn´t need to be aligned as a double - in other words, if s0 is already in use, the object will be passed starting at s1.
- The object won´t use any "wasted" registers. In other words, if s0 is in use, d1 is in use but s1 is available, the object will be passed starting at s4.

I think I know how to proceed now.

#106Andreas Jonsson  Moderators

Posted 19 December 2012 - 04:20 PM

So the other arguments in the function will also determine how the object is passed. This is good to know. At least the object is not split between registers and the stack.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#107Tzarls  Members

Posted 19 December 2012 - 06:37 PM

Yes. I guess it´s only a matter of checking is objectSize <= available registers, and if so then copy the object into those registers. Otherwise, copy the object to the stack area.

#108Tzarls  Members

Posted 19 December 2012 - 11:19 PM

Well, it looks like it´s woking - at least the test passes and all the other previously working tests are still working, so...

Now to the next bug: test_vector3.cpp fails at lines 114 and 120.

// Test some operator overloads
r = ExecuteString(engine, "vector3 v(1,0,0); assert( (v*2).length() == 2 );");
if( r != asEXECUTION_FINISHED )
{
TEST_FAILED;
}
r = ExecuteString(engine, "vector3 v(1,0,0); assert( (2*v).length() == 2 );");
if( r != asEXECUTION_FINISHED )
{
TEST_FAILED;
}


I´ll start investigating this tomorrow, but if you can think of anything obvious to watch for let me know.

#109Andreas Jonsson  Moderators

Posted 20 December 2012 - 11:53 AM

I believe this is actually a problem with how the vector3 type is registered and not the ABI implementation that you've been working on.

Take a look in the scriptmath3d.cpp function RegisterScriptMath3D_Native().


void RegisterScriptMath3D_Native(asIScriptEngine *engine)

{

int r;

// Register the type

r = engine->RegisterObjectType("vector3", sizeof(Vector3), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK); assert( r >= 0 );



I believe addding the asOBJ_APP_CLASS_ALLFLOATS flag will make the tests pass successfully.

Edited by Andreas Jonsson, 20 December 2012 - 11:55 AM.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#110Tzarls  Members

Posted 20 December 2012 - 11:47 PM

I added the asOBJ_APP_CLASS_ALLFLOATS flag when registering the type as you suggested but it didn´t work - meanwhile the operator* is being called using the armFuncR0R1, and based on previous experiences I guess it should be called using armFuncR0, right? So if the problem is that the callConv is getting incremented even when I specify asOBJ_APP_CLASS_ALLFLOATS when registering the object type, maybe there´s some other flag we should check for to see if we have to actually increment callConv or not?

This is the code I´m using to check if I should increment callConv or not:

if( sysFunc->hostReturnInMemory )
{
int vv=1;</span>
if ( !( descr->returnType.GetObjectType()->flags & COMPLEX_RETURN_MASK )  &&
( descr->returnType.GetObjectType()->flags & asOBJ_APP_CLASS_ALLFLOATS ) &&
descr->returnType.GetSizeInMemoryBytes() <= 8 )
vv--;

// The return is made in memory
callConv += vv;
}

(I know this piece of code can be optimized, but it gets the job done and I prefer to leave optimizations as the last step).

#111Andreas Jonsson  Moderators

Posted 21 December 2012 - 09:33 AM

What is the COMPLEX_RETURN_MASK defined as? It is defined in as_config.h.

If you haven't made any changes to the as_config.h with regards to this define, it is defined as asOBJ_APP_CLASS_DESTRUCTOR. But to be consistent with other gcc targets it should probably be defined as:

Still, if this flag was incorrectly defined, you would have gotten errors in testcdecl_class_k.cpp, which I assume you didn't.

I think it's best if you disassemble the operator* so we can see how the type really should be returned before you make any changes.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#112Tzarls  Members

Posted 21 December 2012 - 10:08 AM

I haven´t made any changes regarding to this define.

in as_config.h COMPLEX_RETURN_MASK is defined as (asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT). I´ll disassemble operator* and post the results ASAP.

#113Tzarls  Members

Posted 26 December 2012 - 06:39 PM

Ok, finally found time for some disassembling. First the code I used:

#include <iostream>
#include <string>
#include <math.h>

typedef unsigned long asDWORD;

struct Vector3
{
Vector3();
Vector3(const Vector3 &other);
Vector3(float x, float y, float z);

float length() const;

friend Vector3 operator*(float s, const Vector3 &v);
friend Vector3 operator*(const Vector3 &v, float s);

float x;
float y;
float z;
};

Vector3::Vector3()
{
x = 0;
y = 0;
z = 0;
}

Vector3::Vector3(const Vector3 &other)
{
x = other.x;
y = other.y;
z = other.z;
}

Vector3::Vector3(float _x, float _y, float _z)
{
x = _x;
y = _y;
z = _z;
}

float Vector3::length() const
{
return sqrtf(x*x + y*y + z*z);
}

Vector3 operator*(float s, const Vector3 &v)
{
// Return a new object as a script handle
Vector3 res(v.x * s, v.y * s, v.z * s);
return res;
}

Vector3 operator*(const Vector3 &v, float s)
{
// Return a new object as a script handle
Vector3 res(v.x * s, v.y * s, v.z * s);
return res;
}

int main()
{
Vector3 v(1,0,0);
bool x = false;

if ( (v * 2).length() == 2)
x = true;

std::cout << "El resultado de length() == 2 es " << x << "\n";
}

And the disassemblies:

main:

0x88a8 push {r11, lr}
0x88b0 sub sp, sp, #32
0x88b4 sub r3, r11, #32
0x88b8 mov r0, r3
0x88bc vldr s0, [pc, #164] ; 0x8968 <main()+192>
0x88c0 vldr s1, [pc, #164] ; 0x896c <main()+196>
0x88c4 vldr s2, [pc, #160] ; 0x896c <main()+196>
0x88c8 bl 0x8718 <Vector3::Vector3(float, float, float)>
0x88cc mov r3, #0
0x88d0 strb r3, [r11, #-5]
0x88d4 sub r2, r11, #20
0x88d8 sub r3, r11, #32
0x88dc mov r0, r2
0x88e0 mov r1, r3
0x88e4 vldr s0, [pc, #132] ; 0x8970 <main()+200>
0x88e8 bl 0x8840 <operator*(Vector3 const&, float)>
0x88ec sub r3, r11, #20
0x88f0 mov r0, r3
0x88f4 bl 0x876c <Vector3::length() const>
0x88f8 vmov.f32 s14, s0
0x88fc vldr s15, [pc, #108] ; 0x8970 <main()+200>
0x8900 vcmp.f32 s14, s15
0x8904 vmrs APSR_nzcv, fpscr
0x8908 movne r3, #0
0x890c moveq r3, #1
0x8910 uxtb r3, r3
0x8914 cmp r3, #0
0x8918 beq 0x8924 <main()+124>
0x891c mov r3, #1
0x8920 strb r3, [r11, #-5]
0x8924 ldr r0, [pc, #72] ; 0x8974 <main()+204>
0x8928 ldr r1, [pc, #72] ; 0x8978 <main()+208>
0x892c bl 0x85a4 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x8930 mov r3, r0
0x8934 mov r2, r3
0x8938 ldrb r3, [r11, #-5]
0x893c mov r0, r2
0x8940 mov r1, r3
0x8944 bl 0x85b0 <std::ostream::operator<<(bool)>
0x8948 mov r3, r0
0x894c mov r0, r3
0x8950 ldr r1, [pc, #36] ; 0x897c <main()+212>
0x8954 bl 0x85a4 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x8958 mov r3, #0
0x895c mov r0, r3
0x8960 sub sp, r11, #4
0x8964 pop {r11, pc}
0x8968 svccc 0x00800000
0x896c andeq r0, r0, r0
0x8970 andmi r0, r0, r0
0x8974 andeq r0, r1, r8, lsl r12
0x8978 andeq r8, r0, r0, ror r10
0x897c muleq r0, r4, r10

operator*:

0x8840 push {r11, lr}
0x8848 sub sp, sp, #16
0x884c str r0, [r11, #-8]
0x8850 str r1, [r11, #-12]
0x8854 vstr s0, [r11, #-16]
0x8858 ldr r3, [r11, #-12]
0x885c vldr s14, [r3]
0x8860 vldr s15, [r11, #-16]
0x8864 vmul.f32 s13, s14, s15
0x8868 ldr r3, [r11, #-12]
0x886c vldr s14, [r3, #4]
0x8870 vldr s15, [r11, #-16]
0x8874 vmul.f32 s14, s14, s15
0x8878 ldr r3, [r11, #-12]
0x887c vldr s12, [r3, #8]
0x8880 vldr s15, [r11, #-16]
0x8884 vmul.f32 s15, s12, s15
0x8888 ldr r0, [r11, #-8]
0x888c vmov.f32 s0, s13
0x8890 vmov.f32 s1, s14
0x8894 vmov.f32 s2, s15
0x8898 bl 0x8718 <Vector3::Vector3(float, float, float)>
0x889c ldr r0, [r11, #-8]
0x88a0 sub sp, r11, #4
0x88a4 pop {r11, pc}


I also made a mistake in checking how COMPLEX_RETURN_MASK was defined (I checked its definition under MSVC.... ). In the RPi COMPLEX_RETURN_MASK is defined as  (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR).

So, does this give you any ideas on how to fix the problem with this test?

#114Andreas Jonsson  Moderators

Posted 26 December 2012 - 07:56 PM

The type is clearly returned in memory, and the pointer to the memory is passed to the function in r0, the pointer to the object itself in r1, and the float value in s0.

So, Vector3::operator* should clearly be called with armFuncR0R1.

I'm pretty sure the problem is with the COMPLEX_RETURN_MASK. Despite that you say it is defined as (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) I don't think it is. On Nov 26th you showed the defines that get configured by running 'g++ -dM -E as_config.h' (see page 3 in this thread), and there it is seen that the flag is defined as asOBJ_APP_CLASS_DESTRUCTOR.

Add the following in as_config.h in the section for gnuc, Linux, arm (somewhere around line 780):
#undef COMPLEX_RETURN_MASK


I believe that will solve this test.

While you're at it you may also add the same for the COMPLEX_MASK, which really should be the same. This is similar to what is done for gnuc, Linux, x86 and gnuc, Linux, x64.

Regards,
Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#115Tzarls  Members

Posted 26 December 2012 - 08:41 PM

I'm pretty sure the problem is with the COMPLEX_RETURN_MASK. Despite that you say it is defined as (asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_COPY_CONSTRUCTOR) I don't think it is. On Nov 26th you showed the defines that get configured by running 'g++ -dM -E as_config.h' (see page 3 in this thread), and there it is seen that the flag is defined as asOBJ_APP_CLASS_DESTRUCTOR.

Add the following in as_config.h in the section for gnuc, Linux, arm (somewhere around line 780):
#undef COMPLEX_RETURN_MASK


I believe that will solve this test.

But we´ve already added those #undef - #define sections... check a few posts after the one you mention (november 27) and you´ll find the one in which you told me to do it. What´s more, it actually solved a bug back then.

Now... the problem might be that armFuncR0R1 hasn´t been adapted yet to the ABI... sorry aboyt that, I was assuming that operator* should be called using armFuncR0, but if it must use armFUncR0R1 then I´ll begin editing that function. I´ll be back with results.

Edited by Tzarls, 26 December 2012 - 08:42 PM.

#116Tzarls  Members

Posted 26 December 2012 - 11:44 PM

Just as I suspected - I ported armFuncR0R1 and now it works as it should. The next error is pointing to armFuncR0ObjLast, which is the other function I hadn´t ported yet. Fixing it right now...

#117Andreas Jonsson  Moderators

Posted 27 December 2012 - 08:20 AM

But we´ve already added those #undef - #define sections... check a few posts after the one you mention (november 27) and you´ll find the one in which you told me to do it. What´s more, it actually solved a bug back then.

You're right of course. The thread is too long to remember all that we've discussed before. ;)

I'm glad it's working now. Hopefully after you update the function armFuncR0ObjLast everything will pass successfully.

As I mentioned earlier, I'll increment the tests for the native calling convention in test_feature to cover these last scenarios better, so it will be easier for the next platform that needs to be supported.

Edited by Andreas Jonsson, 27 December 2012 - 09:01 AM.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#118Tzarls  Members

Posted 27 December 2012 - 12:17 PM

Victoria!

All tests pass succesfully! There´s only one thing left that´s bothering me: after the Test_Addon_Serializer passes, I´m getting a "GC cannot free an object of type '_builtin_function_', it is kept alive by the application" message. What does it mean? Do I have to worry about it?

One more question: when passing floats, does the args array carry pointers to the floats or the actual float values? Because depending on the answer my implementation could have one tiny little flaw....

#119Andreas Jonsson  Moderators

Posted 27 December 2012 - 02:15 PM

Excellent news! Congratulations on all your hard work. I know it took quite a bit of effort from your side, but I hope you've enjoyed it.

The GC error message indicates that something is wrong. Some object wasn't released as it should. I'll have to log in to do some debugging in order to find out what it is though.

The args array will contain the actual float values, unless the argument is a reference to a float of course.

Would you mind sending me the files you've modified by e-mail? I'll be going away for vacation in a soon (2nd week of January), but I'll try to find the time to merge your changes into the SVN before that.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#120Tzarls  Members

Posted 27 December 2012 - 02:31 PM

I´ll correct the tiny little flaw and do some cleanup to the code before sending you the files - as you may have already noticed, it´s full of comments and "cout" statements and other stuff to help debugging, even in the .S file. That should give you time to log in and try to find what´s causing the GC error.

Having AS running on the RPi is going to be awesome and I´m sure many people will be atracted to it (it being both RPi and AS)!

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.