예제 #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Updating3DArgs"/> class.
 /// </summary>
 public Updating3DArgs(UpdateState updateState)
 {
     this.UpdateState = updateState;
 }
예제 #2
0
        /// <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);
            }
        }
예제 #3
0
        /// <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;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Starts the engine's main loop.
        /// </summary>
        /// <param name="cancelToken"></param>
        /// <returns></returns>
        internal Task Start(CancellationToken cancelToken)
        {
            if (m_runningTask != null)
            {
                throw new SeeingSharpGraphicsException("Unable to start engine's main loop: Loop has already started!");
            }

            m_runningTask = Task.Factory.StartNew(async() =>
            {
                Stopwatch renderStopWatch = new Stopwatch();
                renderStopWatch.Start();

                List <RenderLoop> renderingRenderLoops = new List <RenderLoop>(16);
                List <Scene> scenesToRender            = new List <Scene>(16);
                List <Camera3DBase> camerasToUpdate    = new List <Camera3DBase>(16);
                List <EngineDevice> devicesInUse       = new List <EngineDevice>(16);
                List <InputFrame> inputFrames          = new List <InputFrame>(16);
                UpdateState updateState = new UpdateState(TimeSpan.Zero);
                while (!cancelToken.IsCancellationRequested)
                {
                    bool exceptionOccurred = false;
                    try
                    {
                        using (var perfToken = m_host.BeginMeasureActivityDuration(Constants.PERF_GLOBAL_PER_FRAME))
                        {
                            // Wait some time before doing anything..
                            double lastRenderMilliseconds = renderStopWatch.GetTrueElapsedMilliseconds();
                            double delayTime = Constants.MINIMUM_FRAME_TIME_MS - lastRenderMilliseconds;
                            if (delayTime < Constants.MINIMUM_DELAY_TIME_MS)
                            {
                                delayTime = Constants.MINIMUM_DELAY_TIME_MS;
                            }

                            using (var perfTokenInner = m_host.BeginMeasureActivityDuration(Constants.PERF_GLOBAL_WAIT_TIME))
                            {
                                CommonTools.MaximumDelay(delayTime);
                            }

                            // Get all render loops
                            renderingRenderLoops.Clear();
                            lock (m_registeredRenderLoopsLock)
                            {
                                renderingRenderLoops.AddRange(m_registeredRenderLoops);
                            }

                            // Queries for devices / scenes in use
                            QueryForScenesAndCameras(renderingRenderLoops, scenesToRender, camerasToUpdate);
                            QueryForDevicesInUse(renderingRenderLoops, devicesInUse);

                            // Build new UpdateState object
                            TimeSpan updateTime = renderStopWatch.Elapsed;
                            if (updateTime.TotalMilliseconds > 100.0)
                            {
                                updateTime = TimeSpan.FromMilliseconds(100.0);
                            }
                            updateState.Reset(updateTime);

                            // Restart the stopwatch
                            renderStopWatch.Restart();

                            // Get all input frames
                            m_host.InputGatherer.QueryForCurrentFrames(inputFrames);

                            // First global pass: Update scene and prepare rendering
                            await UpdateAndPrepareRendering(renderingRenderLoops, scenesToRender, devicesInUse, inputFrames, updateState)
                            .ConfigureAwait(false);
                            foreach (Camera3DBase 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
                            RenderAndUpdateBeside(renderingRenderLoops, scenesToRender, devicesInUse, updateState);

                            // Raise generic input event (if registered)
                            if (this.GenericInput != null)
                            {
                                this.GenericInput.Raise(this, new GenericInputEventArgs(inputFrames));
                            }

                            // Clear unreferenced Scenes finally
                            lock (m_scenesForUnloadLock)
                            {
                                foreach (Scene actScene in m_scenesForUnload)
                                {
                                    actScene.UnloadResources();
                                    actScene.Clear(true);
                                }
                                m_scenesForUnload.Clear();
                            }

                            // Unload all Direct2D resources which are not needed anymore
                            Drawing2DResourceBase act2DResourceToUnload = null;
                            while (m_drawing2DResourcesToUnload.TryDequeue(out act2DResourceToUnload))
                            {
                                foreach (EngineDevice actDevice in m_host.Devices)
                                {
                                    act2DResourceToUnload.UnloadResources(actDevice);
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {
                        exceptionOccurred = true;
                    }

                    // Execute global awaiters
                    while (m_globalLoopAwaiters.Count > 0)
                    {
                        Action currentAction = null;
                        if (m_globalLoopAwaiters.TryDequeue(out currentAction))
                        {
                            currentAction();
                        }
                    }

                    if (exceptionOccurred)
                    {
                        // Wait some time and try rendering again
                        await Task.Delay(1000);
                    }

                    // Handle suspend / resume
                    Task suspendWaiter = m_suspendWaiter;
                    if (suspendWaiter != null)
                    {
                        m_suspendCallWaiterSource.TrySetResult(null);

#if UNIVERSAL
                        // Call Trim on all devices
                        foreach (EngineDevice actDevice in m_host.Devices)
                        {
                            if (actDevice.DeviceDxgi != null)
                            {
                                actDevice.DeviceDxgi.Trim();
                            }
                        }
#endif

                        // Wait for resuming
                        await suspendWaiter;
                    }
                }
            });

            return(m_runningTask);
        }