/// <summary>
        /// Renders file with Settings in pre-defined chunksizes. Each chunk will be rendered independently without batching
        /// Very slow due to blender being initialized for every chunk. But tasks are consumed optimally
        /// Benefits from live update as tiles finish they are send back to client
        /// </summary>
        private async Task <Bitmap> RenderChunked(List <RenderNode> validNodes, Action <RenderSubTask> onSubTaskFinished = null)
        {
            object   drawLock = new object();
            Bitmap   result   = new Bitmap(Settings.OutputWidth, Settings.OutputHeight);
            Graphics g        = Graphics.FromImage(result);


            List <RenderSubTask>            tasks = GetChunkedSubTasks();
            ConcurrentQueue <RenderSubTask> queue = GetTaskQueueInOrder(tasks, Settings.Order);

            List <string> exceptions = new List <string>();

            //Force parallelization
            return(await Task.Run(() =>
            {
                ForceParallel(validNodes, (node) =>
                {
                    int errorCount = 0;
                    while (queue.Count > 0)
                    {
                        RenderSubTask task = null;
                        if (!queue.TryDequeue(out task))
                        {
                            continue;
                        }
                        try
                        {
                            SubTaskResult taskResult = ExecuteSubTask(node, task);

                            ProcessTile(task, (Bitmap)taskResult.Image, ref g, ref result, ref drawLock);

                            onSubTaskFinished?.Invoke(task);
                        }
                        catch (Exception ex)
                        {
                            errorCount++;
                            if (task != null)
                            {
                                queue.Enqueue(task);
                            }
                            node.UpdateException($"Render fail [{errorCount+1}/3]: {ex.Message}");
                            exceptions.Add(ex.Message);
                            if (errorCount > 2)
                            {
                                return;
                            }
                        }
                    }
                });

                if (queue.Count > 0)
                {
                    throw new AggregateException("Not all tiles rendered", exceptions.Select(x => new Exception(x)));
                }

                if (g != null)
                {
                    g.Dispose();
                }

                return result;
            }));
        }
        public async Task <bool> RenderAnimation(int start, int end)
        {
            if (Consumed)
            {
                throw new InvalidOperationException("Already started render..");
            }
            Consumed = true;
            try
            {
                List <RenderNode> pool       = Nodes.Where(x => x.Connected).ToList();
                List <RenderNode> validNodes = new List <RenderNode>();

                await Task.WhenAll(pool.Select(async x =>
                {
                    bool hasVersion = await x.CheckVersion(Version);
                    if (!hasVersion)
                    {
                        return;
                    }

                    bool hasFile = await x.CheckSyncFile(SessionID, FileID);
                    if (!hasFile)
                    {
                        return;
                    }

                    bool isBusy = await x.IsBusy();
                    if (isBusy)
                    {
                        return;
                    }

                    lock (validNodes)
                    {
                        validNodes.Add(x);
                    }
                }));

                if (validNodes.Count == 0)
                {
                    throw new InvalidOperationException("No ready nodes available");
                }
                _usedNodes = validNodes;

                foreach (RenderNode useNode in _usedNodes)
                {
                    useNode.UpdateException("");
                }

                int framesFinished = 0;
                int framesTotal    = end - start;

                Action <RenderSubTask> onSubTaskFinished = (task) =>
                {
                    framesFinished++;
                    Progress = (double)framesFinished / framesTotal;
                    TriggerPropUpdate(nameof(Progress));
                    OnProgress?.Invoke(this, Progress);
                };
                Progress = 0;

                //StartRenderSplit

                object   drawLock = new object();
                Bitmap   result   = new Bitmap(Settings.OutputWidth, Settings.OutputHeight);
                Graphics g        = Graphics.FromImage(result);


                ConcurrentQueue <RenderSubTask> queue = new ConcurrentQueue <RenderSubTask>();
                for (int i = start; i <= end; i++)
                {
                    queue.Enqueue(new RenderSubTask(this, 0, 1, 0, 1, i));
                }


                int           finished   = 0;
                List <string> exceptions = new List <string>();
                return(await Task.Run(() =>
                {
                    ForceParallel(validNodes, (node) =>
                    {
                        string lastException = null;

                        while (queue.Count > 0)
                        {
                            RenderSubTask task = null;
                            if (!queue.TryDequeue(out task))
                            {
                                continue;
                            }
                            SubTaskResult taskPart = null;
                            try
                            {
                                taskPart = ExecuteSubTask(node, task);

                                if (taskPart.Image == null)
                                {
                                    throw new Exception(taskPart.Exception?.Message ?? "Unknown Remote Exception");
                                }

                                ProcessTile(task, (Bitmap)taskPart.Image, ref g, ref result, ref drawLock);

                                onSubTaskFinished?.Invoke(task);

                                finished++;
                            }
                            catch (TaskCanceledException ex)
                            {
                                if (Cancelled)
                                {
                                    return;
                                }
                            }
                            catch (Exception ex)
                            {
                                if (task != null)
                                {
                                    queue.Enqueue(task);
                                }
                                node.UpdateException($"Render fail: {ex.Message}");
                                exceptions.Add(ex.Message);
                                return;
                            }
                            if (taskPart.Exception != null)
                            {
                                node.UpdateException(taskPart.Exception.Message);
                                lastException = taskPart.Exception.Message;
                                Thread.Sleep(1000);
                                continue;
                            }
                        }
                    });

                    if (finished != (end - start) + 1)
                    {
                        throw new AggregateException($"Not all frames rendered ({finished}/{(end-start) + 1})", exceptions.Select(x => new Exception(x)));
                    }

                    return true;
                }));
            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                //Consumed = false;
            }
        }
        //Strategies
        /// <summary>
        /// Renders file with Settings in maximum chunks based on Cores and Performance
        /// eg. 3 valid nodes of equal performance will render a single part on each node with a 0.33 ratio
        /// </summary>
        private async Task <Bitmap> RenderSplit(List <RenderNode> validNodes, Action <RenderSubTask> onSubTaskFinished = null, bool isVertical = false)
        {
            object   drawLock = new object();
            Bitmap   result   = new Bitmap(Settings.OutputWidth, Settings.OutputHeight);
            Graphics g        = Graphics.FromImage(result);

            Dictionary <RenderNode, RenderSubTask> assignment = GetSplitSubTasks(validNodes, isVertical);

            int           finished   = 0;
            List <string> exceptions = new List <string>();

            return(await Task.Run(() =>
            {
                ForceParallel(validNodes, (node) =>
                {
                    RenderSubTask task = assignment[node];
                    if (task == null)
                    {
                        return;
                    }

                    string lastException = null;
                    bool rendered = false;
                    for (int i = 0; i < 3; i++)
                    {
                        if (Cancelled)
                        {
                            return;
                        }

                        SubTaskResult taskPart = null;
                        try
                        {
                            taskPart = ExecuteSubTask(node, task);
                        }
                        catch (TaskCanceledException ex)
                        {
                            if (Cancelled)
                            {
                                return;
                            }
                        }
                        catch (Exception ex)
                        {
                            node.UpdateException($"[{i + 1}/3] " + ex.Message);
                            lastException = ex.Message;
                            Thread.Sleep(1000);
                            continue;
                        }
                        if (taskPart.Exception != null)
                        {
                            node.UpdateException($"[{i+1}/3] " + taskPart.Exception.Message);
                            lastException = taskPart.Exception.Message;
                            Thread.Sleep(1000);
                            continue;
                        }

                        Image part = taskPart.Image;

                        ProcessTile(task, (Bitmap)part, ref g, ref result, ref drawLock);

                        onSubTaskFinished?.Invoke(task);
                        finished++;
                        rendered = true;
                        return;
                    }
                    if (!rendered && lastException != null)
                    {
                        exceptions.Add(lastException);
                    }
                });

                if (finished != validNodes.Count)
                {
                    throw new AggregateException("Not all tiles rendered", exceptions.Select(x => new Exception(x)));
                }

                return result;
            }));
        }
Esempio n. 4
0
        private void SchedulerTaskExecute(string key)
        {
            if (!_isStarted)
                return;
            var task = new SubTaskProcess
            {
                Key = key,
                StartDate = DateTime.Now,
                Thread = Thread.CurrentThread
            };
            lock(this)
             _subTasks.Add(key, task);

            if (!_isStarted)
                return;
            try
            {

                Debug.Assert(_execDomain.HasValue);
                var t = DateTime.Now;
                var ret = _execDomain.Value.Value.Function(key, _storage);
                if (!_isStarted)
                    return;
                var res = new SubTaskResult(key, ret, DateTime.Now - t);
                lock (this)
                {
                    _subTasks.Remove(key);
                    if (!task.ToDrop)
                        _results.Enqueue(res);
                }
            }
            catch(OutOfMemoryException)
            {
                lock (this)
                {
                    _subTasks.Remove(key);
                    if (!task.ToDrop)
                        _results.Enqueue(new SubTaskResult(key, SubTaskResult.ResultType.OutOfMemory));
                }
            }
            catch (TimeoutException)
            {
                lock (this)
                {
                    _subTasks.Remove(key);
                    if (!task.ToDrop)
                        _results.Enqueue(new SubTaskResult(key, SubTaskResult.ResultType.TimeOut));
                }
            }
            catch (ThreadAbortException)
            {
                lock (this)
                {
                    _subTasks.Remove(key);
                    if (!task.ToDrop)
                        _results.Enqueue(new SubTaskResult(key, SubTaskResult.ResultType.Interrupted));
                }
            }
            catch (ThreadInterruptedException)
            {
                lock (this)
                {
                    _subTasks.Remove(key);
                    if (!task.ToDrop)
                        _results.Enqueue(new SubTaskResult(key, SubTaskResult.ResultType.Interrupted));
                }
            }
            catch (Exception ex)
            {
                lock (this)
                {
                    _subTasks.Remove(key);
                    if (!task.ToDrop)
                    {
                        if (_haltOnException)
                            lock (this)
                            {
                                _keys.Clear();
                                _results.Clear();
                                foreach (var tp in _subTasks)
                                    if (tp.Key != key)
                                        tp.Value.Drop();

                            }
                        _results.Enqueue(new SubTaskResult(key, SubTaskResult.ResultType.OtherException, ex.ToString()));
                    }
                }
            }
        }