/// <summary> /// Starts the engine's main loop. /// </summary> /// <param name="cancelToken"></param> /// <returns></returns> internal Task Start(CancellationToken cancelToken) { if (_runningTask != null) { throw new SeeingSharpGraphicsException("Unable to start engine's main loop: Loop has already started!"); } _runningTask = Task.Factory.StartNew(async() => { var renderStopWatch = new Stopwatch(); renderStopWatch.Start(); var renderingRenderLoops = new List <RenderLoop>(16); var scenesToRender = new List <Scene>(16); var camerasToUpdate = new List <Camera3DBase>(16); var devicesInUse = new List <EngineDevice>(16); var inputFrames = new List <InputFrame>(16); var updateState = new UpdateState(TimeSpan.Zero, null); while (!cancelToken.IsCancellationRequested) { var exceptionOccurred = false; try { using (_host.BeginMeasureActivityDuration(SeeingSharpConstants.PERF_GLOBAL_PER_FRAME)) { // Wait some time before doing anything.. var lastRenderMilliseconds = renderStopWatch.GetTrueElapsedMilliseconds(); var delayTime = SeeingSharpConstants.MINIMUM_FRAME_TIME_MS - lastRenderMilliseconds; if (delayTime < SeeingSharpConstants.MINIMUM_DELAY_TIME_MS) { delayTime = SeeingSharpConstants.MINIMUM_DELAY_TIME_MS; } using (_host.BeginMeasureActivityDuration(SeeingSharpConstants.PERF_GLOBAL_WAIT_TIME)) { SeeingSharpUtil.MaximumDelay(delayTime); } // Get all render loops renderingRenderLoops.Clear(); lock (_registeredRenderLoopsLock) { renderingRenderLoops.AddRange(_registeredRenderLoops); } // Queries for devices / scenes in use QueryForScenesAndCameras(renderingRenderLoops, scenesToRender, camerasToUpdate); QueryForDevicesInUse(renderingRenderLoops, devicesInUse); var deviceInUseCount = devicesInUse.Count; // Handle device lost events for (var loop = 0; loop < deviceInUseCount; loop++) { var actDevice = devicesInUse[loop]; if (!actDevice.IsLost) { continue; } actDevice.RecreateAfterDeviceLost(); } // Cleanup device resources for (var loop = 0; loop < deviceInUseCount; loop++) { var actDevice = devicesInUse[loop]; actDevice.CleanupDeviceResourceCollection(); } // Get all input frames _host.InputGatherer !.QueryForCurrentFrames(inputFrames); // Build new UpdateState object var updateTime = renderStopWatch.Elapsed; if (updateTime.TotalMilliseconds > 100.0) { updateTime = TimeSpan.FromMilliseconds(100.0); } updateState.Reset(updateTime, inputFrames); // Restart the stopwatch renderStopWatch.Restart(); // First global pass: Update scene and prepare rendering _logicUpdateAndPrepareRendering.SetPassParameters(renderingRenderLoops, scenesToRender, devicesInUse, updateState); await _logicUpdateAndPrepareRendering.ExecutePassAsync() .ConfigureAwait(false); // Update all cameras foreach (var actCamera in camerasToUpdate) { actCamera.AnimationHandler.Update(updateState); } // Queries for devices / scenes in use (may have changed during prepare) QueryForScenesAndCameras(renderingRenderLoops, scenesToRender, camerasToUpdate); QueryForDevicesInUse(renderingRenderLoops, devicesInUse); // Second global pass: Render scene(s) and update beside _logicRenderAndUpdateBeside.SetPassParameters(renderingRenderLoops, scenesToRender, devicesInUse, updateState); _logicRenderAndUpdateBeside.ExecutePass(); // Raise generic input event (if registered) _cachedInputEventArgs.NotifyNewPass(inputFrames); try { this.GenericInput?.Raise(this, _cachedInputEventArgs); } catch (Exception ex) { GraphicsCore.PublishInternalExceptionInfo(ex, InternalExceptionLocation.EngineMainLoop_GenericInputEvent); } // Clear unreferenced Scenes finally lock (_scenesForUnloadLock) { foreach (var actScene in _scenesForUnload) { actScene.UnloadResources(); actScene.Clear(true); } _scenesForUnload.Clear(); } // Unload all Direct2D resources which are not needed anymore while (_drawing2DResourcesToUnload.TryDequeue(out var act2DResourceToUnload)) { var deviceCount = _host.DeviceCount; for (var loop = 0; loop < deviceCount; loop++) { act2DResourceToUnload.UnloadResources(_host.Devices[loop]); } } } } catch (Exception ex) { exceptionOccurred = true; // Publish exception info GraphicsCore.PublishInternalExceptionInfo(ex, InternalExceptionLocation.EngineMainLoop_Loop); } // Execute global awaitors var prevCount = _globalLoopAwaitors.Count; var currentIndex = 0; while (currentIndex < prevCount) { currentIndex++; if (_globalLoopAwaitors.TryDequeue(out var currentAction)) { currentAction(); } } if (exceptionOccurred) { // Wait some time and try rendering again await Task.Delay(1000); } // Handle suspend / resume var suspendWaiter = _suspendWaiter; if (suspendWaiter != null) { _suspendCallWaiterSource !.TrySetResult(null); // Wait for resuming await suspendWaiter; } // Trigger calculation of performance values _host.PerformanceAnalyzer !.CalculateResults(); } }); return(_runningTask); }