/// <summary> /// Consumer-producer based multithreading work distribution /// Each thread waits for a new Assignment to be added to availableAssignments queue /// Most of the time is number of items in availableAssignments expected to be several times larger than number of threads /// </summary> protected void Consume() { // Set TLS. MT.InitThreadData(); MT.SetRendering(scene, imageFunction, renderer); // Animation time (fore debugging animated scenes). if (scene is ITimeDependent sc) { sc.Time = 0.0; } while (!availableAssignments.IsEmpty || finishedAssignments < totalNumberOfAssignments - threads || NetworkWorker.assignmentsAtClients > 0) { availableAssignments.TryDequeue(out Assignment newAssignment); if (!progressData.Continue) // test whether rendering should end (Stop button pressed) { return; } if (newAssignment == null) // TryDequeue was not successful { continue; } float[] colorArray = newAssignment.Render(false, renderer, progressData); BitmapMerger(colorArray, newAssignment.x1, newAssignment.y1, newAssignment.x2 + 1, newAssignment.y2 + 1); if (newAssignment.stride == 1) { finishedAssignments++; assignmentRoundsFinished++; } else { newAssignment.stride >>= 1; // stride values: 8 > 4 > 2 > 1 assignmentRoundsFinished++; availableAssignments.Enqueue(newAssignment); } } }
/// <summary> /// Shoots single primary ray only /// </summary> /// <param name="x">X-coordinate inside the raster image</param> /// <param name="y">Y-coordinate inside the raster image</param> private void singleSample(int x, int y) { MT.singleRayTracing = true; rayVisualizer.Reset(); // determine output image size: ActualWidth = ImageWidth; if (ActualWidth <= 0) { ActualWidth = panel1.Width; } ActualHeight = ImageHeight; if (ActualHeight <= 0) { ActualHeight = panel1.Height; } if (dirty || imfs == null) { int ss = 1; // Force preprocess. ctx = null; _ = FormSupport.getScene( out _, out _, ref ActualWidth, ref ActualHeight, ref ss, TextParam.Text); sc = FormSupport.getScene( out imfs, out rend, ref ActualWidth, ref ActualHeight, ref ss, TextParam.Text); // IImageFunction. if (imfs == null) // not defined in the script { imfs = getImageFunction(imfs, sc); } else if (imfs is RayCasting imfray) { imfray.Scene = sc; } imfs.Width = ActualWidth; imfs.Height = ActualHeight; // IRenderer. if (rend == null) // not defined in the script { rend = getRenderer(); } rend.ImageFunction = imfs; rend.Width = ActualWidth; rend.Height = ActualHeight; rend.Adaptive = 0; // 8? rend.ProgressData = progress; dirty = false; } // Set TLS. MT.SetRendering(sc, imfs, rend); double[] color = new double[3]; long hash = imfs.GetSample(x + 0.5, y + 0.5, color); labelSample.Text = string.Format(CultureInfo.InvariantCulture, "Sample at [{0},{1}] = [{2:f},{3:f},{4:f}], {5:X}", x, y, color[0], color[1], color[2], hash); // Reset TLS. MT.ResetRendering(); rayVisualizer.AddingRaysFinished(); MT.singleRayTracing = false; }
/// <summary> /// Creates threadpool and starts all threads on Consume method /// Thread which calls this method will take care of preparing assignments and receiving rendered images from RenderClients meanwhile /// </summary> public void RunThreads() { pool = new Thread[MT.threads]; AssignNetworkWorkerToStream(); WaitHandle[] waitHandles = new WaitHandle[MT.threads]; // Multiply animated instances. int i; // Scene definitions. if (scenes[0] is ITimeDependent scenea) { scenea.Time = time; scenes = new IRayScene[MT.threads]; for (i = 0; i < MT.threads; i++) { scenes[i] = i == 0 ? (IRayScene)scenea : (IRayScene)scenea.Clone(); } } // Image functions. if (imageFunctions[0] is ITimeDependent imfa) { imfa.Time = time; imageFunctions = new IImageFunction[MT.threads]; for (i = 0; i < MT.threads; i++) { imageFunctions[i] = i == 0 ? (IImageFunction)imfa : (IImageFunction)imfa.Clone(); } } // Renderers. if (renderers[0] is ITimeDependent renda) { renda.Time = time; renderers = new IRenderer[MT.threads]; for (i = 0; i < MT.threads; i++) { renderers[i] = i == 0 ? (IRenderer)renda : (IRenderer)renda.Clone(); } } for (i = 0; i < MT.threads; i++) { EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset); // Thread-specific instances: thread's id. int tid = i; Thread newThread = new Thread(() => { // Set TLS. MT.threadID = tid; MT.InitThreadData(); MT.SetRendering( scenes[Math.Min(tid, scenes.Length - 1)], imageFunctions[Math.Min(tid, imageFunctions.Length - 1)], renderers[Math.Min(tid, renderers.Length - 1)]); Consume(); // Signal finishing the work. handle.Set(); }); newThread.Name = "RenderThread #" + i; pool[i] = newThread; newThread.Start(); waitHandles[i] = handle; } mainRenderThread = pool[0]; Thread imageReceiver = new Thread(RenderedImageReceiver); imageReceiver.Name = "ImageReceiver"; imageReceiver.Start(); WaitHandle.WaitAll(waitHandles); if (networkWorkers?.Count > 0) { foreach (NetworkWorker worker in networkWorkers) // sends ending assignment to all clients { worker.SendSpecialAssignment(Assignment.AssignmentType.Ending); } } // Reset the pool-thread. pool = null; }