ThreadDemo Program

We need an example to see how threading works and to see how the pthread library is used. The ThreadDemo program (shown in Figure 11.1) includes all of the thread code directly, outside of the engine, so you can learn from it. This program draws translucent sprites over the background just as a visual cue that the game loop is not being affected by the threads. We want to make sure threading does not screw up the framerate, and these circle sprites help in that regard. Afterward, we will incorporate threads into the engine and run some punishing tests (by creating thousands of sprites) to see whether there’s a performance gain.

Figure 11.1. The ThreadDemo program increments a variable in a separate thread.


Advice

A game built with our Advanced2D library will normally use seven threads already due to its libraries. Use this number as a baseline when you examine your program running in Task Manager.


Figure 11.2 shows the Task Manager while the ThreadDemo program is running. Surprisingly enough, the game engine is actually running on core #2 with the engine set to minimal processor usage (with a core update time of 1.7 ms). The third core, which is maxed out, is where the thread function is running! The thread is running ludicrously fast because all it has to do is increment a variable. If you want to really see a better balance between the two cores, you would need to add some load to the thread function (by performing some higher math calculations, such as Distance or AngleToTarget).

Figure 11.2. Processor core usage in Task Manager while ThreadDemo is running.


By pressing F2 to turn off processor throttling, we can maximize the speed of the core game loop—up from 1.7 ms to 0.03 ms (a six-fold increase that results from skipping the Sleep(1) line in the core)—which has been built into the engine all along, so that’s nothing new. The interesting thing is that the thread counter does not speed up or slow down based on the core throttling—it just continues to increment at a steady (but insanely fast) rate. Our engine core is running at about 33,000 fps, but the rate is only that fast because we are doing next to nothing in this demo—just moving a few sprites around. Add some mesh objects and lighting and a few hundred entities, and the rate would drop significantly.

Now let’s take a look at the Task Manager while the ThreadDemo program is running with a maximized core. As Figure 11.3 shows, the second core is now under load, as that is where our engine core is now running at full speed. The third core, though, has dropped down to about 25 percent. What could account for the drop? I might have expected both cores to be running at peak now. There are no mutex locks occurring because the program keeps track of any locks (and it is showing up at zero).

Figure 11.3. The second core is under load, and the third core has dropped to about 25 percent.


Advice

The background image used in the ThreadDemo program is titled “The Antennae Galaxies/NGC 4038-4039,” photographed by Hubble Space Telescope. This image was made available by NASA-STScl at www.hubblesite.org.


This is really just an artifact of the operating system assigning tasks to the processor. If you create more threads, you will see that they are balanced more equitably on a multi-core processor, as shown in Figure 11.4. In this example, processor throttling is turned off, and the engine core is running at 0.04 ms (about 40 microseconds—millionths of a second).

Figure 11.4. More threads are balanced more equitably on a multi-core processor.


Here is the source code for the ThreadDemo program, which is on the CD-ROM in the sourcesch11 folder.

#include <pthread.h>
#include "..EngineAdvanced2D.h"
using namespace Advanced2D;

#define SCREENW 1024
#define SCREENH 768
#define OBJECT_BACKGROUND 1
#define OBJECT_SPRITE 100
#define MAX 50
#define SCALE 70

Texture *circle_image;
Font *font;
Console *console;
std::ostringstream ostr;
int collisions;
//THREAD STUFF
#define MAXTHREADS 2
bool done = false;
unsigned long thread_counter = 0;
void* thread_function(void* data);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int thread_waits = 0;

void* thread_function(void* data)
{
    while(!done)
    {
        //lock the mutex
        if (pthread_mutex_lock(&mutex) != 0) thread_waits++;

        //increment thread counter
        thread_counter++;

        //do something more intensive
        Vector3 vector1(100,200,300);
        Vector3 vector2(400,500,600);
        double dist = g_engine->math->Distance(vector1,vector2);
        double dist_squared = dist*dist;
        double square_root_of_dist = sqrt(dist);
        double answer = square_root_of_dist;

        //unlock the mutex
        if (pthread_mutex_unlock(&mutex) != 0) thread_waits++;
    }
    pthread_exit(NULL);
    return NULL;
}

bool game_preload()
{
    g_engine->setAppTitle("THREAD DEMO");
    g_engine->setFullscreen(false);
    g_engine->setScreenWidth(SCREENW);
    g_engine->setScreenHeight(SCREENH);
    g_engine->setColorDepth(32);
    return 1;
}
bool game_init(HWND)
{
    int n;
    //load background image
    Sprite *background = new Sprite();
    if (!background->loadImage("galaxies.tga")) {
        g_engine->message("Error loading galaxies.tga");
        return false;
    }
    background->setObjectType(OBJECT_BACKGROUND);
    background->setCollidable(false);
    g_engine->addEntity(background);

    //create the console
    console = new Console();
    if (!console->init()) {
        g_engine->message("Error initializing console");
        return false;
    }
    console->setShowing(true);

    //load sprite image
    circle_image = new Texture();
    if (!circle_image->Load("circle.tga")) {
        g_engine->message("Error loading circle.tga");
        return false;
    }

    //create sprites
    Sprite *sprite;
    for (n=0; n < MAX; n++)
    {
        //create a new sprite
        sprite = new Sprite();
        sprite->setObjectType(OBJECT_SPRITE);
        sprite->setImage(circle_image);
        sprite->setColor(D3DCOLOR_RGBA(255,255,255,50));
        sprite->setSize(128,128);
        sprite->setScale( (float)(rand() % SCALE + SCALE/4) / 100.0f );
        sprite->setPosition( rand() % SCREENW, rand() % SCREENH );
        sprite->setCollisionMethod(COLLISION_DIST);

        //set velocity
        float vx = (float)(rand()%30 - 15)/10.0f;
        float vy = (float)(rand()%30 - 15)/10.0f;
        sprite->setVelocity( vx, vy );

        //add sprite to the entity manager
        g_engine->addEntity(sprite);
    }

    //load the Verdana10 font
    font = new Font();
    if (!font->loadImage("verdana10.tga")) {
        g_engine->message("Error loading verdana10.tga");
        return false;
    }
    if (!font->loadWidthData("verdana10.dat")) {
        g_engine->message("Error loading verdana10.dat");
        return false;
    }
    font->setColumns(16);
    font->setCharSize(20,16);

    //create the thread(s)
    int id;
    for (n = 0; n < MAXTHREADS; n++) {
        pthread_t mythread;
        int mythread_id = n;
        id = pthread_create(&mythread, NULL, thread_function, (void*)&mythread_ id);
    }

    return true;
}

void updateConsole()
{
    int y = 0;
    console->print(g_engine->getVersionText(), 1);
    ostr.str("");
    ostr << "CORE : " << (float)(1000.0f/g_engine->getFrameRate_core())
         << " ms (" << g_engine->getFrameRate_core() << " fps)";
    console->print(ostr.str(), 3);
    ostr.str("");
    ostr << "THREAD_COUNTER: " << thread_counter;
    console->print(ostr.str(), 5);
    ostr.str("");
    ostr << "THREAD_WAITS: " << thread_waits;
    console->print(ostr.str(), 7);
    console->print("Press F2 to toggle Processor Throttling", 27);
    ostr.str("");
    ostr << "Entities: " << g_engine->getEntityCount();
    console->print(ostr.str(), 29);
}

void game_update()
{
    //any code that touches thread variables must be wrapped in a mutex
    pthread_mutex_lock(&mutex);
    updateConsole();
    collisions = 0;
    pthread_mutex_unlock(&mutex);
}

void game_render3d()
{
    g_engine->ClearScene(D3DCOLOR_XRGB(0,0,80));
}

void game_render2d()
{
    font->Print(1,SCREENH-20,"Press ~ or F12 to toggle the Console");
    if (console->isShowing()) console->draw();
}

void game_keyRelease(int key)
{
    switch (key) {
        case DIK_ESCAPE:
            g_engine->Close();
            break;
        case DIK_F12:
        case DIK_GRAVE:
            console->setShowing( !console->isShowing() );
            break;
        case DIK_F2:
            g_engine->setMaximizeProcessor( !g_engine->getMaximizeProcessor() );
            break;
    }
}
void game_end()
{
    //kill the persistent thread
    done = true;
    pthread_mutex_destroy(&mutex);

    //delete objects
    delete console;
    delete circle_image;
    delete font;
}
void game_entityUpdate(Advanced2D::Entity* entity)
{
    switch(entity->getObjectType())
    {
        case OBJECT_SPRITE:
            Sprite* spr = (Sprite*)entity;
            float w = (float)spr->getWidth() * spr->getScale();
            float h = (float)spr->getHeight() * spr->getScale();
            float vx = spr->getVelocity().getX();
            float vy = spr->getVelocity().getY();
            if (spr->getX() < 0) {
                spr->setX(0);
                vx = fabs(vx);
            }
            else if (spr->getX() > SCREENW-w) {
                spr->setX(SCREENW-w);
                vx = fabs(vx) * -1;
            }
            if (spr->getY() < 0) {
                spr->setY(0);
                vy = fabs(vy);
            }
            else if (spr->getY() > SCREENH-h) {
                spr->setY(SCREENH-h);
                vy = fabs(vy) * -1;
            }

            spr->setVelocity(vx,vy);
            break;
    }
}
void game_entityCollision(Advanced2D::Entity* entity1,Advanced2D::Entity*
entity2)
{
    Sprite *box;
    Sprite *a = (Sprite*)entity1;
    Sprite *b = (Sprite*)entity2;

    if (a->getObjectType() == OBJECT_SPRITE && b->getObjectType() == OBJECT_
SPRITE)
    {
        collisions++;
        float x1 = a->getX();
        float y1 = a->getY();
        float x2 = b->getX();
        float y2 = b->getY();

        float vx1 = a->getVelocity().getX();
        float vy1 = a->getVelocity().getY();
        float vx2 = b->getVelocity().getX();
        float vy2 = b->getVelocity().getY();

        if (x1 < x2) {
            vx1 = fabs(vx1) * -1;
            vx2 = fabs(vx1);
        }
        else if (x1 > x2) {
            vx1 = fabs(vx1);
            vx2 = fabs(vx2) * -1;
        }
        if (y1 < y2) {
            vy1 = fabs(vy1) * -1;
            vy2 = fabs(vy2);
        }
        else {
            vy1 = fabs(vy1);
            vy2 = fabs(vy2) * -1;
        }
        a->setVelocity(vx1,vy1);
        b->setVelocity(vx2,vy2);
    }
}
void game_keyPress(int key) { }
void game_mouseButton(int button) { }
void game_mouseMotion(int x,int y) { }
void game_mouseMove(int x,int y) { }
void game_mouseWheel(int wheel) { }
void game_entityRender(Advanced2D::Entity* entity) { }

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset