2 Commits

Author SHA1 Message Date
Pc
233d4189d1 feat: console on new thread 2026-01-26 22:46:33 +01:00
Pc
ffa5a929df fix: FPS limit and counter fix 2026-01-26 22:41:02 +01:00
5 changed files with 172 additions and 59 deletions

View File

@@ -1,25 +1,40 @@
#include <iostream> #pragma once // Zabezpieczenie przed wielokrotnym dołączeniem
#include <chrono> #include <chrono>
class FPSCounter { class FPSCounter {
public: public:
FPSCounter() : frameCount(0), lastTime(std::chrono::high_resolution_clock::now()) {} FPSCounter() :
frameCount(0),
currentFPS(0.0),
currentFrameTime(0.0),
lastTime(std::chrono::high_resolution_clock::now()) {
}
void update() { // Zwraca true, jeśli minęła sekunda i zaktualizowano dane
bool update() {
frameCount++; frameCount++;
auto currentTime = std::chrono::high_resolution_clock::now(); auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = currentTime - lastTime; std::chrono::duration<double> elapsed = currentTime - lastTime;
// Aktualizujemy FPS co 1 sekundê
if (elapsed.count() >= 1.0) { if (elapsed.count() >= 1.0) {
double fps = frameCount / elapsed.count(); currentFPS = frameCount / elapsed.count();
std::cout << "FPS: " << fps << "\n"; // Obliczamy czas klatki w ms
currentFrameTime = 1000.0 / (currentFPS == 0 ? 1 : currentFPS);
frameCount = 0; frameCount = 0;
lastTime = currentTime; lastTime = currentTime;
return true; // Zgłaszamy, że dane się zmieniły
} }
return false;
} }
// --- TEGO BRAKOWAŁO W TWOIM KODZIE ---
double getFPS() const { return currentFPS; }
double getFrameTime() const { return currentFrameTime; }
private: private:
int frameCount; long long frameCount;
double currentFPS; // Przechowuje ostatnio obliczone FPS
double currentFrameTime; // Przechowuje czas klatki
std::chrono::time_point<std::chrono::high_resolution_clock> lastTime; std::chrono::time_point<std::chrono::high_resolution_clock> lastTime;
}; };

72
Logger.hpp Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <atomic>
#include <sstream> // <--- TO JEST NIEZBĘDNE
class AsyncLogger {
public:
static AsyncLogger& getInstance() {
static AsyncLogger instance;
return instance;
}
void log(const std::string& message) {
std::lock_guard<std::mutex> lock(queueMutex);
logQueue.push(message);
cv.notify_one();
}
void start() {
if (running) return;
running = true;
loggingThread = std::thread(&AsyncLogger::processQueue, this);
}
void stop() {
running = false;
cv.notify_one();
if (loggingThread.joinable()) {
loggingThread.join();
}
}
private:
AsyncLogger() : running(false) {}
~AsyncLogger() { stop(); }
void processQueue() {
while (running || !logQueue.empty()) {
std::unique_lock<std::mutex> lock(queueMutex);
cv.wait(lock, [this] { return !logQueue.empty() || !running; });
while (!logQueue.empty()) {
std::string msg = logQueue.front();
logQueue.pop();
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;
};
// --- POPRAWIONE MAKRO ---
// Tworzy tymczasowy strumień (ostringstream), który "rozumie" operator <<
#define LOG(stream_args) { \
std::ostringstream ss; \
ss << stream_args; \
AsyncLogger::getInstance().log(ss.str()); \
}

View File

@@ -138,6 +138,7 @@
<ClInclude Include="fabula.hpp" /> <ClInclude Include="fabula.hpp" />
<ClInclude Include="lazik.hpp" /> <ClInclude Include="lazik.hpp" />
<ClInclude Include="loadOBJ.h" /> <ClInclude Include="loadOBJ.h" />
<ClInclude Include="Logger.hpp" />
<ClInclude Include="plane.hpp" /> <ClInclude Include="plane.hpp" />
<ClInclude Include="RESOURCE.H" /> <ClInclude Include="RESOURCE.H" />
<ClInclude Include="shader.hpp" /> <ClInclude Include="shader.hpp" />

View File

@@ -80,6 +80,9 @@
<ClInclude Include="teksturowane.hpp"> <ClInclude Include="teksturowane.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Logger.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="glfw3.dll"> <None Include="glfw3.dll">

116
main.cpp
View File

@@ -37,6 +37,9 @@
#include <thread> #include <thread>
#include "teksturowane.hpp" #include "teksturowane.hpp"
#include "fabula.hpp" #include "fabula.hpp"
#include "GL/wglew.h"
#include "Logger.hpp"
using namespace glm; using namespace glm;
@@ -48,13 +51,16 @@ using namespace glm;
//using namespace std; //using namespace std;
HPALETTE hPalette = NULL; HPALETTE hPalette = NULL;
// Application name and instance storage // Application name and instance storage
static LPCTSTR lpszAppName = "grafikaKBT"; static LPCTSTR lpszAppName = "grafikaKBT";
static HINSTANCE hInstance; static HINSTANCE hInstance;
GLFWwindow* window; GLFWwindow* window;
///////////////////////
void DisableQuickEdit();
// Rotation amounts // Rotation amounts
static GLfloat xRot = 0.0f; static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f; static GLfloat yRot = 0.0f;
@@ -86,23 +92,30 @@ bool panoramic_view = 0;
//Zmienne do ruchu ##############################################^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //Zmienne do ruchu ##############################################^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FPSCounter fpsCounter; FPSCounter fpsCounter;
static const int targetFPS = 250; // Celowany FPS static const int targetFPS = 444; // Celowany FPS
//Fps counter //Fps counter
static void LimitFPS(int targetFPS) { static void LimitFPS(int targetFPS) {
static auto lastTime = std::chrono::high_resolution_clock::now(); 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(); auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = currentTime - lastTime; std::chrono::duration<double> elapsed = currentTime - lastTime;
// Obliczamy czas na jedną klatkę // --- CZYSTA PĘTLA (Busy Wait) ---
double frameTime = 1.0 / targetFPS; // Czas na jedną klatkę w sekundach // Usunęliśmy sleep_for, bo w Windows jest zbyt nieprecyzyjny dla FPS > 64.
double timeToWait = frameTime - elapsed.count(); // Obliczamy czas do czekania // 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) { currentTime = std::chrono::high_resolution_clock::now();
// Jeśli czas do czekania jest większy niż 0, to śpimy przez tę wartość elapsed = currentTime - lastTime;
std::this_thread::sleep_for(std::chrono::duration<double>(timeToWait));
} }
lastTime = currentTime; // Zaktualizuj czas dla następnej iteracji lastTime = std::chrono::high_resolution_clock::now();
} }
bool Kolizja = false; bool Kolizja = false;
@@ -506,6 +519,14 @@ static void SetupRC() {
} }
timestampedCout("Zainicjalizowano GLEW."); 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 // Initialize GLFW3
timestampedCout("Inicjalizowanie GLFW3..."); timestampedCout("Inicjalizowanie GLFW3...");
@@ -520,7 +541,7 @@ static void SetupRC() {
timestampedCout("Ladowanie modelu mapki..."); timestampedCout("Ladowanie modelu mapki...");
mapa.loadModel(); mapa.loadModel();
glfwSwapInterval(1); glfwSwapInterval(0);
} }
@@ -742,63 +763,63 @@ void static CreateConsole() {
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
CreateConsole(); CreateConsole();
MSG msg; // Windows message structure DisableQuickEdit();
WNDCLASS wc{}; // Windows class structure AsyncLogger::getInstance().start();
HWND hWnd; // Storeage for window handle MSG msg;
WNDCLASS wc{};
HWND hWnd;
hInstance = hInst; hInstance = hInst;
// Register Window style // Rejestracja klasy okna
wc.style = CS_HREDRAW | CS_VREDRAW; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // DODANO CS_OWNDC - ważne dla OpenGL
wc.lpfnWndProc = (WNDPROC)WndProc; wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0; wc.cbClsExtra = 0;
wc.cbWndExtra = 0; wc.cbWndExtra = 0;
wc.hInstance = hInstance; wc.hInstance = hInstance;
wc.hIcon = NULL; wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hCursor = LoadCursor(NULL, IDC_ARROW);
// No need for background brush for OpenGL window
wc.hbrBackground = NULL; wc.hbrBackground = NULL;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
wc.lpszClassName = lpszAppName; wc.lpszClassName = lpszAppName;
// Register the window class
if (RegisterClass(&wc) == 0) return FALSE; if (RegisterClass(&wc) == 0) return FALSE;
// Create the main application window
hWnd = CreateWindow( hWnd = CreateWindow(
lpszAppName, lpszAppName, lpszAppName,
lpszAppName,
// OpenGL requires WS_CLIPCHILDREN and WS_CLIPSIBLINGS
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 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; if (hWnd == NULL) return FALSE;
// --- POPRAWKA 1: Pobierz HDC raz ---
HDC hDC = GetDC(hWnd);
const WORD ID_TIMER = 1; const WORD ID_TIMER = 1;
SetTimer(hWnd, ID_TIMER, 100, NULL); SetTimer(hWnd, ID_TIMER, 100, NULL);
// Display the window
ShowWindow(hWnd, SW_SHOW); ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd); UpdateWindow(hWnd);
// Process application messages until the application closes ZeroMemory(&msg, sizeof(msg));
while (GetMessage(&msg, NULL, 0, 0)) { while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg); TranslateMessage(&msg);
DispatchMessage(&msg); DispatchMessage(&msg);
} }
else {
RenderScene();
// --- POPRAWKA 2: Używaj zapisanego hDC, a nie pobieraj nowego ---
SwapBuffers(hDC);
LOG("FPS: " << fpsCounter.getFPS() << " | Czas: " << fpsCounter.getFrameTime() << "ms");
LimitFPS(targetFPS);
}
}
// --- POPRAWKA 3: Zwolnij HDC przy zamykaniu ---
ReleaseDC(hWnd, hDC);
return msg.wParam; 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 // Tell the application to terminate after the window
// is gone. // is gone.
PostQuitMessage(0); PostQuitMessage(0);
AsyncLogger::getInstance().stop();
break; break;
// Window is resized. // 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 // The painting function. This message sent by Windows
// whenever the screen needs updating. // whenever the screen needs updating.
case WM_PAINT: 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; break;
case WM_QUERYNEWPALETTE: case WM_QUERYNEWPALETTE:
@@ -1125,9 +1141,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_TIMER: case WM_TIMER:
RenderScene();
SwapBuffers(hDC);
ValidateRect(hWnd, NULL);
break; break;
default: // Passes it on if unproccessed default: // Passes it on if unproccessed
@@ -1187,3 +1201,11 @@ BOOL APIENTRY AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam) {
return FALSE; 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);
}