2

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);
    }
}
wohlstad
  • 12,661
  • 10
  • 26
  • 39
Saw
  • 115
  • 1
  • 16
  • 3
    Define "so slow". Quantify what you are observing vs what you expect. And please reduce the code to a [mre]. Aside being a requirement for Stack Overflow questions it may also help you find problems in your code. – kaylum Sep 11 '22 at 21:25
  • 1
    Isn't that a feature of whatever platform (windows?) you use? – Allan Wind Sep 11 '22 at 21:38
  • @kaylum With so slow I mean an average of 14 FPS – Saw Sep 12 '22 at 17:51
  • @Allan Wind Still shouldnt it be fater even so – Saw Sep 12 '22 at 17:52
  • 2
    One thing I'd look into is the overhead of `system("cls")`. It's starting a whole new process and running an external program just to print a few control characters. – Ilmari Karonen Sep 13 '22 at 09:49
  • @IlmariKaronen Are there any tother options to clear the console other than system("cls") – Saw Sep 13 '22 at 16:03

3 Answers3

1

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

st4rk111er
  • 91
  • 3
  • Even with my updated code nothing changes and my fps is still very low, there seems to be a limit at 15 fps because it has never gone higher than that. – Saw Sep 12 '22 at 17:53
1

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);
 }
}
Miguel
  • 119
  • 1
  • 2
  • you mentioned drawing frame by frame, how can this be done, any resources would be very helpfull – Saw Oct 13 '22 at 19:18
1

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.
James Risner
  • 5,451
  • 11
  • 25
  • 47