J.Doe
J.Doe

Reputation: 1552

Should I switch from structs and classes to arrays?

So I have a program. And I am trying to simulate tons of moving particles with intricate moment logic that i would not want to have going on the CGP for many reasons. Of course I am then going to draw this all on the GPU.

Now originally I thought that when simulating TONS of particles that GPU delay would be a problem not the CPU. Unfortunately I am running 500 particles at a whopping 6fps :(.

I have tracked the latency down to how I send the vertices to the particle simulator. And not even the buffer creation, simply how I build the arrays. Basically I have arrays I clear every frame, and then go through for each particle in an array of particles and create arrays for each of them. And this leads to around 17500 append calls (with 500 particles). So I need a different way to do this because without building these arrays it runs at 60fps no cpu latency. Most of these append calls call a member of a struct.

Currently each particle is made based off of a class object. And it has things like position and color that are stored in structs. Would it be wroth my while to switch structs to arrays? Or perhaps I should switch everything to arrays? Obviously doing any of that would make things much harder to program. But would it be worth it?

A big problem is that I need each particle to be drawn as a capsule. Which I would make out of two dots and a thick line. Unfortunately OpenGL es 2.0 doesn't support thick lines so I have to draw it with two dots and two triangles :(. As you can see the function "calculateSquare" makes these two triangles based off to the two points. It is also very laggy, however it isn't the only problem, I will try to find a different way later.

What are your thoughts?

Note: According to xcode ram usage is only at 10 mb. However the cpu frame time is 141 ms.

Here is the code BTW:

func buildParticleArrays()
    {
        lineStrip = []
        lineStripColors = []
        lineStripsize = []
        s_vertes = []
        s_color = []
        s_size = []
        for cparticle in particles
        {
            let pp = cparticle.lastPosition
            let np = cparticle.position
            if (cparticle.frozen == true)
            {
                addPoint(cparticle.position, color: cparticle.color, size: cparticle.size)
            }
            else
            {
                let s = cparticle.size / 2.0

                //Add point merely adds the data in array format
                addPoint(cparticle.position, color: cparticle.color, size: cparticle.size)
                addPoint(cparticle.lastPosition, color: cparticle.color, size: cparticle.size)

                lineStrip += calculateSquare(pp, pp2: np, size: s)


                for var i = 0; i < 6; i++
                {
                    let rgb = hsvtorgb(cparticle.color)
                    lineStripColors.append(GLfloat(rgb.r))
                    lineStripColors.append(GLfloat(rgb.g))
                    lineStripColors.append(GLfloat(rgb.b))
                    lineStripColors.append(GLfloat(rgb.a))
                    lineStripsize.append(GLfloat(cparticle.size))
                }
            }



        }
    }
func addPoint(theObject: point, color: colorhsv, size: CGFloat)
    {

        let rgb = hsvtorgb(color)
        s_vertes += [GLfloat(theObject.x), GLfloat(theObject.y), GLfloat(theObject.z)]
        s_color += [GLfloat(rgb.r), GLfloat(rgb.g), GLfloat(rgb.b), GLfloat(rgb.a)]
        s_size.append(GLfloat(size))
    }

func calculateSquare(pp1: point, pp2: point, size: CGFloat) -> [GLfloat]
{

    let p1 = pp1
    var p2 = pp2
    var s1 = point()
    var s2 = point()
    let center = CGPointMake((p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0)


    var angle:CGFloat = 0
    if ((p1.x == p2.x) && (p1.y == p2.y))
    {
        //They are ontop of eachother
        angle = CGFloat(M_PI) / 2.0
        p2.x += 0.0001
        p2.y += 0.0001
    }
    else
    {
        if(p1.x == p2.x)
        {
            //UH OH x axis is equal
            if (p1.y < p2.y)
            {
                //RESULT: p1 is lower so should be first
                s1 = p1
                s2 = p2
            }
            else
            {
                //RESULT: p2 is lower and should be first
                s1 = p2
                s2 = p1
            }
        }
        else
        {
            //We could be all good
            if (p1.y == p2.y)
            {
                //Uh oh y axis is equal
                if (p1.x < p2.x)
                {
                    //RESULT: p1 is left so should be first
                    s1 = p1
                    s2 = p2
                }
                else
                {
                    //RESULT: p2 is to the right so should be first
                    s1 = p2
                    s2 = p1
                }
            }
            else
            {
                //Feuf everything is ok
                if ((p1.x < p2.x) && (p1.y < p2.y)) //First point is left and below
                {
                    //P1 should be first
                    s1 = p1
                    s2 = p2
                }
                else //First point is right and top
                {
                    //P2 should be first
                    s1 = p2
                    s2 = p1
                }
            }

        }
        angle = angle2p(s1, p2: s2)
    }


    if (angle < 0)
    {
        angle += CGFloat(M_PI) * 2.0
    }

    let yh = size / 2.0
    let distance = dist(p1, p2: p2)
    let xh = distance / 2.0

    let tl = rotateVector(CGPointMake(-xh, yh), angle: angle) + center
    let tr = rotateVector(CGPointMake(xh, yh), angle: angle) + center
    let bl = rotateVector(CGPointMake(-xh, -yh), angle: angle) + center
    let br = rotateVector(CGPointMake(xh, -yh), angle: angle) + center

    let c1:[GLfloat] = [GLfloat(bl.x), GLfloat(bl.y), 0]
    let c2:[GLfloat] = [GLfloat(tl.x), GLfloat(tl.y), 0]
    let c3:[GLfloat] = [GLfloat(br.x), GLfloat(br.y), 0]
    let c4:[GLfloat] = [GLfloat(tr.x), GLfloat(tr.y), 0]
    let part1 = c1 + c2 + c3
    let part2 = c2 + c3 + c4
    return part1 + part2
}

Upvotes: 0

Views: 58

Answers (2)

J.Doe
J.Doe

Reputation: 1552

Ok so after hours of tweaking the code for small bits in efficiency I have it running 500 particles at a fps of 28 which looks pretty smooth! I still have some ways to go. The best piece of advice had to do with allocating memory instead appending it. That saved tons of problems.

Special thanks to @Darko, @Marcelo_Cantos for coming up with the ideas that would ultimately optimize my code!

Upvotes: 0

Darko
Darko

Reputation: 9845

Do you really need all particles in system RAM? e.g. for some physics collision calculation in relation to other objects in the scene? Otherwise you could just create one particle, send it to the GPU and do the calculations in a GPU shader.

Upvotes: 0

Related Questions