void CalcElapsedTime(SYSTEMTIME* StartTime, SYSTEMTIME* FinishTime, SYSTEMTIME* ElapsedTime) { DWORD SecondsElapsed = 0; memset(ElapsedTime,0,sizeof(SYSTEMTIME)); SecondsElapsed = GetSecondsElapsed(StartTime, FinishTime); if(SecondsElapsed >= SECONDS_IN_A_DAY) { ElapsedTime->wDay = (WORD) (SecondsElapsed / SECONDS_IN_A_DAY); SecondsElapsed -= (ElapsedTime->wDay*SECONDS_IN_A_DAY); } if(SecondsElapsed >= SECONDS_IN_A_HOUR) { ElapsedTime->wHour = (WORD) (SecondsElapsed / SECONDS_IN_A_HOUR); SecondsElapsed -= (ElapsedTime->wHour * SECONDS_IN_A_HOUR); } if(SecondsElapsed >= SECONDS_IN_A_MINUTE) { ElapsedTime->wMinute = (WORD) (SecondsElapsed / SECONDS_IN_A_MINUTE); SecondsElapsed -= (ElapsedTime->wMinute * SECONDS_IN_A_MINUTE); } ElapsedTime->wSecond = (WORD) SecondsElapsed; }
void PrintTimeElapsed(LARGE_INTEGER start, LARGE_INTEGER end) { float msPerFrame = 1000.0f * GetSecondsElapsed(start, end); float fps = 1000.0f / msPerFrame; std::cout<< "ms/f: " << msPerFrame << ", FPS: " << fps << "\n"; }
int main(int argc, char *args[]) { #if _WIN32 // NOTE(nathan): Set the windows schedular granularity to 1ms // so that our SDL_Delay() and be more granular // SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); // NOTE(nathan): This seems to give a more consistant result than above timeBeginPeriod(1); #if _DEBUG HWND console = GetConsoleWindow(); if(console != 0) MoveWindow(console, -900, 100, 700, 800, TRUE); #endif #endif SDL_Init(SDL_INIT_EVERYTHING); // NOTE(nathan): This also initializes the window_with and window_height WindowData window_data = { }; window_data.target_width = 1920; window_data.target_height = 1080; FixViewport(1280, 720, &window_data); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4); SDL_Window *window = SDL_CreateWindow( "Banana", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_data.width, window_data.height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); // ToggleFullScreen(window); SDL_ShowCursor(SDL_DISABLE); // OutputSystemInfo(); SDL_GLContext glContext = SDL_GL_CreateContext(window); glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) Error("glewInit"); // Goes stuttery if we turn on vsync SDL_GL_SetSwapInterval((int)window_data.vsync); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); bool running = true; SDL_Event e; uint32 gameUpdateHz = 60; float32 targetSecondsPerFrame = 1.0f / (float32)gameUpdateHz; perfCountFreq = SDL_GetPerformanceFrequency(); uint64 lastCounter = GetWallClock(); GameMemory game_memory = { 0 }; game_memory.size = Megabytes(64); game_memory.memory = SDL_malloc(game_memory.size); EditorMemory editor_memory = { 0 }; editor_memory.size = Megabytes(64); editor_memory.memory = SDL_malloc(editor_memory.size); RenderContext render_context = {}; render_context.diffuse = CreateShader("assets/shaders/diffuse.vert", "assets/shaders/diffuse.frag"); InitializeContext(&render_context); VoxelRenderContext voxel_render_context = {}; voxel_render_context.diffuse = CreateShader("assets/shaders/voxels.vert", "assets/shaders/voxels.frag"); InitializeVoxelContext(&voxel_render_context); InputData input = { }; input.window = window; UIContext ui_context = { 0 }; ui_context.input = &input; ui_context.render_context = &render_context; Mode mode = MODE_GAME; bool game_paused = false; while (running) { while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) running = false; if (e.type == SDL_WINDOWEVENT) { if (e.window.event == SDL_WINDOWEVENT_RESIZED) { FixViewport((int)e.window.data1, (int)e.window.data2, &window_data); } } if (e.type == SDL_KEYDOWN) { SDL_Scancode key = e.key.keysym.scancode; input.keyboard_state[key] = true; if (key == SDL_SCANCODE_ESCAPE) running = false; if (key == SDL_SCANCODE_F12) window_data.vsync = !window_data.vsync; if (key == SDL_SCANCODE_F11) ToggleFullScreen(window, &window_data); if (key == SDL_SCANCODE_F10) { if (mode == MODE_GAME) { game_paused = true; mode = MODE_EDITOR; } else { mode = MODE_GAME; game_paused = false; } } } if (e.type == SDL_KEYUP) { SDL_Scancode key = e.key.keysym.scancode; input.keyboard_state[key] = false; } if (e.type == SDL_MOUSEMOTION) { input.mouse_pos.x = (((float)window_data.target_width / (float)window_data.vp_width) * ((float)e.motion.x - window_data.vp_x)); input.mouse_pos.y = (((float)window_data.target_height / (float)window_data.vp_height) * ((float)e.motion.y - window_data.vp_y)); } if (e.type == SDL_MOUSEBUTTONDOWN) { if (e.button.button == SDL_BUTTON_LEFT) input.mb_left = true; if (e.button.button == SDL_BUTTON_RIGHT) input.mb_right = true; if (e.button.button == SDL_BUTTON_MIDDLE) input.mb_middle = true; } if (e.type == SDL_MOUSEBUTTONUP) { if (e.button.button == SDL_BUTTON_LEFT) input.mb_left = false; if (e.button.button == SDL_BUTTON_RIGHT) input.mb_right = false; if (e.button.button == SDL_BUTTON_MIDDLE) input.mb_middle = false; } } float32 delta = 1.0f / (float32)gameUpdateHz; GameUpdateAndRender(&game_memory, &input, &render_context, &voxel_render_context, &window_data, game_paused, delta); uint64 workCounter = GetWallClock(); float32 workSecondsElapsed = GetSecondsElapsed(lastCounter, workCounter); // Dont include this in frametime if (mode == MODE_EDITOR) EditorUpdateAndRender(&editor_memory, &input, &render_context, &ui_context, delta); float32 secondsElapsedForFrame = workSecondsElapsed; if (secondsElapsedForFrame < targetSecondsPerFrame) { uint32 sleepMS = (uint32)((1000.0f * (targetSecondsPerFrame - secondsElapsedForFrame)) - 1); if (sleepMS > 0) SDL_Delay(sleepMS); while (secondsElapsedForFrame < targetSecondsPerFrame) { secondsElapsedForFrame = GetSecondsElapsed(lastCounter, GetWallClock()); } } else { // Error("Missed Frame Rate"); } uint64 endCounter = GetWallClock(); float64 msPerFrame = 1000.0f * GetSecondsElapsed(lastCounter, endCounter); lastCounter = endCounter; // Debug Render BeginRenderer(&render_context); std::string s = "FRAMETIME " + std::to_string(msPerFrame) + " VSYNC " + std::to_string(window_data.vsync); RenderString(&render_context, 40.0f, 40.0f, s.c_str(), 0.0f); EndRenderer(); SDL_GL_SwapWindow(window); input.prev_keyboard_state = input.keyboard_state; input.mb_left_prev = input.mb_left; input.mb_right_prev = input.mb_right; input.mb_middle_prev = input.mb_middle; } UnloadContext(&render_context); UnloadVoxelContext(&voxel_render_context); SDL_free(game_memory.memory); SDL_GL_DeleteContext(glContext); SDL_DestroyWindow(window); SDL_Quit(); return(0); }
//Our main loop which should continue running as long as we don't quite the game static void MainLoop() { char DLLFilePath[MAX_PATH]; char *onePastLastSlash; DWORD pathSize = GetModuleFileNameA(NULL, DLLFilePath, sizeof(DLLFilePath)); onePastLastSlash = DLLFilePath; for (char *scan = DLLFilePath; *scan; scan++) { if (*scan == '\\') { onePastLastSlash = scan + 1; } } char DLLFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game.dll", DLLFullPath, sizeof(DLLFullPath)); char tempDLLFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game_temp.dll", tempDLLFullPath, sizeof(tempDLLFullPath)); char PDBFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game.pdb", PDBFullPath, sizeof(PDBFullPath)); char tempPDBFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game_temp.pdb", tempPDBFullPath, sizeof(tempPDBFullPath)); Input = {}; Input.UP.Button = VK_UP; Input.DOWN.Button = VK_DOWN; Input.RIGHT.Button = VK_RIGHT; Input.LEFT.Button = VK_LEFT; LARGE_INTEGER performanceFrequency; QueryPerformanceFrequency(&performanceFrequency); TicksPerSecond = performanceFrequency.QuadPart; int monitorRefreshHZ = 60; HDC deviceContext = GetDC(Window.Window); int refreshHz = GetDeviceCaps(deviceContext, VREFRESH); ReleaseDC(Window.Window, deviceContext); if (refreshHz > 1) { monitorRefreshHZ = refreshHz; } float gameUpdateHZ = (float)(monitorRefreshHZ); float targetSecondsPerFrame = 1.0f / gameUpdateHZ; UINT desiredSchedulerTime = 1; bool sleepIsSmaller = true;//timeBeginPeriod(desiredSchedulerTime) == TIMERR_NOERROR; LARGE_INTEGER lastTick = GetTicks(); float updateTime = 0; int updates = 0; double frames = 0; double frameTime = 0; while (IsRunning) { /* start_loop = clock(); */ FILETIME newWriteTimeDLL = GetLastWriteTime(DLLFullPath); FILETIME newWriteTimePDB = GetLastWriteTime(PDBFullPath); if (CompareFileTime(&newWriteTimeDLL, &Game.LastWriteTimeDLL) != 0 && CompareFileTime(&newWriteTimeDLL, &Game.LastWriteTimePDB) != 0) { UnloadGameCode(&Game); CopyFile(PDBFullPath, tempPDBFullPath, FALSE); Game = LoadGameCode(DLLFullPath, tempDLLFullPath); Game.Game_Init(Dimensions); } LARGE_INTEGER gameTimerStart = GetTicks(); ProcessPendingMessages(&Keys); ProcessInput(&Input); //Update everything //Update the game Game.Game_Update(&Input); /*NOTE(kai): TEST ONLY*/ //Testing if A button is pressed if (IsKeyDown(&Keys, 'A')) { OutputDebugString("Key: a is pressed\n"); } //Testing if A button is released if (IsKeyUp(&Keys, 'A')) { OutputDebugString("Key: a is released\n"); } //Render everything //Clear the window ClearWindow(); //Render the game Game.Game_Render(); LARGE_INTEGER gameTimerEnd = GetTicks(); frameTime += (double)(1000.0f * GetSecondsElapsed(gameTimerStart, gameTimerEnd)); frames++; //frames += 1000.0f / (double)(1000.0f * GetSecondsElapsed(gameTimerStart, gameTimerEnd)); //PrintTimeElapsed(lastTick, gameTimerEnd); float secondsElapsedForFrame = GetSecondsElapsed(lastTick, GetTicks()); if (secondsElapsedForFrame < targetSecondsPerFrame) { if (sleepIsSmaller) { DWORD sleepTime = (DWORD)(1000.0f * (targetSecondsPerFrame - secondsElapsedForFrame)); if (sleepTime > 0) { Sleep(sleepTime); } } while (secondsElapsedForFrame < targetSecondsPerFrame) { secondsElapsedForFrame = GetSecondsElapsed(lastTick, GetTicks()); } updates++; } updateTime += GetSecondsElapsed(lastTick, GetTicks()); if (updateTime >= 1.0f) { double avgFPS = 1000.0f / ((frameTime) / frames); std::cout << "UPS: " << updates << ", average FPS: " << avgFPS << ", average work/frame: " << (frameTime) / frames << "\n"; frames = 0; frameTime = 0; updates = 0; updateTime = 0; } LARGE_INTEGER endTick = GetTicks(); //PrintTimeElapsed(lastTick, endTick); lastTick = endTick; //Render the window RenderWindow(Window.Window); /* //calc fps calcfps(); static int framecount = 0; framecount++; if (framecount == 10) { framecount = 0; std::cout << "frame per second is : " << (fps) << std::endl; } //QueryPerformanceCounter(&t_current_loop); end_loop = clock(); //float frameticks = (t_current_loop.QuadPart - t_previous_loop.QuadPart) / ((frequency_loop.QuadPart) / 1000.0); float frameticks = ((float)(end_loop - start_loop) / CLOCKS_PER_SEC) * 1000.0f; //print the current fps // std::cout << 1000/frameticks << std::endl; if (1000.0f / max_fps > frameticks){ Sleep(1000.0f / max_fps - frameticks); } */ } //Release resources (if there is any) and destory the window Release(); }