/// <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; })); }
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())); } } } }