Why are console graphics so slow in C?

484 Views Asked by At

I am trying to make s imple game using only console graphics but for some reason even when I use putc() instead of printf() it is stille extremely slow averaging 14 FPS even though all I am doing is displayinga bunch of @ sighns.

code:

#include <stdio.h>
#include <time.h>
#include <windows.h>

const int sizeX = 20;
const int sizeY = 20;

int grid[20][20];

void draw_screen() {
    system("cls");
    int y, x;
    for (y = 0; y < sizeY; y++) {
        for (x = 0; x < sizeX; x++) {
            if (grid[y][x] == 0) {
                putc('@', stdout);
            }
        }
        putc('\n', stdout);
    }
}

int main(void) {
    int x, y;
    float frameTime, FPS;

    while (1) {
        clock_t start = clock();
        draw_screen();
        clock_t stop = clock();
        frameTime = (float)(stop - start) / CLOCKS_PER_SEC;
        FPS = 1.0 / frameTime;
        printf("FPS: %f\n", FPS);
    }
}
3

There are 3 best solutions below

1
Miguel On BEST ANSWER

There are two tricks to make console graphics fast. First don't clean the screeen every frame, just move the cursor to the top left (using SetConsoleCursorPosition). Second don't print character by character, print line by line (you can even print frame by frame). See the code below. Change the value in Sleep to have a reasonable frame rate.

#include <stdio.h>
#include <time.h>
#include <windows.h>

#define SIZE_X 75
#define SIZE_Y 25

int grid[SIZE_X][SIZE_Y];

void draw_screen() {
 static const HANDLE std_handle = GetStdHandle(STD_OUTPUT_HANDLE);
 static const COORD top_left = {0, 0};
 SetConsoleCursorPosition(std_handle, top_left);
 
 char line[SIZE_X + 1];
 line[SIZE_X] = '\0';
 int y,x;
 for(y=0;y<SIZE_Y;y++){
  for(x=0;x<SIZE_X;x++){
   line[x] = (grid[y][x] == 0) ? '@' : ' ';
  }
  puts(line);
 }
 // Use Sleep to control frame rate, comment to get maximum frame rate
// Sleep(33);
}

int main(void){
 int t,y,x;
 float frameTime, FPS;
 t = 0;
 while(1){
  // Simple grid update every frame
  for(y=0;y<SIZE_Y;y++){
   for(x=0;x<SIZE_X;x++){
    grid[y][x] = (t + 11*x + 7*y) & 1;
   }
  }
  ++t;
  clock_t start = clock();
  draw_screen();
  clock_t stop = clock();
  frameTime = (float)(stop - start) / CLOCKS_PER_SEC;
  FPS = 1.0 / frameTime;
  printf("FPS: %f\n",FPS);
 }
}
1
st4rk111er On

The problem seems to be SetConsoleTextAttribute(). It probably wasn't designed to be called this frequently. This question also complains about it's speed. See this question (which talks about the WriteConsoleOutput() function) for faster coloured console output

0
James Risner On

Every one of these calls listed below require a context switch to kernel mode. That involves giving up being on the run queue until it is next slotted into the run queue after the system call.

For performance, you will wish to avoid context switches as much as possible. I'll run down what is involved in each of these calls:

  • system("cls");
    This forces Windows to duplicate the entire process, all of the memory the process has is copied. Then when done the two processes take different forks. The parent continues running the game. The child loads and runs cls. Once done, control is returned to the parent.
  • putc('@', stdout); This send only a single byte into kernel space. You would wish to write as much as you can for each call. Ideally issuing these calls in batches a hundred times a second or slightly more with hundreds or thousands of bytes per call.
  • clock_t start = clock();
    This call obtains the time from the kernel. Of all the calls, this is the least expensive in terms of delay.
  • printf("FPS: %f\n", FPS);
    This can be expensive, as there are a lot of options for printf().
  • GetStdHandle(STD_OUTPUT_HANDLE); This call requires handle information for input and output devices. It isn't all that expensive in delay.
  • SetConsoleCursorPosition(std_handle, top_left);
    This can be expensive, it isn't typically used many times a second.