Neon optimization


And vectorize, noting that NEON has support for scalar * vector multiplication and instructions for interleaving variables:

I was trying to speed up godot's 3d-physics by vectorizing some functions used by collision-tests.

So I when I found a loop, where results of one iteration didn't depend on results of any other iteration I decided to do 4 iterations at once using float32x4_t.

however I get an error when I try to compile:

error: invalid operands of types 'float32x4_t {aka __vector(4) __builtin_neon_sf}' and 'real_t {aka float}' to binary 'operator*'

from my understanding that means that it does not like multiplication of vectors with scalars ...

Does anyone have any idea what I might be doing wrong?

Are those scalar*vector operations only available for float32x2_t? (edit: changing them from float32x4_t to float32x2_t did nothing)

Or am I missing some compiler-switch or include?
 
Last edited by a moderator:
And vectorize, noting that NEON has support for scalar * vector multiplication and instructions for interleaving variables:

I was trying to speed up godot's 3d-physics by vectorizing some functions used by collision-tests.

So I when I found a loop, where results of one iteration didn't depend on results of any other iteration I decided to do 4 iterations at once using float32x4_t.

however I get an error when I try to compile:

error: invalid operands of types 'float32x4_t {aka __vector(4) __builtin_neon_sf}' and 'real_t {aka float}' to binary 'operator*'

from my understanding that means that it does not like multiplication of vectors with scalars ...

Does anyone have any idea what I might be doing wrong?

Are those scalar*vector operations only available for float32x2_t? (edit: changing them from float32x4_t to float32x2_t did nothing)

Or am I missing some compiler-switch or include?
Which version of GCC do you used?

Can you put a sample of your code?
 
g++ --version gives me "g++ (Sourcery CodeBench Lite 2011.09-70) 4.6.1"
 

also here the function I'm trying to optimize: (big wall of code ... will try to clean that up asap)
 

#ifdef NEON
#include <arm_neon.h>
#endif

...

void ConvexPolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {


int vertex_count=mesh.vertices.size();
if (vertex_count==0)
return;

const Vector3 *vrts=&mesh.vertices[0];
#ifndef NEON
for (int i=0;i<vertex_count;i++) {

float d=p_normal.dot( p_transform.xform( vrts ) );

if (i==0 || d > r_max)
r_max=d;
if (i==0 || d < r_min)
r_min=d;
}
#else
int i;
for (i=0;i<vertex_count-4;i+=4) { // as long as 4 calculations at a time are possible
/*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

return Vector3(
basis[0].dot(p_vector)+origin.x,
basis[1].dot(p_vector)+origin.y,
basis[2].dot(p_vector)+origin.z
);

}*/
//float d1, d2, d3, d4;

Matrix3 m = p_transform.get_basis();
Vector3 o = p_transform.get_origin();

//float f1_1, f1_2, f1_3, f1_4, f2_1, f2_2, f2_3, f2_4, f3_1, f3_2, f3_3, f3_4;
float32x4_t f1, f2, f3;
float32x4_t d;
float32x4_t vrts_x = {vrts.x, vrts[i+1].x, vrts[i+2].x, vrts[i+3].x};
float32x4_t vrts_y = {vrts.y, vrts[i+1].y, vrts[i+2].y, vrts[i+3].y};
float32x4_t vrts_z = {vrts.z, vrts[i+1].z, vrts[i+2].z, vrts[i+3].z};

/*f1_1 = m[0][0]*vrts[0];
f1_2 = m[0][0]*vrts[i+1][0];
f1_3 = m[0][0]*vrts[i+2][0];
f1_4 = m[0][0]*vrts[i+3][0];*/
f1 = vrts_x * m[0][0];

/*f2_1 = m[1][0]*vrts[0];
f2_2 = m[1][0]*vrts[i+1][0];
f2_3 = m[1][0]*vrts[i+2][0];
f2_4 = m[1][0]*vrts[i+3][0];*/
f2 = m[1][0] * vrts_x;

/*f3_1 = m[2][0]*vrts[0];
f3_2 = m[2][0]*vrts[i+1][0];
f3_3 = m[2][0]*vrts[i+2][0];
f3_4 = m[2][0]*vrts[i+3][0];*/
f3 = m[2][0] * vrts_x;

/*f1_1 += m[0][1]*vrts[1];
f1_2 += m[0][1]*vrts[i+1][1];
f1_3 += m[0][1]*vrts[i+2][1];
f1_4 += m[0][1]*vrts[i+3][1];*/
f1 += m[0][1] * vrts_y;

/*f2_1 += m[1][1]*vrts[1];
f2_2 += m[1][1]*vrts[i+1][1];
f2_3 += m[1][1]*vrts[i+2][1];
f2_4 += m[1][1]*vrts[i+3][1];*/
f2 += m[1][1] * vrts_y;

/*f3_1 += m[2][1]*vrts[1];
f3_2 += m[2][1]*vrts[i+1][1];
f3_3 += m[2][1]*vrts[i+2][1];
f3_4 += m[2][1]*vrts[i+3][1];*/
f3 += m[2][1] * vrts_y;


/*f1_1 += m[0][2]*vrts[2];
f1_2 += m[0][2]*vrts[i+1][2];
f1_3 += m[0][2]*vrts[i+2][2];
f1_4 += m[0][2]*vrts[i+3][2];*/
f1 += m[0][2] * vrts_z;

/*f2_1 += m[1][2]*vrts[2];
f2_2 += m[1][2]*vrts[i+1][2];
f2_3 += m[1][2]*vrts[i+2][2];
f2_4 += m[1][2]*vrts[i+3][2];*/
f2 += m[1][2] * vrts_z;

/*f3_1 += m[2][2]*vrts[2];
f3_2 += m[2][2]*vrts[i+1][2];
f3_3 += m[2][2]*vrts[i+2][2];
f3_4 += m[2][2]*vrts[i+3][2];*/
f3 += m[2][2] * vrts_z;

/*f1_1 += o[0];
f1_2 += o[0];
f1_3 += o[0];
f1_4 += o[0];*/
f1 += o[0];

/*f2_1 += o[1];
f2_2 += o[1];
f2_3 += o[1];
f2_4 += o[1];*/
f2 += o[1];

/*f3_1 += o[2];
f3_2 += o[2];
f3_3 += o[2];
f3_4 += o[2];*/
f3 += o[2];

/*d1 = f1_1*p_normal[0];
d2 = f1_2*p_normal[0];
d3 = f1_3*p_normal[0];
d4 = f1_4*p_normal[0];*/
d = f1 * p_normal[0];

/*d1 += f2_1*p_normal[1];
d2 += f2_2*p_normal[1];
d3 += f2_3*p_normal[1];
d4 += f2_4*p_normal[1];*/
d = f2 * p_normal[1];

/*d1 += f3_1*p_normal[2];
d2 += f3_2*p_normal[2];
d3 += f3_3*p_normal[2];
d4 += f3_4*p_normal[2];*/
d = f3 * p_normal[2];

if (i==0 || d[0] > r_max)
r_max=d[0];
if (i==0 || d[0] < r_min)
r_min=d[0];

if (i==0 || d[1] > r_max)
r_max=d[1];
if (i==0 || d[1] < r_min)
r_min=d[1];

if (i==0 || d[2] > r_max)
r_max=d[2];
if (i==0 || d[2] < r_min)
r_min=d[2];

if (i==0 || d[3] > r_max)
r_max=d[3];
if (i==0 || d[3] < r_min)
r_min=d[3];
}
for (i=i;i<vertex_count;i++) { // rest
/*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

return Vector3(
basis[0].dot(p_vector)+origin.x,
basis[1].dot(p_vector)+origin.y,
basis[2].dot(p_vector)+origin.z
);

}*/

float d;

Matrix3 m = p_transform.get_basis();
Vector3 o = p_transform.get_origin();

float f1, f2, f3;

f1 = m[0][0]*vrts[0];
f2 = m[1][0]*vrts[0];
f3 = m[2][0]*vrts[0];

f1 += m[0][1]*vrts[1];
f2 += m[1][1]*vrts[1];
f3 += m[2][1]*vrts[1];

f1 += m[0][2]*vrts[2];
f2 += m[1][2]*vrts[2];
f3 += m[2][2]*vrts[2];

f1 += o[0];
f2 += o[1];
f3 += o[2];

d = f1*p_normal[0];
d += f2*p_normal[1];
d += f3*p_normal[2];

if (i==0 || d > r_max)
r_max=d;
if (i==0 || d < r_min)
r_min=d;
}
#endif
}





edit: here also the compiler-switches that I can see in the shell:

-march=armv7-a -mcpu=cortex-a8 -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard  -DNEON -O2 -ffast-math -fomit-frame-pointer

edit2: shortened version of the code:

Code:
#ifdef NEON
    #include <arm_neon.h>
#endif

...

void ConvexPolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {
	...
    #ifndef NEON
	for (int i=0;i<vertex_count;i++) {

		...
	}
    #else
    int i;
    for (i=0;i<vertex_count-4;i+=4) { // as long as 4 calculations at a time are possible
            
            Matrix3 m = p_transform.get_basis();
            Vector3 o = p_transform.get_origin();
            
            float32x4_t f1, f2, f3;
            float32x4_t d;
            float32x4_t vrts_x = {vrts[i].x, vrts[i+1].x, vrts[i+2].x, vrts[i+3].x};
            float32x4_t vrts_y = {vrts[i].y, vrts[i+1].y, vrts[i+2].y, vrts[i+3].y};
            float32x4_t vrts_z = {vrts[i].z, vrts[i+1].z, vrts[i+2].z, vrts[i+3].z};

            f1 = vrts_x * m[0][0];
            f2 = m[1][0] * vrts_x;
            f3 = m[2][0] * vrts_x;

            f1 += m[0][1] * vrts_y;
            f2 += m[1][1] * vrts_y;
            f3 += m[2][1] * vrts_y;

            f1 += m[0][2] * vrts_z;
            f2 += m[1][2] * vrts_z;
            f3 += m[2][2] * vrts_z;
            f1 += o[0];
            f2 += o[1];
            f3 += o[2];
            

            d = f1 * p_normal[0];
            d = f2 * p_normal[1];
            d = f3 * p_normal[2];
            
            ...
    }  
    for (i=i;i<vertex_count;i++) { 
           ...
    }    
    #endif
}
 
Last edited by a moderator:
g++ --version gives me "g++ (Sourcery CodeBench Lite 2011.09-70) 4.6.1"


also here the function I'm trying to optimize: (big wall of code ... will try to clean that up asap)

#ifdef NEON
#include <arm_neon.h>
#endif

...

void ConvexPolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {


int vertex_count=mesh.vertices.size();
if (vertex_count==0)
return;

const Vector3 *vrts=&mesh.vertices[0];
#ifndef NEON
for (int i=0;i<vertex_count;i++) {

float d=p_normal.dot( p_transform.xform( vrts ) );

if (i==0 || d > r_max)
r_max=d;
if (i==0 || d < r_min)
r_min=d;
}
#else
int i;
for (i=0;i<vertex_count-4;i+=4) { // as long as 4 calculations at a time are possible
/*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

return Vector3(
basis[0].dot(p_vector)+origin.x,
basis[1].dot(p_vector)+origin.y,
basis[2].dot(p_vector)+origin.z
);

}*/
//float d1, d2, d3, d4;

Matrix3 m = p_transform.get_basis();
Vector3 o = p_transform.get_origin();

//float f1_1, f1_2, f1_3, f1_4, f2_1, f2_2, f2_3, f2_4, f3_1, f3_2, f3_3, f3_4;
float32x4_t f1, f2, f3;
float32x4_t d;
float32x4_t vrts_x = {vrts.x, vrts[i+1].x, vrts[i+2].x, vrts[i+3].x};
float32x4_t vrts_y = {vrts.y, vrts[i+1].y, vrts[i+2].y, vrts[i+3].y};
float32x4_t vrts_z = {vrts.z, vrts[i+1].z, vrts[i+2].z, vrts[i+3].z};

/*f1_1 = m[0][0]*vrts[0];
f1_2 = m[0][0]*vrts[i+1][0];
f1_3 = m[0][0]*vrts[i+2][0];
f1_4 = m[0][0]*vrts[i+3][0];*/
f1 = vrts_x * m[0][0];

/*f2_1 = m[1][0]*vrts[0];
f2_2 = m[1][0]*vrts[i+1][0];
f2_3 = m[1][0]*vrts[i+2][0];
f2_4 = m[1][0]*vrts[i+3][0];*/
f2 = m[1][0] * vrts_x;

/*f3_1 = m[2][0]*vrts[0];
f3_2 = m[2][0]*vrts[i+1][0];
f3_3 = m[2][0]*vrts[i+2][0];
f3_4 = m[2][0]*vrts[i+3][0];*/
f3 = m[2][0] * vrts_x;

/*f1_1 += m[0][1]*vrts[1];
f1_2 += m[0][1]*vrts[i+1][1];
f1_3 += m[0][1]*vrts[i+2][1];
f1_4 += m[0][1]*vrts[i+3][1];*/
f1 += m[0][1] * vrts_y;

/*f2_1 += m[1][1]*vrts[1];
f2_2 += m[1][1]*vrts[i+1][1];
f2_3 += m[1][1]*vrts[i+2][1];
f2_4 += m[1][1]*vrts[i+3][1];*/
f2 += m[1][1] * vrts_y;

/*f3_1 += m[2][1]*vrts[1];
f3_2 += m[2][1]*vrts[i+1][1];
f3_3 += m[2][1]*vrts[i+2][1];
f3_4 += m[2][1]*vrts[i+3][1];*/
f3 += m[2][1] * vrts_y;


/*f1_1 += m[0][2]*vrts[2];
f1_2 += m[0][2]*vrts[i+1][2];
f1_3 += m[0][2]*vrts[i+2][2];
f1_4 += m[0][2]*vrts[i+3][2];*/
f1 += m[0][2] * vrts_z;

/*f2_1 += m[1][2]*vrts[2];
f2_2 += m[1][2]*vrts[i+1][2];
f2_3 += m[1][2]*vrts[i+2][2];
f2_4 += m[1][2]*vrts[i+3][2];*/
f2 += m[1][2] * vrts_z;

/*f3_1 += m[2][2]*vrts[2];
f3_2 += m[2][2]*vrts[i+1][2];
f3_3 += m[2][2]*vrts[i+2][2];
f3_4 += m[2][2]*vrts[i+3][2];*/
f3 += m[2][2] * vrts_z;

/*f1_1 += o[0];
f1_2 += o[0];
f1_3 += o[0];
f1_4 += o[0];*/
f1 += o[0];

/*f2_1 += o[1];
f2_2 += o[1];
f2_3 += o[1];
f2_4 += o[1];*/
f2 += o[1];

/*f3_1 += o[2];
f3_2 += o[2];
f3_3 += o[2];
f3_4 += o[2];*/
f3 += o[2];

/*d1 = f1_1*p_normal[0];
d2 = f1_2*p_normal[0];
d3 = f1_3*p_normal[0];
d4 = f1_4*p_normal[0];*/
d = f1 * p_normal[0];

/*d1 += f2_1*p_normal[1];
d2 += f2_2*p_normal[1];
d3 += f2_3*p_normal[1];
d4 += f2_4*p_normal[1];*/
d = f2 * p_normal[1];

/*d1 += f3_1*p_normal[2];
d2 += f3_2*p_normal[2];
d3 += f3_3*p_normal[2];
d4 += f3_4*p_normal[2];*/
d = f3 * p_normal[2];

if (i==0 || d[0] > r_max)
r_max=d[0];
if (i==0 || d[0] < r_min)
r_min=d[0];

if (i==0 || d[1] > r_max)
r_max=d[1];
if (i==0 || d[1] < r_min)
r_min=d[1];

if (i==0 || d[2] > r_max)
r_max=d[2];
if (i==0 || d[2] < r_min)
r_min=d[2];

if (i==0 || d[3] > r_max)
r_max=d[3];
if (i==0 || d[3] < r_min)
r_min=d[3];
}
for (i=i;i<vertex_count;i++) { // rest
/*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

return Vector3(
basis[0].dot(p_vector)+origin.x,
basis[1].dot(p_vector)+origin.y,
basis[2].dot(p_vector)+origin.z
);

}*/

float d;

Matrix3 m = p_transform.get_basis();
Vector3 o = p_transform.get_origin();

float f1, f2, f3;

f1 = m[0][0]*vrts[0];
f2 = m[1][0]*vrts[0];
f3 = m[2][0]*vrts[0];

f1 += m[0][1]*vrts[1];
f2 += m[1][1]*vrts[1];
f3 += m[2][1]*vrts[1];

f1 += m[0][2]*vrts[2];
f2 += m[1][2]*vrts[2];
f3 += m[2][2]*vrts[2];

f1 += o[0];
f2 += o[1];
f3 += o[2];

d = f1*p_normal[0];
d += f2*p_normal[1];
d += f3*p_normal[2];

if (i==0 || d > r_max)
r_max=d;
if (i==0 || d < r_min)
r_min=d;
}
#endif
}






edit: here also the compiler-switches that I can see in the shell:

-march=armv7-a -mcpu=cortex-a8 -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard  -DNEON -O2 -ffast-math -fomit-frame-pointer

for Pandora, -mfloat-abi is softfp.

Instead of trying to use scalar*float32x4_t, maybe try to use vmulq_n_f32(dest, source, scalar) ?
 
Ah ok, I switched to hardfp after I got the first errors with those optimizations because of something I read on the wiki:

Therefore i recommend the following flags:


-O3 -mcpu=cortex-a8 -mfpu=neon -ftree-vectorize -mfloat-abi=(softfp|hard) -ffast-math -fsingle-precision-constant
where -mfloat-abi=hard for the CSL 2009q1 release and softfp for all the others.
Now I switched back to softfp the errors are still present, but changing


f1 = vrts_x * m[0][0];

to:


f1 = vmulq_n_f32(vrts_x, m[0][0]);

made the error dissapear for at least that line.

Thanks, I will change all those lines now to use these functions and see if that works (or if I broke everything).
 
Last edited by a moderator:
Thanks, got it to work.
I didn't expect any wonders since that was just a single function used by the collisiondetection.
However it did:
 
 

https://www.youtube.com/embed/B71zu28GK24?feature=oembed
 
Looks like profiling on PC with gprof was a good idea :D
 
 
In case anyone is interested, here the final code: (I left that commented out code in there because it might help to understand what is going on)


Code:
void ConvexPolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {


	int vertex_count=mesh.vertices.size();
	if (vertex_count==0)
		return;

	const Vector3 *vrts=&mesh.vertices[0];
    #ifndef NEON
	for (int i=0;i<vertex_count;i++) {

		float d=p_normal.dot( p_transform.xform( vrts[i] ) );

		if (i==0 || d > r_max)
			r_max=d;
		if (i==0 || d < r_min)
			r_min=d;
	}
    #else
    int i;
    Matrix3 m = p_transform.get_basis();
    Vector3 o = p_transform.get_origin();
    float32x4_t vo[4] = {{o[0],o[0],o[0],o[0]} , {o[1],o[1],o[1],o[1]} , {o[2],o[2],o[2],o[2]} , {o[3],o[3],o[3],o[3]}};
    for (i=0;i<vertex_count-4;i+=4) { // as long as 4 calculations at a time are possible
            /*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

            return Vector3(
                basis[0].dot(p_vector)+origin.x,
                basis[1].dot(p_vector)+origin.y,
                basis[2].dot(p_vector)+origin.z
            );
            
            }*/
            //print_line("yay");
            //float d1, d2, d3, d4;
            
            
            
            //float f1_1, f1_2, f1_3, f1_4, f2_1, f2_2, f2_3, f2_4, f3_1, f3_2, f3_3, f3_4;
            float32x4_t f1, f2, f3;
            float32x4_t d;
            float32x4_t vrts_x = {vrts[i].x, vrts[i+1].x, vrts[i+2].x, vrts[i+3].x};
            float32x4_t vrts_y = {vrts[i].y, vrts[i+1].y, vrts[i+2].y, vrts[i+3].y};
            float32x4_t vrts_z = {vrts[i].z, vrts[i+1].z, vrts[i+2].z, vrts[i+3].z};
            
            /*f1_1 = m[0][0]*vrts[i][0];
            f1_2 = m[0][0]*vrts[i+1][0];
            f1_3 = m[0][0]*vrts[i+2][0];
            f1_4 = m[0][0]*vrts[i+3][0];*/
            
            //f1 = vrts_x * m[0][0];
            f1 = vmulq_n_f32(vrts_x, m[0][0]);
            
            /*f2_1 = m[1][0]*vrts[i][0];
            f2_2 = m[1][0]*vrts[i+1][0];
            f2_3 = m[1][0]*vrts[i+2][0];
            f2_4 = m[1][0]*vrts[i+3][0];*/
            
            //f2 = m[1][0] * vrts_x;
            f2 = vmulq_n_f32(vrts_x, m[1][0]);
            
            /*f3_1 = m[2][0]*vrts[i][0];
            f3_2 = m[2][0]*vrts[i+1][0];
            f3_3 = m[2][0]*vrts[i+2][0];
            f3_4 = m[2][0]*vrts[i+3][0];*/
            
            //f3 = m[2][0] * vrts_x;
            f3 = vmulq_n_f32(vrts_x, m[2][0]);
            
            /*f1_1 += m[0][1]*vrts[i][1];
            f1_2 += m[0][1]*vrts[i+1][1];
            f1_3 += m[0][1]*vrts[i+2][1];
            f1_4 += m[0][1]*vrts[i+3][1];*/
            
            //f1 += m[0][1] * vrts_y;
            f1 += vmulq_n_f32(vrts_y, m[0][1]);
            
            /*f2_1 += m[1][1]*vrts[i][1];
            f2_2 += m[1][1]*vrts[i+1][1];
            f2_3 += m[1][1]*vrts[i+2][1];
            f2_4 += m[1][1]*vrts[i+3][1];*/
            
            //f2 += m[1][1] * vrts_y;
            f2 += vmulq_n_f32(vrts_y, m[1][1]);
            
            /*f3_1 += m[2][1]*vrts[i][1];
            f3_2 += m[2][1]*vrts[i+1][1];
            f3_3 += m[2][1]*vrts[i+2][1];
            f3_4 += m[2][1]*vrts[i+3][1];*/
            
            //f3 += m[2][1] * vrts_y;
            f3 += vmulq_n_f32(vrts_y, m[2][1]);
            
            
            /*f1_1 += m[0][2]*vrts[i][2];
            f1_2 += m[0][2]*vrts[i+1][2];
            f1_3 += m[0][2]*vrts[i+2][2];
            f1_4 += m[0][2]*vrts[i+3][2];*/
            
            //f1 += m[0][2] * vrts_z;
            f1 += vmulq_n_f32(vrts_z, m[0][2]);
            
            /*f2_1 += m[1][2]*vrts[i][2];
            f2_2 += m[1][2]*vrts[i+1][2];
            f2_3 += m[1][2]*vrts[i+2][2];
            f2_4 += m[1][2]*vrts[i+3][2];*/
            
            //f2 += m[1][2] * vrts_z;
            f2 += vmulq_n_f32(vrts_z, m[1][2]);
            
            /*f3_1 += m[2][2]*vrts[i][2];
            f3_2 += m[2][2]*vrts[i+1][2];
            f3_3 += m[2][2]*vrts[i+2][2];
            f3_4 += m[2][2]*vrts[i+3][2];*/
            
            //f3 += m[2][2] * vrts_z;
            f3 += vmulq_n_f32(vrts_z, m[2][2]);
            
            /*f1_1 += o[0];
            f1_2 += o[0];
            f1_3 += o[0];
            f1_4 += o[0];*/
            f1 += vo[0];
            
            /*f2_1 += o[1];
            f2_2 += o[1];
            f2_3 += o[1];
            f2_4 += o[1];*/
            f2 += vo[1];
            
            /*f3_1 += o[2];
            f3_2 += o[2];
            f3_3 += o[2];
            f3_4 += o[2];*/
            f3 += vo[2];
            
            /*d1 = f1_1*p_normal[0];
            d2 = f1_2*p_normal[0];
            d3 = f1_3*p_normal[0];
            d4 = f1_4*p_normal[0];*/
            d = vmulq_n_f32(f1 , p_normal[0]);
            
            /*d1 += f2_1*p_normal[1];
            d2 += f2_2*p_normal[1];
            d3 += f2_3*p_normal[1];
            d4 += f2_4*p_normal[1];*/
            d += vmulq_n_f32(f2 , p_normal[1]);
            
            /*d1 += f3_1*p_normal[2];
            d2 += f3_2*p_normal[2];
            d3 += f3_3*p_normal[2];
            d4 += f3_4*p_normal[2];*/
            d += vmulq_n_f32(f3 , p_normal[2]);
            
            float *fd = (float *)&d;
            if (i==0 || fd[0] > r_max)
                r_max=fd[0];
            if (i==0 || fd[0] < r_min)
                r_min=fd[0];
                
            if (i==0 || fd[1] > r_max)
                r_max=fd[1];
            if (i==0 || fd[1] < r_min)
                r_min=fd[1];
                
            if (i==0 || fd[2] > r_max)
                r_max=fd[2];
            if (i==0 || fd[2] < r_min)
                r_min=fd[2];
                
            if (i==0 || fd[3] > r_max)
                r_max=fd[3];
            if (i==0 || fd[3] < r_min)
                r_min=fd[3];
    }  
    for (i=i;i<vertex_count;i++) { // rest
            /*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

            return Vector3(
                basis[0].dot(p_vector)+origin.x,
                basis[1].dot(p_vector)+origin.y,
                basis[2].dot(p_vector)+origin.z
            );
            
            }*/
            
            float d;
            
            Matrix3 m = p_transform.get_basis();
            Vector3 o = p_transform.get_origin();
            
            float f1, f2, f3;
            
            f1 = m[0][0]*vrts[i][0];
            f2 = m[1][0]*vrts[i][0];
            f3 = m[2][0]*vrts[i][0];
            
            f1 += m[0][1]*vrts[i][1];
            f2 += m[1][1]*vrts[i][1];
            f3 += m[2][1]*vrts[i][1];
            
            f1 += m[0][2]*vrts[i][2];
            f2 += m[1][2]*vrts[i][2];
            f3 += m[2][2]*vrts[i][2];
            
            f1 += o[0];
            f2 += o[1];
            f3 += o[2];
            
            d = f1*p_normal[0];
            d += f2*p_normal[1];
            d += f3*p_normal[2];
            
            if (i==0 || d > r_max)
                r_max=d;
            if (i==0 || d < r_min)
                r_min=d;
    }    
    #endif
}
 
Thanks, got it to work.


I didn't expect any wonders since that was just a single function used by the collisiondetection.


However it did:


Looks like profiling on PC with gprof was a good idea :D


In case anyone is interested, here the final code: (I left that commented out code in there because it might help to understand what is going on)

void ConvexPolygonShapeSW::project_range(const Vector3& p_normal, const Transform& p_transform, real_t &r_min, real_t &r_max) const {


int vertex_count=mesh.vertices.size();
if (vertex_count==0)
return;

const Vector3 *vrts=&mesh.vertices[0];
#ifndef NEON
for (int i=0;i<vertex_count;i++) {

float d=p_normal.dot( p_transform.xform( vrts ) );

if (i==0 || d > r_max)
r_max=d;
if (i==0 || d < r_min)
r_min=d;
}
#else
int i;
Matrix3 m = p_transform.get_basis();
Vector3 o = p_transform.get_origin();
float32x4_t vo[4] = {{o[0],o[0],o[0],o[0]} , {o[1],o[1],o[1],o[1]} , {o[2],o[2],o[2],o[2]} , {o[3],o[3],o[3],o[3]}};
for (i=0;i<vertex_count-4;i+=4) { // as long as 4 calculations at a time are possible
/*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

return Vector3(
basis[0].dot(p_vector)+origin.x,
basis[1].dot(p_vector)+origin.y,
basis[2].dot(p_vector)+origin.z
);

}*/
//print_line("yay");
//float d1, d2, d3, d4;



//float f1_1, f1_2, f1_3, f1_4, f2_1, f2_2, f2_3, f2_4, f3_1, f3_2, f3_3, f3_4;
float32x4_t f1, f2, f3;
float32x4_t d;
float32x4_t vrts_x = {vrts.x, vrts[i+1].x, vrts[i+2].x, vrts[i+3].x};
float32x4_t vrts_y = {vrts.y, vrts[i+1].y, vrts[i+2].y, vrts[i+3].y};
float32x4_t vrts_z = {vrts.z, vrts[i+1].z, vrts[i+2].z, vrts[i+3].z};

/*f1_1 = m[0][0]*vrts[0];
f1_2 = m[0][0]*vrts[i+1][0];
f1_3 = m[0][0]*vrts[i+2][0];
f1_4 = m[0][0]*vrts[i+3][0];*/

//f1 = vrts_x * m[0][0];
f1 = vmulq_n_f32(vrts_x, m[0][0]);

/*f2_1 = m[1][0]*vrts[0];
f2_2 = m[1][0]*vrts[i+1][0];
f2_3 = m[1][0]*vrts[i+2][0];
f2_4 = m[1][0]*vrts[i+3][0];*/

//f2 = m[1][0] * vrts_x;
f2 = vmulq_n_f32(vrts_x, m[1][0]);

/*f3_1 = m[2][0]*vrts[0];
f3_2 = m[2][0]*vrts[i+1][0];
f3_3 = m[2][0]*vrts[i+2][0];
f3_4 = m[2][0]*vrts[i+3][0];*/

//f3 = m[2][0] * vrts_x;
f3 = vmulq_n_f32(vrts_x, m[2][0]);

/*f1_1 += m[0][1]*vrts[1];
f1_2 += m[0][1]*vrts[i+1][1];
f1_3 += m[0][1]*vrts[i+2][1];
f1_4 += m[0][1]*vrts[i+3][1];*/

//f1 += m[0][1] * vrts_y;
f1 += vmulq_n_f32(vrts_y, m[0][1]);

/*f2_1 += m[1][1]*vrts[1];
f2_2 += m[1][1]*vrts[i+1][1];
f2_3 += m[1][1]*vrts[i+2][1];
f2_4 += m[1][1]*vrts[i+3][1];*/

//f2 += m[1][1] * vrts_y;
f2 += vmulq_n_f32(vrts_y, m[1][1]);

/*f3_1 += m[2][1]*vrts[1];
f3_2 += m[2][1]*vrts[i+1][1];
f3_3 += m[2][1]*vrts[i+2][1];
f3_4 += m[2][1]*vrts[i+3][1];*/

//f3 += m[2][1] * vrts_y;
f3 += vmulq_n_f32(vrts_y, m[2][1]);


/*f1_1 += m[0][2]*vrts[2];
f1_2 += m[0][2]*vrts[i+1][2];
f1_3 += m[0][2]*vrts[i+2][2];
f1_4 += m[0][2]*vrts[i+3][2];*/

//f1 += m[0][2] * vrts_z;
f1 += vmulq_n_f32(vrts_z, m[0][2]);

/*f2_1 += m[1][2]*vrts[2];
f2_2 += m[1][2]*vrts[i+1][2];
f2_3 += m[1][2]*vrts[i+2][2];
f2_4 += m[1][2]*vrts[i+3][2];*/

//f2 += m[1][2] * vrts_z;
f2 += vmulq_n_f32(vrts_z, m[1][2]);

/*f3_1 += m[2][2]*vrts[2];
f3_2 += m[2][2]*vrts[i+1][2];
f3_3 += m[2][2]*vrts[i+2][2];
f3_4 += m[2][2]*vrts[i+3][2];*/

//f3 += m[2][2] * vrts_z;
f3 += vmulq_n_f32(vrts_z, m[2][2]);

/*f1_1 += o[0];
f1_2 += o[0];
f1_3 += o[0];
f1_4 += o[0];*/
f1 += vo[0];

/*f2_1 += o[1];
f2_2 += o[1];
f2_3 += o[1];
f2_4 += o[1];*/
f2 += vo[1];

/*f3_1 += o[2];
f3_2 += o[2];
f3_3 += o[2];
f3_4 += o[2];*/
f3 += vo[2];

/*d1 = f1_1*p_normal[0];
d2 = f1_2*p_normal[0];
d3 = f1_3*p_normal[0];
d4 = f1_4*p_normal[0];*/
d = vmulq_n_f32(f1 , p_normal[0]);

/*d1 += f2_1*p_normal[1];
d2 += f2_2*p_normal[1];
d3 += f2_3*p_normal[1];
d4 += f2_4*p_normal[1];*/
d += vmulq_n_f32(f2 , p_normal[1]);

/*d1 += f3_1*p_normal[2];
d2 += f3_2*p_normal[2];
d3 += f3_3*p_normal[2];
d4 += f3_4*p_normal[2];*/
d += vmulq_n_f32(f3 , p_normal[2]);

float *fd = (float *)&d;
if (i==0 || fd[0] > r_max)
r_max=fd[0];
if (i==0 || fd[0] < r_min)
r_min=fd[0];

if (i==0 || fd[1] > r_max)
r_max=fd[1];
if (i==0 || fd[1] < r_min)
r_min=fd[1];

if (i==0 || fd[2] > r_max)
r_max=fd[2];
if (i==0 || fd[2] < r_min)
r_min=fd[2];

if (i==0 || fd[3] > r_max)
r_max=fd[3];
if (i==0 || fd[3] < r_min)
r_min=fd[3];
}
for (i=i;i<vertex_count;i++) { // rest
/*_FORCE_INLINE_ Vector3 Transform::xform(const Vector3& p_vector) const {

return Vector3(
basis[0].dot(p_vector)+origin.x,
basis[1].dot(p_vector)+origin.y,
basis[2].dot(p_vector)+origin.z
);

}*/

float d;

Matrix3 m = p_transform.get_basis();
Vector3 o = p_transform.get_origin();

float f1, f2, f3;

f1 = m[0][0]*vrts[0];
f2 = m[1][0]*vrts[0];
f3 = m[2][0]*vrts[0];

f1 += m[0][1]*vrts[1];
f2 += m[1][1]*vrts[1];
f3 += m[2][1]*vrts[1];

f1 += m[0][2]*vrts[2];
f2 += m[1][2]*vrts[2];
f3 += m[2][2]*vrts[2];

f1 += o[0];
f2 += o[1];
f3 += o[2];

d = f1*p_normal[0];
d += f2*p_normal[1];
d += f3*p_normal[2];

if (i==0 || d > r_max)
r_max=d;
if (i==0 || d < r_min)
r_min=d;
}
#endif
}




Nice improvement.

You still have a few issue in your GLES2 port. It looks like some type in some geometry transform to me, like the "z" that is in fact "y" coordinates (so review your code, I think you have a [2] instead of [3], i.e. 2 times [2] somewhere in the geometry transformation).
 
You still have a few issue in your GLES2 port. It looks like some type in some geometry transform to me, like the "z" that is in fact "y" coordinates (so review your code, I think you have a [2] instead of [3], i.e. 2 times [2] somewhere in the geometry transformation).
I was able to fix that with the last build, however I don't understand why ... I got pretty desperate and started changing highp variables within the vertexshader to mediump. Before doing so depending on the cameras and the objects z-axis-rotation one of the axis got a scaling by a value between 1 and 0 (0 at 90 and 270 degrees and 1 at 0 and 180 degrees). This only happened when there was a light (and in case of a pointlight it had to be of at least a certain size ...) within the scene ... this was a nightmare (and since I don't really under stand what the cause was it still is somehow)...
 
Just figured I'd throw this down somewhere, and this seems like kinda the right place... the math-neon code, if you have issues with it returning weirdness from cos (or any of the functions), just make sure you modify the code to use the PROPER functions:

Make sure cosf_neon_hfp(float) returns


sinf_neon(xx)
not


sinf_neon_hfp(xx)
Otherwise problems will ensue, I'm sure other functions have small quirks like this, but figured seeing as it's one that I ran into I'd bring it to people's attention ;)

P.S. Feels like I'm digging around in a Cemetery looking @ these and the GP32x boards again...
 
Back
Top