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>
class FPSCounter {
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++;
auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = currentTime - lastTime;
// Aktualizujemy FPS co 1 sekundê
if (elapsed.count() >= 1.0) {
double fps = frameCount / elapsed.count();
std::cout << "FPS: " << fps << "\n";
currentFPS = frameCount / elapsed.count();
// Obliczamy czas klatki w ms
currentFrameTime = 1000.0 / (currentFPS == 0 ? 1 : currentFPS);
frameCount = 0;
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:
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;
};
};

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="lazik.hpp" />
<ClInclude Include="loadOBJ.h" />
<ClInclude Include="Logger.hpp" />
<ClInclude Include="plane.hpp" />
<ClInclude Include="RESOURCE.H" />
<ClInclude Include="shader.hpp" />

View File

@@ -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">

124
main.cpp
View File

@@ -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)) {
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);
LOG("FPS: " << fpsCounter.getFPS() << " | Czas: " << fpsCounter.getFrameTime() << "ms");
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);
}