|
Page Flipping Problem - Flicker |
armond
Member #8,282
January 2007
|
Hi! I'm using a combination of page flipping and double buffering. I'm trying to use pageflip for background scrolling and Double buffering for drawing all the sprites, bullets, etc. However, my screen seems to flicker so much. I don't know what I'm doing wrong... Here are my classes, I hope you could help me. Thanks!
I have a class which is in-charge of overall drawing/rendering. [code] #include "globals.h" GameManager::GameManager() { } GameManager::~GameManager() { } void GameManager::blitBuffer() { acquire_screen(); blit(buffer, screen, 0, 0, 0, 0, GAME_WIDTH, GAME_HEIGHT); release_screen(); clear_bitmap(buffer); } void GameManager::blitVideoBuffer() { //acquire_screen(); acquire_bitmap(videoBuffer); blit(videoBuffer, screen, 0, 0, 0, 0, GAME_WIDTH, GAME_HEIGHT); release_bitmap(videoBuffer); show_video_bitmap(videoBuffer); //release_screen(); //clear_bitmap(videoBuffer); } BITMAP* GameManager::buffer; BITMAP* GameManager::videoBuffer; const short int GameManager::GAME_WIDTH = 640; const short int GameManager::GAME_HEIGHT = 480; const short int GameManager::COLOR_DEPTH = 16; int GameManager::game_score; [/code] Now, I have a GameBackground class in which I try to scroll the parallax background. [code] #include "globals.h" GameBackground::GameBackground(BITMAP* bitmap, BackgroundScrollType scrollType, int scrollSpeed, int x, int y) { this->bitmap = bitmap; this->scrollType = scrollType; this->scrollSpeed = scrollSpeed; this->x = x; this->y = y; //tmpBuffer = create_bitmap(SCREEN_W, SCREEN_H); tmpBuffer = create_video_bitmap(SCREEN_W, SCREEN_H); } GameBackground::~GameBackground() { destroy_bitmap(tmpBuffer); } void GameBackground::update() { scrollBackground(); } void GameBackground::scrollBackground() { switch(scrollType) { case HORIZONTAL_LEFT: scrollLeft(); break; default: break; } } void GameBackground::draw() { //draw_sprite(GameManager::buffer, tmpBuffer, 0, 0); draw_sprite(GameManager::videoBuffer, tmpBuffer, 0, 0); } void GameBackground::setScrollSpeed(int speed) { scrollSpeed = speed; } void GameBackground::scrollLeft() { x -= scrollSpeed; int bw = bitmap->w; int bh = bitmap->h; int xOffSet = (SCREEN_W + bw); if ( x > bw || x < -(bw) ) { x = 0; } int newX = 0; for (int i = -(bw); i < (xOffSet); i += bw) { newX = (x + i); for (int j = 0; j < SCREEN_H; j += bh) { //draw tile //Optimization: draw only parts that are visible to the screen if ( ( newX + bw) > 0 ) { draw_sprite(tmpBuffer, bitmap, newX, j); } else { break; } } } } [/code] And the update and draw methods for GameBackground class are called in this class which functions as the main entry point of my game... [code] #include "globals.h" PlayState::PlayState() { currentLevel = 1; GameManager::game_score = 0; //create buffer image (double bufferring) GameManager::buffer = create_bitmap(SCREEN_W, SCREEN_H); //create video image (page flipping) GameManager::videoBuffer = create_video_bitmap(SCREEN_W, SCREEN_H); //load sprites BitmapManager::addBitmap(SHIP_SPRITE, "graphics/fs.bmp"); BitmapManager::addBitmap(VULCAN_SPRITE, "graphics/vulcan.bmp"); BitmapManager::addBitmap(ASTEROID_SPRITE_FLYING, "graphics/asteroid_flying.bmp"); BitmapManager::addBitmap(ASTEROID_SPRITE_HIT, "graphics/asteroid_hit.bmp"); BitmapManager::addBitmap(ASTEROID_SPRITE_DYING, "graphics/asteroid_destroyed.bmp"); BitmapManager::addBitmap(BULLET_HEAD_SPRITE_FLYING, "graphics/bullet_head_flying.bmp"); BitmapManager::addBitmap(BULLET_HEAD_SPRITE_HIT, "graphics/bullet_head_hit.bmp"); BitmapManager::addBitmap(SPRITE_EXPLOSION, "graphics/explosion.bmp"); BitmapManager::addBitmap(SWIRL_SPRITE_FLYING, "graphics/swirl_flying.bmp"); BitmapManager::addBitmap(SWIRL_SPRITE_HIT, "graphics/swirl_hit.bmp"); BitmapManager::addBitmap(SWIRL_SPRITE_DYING, "graphics/swirl_destroyed.bmp"); BitmapManager::addBitmap(POWER_UP, "graphics/power_up.bmp"); //load SFX SoundManager::addSample(METEOR_DESTROYED_SFX, "sfx/meteordestroyed.wav"); SoundManager::addSample(VULCAN_GUN_FIRE_SFX, "sfx/vulcan.wav"); SoundManager::addSample(POWER_UP_SFX, "sfx/power_up.wav"); //ship = new Ship(BitmapManager::SHIP_SPRITE, 50, 50, 50, 32, 1); //Ship(int x, int y, int w, int h, int speed, int recoveryRate, int recoveryTime, Status stat, int lfe); ship = new Ship(50, 50, 50, 45, 3, MAX_RECOVERY_RATE, MAX_RECOVERY_TIME, ALIVE, MAX_PLAYER_LIFE); //add a weapon for the ship ship->addWeapon(VULCAN_GUN, WeaponFactory::getWeapon(VULCAN_GUN) ); ship->setCurrentWeapon(VULCAN_GUN); ship->move(0, (SCREEN_H / 2) ); } PlayState::~PlayState() { BitmapManager::removeAll(); destroy_bitmap(GameManager::buffer); delete ship; } PlayState* PlayState::getInstance() { if (instance == NULL) { instance = new PlayState(); } return instance; } void PlayState::init() { } void PlayState::pause() { } void PlayState::run() { GameStateManager::pushState( GameOverState::getInstance() ); GameStateManager::getCurrentState()->run();//--execute run method GameStateManager::popState(); //--pop the element on top loadLevel(currentLevel); //begin main game loop while ( !key[KEY_ESC] ) { while (GameTimer::game_ticker > 0) { //move left if ( key[KEY_A] ) { ship->move( ship->x - ship->getSpeed(), ship->y ); if ( ship->x <= 0 ) { ship->move( ship->x + ship->getSpeed(), ship->y ); } } //move down if ( key[KEY_S] ) { ship->move( ship->x, ship->y + ship->getSpeed() ); if ( (ship->y + ship->height) >= SCREEN_H ) { ship->move( ship->x, ship->y - ship->getSpeed() ); } } //move right if ( key[KEY_D] ) { ship->move( ship->x + ship->getSpeed(), ship->y ); if ( (ship->x + ship->width) >= SCREEN_W ) { ship->move( ship->x - ship->getSpeed(), ship->y ); } } //move up if ( key[KEY_W] ) { ship->move( ship->x, ship->y - ship->getSpeed() ); if ( ship->y <= 20 ) { ship->move( ship->x, ship->y + ship->getSpeed() ); } } //fire weapon if ( key[KEY_K] ) { ship->fireWeapon(); } //change weapon if ( key[KEY_TAB] ) { //ship->fireWeapon(); } //fire special if ( key[KEY_L] ) { } //exit to menu if ( key[KEY_ESC] ) { break; } //scroll the background bg->update(); bg->draw(); //animate the ship ship->update(); //move all powerups for (std::vector<PowerUp*>::iterator it = PowerUp::powerUpPool.begin(); it != PowerUp::powerUpPool.end(); ++it) { PowerUp* pUp = (*it); if ( pUp->isActive() ) { pUp->update(); pUp->draw(); if ( ship->collides(pUp) ) { pUp->makeInactive(); ship->upgradeWeapon(); play_sample( SoundManager::getSample(POWER_UP_SFX), 255, 0, 2000, 0); } } pUp = NULL; } //move all enemies for(std::vector<Enemy*>::iterator it = EnemyFactory::enemyPool.begin(); it != EnemyFactory::enemyPool.end(); ++it) { Enemy* e = *it; //enemy is alive or dying if ( !( e->isDead() ) || e->isDying() ) { e->update(); e->draw(); //enemy is alive, ship is not recovering and ship hits enemy if ( !( e->isDead() ) && !( ship->isRecovering() ) && ship->collides(e) ) { ship->takeDamage(10); //TODO: change to enemy attack power e->takeDamage(1); } } } //move all projectiles for (std::vector<Projectile*>::iterator it = ProjectileFactory::projectilePool.begin(); it != ProjectileFactory::projectilePool.end(); ++it) { if ( (*it)->isVisible() == true && (*it)->isActive() ) { Projectile* p = *it; p->update(); //(*it)->update(); p->draw(); //(*it)->draw(); if ( p->isHostile() && !( ship->isRecovering() ) ) { /* check if ship collides projectile instead of projectile collides ship because ship provides a more precise collision that a player would not get annoyed with */ if ( ship->collides(p) ) { ship->takeDamage(10); //change this to projectile power p->makeInactive(); } } //check for collision on bullet and enemies for(std::vector<Enemy*>::iterator eit = EnemyFactory::enemyPool.begin(); eit != EnemyFactory::enemyPool.end(); ++eit) { Enemy* e = *eit; //projectile hits an enemy if ( p->collides(e) && p->isActive() && !( p->isHostile() ) && !( e->isDead() ) && !( e->isDying() ) ) { e->takeDamage(1); //TODO: change 1 to weapon power p->makeInactive(); //player killed the enemy if ( !( e->isAlive() ) ) { GameManager::game_score += 10; } //exit the inner loop break; } } } else { Projectile* p = *it; it = ProjectileFactory::projectilePool.erase(it); // we must decrement, because it will be incremented when the loop starts over again --it; delete p; } } //control game speed GameTimer::game_ticker--; ship->draw(); textout_ex(GameManager::buffer, font, "Life: ", 10, 10, makecol(0, 0, 255), -1); //drawLifeBar(int x, int y, int length, height, BITMAP* bitmap); drawLifeBar(50, 0, ship->getLife() , 20, GameManager::buffer); textprintf_centre_ex(GameManager::buffer, font, SCREEN_W/2, 10, makecol(255, 255, 255), makecol(0, 0, 0), "Current Weapon: %s", ship->getCurrentWeapon()->getWeaponName() ); textprintf_centre_ex(GameManager::buffer, font, (SCREEN_W - (SCREEN_W/8) ), 10, makecol(255, 255, 255), makecol(0, 0, 0), "Score: %d", GameManager::game_score); textprintf_centre_ex(GameManager::buffer, font, SCREEN_W/2, 176, makecol(255, 255, 255), makecol(0, 0, 0), "num=%d", GameTimer::game_ticker); //blit buffer GameManager::blitVideoBuffer(); GameManager::blitBuffer(); } } unloadLevel(currentLevel); } void PlayState::update() { } void PlayState::clean() { } void PlayState::loadLevel(int level) { PACKFILE* levelMap; switch(level) { case 1: levelMap = pack_fopen("levels/level_01.lvl", "r"); createLevel(levelMap); bg = new GameBackground(BitmapManager::addBitmap(SPACE_BACKGROUND, "graphics/bg.bmp"), HORIZONTAL_LEFT, 1, SCREEN_W, SCREEN_H); break; default: break; } if (levelMap) { pack_fclose(levelMap); } } void PlayState::createLevel(PACKFILE* packFile) { std::string line; char buf[255]; int currX = 0; int currY = 0; int xIncrement = (SCREEN_W / 20); int yIncrement = (SCREEN_H / 20); if (!packFile) { allegro_message("COULD NOT CREATE LEVEL!: %s", allegro_error); } while ( pack_feof(packFile) == 0 ) { //read a line from the file (levelMap) line = pack_fgets(buf, sizeof(buf), packFile); //loop through each character in the line for (int i = 0; i < line.length(); i++) { //only an empty space if ( line.at(i) == ' ') { currX += xIncrement; } else if( line.at(i) == 'P' ) { PowerUp* pUp = new PowerUp(currX, currY, 32, 32, 2, true); PowerUp::powerUpPool.push_back(pUp); pUp = NULL; currX += xIncrement; } else //create an enemy object { Enemy* e = EnemyFactory::getEnemy( line.at(i) ); //set initial position of the enemy e->move(currX, currY); EnemyFactory::enemyPool.push_back(e); e = NULL; currX += xIncrement; } } currX = 0; currY += yIncrement; } } void PlayState::unloadLevel(int level) { switch(level) { case 1: std::vector<Enemy*>().swap(EnemyFactory::enemyPool); std::vector<Projectile*>().swap(ProjectileFactory::projectilePool); std::vector<PowerUp*>().swap(PowerUp::powerUpPool); delete bg; break; default: break; } } void PlayState::drawLifeBar(int x, int y, int length, int height, BITMAP* bitmap) { //rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color); if (length <= 0) { length = 0; } rectfill(bitmap, x, y, (x + length), height, 255); } PlayState* PlayState::instance; int PlayState::currentLevel; Ship* PlayState::ship; [/code] I'm trying to use page flipping to gain extra performance since my game runs pretty slow on PIII machines and below. I just want to try it out and see if it makes any difference. Thanks! |
Tobias Dammers
Member #2,604
August 2002
|
Quote:
void GameManager::blitVideoBuffer()
This is not proper page flipping. You blit the bitmap AND flip. This is not right. --- |
armond
Member #8,282
January 2007
|
Does create_video_bitmap(); give you a memory bitmap and not a video bitmap? And by rendering, you mean calling draw_sprite() and probably drawing on the BITMAP returned by create_video_bitmap? Thanks! |
Tobias Dammers
Member #2,604
August 2002
|
Quote: Does create_video_bitmap(); give you a memory bitmap and not a video bitmap?
Please read TFM. Quote: And by rendering, you mean calling draw_sprite() and probably drawing on the BITMAP returned by create_video_bitmap? Thanks!
Rendering your scene. I don't know what else to call it. Draw all the sprites and lines and whatnot, basically everything you want to display, to the back buffer, not to the screen. Most likely, this will involve draw_sprite(), but it doesn't have to. --- |
armond
Member #8,282
January 2007
|
Thank you, I've been reading TFM for several hours now. I just wanted to be sure and that's why I asked. Now, I tried changing from video bitmap to memory bitmap as you said. However, it doesn't show anything on the screen. When I use video bitmaps, the background overlaps all other sprites. But I'm sure I'm rendering and drawing the background first. You can see my trial-and-error stuff through comments here in my code. I also made a variable called frameCounter so I'd be changing the buffer on which to draw on in its every increment. Thanks!
And BTW, I also have a double buffer after the update and draw routines for this class's object is called. But the pageflip is called first. |
Kauhiz
Member #4,798
July 2004
|
I didn't bother to read through your source, so sorry if this isn't helping. Anyway, there's a gereat article on page flipping in the wiki: http://wiki.allegro.cc/Page_flipping. I suggest you read through that and see if it helps at all. --- |
Kris Asick
Member #1,424
July 2001
|
Tobias: I think you had initially confused Armond because you said to use show_video_bitmap() on memory bitmaps: Quote: Instead, you create TWO memory bitmaps, A and B. Set A as the front buffer by calling show_video_bitmap() on it... Armond: The two simplest ways to render to the screen are Double Buffering and Page Flipping. Double Buffering allows you to take certain liberties you can't with Page Flipping, such as not syncing to the monitor refresh rate and being able to render to the buffer at any point in time. Page Flipping has the advantage of taking less CPU time and being smoother. To Double Buffer, all you need to do is have a regular bitmap to draw on as your buffer, and then you blit that to the screen at the end of your game loop. You don't need to ever call the acquire or release commands using this approach. You can also add the vsync() command to just before the blit to potentially improve the smoothness of the display. (However, poor timer logic or a slow computer may degrade performance when using vsync().) To Page Flip, you need to make three BITMAP pointers. Two will be video bitmaps made with create_video_bitmap(), the third will be a reference pointer which swaps between the other two video pages every frame. You must group all of your drawing to the reference bitmap into one chunk that lies between acquire and release commands for the reference bitmap. Once you render everything and release the reference bitmap, call show_video_bitmap() on the reference pointer, then switch the reference pointer to the alternate video bitmap. (Thus the video bitmap you draw to "flips" every frame.) Two things to note when you do Page Flipping: 1. By default, show_video_bitmap() will automatically vsync. You can disable this behaviour, but then the resulting display quality really isn't much better than Double Buffering. 2. The very first video bitmap you make with create_video_bitmap() will almost certainly use the same video memory as the screen. (Not really a problem, it just means if you're going to put more stuff in video memory than the video pages you should probably define the video pages first. Hope that helps. I personally recommend double buffering because bad things can happen if you do too much stuff between an acquire/release pair. (And because page flipping + Allegro != working properly on my particular computer.) --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
gnolam
Member #2,030
March 2002
|
I'd do away with the acquire/releases altogether. They're not actually needed, and mainly just increase the risk of you fucking up somewhere further down the line. -- |
Kris Asick
Member #1,424
July 2001
|
Quote: I'd do away with the acquire/releases altogether. They're not actually needed, and mainly just increase the risk of you fucking up somewhere further down the line. True for Double Buffering, not true for Page Flipping, since in Page Flipping you render everything directly to video memory. (Thus if you removed the acquire and release commands there, video memory would have to be locked and unlocked for every single call to draw something to video memory. This is why you can remove them with double buffering, since you generally only need to make one render to video memory (the screen) per frame that way.) Unless of course you combine double buffering with page flipping, but then you're just wasting CPU time and eliminating the speed boost page flipping provides. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
Kauhiz
Member #4,798
July 2004
|
Quote: True for Double Buffering, not true for Page Flipping, since in Page Flipping you render everything directly to video memory. Nope, gnolam's right. You never need the acquire/release calls. TFM said: You never need to call the function explicitly as it is low level, and will only give you a speed up if you know what you are doing. Using it wrongly may cause slowdown, or even lock up your program.
--- |
Kris Asick
Member #1,424
July 2001
|
Quote: Nope, gnolam's right. You never need the acquire/release calls. Permit me to explain what exactly those commands are doing. DirectX, in all its glory, is made in such a way that in order to successfully write to video memory that memory must first be locked so that Windows doesn't mess with it while a write is happening. This memory lock takes a good amount of CPU time to engage, and while it's active, making any kind of system call can do bad things. Allegro controls the state of this lock with its acquire and release commands. Because video memory must be locked in order to draw to it, Allegro will automatically lock and unlock video memory when you draw to it if you haven't locked it already. So long as you only make one video memory draw per frame, using acquire and release doesn't save you any time. But, when doing proper page flipping, ALL rendering is happening direct to video memory, which means if you don't enclose all those functions within an acquire/release pair, video memory will be locked and unlocked for every single call to stuff like putpixel, blit, draw_sprite, etc... which will eat CPU time. If you're rendering everything to a buffer, blitting that to video memory and then doing a page flip, that's essentially combining double buffering with page flipping, which doesn't give you the speed benefits page flipping is supposed to provide. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
Kauhiz
Member #4,798
July 2004
|
Quote: This memory lock takes a good amount of CPU time to engage Really? So what is "a good amount", I mean, I've never used the acquire/release calls anywhere and I've never ran into any problems. How much speed can you actually gain by adding those calls? --- |
Tobias Dammers
Member #2,604
August 2002
|
Quote: Tobias: I think you had initially confused Armond because you said to use show_video_bitmap() on memory bitmaps: You are, of course, right. That is utter nonsense I wrote there. Armond: class cFramebuffer { // protected / private members left out. public: cFramebuffer(int buffer_method = MEMORY_BLIT); // The parameter is only needed if you put all buffer methods into the same class. // Otherwise, each buffering method would have its own constructor. // If you have a dedicated class for each buffering method, the member funcs below // need to be virtual. ~cFramebuffer(); void flip(); // takes the appropriate action depending on the buffer method used. BITMAP* get_backbuffer(); const char* get_buffer_mode_desc() const; // optional, and useful mainly for debugging purposes; };
Of course, this doesn't have to be a class; were you using plain C, you'd just provide the plain functions in the header and use static variables in the .c file. In C++, though, a class is a much better choice. cGame::draw() { // note that this method does not know which frame buffering method is being used. BITMAP* backbuf = framebuffer->get_backbuffer(); // get a valid back buffer from the framebuffer module assert(backbuf); // make sure we really have a back buffer! // OK, now draw our scene back-to-front: background->draw(backbuf, scroll_x, scroll_y); for (int i = 0; i < num_actors; ++i) actor<i>->draw(backbuf, scroll_x, scroll_y); // if you want to display overlay like scores, health bars, etc., here's the place framebuffer->flip(); // Tell the framebuffer module we're done with this frame. } - A "main" module that sets up allegro, creates a global frame buffer object, spawns the game module, and finally cleans up by destroying the game and frame buffer objects and exiting allegro. The nice thing about this approach is that once you have one frame buffering system ready to go, the interface can be reused, so if you later want to add another option, you only need to change the frame buffer module, and add a few lines to the main module, while all the other modules can remain unchanged. --- |
Kitty Cat
Member #2,815
October 2002
|
Quote: Really? So what is "a good amount", I mean, I've never used the acquire/release calls anywhere and I've never ran into any problems. "A good amount" can be anywhere from near instantanious to several tens to hundreds of milliseconds, or more. Since it involves a mutex lock, if something else is holding that lock, your program will pause until the lock becomes free. There could be other factors involved too.. I'm not very familiar with the Windows code. But as mentioned, as long as you draw to the screen/video bitmaps, the screen must be acquired no matter what. And if all you do is blit a memory buffer to the screen, there's nothing to gain since Allegro will acquire for you. -- |
Kauhiz
Member #4,798
July 2004
|
Quote: But as mentioned, as long as you draw to the screen/video bitmaps, the screen must be acquired no matter what. And if all you do is blit a memory buffer to the screen, there's nothing to gain since Allegro will acquire for you. Yeah, I know. I'd just like to know what kind of speed differences we're talking about. --- |
Tobias Dammers
Member #2,604
August 2002
|
For blitting a memory buffer to screen: zero. --- |
Kauhiz
Member #4,798
July 2004
|
Quote: For drawing directly to video memory: depends. On what. Amount of stuff getting drawn, obviously, but how about hardware? Anything else? If I don't see a big difference on my system, could there still be a big difference on another system? --- |
Kris Asick
Member #1,424
July 2001
|
Kauhiz: It is dependent on a number of factors, but the speed difference can be insane if you do enough writes. For instance, if you draw 200 objects directly to the screen on one update, but fail to call the acquire and release commands first, those 200 objects will each call those commands themselves. Thus instead of one locking operation you suddenly have 200. And again, this is only if you do all of your drawing directly to video memory. If you draw everything to a memory buffer then blit that to video memory, the lock is only engaged once because regular bitmaps don't need to be locked to be written to. Here's a quick example you can test:
You're going to find that Example 2 runs much faster than Example 1. Note however that not all operating systems require locking. For instance, these two examples will likely run the same speed if you run them in DOS. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
Kauhiz
Member #4,798
July 2004
|
Quote: If you draw everything to a memory buffer then blit that to video memory, the lock is only engaged once because regular bitmaps don't need to be locked to be written to. Why does everyone keep saying this? I'M NOT RETARDED! Besides, I knew that much before the thread anyway. Sorry, Kris, but you really didn't help that much. Well, apart from the operating system thing. --- |
Kris Asick
Member #1,424
July 2001
|
Kauhiz: We're helping you perfectly fine, you just don't like our answers. You want to know exactly what factors go into the delay of locking a video bitmap. The trouble is, everything is a factor. The speed of the computer, the speed of the video card, the speed of your RAM, the speed of the video RAM, the state of your CPU registers... no matter what computer you are on the lock can take anywhere from an insignificant amount of CPU time to a rather large amount of CPU time to engage. There's absolutely no way to calculate or control it so there's no sense trying to. All you need to understand is that locking and unlocking a video bitmap multiple times in one frame is slower than locking and unlocking once per frame. By how much? There's no way to know until you do it. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
Kauhiz
Member #4,798
July 2004
|
Quote: We're helping you perfectly fine, you just don't like our answers. Nope, you're not answering my question. I don't really need help, since I don't use allegro for graphics anymore, I'm just curious. Quote: The speed of the computer, the speed of the video card, the speed of your RAM, the speed of the video RAM, the state of your CPU registers... That's the answer I was looking for, thanks. --- |
Tobias Dammers
Member #2,604
August 2002
|
Quote: The speed of the computer, the speed of the video card, the speed of your RAM, the speed of the video RAM, the state of your CPU registers... ...the graphics mode you're in, directx version, other active processes... --- |
armond
Member #8,282
January 2007
|
Thank you people. I think you've done everything you can to help this poor fella. It's up to me now to incorporate your ideas with my code. If I succeed, I'll get back at you and post the working code. Thanks! |
|