/// <summary> /// Registers the given view on this thread. /// </summary> internal void RegisterView(IInputEnabledView view) { view.EnsureNotNull(nameof(view)); m_commandQueue.Enqueue(() => { List <IInputHandler> inputHandlers = InputHandlerFactory.CreateInputHandlersForView(view); if (inputHandlers == null) { return; } if (inputHandlers.Count == 0) { return; } // Deregister old input handlers if necessary if (m_viewInputHandlers.ContainsKey(view)) { List <IInputHandler> oldList = m_viewInputHandlers[view]; foreach (IInputHandler actOldInputHanlder in oldList) { actOldInputHanlder.Stop(); } m_viewInputHandlers.Remove(view); } // Register new ones m_viewInputHandlers[view] = inputHandlers; foreach (IInputHandler actInputHandler in inputHandlers) { actInputHandler.Start(view); } }); }
/// <summary> /// Waits until the given object is visible. /// </summary> /// <param name="sceneObjects">The scene objects to check for.</param> /// <param name="viewInfo">The view on which to check for visibility.</param> /// <param name="cancelToken">The cancellation token.</param> public Task WaitUntilVisibleAsync(IEnumerable <SceneObject> sceneObjects, ViewInformation viewInfo, CancellationToken cancelToken = default(CancellationToken)) { sceneObjects.EnsureNotNull(nameof(sceneObjects)); viewInfo.EnsureNotNull(nameof(viewInfo)); TaskCompletionSource <object> taskComplSource = new TaskCompletionSource <object>(); // Define the poll action (polling is done inside scene update Action pollAction = null; pollAction = () => { if (AreAllObjectsVisible(sceneObjects, viewInfo)) { taskComplSource.SetResult(null); } else if (cancelToken.IsCancellationRequested) { taskComplSource.SetCanceled(); } else { m_asyncInvokesBeforeUpdate.Enqueue(pollAction); } }; // Register first call of the polling action m_asyncInvokesBeforeUpdate.Enqueue(pollAction); return(taskComplSource.Task); }
/// <summary> /// Attaches the given component to this scene. /// </summary> /// <param name="component">The component to be attached.</param> /// <param name="sourceView">The view which attaches the component.</param> internal void AttachComponent(SceneComponentBase component, ViewInformation sourceView) { component.EnsureNotNull(nameof(component)); if (component.IsViewSpecific) { sourceView.EnsureNotNull(nameof(sourceView)); } m_componentRequests.Enqueue(new SceneComponentRequest() { RequestType = SceneComponentRequestType.Attach, Component = component, CorrespondingView = component.IsViewSpecific ? sourceView : null }); }
/// <summary> /// Gets all gathered InputFrames. /// </summary> internal void QueryForCurrentFrames(List <InputFrame> targetList) { // Do first recover all old frames m_recoveredInputFrames.Enqueue(targetList); targetList.Clear(); // Enqueue new frames m_gatheredInputFrames.DequeueAll(targetList); }
/// <summary> /// Awaits next render. /// </summary> public Task AwaitRenderAsync() { if (!this.IsOperational) { return(Task.Delay(100)); } TaskCompletionSource <object> result = new TaskCompletionSource <object>(); m_renderAwaitors.Enqueue(result); return(result.Task); }
/// <summary> /// Performs the given action beside rendering process. /// (given action gets called by update thread while render threads are rendering.) /// </summary> /// <param name="actionToInvoke">The action to be invoked.</param> public Task PerformBesideRenderingAsync(Action actionToInvoke) { actionToInvoke.EnsureNotNull(nameof(actionToInvoke)); TaskCompletionSource <bool> taskCompletionSource = new TaskCompletionSource <bool>(); m_asyncInvokesUpdateBesideRendering.Enqueue(() => { try { actionToInvoke(); taskCompletionSource.TrySetResult(true); } catch (Exception ex) { taskCompletionSource.TrySetException(ex); } }); return(taskCompletionSource.Task); }
protected override void OnTick(EventArgs eArgs) { base.OnTick(eArgs); if (!GraphicsCore.IsInitialized) { return; } // Query for all input handlers on first tick if (m_globalInputHandlers == null) { m_globalInputHandlers = InputHandlerFactory.CreateInputHandlersForGlobal(); foreach (IInputHandler actInputHandler in m_globalInputHandlers) { actInputHandler.Start(null); } } // Execute all commands within the command queue if (m_commandQueue.Count > 0) { Action actCommand = null; while (m_commandQueue.Dequeue(out actCommand)) { actCommand(); } } // Gather all input data int expectedStateCount = m_lastInputFrame != null ? m_lastInputFrame.CountStates : 6; // Create new InputFrame object or reuse an old one InputFrame newInputFrame = null; if (m_recoveredInputFrames.Dequeue(out newInputFrame)) { newInputFrame.Reset(expectedStateCount, SINGLE_FRAME_DURATION); } else { newInputFrame = new InputFrame(expectedStateCount, SINGLE_FRAME_DURATION); } // Gather all input states foreach (IInputHandler actInputHandler in m_globalInputHandlers) { foreach (InputStateBase actInputState in actInputHandler.GetInputStates()) { actInputState.EnsureNotNull(nameof(actInputState)); newInputFrame.AddCopyOfState(actInputState, null); } } foreach (KeyValuePair <IInputEnabledView, List <IInputHandler> > actViewSpecificHandlers in m_viewInputHandlers) { RenderLoop renderLoop = actViewSpecificHandlers.Key.RenderLoop; if (renderLoop == null) { continue; } foreach (IInputHandler actInputHandler in actViewSpecificHandlers.Value) { foreach (InputStateBase actInputState in actInputHandler.GetInputStates()) { actInputState.EnsureNotNull(nameof(actInputState)); newInputFrame.AddCopyOfState(actInputState, renderLoop.ViewInformation); } } } // Store the generated InputFrame m_lastInputFrame = newInputFrame; m_gatheredInputFrames.Enqueue(newInputFrame); // Ensure that we hold input frames for a maximum time range of a second // (older input is obsolete) while (m_gatheredInputFrames.Count > Constants.INPUT_FRAMES_PER_SECOND) { InputFrame dummyFrame = null; m_gatheredInputFrames.Dequeue(out dummyFrame); } }
/// <summary> /// Renders all given scenes using the different devices and performs "UpdateBesideRendering" step. /// </summary> /// <param name="registeredRenderLoops">The registered render loops on the current pass.</param> /// <param name="scenesToRender">All scenes to be updated / rendered.</param> /// <param name="devicesInUse">The rendering devices that are in use.</param> /// <param name="updateState">Current global update state.</param> private void RenderAndUpdateBeside( List <RenderLoop> registeredRenderLoops, List <Scene> scenesToRender, List <EngineDevice> devicesInUse, UpdateState updateState) { using (var perfToken = m_host.BeginMeasureActivityDuration(Constants.PERF_GLOBAL_RENDER_AND_UPDATE_BESIDE)) { ThreadSaveQueue <RenderLoop> invalidRenderLoops = new ThreadSaveQueue <RenderLoop>(); // Trigger all tasks for 'Update' pass Parallel.For(0, devicesInUse.Count + scenesToRender.Count, (actTaskIndex) => { if (actTaskIndex < devicesInUse.Count) { // Render all targets for the current device EngineDevice actDevice = devicesInUse[actTaskIndex]; using (var perfTokenInner = m_host.BeginMeasureActivityDuration(string.Format(Constants.PERF_GLOBAL_RENDER_DEVICE, actDevice.AdapterDescription))) { for (int loop = 0; loop < registeredRenderLoops.Count; loop++) { RenderLoop actRenderLoop = registeredRenderLoops[loop]; try { if (actRenderLoop.Device == actDevice) { actRenderLoop.Render(); } } catch (Exception) { // Mark this renderloop as invalid invalidRenderLoops.Enqueue(actRenderLoop); } } } } else { // Perform updates beside rendering for the current scene int sceneIndex = actTaskIndex - devicesInUse.Count; using (var perfTokenInner = m_host.BeginMeasureActivityDuration(string.Format(Constants.PERF_GLOBAL_UPDATE_BESIDE, sceneIndex))) { Scene actScene = scenesToRender[sceneIndex]; SceneRelatedUpdateState actUpdateState = actScene.CachedUpdateState; actUpdateState.OnStartSceneUpdate(actScene, updateState, null); actScene.UpdateBesideRender(actUpdateState); } } }); // Handle all invalid render loops if (invalidRenderLoops.HasAny()) { foreach (var actRenderLoop in invalidRenderLoops.DequeueAll()) { DeregisterRenderLoop(actRenderLoop); } } // Reset camera changed flags foreach (RenderLoop actRenderLoop in registeredRenderLoops) { actRenderLoop.Camera.StateChanged = false; } } }
/// <summary> /// Updates the scene's and prepares all views for rendering. /// </summary> /// <param name="renderingRenderLoops">The registered render loops on the current pass.</param> /// <param name="scenesToRender">All scenes to be updated / rendered.</param> /// <param name="devicesInUse">The rendering devices that are in use.</param> /// <param name="inputFrames">All InputFrames gathered during last render.</param> /// <param name="updateState">Current global update state.</param> private async Task UpdateAndPrepareRendering(List <RenderLoop> renderingRenderLoops, List <Scene> scenesToRender, List <EngineDevice> devicesInUse, IEnumerable <InputFrame> inputFrames, UpdateState updateState) { using (var perfToken = m_host.BeginMeasureActivityDuration(Constants.PERF_GLOBAL_UPDATE_AND_PREPARE)) { List <Action> additionalContinuationActions = new List <Action>(); object additionalContinuationActionsLock = new object(); // Trigger all tasks for preparing views List <Task <List <Action> > > prepareRenderTasks = new List <Task <List <Action> > >(renderingRenderLoops.Count); for (int actDeviceIndex = 0; actDeviceIndex < devicesInUse.Count; actDeviceIndex++) { EngineDevice actDevice = devicesInUse[actDeviceIndex]; for (int loop = 0; loop < renderingRenderLoops.Count; loop++) { RenderLoop actRenderLoop = renderingRenderLoops[loop]; if (actRenderLoop.Device == actDevice) { // Call prepare render and wait for the answer // => Error handling is a bit tricky.. // Errors are catched by the continuation action var actTask = actRenderLoop.PrepareRenderAsync(); prepareRenderTasks.Add(actTask.ContinueWith((givenTask) => { if (!givenTask.IsFaulted) { return(givenTask.Result); } else { // Deregister this RenderLoop lock (additionalContinuationActionsLock) { additionalContinuationActions.Add(() => { this.DeregisterRenderLoop(actRenderLoop); renderingRenderLoops.Remove(actRenderLoop); }); } return(new List <Action>()); } })); } } } // Handle initial configuration of render loops (=> No current device) for (int loop = 0; loop < renderingRenderLoops.Count; loop++) { RenderLoop actRenderLoop = renderingRenderLoops[loop]; if (actRenderLoop.Device == null) { try { Task <List <Action> > actPrepareRenderTask = actRenderLoop.PrepareRenderAsync(); await actPrepareRenderTask; lock (additionalContinuationActionsLock) { additionalContinuationActions.AddRange(actPrepareRenderTask.Result); } } catch (Exception) { // Deregister this RenderLoop lock (additionalContinuationActionsLock) { additionalContinuationActions.Add(() => { this.DeregisterRenderLoop(actRenderLoop); renderingRenderLoops.Remove(actRenderLoop); }); } } } } // Update all scenes ThreadSaveQueue <Exception> exceptionsDuringUpdate = new ThreadSaveQueue <Exception>(); Parallel.For(0, scenesToRender.Count, (actTaskIndex) => { try { using (var perfToken2 = m_host.BeginMeasureActivityDuration( string.Format(Constants.PERF_GLOBAL_UPDATE_SCENE, actTaskIndex))) { Scene actScene = scenesToRender[actTaskIndex]; SceneRelatedUpdateState actUpdateState = actScene.CachedUpdateState; actUpdateState.OnStartSceneUpdate(actScene, updateState, inputFrames); actScene.Update(actUpdateState); } } catch (Exception ex) { exceptionsDuringUpdate.Enqueue(ex); } }); // Await synchronizations with the view(s) if (prepareRenderTasks.Count > 0) { await Task.WhenAll(prepareRenderTasks.ToArray()); } // Throw exceptions if any occurred during scene update // => This would be a fatal exception, so throw up to main loop if (exceptionsDuringUpdate.HasAny()) { throw new AggregateException("Error(s) during Scene update!", exceptionsDuringUpdate.DequeueAll().ToArray()); } // Trigger all continuation actions returned by the previously executed prepare tasks foreach (var actPrepareTasks in prepareRenderTasks) { if (actPrepareTasks.IsFaulted || actPrepareTasks.IsCanceled) { continue; } foreach (Action actContinuationAction in actPrepareTasks.Result) { actContinuationAction(); } } foreach (var actAction in additionalContinuationActions) { actAction(); } // Reset all dummy flags before rendering foreach (var actRenderLoop in renderingRenderLoops) { actRenderLoop.ResetFlagsBeforeRendering(); } // Unload all derigistered RenderLoops await UpdateRenderLoopRegistrationsAsync(renderingRenderLoops); } }