Double Buffering per WriteFile



  • Hier ein Snake Programm ... damit die Konsole nicht so flackert benutze ich double Buffering in der Konsole. Zur Anzeige schreibe ich erst in einen Buffer und den dann anschließend in den Konsolenbuffer per WriteFile. Ich versteh jedoch nicht so ganz wo die erste Zeile in der Anzeige herkommt. Sie wird nicht in den Buffer gescrhieben und taucht ab frame 3 auf. Hier der betroffende Abschnitt:

    // ...
    void snake_draw(void)
    {
        int zeile, spalte, tail, c = 0, pos = 0;
        char buffer[SCREEN_WIDTH * SCREEN_HEIGHT];
        DWORD dummy;
    
        pos += sprintf(&buffer[pos], "+");
        for(spalte = 0; spalte < NUM_COLS; spalte++) {
            pos += sprintf(&buffer[pos], "-");
        }
        pos += sprintf(&buffer[pos], "+\n");
        for (zeile = 0; zeile < NUM_ROWS; zeile++) {
            pos += sprintf(&buffer[pos], "|");
            for(spalte = 0; spalte < NUM_COLS; spalte++) {
                for(tail = 0; tail < snake.length; tail++) {
                    if(snake.tail[tail].x == spalte && snake.tail[tail].y == zeile) {
                        c = (tail == 0 ? 'O' : 'o');
                        break;
                    }
                }
                if(c == 0) {
                    c = (snake_food.x == spalte && snake_food.y == zeile) ? '*' : ' ';
                }
                pos += sprintf(&buffer[pos], "%c", c);
                c = 0;
            }
            pos += sprintf(&buffer[pos], "|\n");
        }
        pos += sprintf(&buffer[pos], "+");
        for(spalte = 0; spalte < NUM_COLS; spalte++) {
            pos += sprintf(&buffer[pos], "-");
        }
        pos += sprintf(&buffer[pos], "+\n");
        pos += sprintf(&buffer[pos], "Level: %-2d   Punkte: %-3d\n",
            snake.level, snake.points);
        buffer[pos] = '\0';
    
        console.active_buffer = (console.active_buffer == 1 ? 2 : 1);
        WriteFile(console.buffer[console.active_buffer], buffer, strlen(buffer),
            &dummy, NULL);
        SetConsoleActiveScreenBuffer(console.buffer[console.active_buffer]);
    }
    // ...
    

    Die unterste Zeile "Level: ... Punkte: ..." findet sich dann auf einmal auch am oberen Rand der Konsole wieder, obwohl sie da nicht hingescrhieben wurde. Woran liegts?

    Hier das gesamte Programm:
    http://pastie.org/387161

    EDIT: weiterhin stürzt die Konsole irgendwann ab?! ... man muß nur lang genug spielen.



  • Das hilft zwar jetzt nicht wirklich, aber ich verwende zum schreiben für den doppelpuffer in der konsole WriteConsoleOutput. Das funktioniert bei mir ohne probleme.



  • Hm ich denke ich habe den Fehler. Wenn ich das richtig sehe appended WriteFile standardmäßig. D.h. ich müßte den Filepointer jedesmal manuell wieder auf den Anfang des Handles setzen.



  • Caster kannst du mir mal kurz zeigen, wie ich einen ASCII Text in die Konsole schribe per WriteConsoleOutput? Ich habe gesehen man muß noch Farbinformationen angeben und die Zeichen müssen Char_Info sein und man muß REcts angeben ... 😕



  • #include <windows.h>
    
    #define WIDTH 80
    #define HEIGHT 25
    
    int main(void)
    {
        HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD dwFrameBufferSize, dwFrameBufferCoord;
        SMALL_RECT rcConsoleWindow;
        CHAR_INFO Buffer[WIDTH * HEIGHT];
        unsigned int i;
    
        dwFrameBufferSize.X = WIDTH;
        dwFrameBufferSize.Y = HEIGHT;
        rcConsoleWindow.Left = 0;
        rcConsoleWindow.Top = 0;
        rcConsoleWindow.Right = WIDTH;
        rcConsoleWindow.Bottom = HEIGHT;
        dwFrameBufferCoord.X = 0;
        dwFrameBufferCoord.Y = 0;
    
        //Hier ändere ich die Größe des Konsolenpuffers, so dass er nur noch die
        //Größe des tatsächlichen Puffers umfasst
        SetConsoleScreenBufferSize(hConsoleOutput, dwFrameBufferSize);
        SetConsoleWindowInfo(hConsoleOutput, 1, &rcConsoleWindow);
    
        for (i = 0; i < WIDTH * HEIGHT; ++i)
        {
            Buffer[i].Attributes = FOREGROUND_GREEN;
            Buffer[i].Char.AsciiChar = 'a';
        }
    
        //Wenn man Strings ausgeben will muss man halt durch den puffer durchgehen
        //und die buchstaben an den richtigen koordinaten reinschreiben
    
        //Buffer[Y * WIDTH + X] = ...;
        //so kann man auf bestimmte Koordinaten im Puffer zugreifen
    
        WriteConsoleOutput(hConsoleOutput, Buffer, dwFrameBufferSize, dwFrameBufferCoord, &rcConsoleWindow);
        return 0;
    }
    

    Wenn man das allerdings so macht, kann man keine normalen stringfunktionen wie printf zum ausgeben verwenden und muss alles selber in den puffer schreiben. So ist das ganze aber viel schneller.



  • Danke. Damit werde ich es hinkriegen. Erinnert mich an alte MS-DOS Zeiten, wenn man direkt den Grafikkartenspeicher ansprechen wollte. Warum MS jedoch eine Testkonsole so designt, erschließt sich mir noch nicht ganz.


Anmelden zum Antworten