From ffa5a929df84a919aaf3d8069d8e1b094318720f Mon Sep 17 00:00:00 2001 From: Pc Date: Mon, 26 Jan 2026 22:41:02 +0100 Subject: [PATCH] fix: FPS limit and counter fix --- FPSCounter.cpp | 12 ++-- Logger.hpp | 75 +++++++++++++++++++++++ grafikaKBT.vcxproj | 1 + grafikaKBT.vcxproj.filters | 3 + main.cpp | 122 ++++++++++++++++++++++--------------- 5 files changed, 159 insertions(+), 54 deletions(-) create mode 100644 Logger.hpp diff --git a/FPSCounter.cpp b/FPSCounter.cpp index 98bc060..3ee3cdb 100644 --- a/FPSCounter.cpp +++ b/FPSCounter.cpp @@ -1,4 +1,4 @@ -#include +#include #include class FPSCounter { @@ -10,10 +10,14 @@ public: auto currentTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = currentTime - lastTime; - // Aktualizujemy FPS co 1 sekund + // Aktualizujemy i wypisujemy TYLKO jeśli minęła co najmniej 1 sekunda if (elapsed.count() >= 1.0) { double fps = frameCount / elapsed.count(); - std::cout << "FPS: " << fps << "\n"; + double msPerFrame = 1000.0 / fps; + + std::cout << "FPS: " << (int)fps + << " | Czas klatki: " << msPerFrame << " ms" << std::endl; + frameCount = 0; lastTime = currentTime; } @@ -22,4 +26,4 @@ public: private: int frameCount; std::chrono::time_point lastTime; -}; +}; \ No newline at end of file diff --git a/Logger.hpp b/Logger.hpp new file mode 100644 index 0000000..044d909 --- /dev/null +++ b/Logger.hpp @@ -0,0 +1,75 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +class AsyncLogger { +public: + // Singleton - jedna instancja loggera na całą grę + static AsyncLogger& getInstance() { + static AsyncLogger instance; + return instance; + } + + // Dodaj wiadomość do kolejki (wywoływane z gry) - to jest super szybkie + void log(const std::string& message) { + std::lock_guard lock(queueMutex); + logQueue.push(message); + cv.notify_one(); // Obudź wątek loggera + } + + // Uruchom wątek (wywołaj raz na początku programu) + void start() { + running = true; + loggingThread = std::thread(&AsyncLogger::processQueue, this); + } + + // Zatrzymaj wątek (wywołaj przy zamykaniu) + void stop() { + running = false; + cv.notify_one(); + if (loggingThread.joinable()) { + loggingThread.join(); + } + } + +private: + AsyncLogger() : running(false) {} + ~AsyncLogger() { stop(); } + + // To jest funkcja, która działa w osobnym wątku + void processQueue() { + while (running || !logQueue.empty()) { + std::unique_lock lock(queueMutex); + + // Czekaj, aż coś pojawi się w kolejce lub zakończymy program + cv.wait(lock, [this] { return !logQueue.empty() || !running; }); + + while (!logQueue.empty()) { + std::string msg = logQueue.front(); + logQueue.pop(); + + // Zwalniamy mutex przed wypisywaniem, żeby nie blokować głównego wątku + // gdy konsola jest "zamrożona" przez użytkownika + lock.unlock(); + + std::cout << msg << std::endl; + + lock.lock(); + } + } + } + + std::thread loggingThread; + std::mutex queueMutex; + std::condition_variable cv; + std::queue logQueue; + std::atomic running; +}; + +// Makro dla wygody używania +#define LOG(msg) AsyncLogger::getInstance().log(msg) \ No newline at end of file diff --git a/grafikaKBT.vcxproj b/grafikaKBT.vcxproj index 4258007..86452c6 100644 --- a/grafikaKBT.vcxproj +++ b/grafikaKBT.vcxproj @@ -138,6 +138,7 @@ + diff --git a/grafikaKBT.vcxproj.filters b/grafikaKBT.vcxproj.filters index 97ebe4c..13cb0f2 100644 --- a/grafikaKBT.vcxproj.filters +++ b/grafikaKBT.vcxproj.filters @@ -80,6 +80,9 @@ Header Files + + Header Files + diff --git a/main.cpp b/main.cpp index fc15e28..87c7509 100644 --- a/main.cpp +++ b/main.cpp @@ -37,6 +37,9 @@ #include #include "teksturowane.hpp" #include "fabula.hpp" +#include "GL/wglew.h" +#include "Logger.hpp" + using namespace glm; @@ -48,13 +51,16 @@ using namespace glm; //using namespace std; + + HPALETTE hPalette = NULL; // Application name and instance storage static LPCTSTR lpszAppName = "grafikaKBT"; static HINSTANCE hInstance; GLFWwindow* window; - +/////////////////////// +void DisableQuickEdit(); // Rotation amounts static GLfloat xRot = 0.0f; static GLfloat yRot = 0.0f; @@ -86,23 +92,30 @@ bool panoramic_view = 0; //Zmienne do ruchu ##############################################^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FPSCounter fpsCounter; -static const int targetFPS = 250; // Celowany FPS +static const int targetFPS = 444; // Celowany FPS //Fps counter static void LimitFPS(int targetFPS) { static auto lastTime = std::chrono::high_resolution_clock::now(); + + // Obliczamy czas na jedną klatkę + double targetFrameDuration = 1.0 / targetFPS; + auto currentTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = currentTime - lastTime; - // Obliczamy czas na jedną klatkę - double frameTime = 1.0 / targetFPS; // Czas na jedną klatkę w sekundach - double timeToWait = frameTime - elapsed.count(); // Obliczamy czas do czekania + // --- CZYSTA PĘTLA (Busy Wait) --- + // Usunęliśmy sleep_for, bo w Windows jest zbyt nieprecyzyjny dla FPS > 64. + // Teraz procesor będzie pracował na 100% jednego wątku, ale FPS będzie idealny. + while (elapsed.count() < targetFrameDuration) { + // Tylko aktualizujemy czas, nic więcej. + // Jeśli chcesz trochę odciążyć CPU, można użyć instrukcji procesora 'pause', + // ale w standardowym C++ po prostu pusta pętla z pomiarem czasu jest najpewniejsza. - if (timeToWait > 0.0) { - // Jeśli czas do czekania jest większy niż 0, to śpimy przez tę wartość - std::this_thread::sleep_for(std::chrono::duration(timeToWait)); + currentTime = std::chrono::high_resolution_clock::now(); + elapsed = currentTime - lastTime; } - lastTime = currentTime; // Zaktualizuj czas dla następnej iteracji + lastTime = std::chrono::high_resolution_clock::now(); } bool Kolizja = false; @@ -506,6 +519,14 @@ static void SetupRC() { } timestampedCout("Zainicjalizowano GLEW."); + // Sprawdź czy rozszerzenie jest dostępne i wyłącz V-Sync + if (wglewIsSupported("WGL_EXT_swap_control")) { + wglSwapIntervalEXT(0); // 0 = WYŁĄCZ V-Sync (nielimitowane FPS) + timestampedCout("V-Sync wylaczony (unlocked FPS)."); + } + else { + timestampedCout("Brak obslugi WGL_EXT_swap_control - nie mozna programowo wylaczyc V-Sync."); + } // Initialize GLFW3 timestampedCout("Inicjalizowanie GLFW3..."); @@ -520,7 +541,7 @@ static void SetupRC() { timestampedCout("Ladowanie modelu mapki..."); mapa.loadModel(); - glfwSwapInterval(1); + glfwSwapInterval(0); } @@ -742,64 +763,64 @@ void static CreateConsole() { int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { CreateConsole(); - MSG msg; // Windows message structure - WNDCLASS wc{}; // Windows class structure - HWND hWnd; // Storeage for window handle + DisableQuickEdit(); + AsyncLogger::getInstance().start(); + MSG msg; + WNDCLASS wc{}; + HWND hWnd; hInstance = hInst; - - // Register Window style - wc.style = CS_HREDRAW | CS_VREDRAW; + + // Rejestracja klasy okna + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // DODANO CS_OWNDC - ważne dla OpenGL wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); - - // No need for background brush for OpenGL window wc.hbrBackground = NULL; - wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); wc.lpszClassName = lpszAppName; - // Register the window class if (RegisterClass(&wc) == 0) return FALSE; - - // Create the main application window hWnd = CreateWindow( - lpszAppName, - lpszAppName, - - // OpenGL requires WS_CLIPCHILDREN and WS_CLIPSIBLINGS + lpszAppName, lpszAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 50, 50, 800, 800, + NULL, NULL, hInstance, NULL); - // Window position and size - 50, 50, - 800, 800, - NULL, - NULL, - hInstance, - NULL); - - // If window was not created, quit if (hWnd == NULL) return FALSE; + // --- POPRAWKA 1: Pobierz HDC raz --- + HDC hDC = GetDC(hWnd); const WORD ID_TIMER = 1; SetTimer(hWnd, ID_TIMER, 100, NULL); - // Display the window ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); - // Process application messages until the application closes - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); + ZeroMemory(&msg, sizeof(msg)); + while (msg.message != WM_QUIT) { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else { + RenderScene(); + + // --- POPRAWKA 2: Używaj zapisanego hDC, a nie pobieraj nowego --- + SwapBuffers(hDC); + + LimitFPS(targetFPS); + } } + // --- POPRAWKA 3: Zwolnij HDC przy zamykaniu --- + ReleaseDC(hWnd, hDC); + return msg.wParam; } @@ -907,6 +928,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // Tell the application to terminate after the window // is gone. PostQuitMessage(0); + AsyncLogger::getInstance().stop(); break; // Window is resized. @@ -920,15 +942,9 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // The painting function. This message sent by Windows // whenever the screen needs updating. case WM_PAINT: - LimitFPS(targetFPS); - // Call OpenGL drawing code - RenderScene(); - SwapBuffers(hDC); + ValidateRect(hWnd, NULL); - // Validate the newly painted client area - if (!monitormode) ValidateRect(hWnd, NULL); - else InvalidateRect(hWnd, NULL, FALSE); break; case WM_QUERYNEWPALETTE: @@ -1125,9 +1141,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_TIMER: - RenderScene(); - SwapBuffers(hDC); - ValidateRect(hWnd, NULL); + break; default: // Passes it on if unproccessed @@ -1186,4 +1200,12 @@ BOOL APIENTRY AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam) { } return FALSE; +} +void DisableQuickEdit() { + HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); + DWORD prev_mode; + GetConsoleMode(hInput, &prev_mode); + + // Wyłączamy opcję ENABLE_QUICK_EDIT_MODE oraz ENABLE_INSERT_MODE + SetConsoleMode(hInput, prev_mode & ~ENABLE_QUICK_EDIT_MODE & ~ENABLE_INSERT_MODE); } \ No newline at end of file