/// <summary> /// Routine of one worker-thread. /// Result image and rendering progress are the only two shared objects. /// </summary> /// <param name="spec">Thread-specific data (worker-thread-selector).</param> private void RenderWorker(Object spec) { WorkerThreadInit init = spec as WorkerThreadInit; if (init != null) { MT.InitThreadData(); rend.RenderRectangle(init.image, 0, 0, init.width, init.height, init.sel); } }
/// <summary> /// Routine of one worker-thread. /// Collect arrays and rendering progress are the only two shared objects. /// </summary> /// <param name="spec">Thread-specific data (worker-thread-selector).</param> private void SimulationWorker(Object spec) { WorkerThreadInit init = spec as WorkerThreadInit; if (init != null) { init.sim.Init(init.nPart, init.ppt); // infinite simulation loop: do { double deltaT = init.dt + init.vart * (init.sim.rnd.UniformNumber() - 1.0); init.sim.Tick(deltaT); init.sim.SimTime += deltaT; lock (progress) if (!progress.Continue) { break; } init.sim.GatherBuffers(); }while (true); } }
/// <summary> /// [Re]-renders the whole image (in separate thread). OLD VERSION!!! /// </summary> private void RenderImage_OLD() { Cursor.Current = Cursors.WaitCursor; // determine output image size: int width = ImageWidth; if (width <= 0) { width = panel1.Width; } int height = ImageHeight; if (height <= 0) { height = panel1.Height; } Bitmap newImage = new Bitmap(width, height, PixelFormat.Format24bppRgb); int threads = CheckMultithreading.Checked ? Environment.ProcessorCount : 1; int t; // thread ordinal number WorkerThreadInit[] wti = new WorkerThreadInit[threads]; // separate renderer, image function and the scene for each thread (safety precaution) for (t = 0; t < threads; t++) { IRayScene sc = FormSupport.getScene(); IImageFunction imf = getImageFunction(sc, width, height); IRenderer r = getRenderer(imf, width, height); wti[t] = new WorkerThreadInit(r, sc as ITimeDependent, imf as ITimeDependent, newImage, width, height, t, threads); } progress.SyncInterval = ((width * (long)height) > (2L << 20)) ? 3000L : 1000L; progress.Reset(); CSGInnerNode.ResetStatistics(); lock (sw) sw.Restart(); if (threads > 1) { Thread[] pool = new Thread[threads]; for (t = 0; t < threads; t++) { pool[t] = new Thread(new ParameterizedThreadStart(RenderWorker)); } for (t = threads; --t >= 0;) { pool[t].Start(wti[t]); } for (t = 0; t < threads; t++) { pool[t].Join(); pool[t] = null; } } else { MT.InitThreadData(); wti[0].rend.RenderRectangle(newImage, 0, 0, width, height); } long elapsed; lock (sw) { sw.Stop(); elapsed = sw.ElapsedMilliseconds; } string msg = string.Format(CultureInfo.InvariantCulture, "{0:f1}s [ {1}x{2}, mt{3}, r{4:#,#}k, i{5:#,#}k, bb{6:#,#}k, t{7:#,#}k ]", 1.0e-3 * elapsed, width, height, threads, (Intersection.countRays + 500L) / 1000L, (Intersection.countIntersections + 500L) / 1000L, (CSGInnerNode.countBoundingBoxes + 500L) / 1000L, (CSGInnerNode.countTriangles + 500L) / 1000L); SetText(msg); Console.WriteLine(@"Rendering finished: " + msg); SetImage(newImage); Cursor.Current = Cursors.Default; StopRendering(); }
/// <summary> /// Worker thread (picks up individual frames and renders them one by one). /// </summary> protected void RenderWorker(object spec) { // thread-specific data: WorkerThreadInit init = spec as WorkerThreadInit; if (init == null) { return; } MT.InitThreadData(); // worker loop: while (true) { double myTime; double myEndTime; int myFrameNumber; lock ( progress ) { if (!progress.Continue || time > end) { sem.Release(); // chance for the main animation thread to give up as well.. return; } // got a frame to compute: myTime = time; myEndTime = (time += dt); myFrameNumber = frameNumber++; } // set up the new result record: Result r = new Result(); r.image = new Bitmap(init.width, init.height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); r.frameNumber = myFrameNumber; // set specific time to my scene: if (init.scene != null) { init.scene.Time = myTime; } ITimeDependent anim = init.rend as ITimeDependent; if (anim != null) { anim.Start = myTime; anim.End = myEndTime; } if (init.imfunc != null) { init.imfunc.Start = myTime; init.imfunc.End = myEndTime; } // render the whole frame: init.rend.RenderRectangle(r.image, 0, 0, init.width, init.height); // ... and put the result into the output queue: lock ( queue ) { queue.Enqueue(r); } sem.Release(); // notify the main animation thread } }
/// <summary> /// Main animation rendering thread. /// Initializes worker threads and collects the results. /// </summary> protected void RenderAnimation() { Cursor.Current = Cursors.WaitCursor; int threads = Environment.ProcessorCount; int t; // thread ordinal number WorkerThreadInit[] wti = new WorkerThreadInit[threads]; for (t = 0; t < threads; t++) { IRayScene sc = FormSupport.getScene(textParam.Text); IImageFunction imf = FormSupport.getImageFunction(sc); imf.Width = width; imf.Height = height; IRenderer r = FormSupport.getRenderer(imf); r.Width = width; r.Height = height; r.Adaptive = 0; // turn off adaptive bitmap synthesis completely (interactive preview not needed) r.ProgressData = progress; wti[t] = new WorkerThreadInit(r, sc as ITimeDependent, imf as ITimeDependent, width, height); } initQueue(); sem = new Semaphore(0, 10 * threads); // pool of working threads: Thread[] pool = new Thread[threads]; for (t = 0; t < threads; t++) { pool[t] = new Thread(new ParameterizedThreadStart(RenderWorker)); } for (t = threads; --t >= 0;) { pool[t].Start(wti[t]); } // loop for collection of computed frames: int frames = 0; int lastDisplayedFrame = -1; const long DISPLAY_GAP = 10000L; long lastDisplayedTime = -DISPLAY_GAP; Stopwatch sw = new Stopwatch(); sw.Start(); while (true) { sem.WaitOne(); // wait until a frame is finished lock ( progress ) // regular finish, escape, user break? { if (!progress.Continue || time >= end && frames >= frameNumber) { break; } } // there could be a frame to process: Result r; lock ( queue ) { if (queue.Count == 0) { continue; } r = queue.Dequeue(); } // GUI progress indication: double seconds = 1.0e-3 * sw.ElapsedMilliseconds; double fps = ++frames / seconds; SetText(string.Format(CultureInfo.InvariantCulture, "Frames (mt{0}): {1} ({2:f0} s, {3:f2} fps)", threads, frames, seconds, fps)); if (r.frameNumber > lastDisplayedFrame && sw.ElapsedMilliseconds > lastDisplayedTime + DISPLAY_GAP) { lastDisplayedFrame = r.frameNumber; lastDisplayedTime = sw.ElapsedMilliseconds; SetImage((Bitmap)r.image.Clone()); } // save the image file: string fileName = string.Format("out{0:0000}.png", r.frameNumber); r.image.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); r.image.Dispose(); } for (t = 0; t < threads; t++) { pool[t].Join(); pool[t] = null; } Cursor.Current = Cursors.Default; StopAnimation(); }
/// <summary> /// [Re]-renders the whole image (in separate thread). OLD VERSION!!! /// </summary> private void RenderImage_OLD() { Cursor.Current = Cursors.WaitCursor; // Determine output image size. int width = ImageWidth; if (width <= 0) { width = panel1.Width; } int height = ImageHeight; if (height <= 0) { height = panel1.Height; } Bitmap newImage = new Bitmap(width, height, PixelFormat.Format24bppRgb); int threads = CheckMultithreading.Checked ? Environment.ProcessorCount : 1; int t; // thread ordinal number int superSampling = (int)NumericSupersampling.Value; WorkerThreadInit[] wti = new WorkerThreadInit[threads]; // 1. preprocessing - compute simulation, animation data, etc. _ = FormSupport.getScene(true, out _, out _, superSampling, TextParam.Text); // Separate renderer, image function and the scene for each thread (safety precaution). for (t = 0; t < threads; t++) { // 2. initialize data for regular frames (using the pre-computed context). IRayScene sc = FormSupport.getScene( false, out IImageFunction imf, out IRenderer rend, superSampling, TextParam.Text); // IImageFunction. imf = getImageFunction(imf, sc); imf.Width = width; imf.Height = height; // IRenderer. if (rend == null) // not defined in the script { rend = getRenderer(); } rend.ImageFunction = imf; rend.Width = width; rend.Height = height; rend.Adaptive = 8; rend.ProgressData = progress; wti[t] = new WorkerThreadInit(rend, sc as ITimeDependent, imf as ITimeDependent, newImage, width, height, t, threads); } progress.SyncInterval = ((width * (long)height) > (2L << 20)) ? 3000L : 1000L; progress.Reset(); CSGInnerNode.ResetStatistics(); lock (sw) sw.Restart(); if (threads > 1) { Thread[] pool = new Thread[threads]; for (t = 0; t < threads; t++) { pool[t] = new Thread(new ParameterizedThreadStart(RenderWorker)); } for (t = threads; --t >= 0;) { pool[t].Start(wti[t]); } for (t = 0; t < threads; t++) { pool[t].Join(); pool[t] = null; } } else { MT.InitThreadData(); wti[0].rend.RenderRectangle(newImage, 0, 0, width, height); } long elapsed; lock (sw) { sw.Stop(); elapsed = sw.ElapsedMilliseconds; } string msg = string.Format(CultureInfo.InvariantCulture, "{0:f1}s [ {1}x{2}, mt{3}, r{4:#,#}k, i{5:#,#}k, bb{6:#,#}k, t{7:#,#}k ]", 1.0e-3 * elapsed, width, height, threads, (Intersection.countRays + 500L) / 1000L, (Intersection.countIntersections + 500L) / 1000L, (CSGInnerNode.countBoundingBoxes + 500L) / 1000L, (CSGInnerNode.countTriangles + 500L) / 1000L); SetText(msg); Console.WriteLine(@"Rendering finished: " + msg); SetImage(newImage); Cursor.Current = Cursors.Default; StopRendering(); }
/// <summary> /// Worker thread (picks up individual frames and renders them one by one). /// </summary> protected void RenderWorker(object spec) { // Thread-specific data. WorkerThreadInit init = spec as WorkerThreadInit; if (init == null) { return; } MT.InitThreadData(); // Worker loop. while (true) { double myTime; double myEndTime; int myFrameNumber; lock (progress) { if (!progress.Continue || time > end) { sem.Release(); // chance for the main animation thread to give up as well.. return; } // I've got a frame to compute. myTime = time; myEndTime = (time += dt); myFrameNumber = frameNumber++; } // Set up the new result record. Result r = new Result(); r.image = new Bitmap(init.width, init.height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); r.frameNumber = myFrameNumber; // Set specific time to my scene. if (init.scene != null) { #if DEBUG Debug.WriteLine($"Scene #{init.scene.getSerial()} setTime({myTime})"); #endif init.scene.Time = myTime; } if (init.rend is ITimeDependent arend) { arend.Start = myTime; arend.End = myEndTime; } if (init.imfunc != null) { init.imfunc.Start = myTime; init.imfunc.End = myEndTime; } // Render the whole frame... init.rend.RenderRectangle(r.image, 0, 0, init.width, init.height); // ...and put the result into the output queue. lock (queue) { queue.Enqueue(r); } sem.Release(); // notify the main animation thread } }
/// <summary> /// Main animation rendering thread. /// Initializes worker threads and collects the results. /// </summary> protected void RenderAnimation() { Cursor.Current = Cursors.WaitCursor; int threads = Environment.ProcessorCount; int t; // thread ordinal number int superSampling = (int)numericSupersampling.Value; double minTime = (double)numFrom.Value; double maxTime = (double)numTo.Value; double fps = (double)numFps.Value; WorkerThreadInit[] wti = new WorkerThreadInit[threads]; // 1. preprocessing - compute simulation, animation data, etc. FormSupport.getScene( true, out _, out _, ref ActualWidth, ref ActualHeight, ref superSampling, ref minTime, ref maxTime, ref fps, textParam.Text); for (t = 0; t < threads; t++) { // 2. initialize data for regular frames (using the pre-computed context). IRayScene sc = FormSupport.getScene( false, out IImageFunction imf, out IRenderer rend, ref ActualWidth, ref ActualHeight, ref superSampling, ref minTime, ref maxTime, ref fps, textParam.Text); if (t == 0) { // Update GUI. if (ImageWidth > 0) // preserving default (form-size) resolution { ImageWidth = ActualWidth; ImageHeight = ActualHeight; UpdateResolutionButton(); } UpdateSupersampling(superSampling); UpdateAnimationTiming(minTime, maxTime, fps); } if (sc is ITimeDependent sca) { sc = (IRayScene)sca.Clone(); } // IImageFunction. if (imf == null) // not defined in the script { imf = FormSupport.getImageFunction(sc); } else if (imf is RayCasting imfray) { imfray.Scene = sc; } imf.Width = ActualWidth; imf.Height = ActualHeight; // IRenderer. if (rend == null) // not defined in the script { rend = FormSupport.getRenderer(superSampling); } rend.ImageFunction = imf; rend.Width = ActualWidth; rend.Height = ActualHeight; rend.Adaptive = 0; // turn off adaptive bitmap synthesis completely (interactive preview not needed) rend.ProgressData = progress; wti[t] = new WorkerThreadInit(rend, sc as ITimeDependent, imf as ITimeDependent, ActualWidth, ActualHeight); } // Update animation timing. time = minTime; end = maxTime; if (end <= time) { end = time + 1.0; } dt = (fps > 0.0) ? 1.0 / fps : 25.0; end += 0.5 * dt; frameNumber = 0; initQueue(); sem = new Semaphore(0, 10 * threads); // Pool of working threads. Thread[] pool = new Thread[threads]; for (t = 0; t < threads; t++) { pool[t] = new Thread(new ParameterizedThreadStart(RenderWorker)); } for (t = threads; --t >= 0;) { pool[t].Start(wti[t]); } // Loop for collection of computed frames. int frames = 0; int lastDisplayedFrame = -1; const long DISPLAY_GAP = 10000L; long lastDisplayedTime = -DISPLAY_GAP; Stopwatch sw = new Stopwatch(); sw.Start(); while (true) { sem.WaitOne(); // wait until a frame is finished lock (progress) // regular finish, escape, user break? { if (!progress.Continue || time >= end && frames >= frameNumber) { break; } } // There could be a frame to process. Result r; lock (queue) { if (queue.Count == 0) { continue; } r = queue.Dequeue(); } // GUI progress indication: double seconds = 1.0e-3 * sw.ElapsedMilliseconds; double cfps = ++frames / seconds; SetText(string.Format(CultureInfo.InvariantCulture, "Frames (mt{0}): {1} ({2:f0} s, {3:f2} fps)", threads, frames, seconds, cfps)); if (r.frameNumber > lastDisplayedFrame && sw.ElapsedMilliseconds > lastDisplayedTime + DISPLAY_GAP) { lastDisplayedFrame = r.frameNumber; lastDisplayedTime = sw.ElapsedMilliseconds; SetImage((Bitmap)r.image.Clone()); } // Save the image file. string fileName = string.Format("out{0:0000}.png", r.frameNumber); r.image.Save(fileName, System.Drawing.Imaging.ImageFormat.Png); r.image.Dispose(); } for (t = 0; t < threads; t++) { pool[t].Join(); pool[t] = null; } Cursor.Current = Cursors.Default; StopAnimation(); }