fix: FPS limit and counter fix
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#include <iostream>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
class FPSCounter {
|
||||
@@ -10,10 +10,14 @@ public:
|
||||
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> 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;
|
||||
}
|
||||
|
||||
75
Logger.hpp
Normal file
75
Logger.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
|
||||
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<std::mutex> 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<std::mutex> 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<std::string> logQueue;
|
||||
std::atomic<bool> running;
|
||||
};
|
||||
|
||||
// Makro dla wygody używania
|
||||
#define LOG(msg) AsyncLogger::getInstance().log(msg)
|
||||
@@ -138,6 +138,7 @@
|
||||
<ClInclude Include="fabula.hpp" />
|
||||
<ClInclude Include="lazik.hpp" />
|
||||
<ClInclude Include="loadOBJ.h" />
|
||||
<ClInclude Include="Logger.hpp" />
|
||||
<ClInclude Include="plane.hpp" />
|
||||
<ClInclude Include="RESOURCE.H" />
|
||||
<ClInclude Include="shader.hpp" />
|
||||
|
||||
@@ -80,6 +80,9 @@
|
||||
<ClInclude Include="teksturowane.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Logger.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="glfw3.dll">
|
||||
|
||||
116
main.cpp
116
main.cpp
@@ -37,6 +37,9 @@
|
||||
#include <thread>
|
||||
#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<double> 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<double>(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,63 +763,63 @@ 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)) {
|
||||
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
|
||||
@@ -1187,3 +1201,11 @@ 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);
|
||||
}
|
||||
Reference in New Issue
Block a user