// All code here should be running in it's own thread created in INTERNAL_Init_SDLThread() // and should never be called outside of the thread itself. void INTERNAL_SDLThread_Main() { Console.Write("INTERNAL_SDLThread_Main()\n"); _threadState = SDLThreadState.Starting; // Create the SDL_Window var windowOK = INTERNAL_SDLThread_InitWindow(); if (!windowOK) { INTERNAL_SDLThread_Cleanup(SDLThreadState.Error); return; } // Create the SDL_Renderer var rendererOK = INTERNAL_SDLThread_InitRenderer(); if (!rendererOK) { INTERNAL_SDLThread_Cleanup(SDLThreadState.Error); return; } // Translate the state machine into something meaningful to SDL INTERNAL_UpdateState_FunctionPointers(); INTERNAL_UpdateState_CursorVisibility(); INTERNAL_UpdateState_ThreadIntervals(); // Now do the main thread loop INTERNAL_SDLThread_MainLoop(); // Clean up after the thread INTERNAL_SDLThread_Cleanup(SDLThreadState.Inactive); }
void INTERNAL_SDLThread_Cleanup(SDLThreadState newState) { Console.Write("INTERNAL_SDLThread_Cleanup()\n"); // Dispose of the renderer, window, etc INTERNAL_SDLThread_ReleaseWindowAndRenderer(); _sdlThread = null; _threadState = newState; }
void INTERNAL_SDLThread_MainLoop() { Console.Write("INTERNAL_SDLThread_MainLoop()\n"); TimeSpan loopTime = TimeSpan.FromTicks(0); long loopStartTick = 0; long loopDelayTicks = 0; long loopEndTick = 0; TimeSpan loopSleepTime; int loopSleepMS = 0; long lastDrawTick = 0; long lastEventTick = 0; long drawTickDelta = 0; long eventTickDelta = 0; long lastFPSCount = 0; long fpsTicks = 0; long frameStart = 0; long frameEnd = 0; long frameTime = 0; // Thread timing _threadTimer = new Stopwatch(); // Mark the thread as running _threadState = SDLThreadState.Running; _threadTimer.Start(); // Loop until we exit Console.Write("INTERNAL_SDLThread_MainLoop() :: Loop_Enter\n"); while (!_exitRequested) { //Console.Write( "INTERNAL_SDLThread_MainLoop() :: Loop_Top\n" ); // Get the loop start timestamp if (!_exitRequested) { loopStartTick = _threadTimer.Elapsed.Ticks; loopDelayTicks += loopTime.Ticks; } // Pause the thread at the top of the loop if (_pauseThread) { // Pause the thread until _pauseThread returns to false. "Pausing" in // this case is sleeping for 1ms in a loop. Ideally we would sleep for // less than 1ms though, but 0 will make us return as soon as we can // and use a bunch of CPU which we don't want. 1ms isn't even the true // amount of time we'll sleep for, but 1 is the lowest positive value // and will translate into the actual platform minimum sleep time // (typically 15ms on Windows). _threadState = SDLThreadState.Paused; Console.Write("INTERNAL_SDLThread_MainLoop() :: Loop_Pause\n"); while (_pauseThread) { Thread.Sleep(1); } Console.Write("INTERNAL_SDLThread_MainLoop() :: Loop_Resume\n"); _threadState = SDLThreadState.Running; } // Save the last frame drawn to an image file if ((!_exitRequested) && (_windowSaveRequested)) { INTERNAL_SDLThread_SaveWindowToFile(); } // Handle any invokes that have been queued if ((!_exitRequested) && (!_invokeQueue.NullOrEmpty())) { INTERNAL_SDLThread_InvokeQueue_Dispatcher(); } // Window needs to be recreated, do that before anything else if ((!_exitRequested) && (_windowResetRequired)) { _exitRequested = !INTERNAL_SDLThread_ResetWindow(); } // Renderer needs to be recreated, do that before anything else if ((!_exitRequested) && (_rendererResetRequired)) { _exitRequested = !INTERNAL_SDLThread_ResetRenderer(); } // Render the scene and handle events if we haven't been asked to terminate if (!_exitRequested) { if (loopDelayTicks >= _baseFrameDelay) { loopDelayTicks -= _baseFrameDelay; drawTickDelta = loopStartTick - lastDrawTick; eventTickDelta = loopStartTick - lastEventTick; if (drawTickDelta >= _drawTicks) { // Time to render the scene lastFPSCount++; drawTickDelta -= _drawTicks; lastDrawTick = loopStartTick; frameStart = _threadTimer.Elapsed.Ticks; INTERNAL_SDLThread_RenderScene(); frameEnd = _threadTimer.Elapsed.Ticks; frameTime += (frameEnd - frameStart); } if (eventTickDelta >= _eventTicks) { // Time to check and handle events eventTickDelta -= _eventTicks; lastEventTick = loopStartTick; INTERNAL_SDLThread_EventDispatcher(); } } } // If a client event handler requested an exit, don't worry about timings if (!_exitRequested) { // Sleep until the next expected update loopSleepTime = TimeSpan.FromTicks(_baseFrameDelay - loopDelayTicks); loopSleepMS = (int)loopSleepTime.TotalMilliseconds; if (loopSleepMS >= 0) { Thread.Sleep(loopSleepMS); } // End of loop, get time elapsed for this loop loopEndTick = _threadTimer.Elapsed.Ticks; loopTime = TimeSpan.FromTicks(loopEndTick - loopStartTick); // Performance feedback fpsTicks += loopTime.Ticks; if ((fpsTicks >= TimeSpan.TicksPerSecond) && (lastFPSCount > 0)) { _actualFPS = (int)((lastFPSCount * TimeSpan.TicksPerSecond) / fpsTicks); _potentialFPS = (int)((lastFPSCount * TimeSpan.TicksPerSecond) / frameTime); _averageFrameTime = frameTime / lastFPSCount; lastFPSCount = 0; fpsTicks = 0; frameTime = 0; } } //Console.Write( "INTERNAL_SDLThread_MainLoop() :: Loop_Bottom\n" ); } Console.Write("INTERNAL_SDLThread_MainLoop() :: Loop_Exit\n"); // Mark the thread as no longer running _threadTimer.Stop(); _threadState = SDLThreadState.Stopping; _actualFPS = 0; _potentialFPS = 0; _averageFrameTime = 0; }