WPF 3D graphic loop takes too long - c#

I'm trying to create goldbergs polyhedra, but the code that should draw it on my screen works too slow (about 22 seconds to draw 6th lvl of detalization)
Stopwatch sw = new Stopwatch();
var hexes = sphere.hexes.ToArray();
sw.Start();
for (int j = 0; j < hexes.Length; j++)
{
MeshGeometry3D myMeshGeometry3D = new MeshGeometry3D();
Vector3DCollection myNormalCollection = new Vector3DCollection();
foreach (var verts in hexes[j].Normals)
{
myNormalCollection.Add(verts);
}
myMeshGeometry3D.Normals = myNormalCollection;
Point3DCollection myPositionCollection = new Point3DCollection();
foreach (var verts in hexes[j].Normals)
{
myPositionCollection.Add(new Point3D(verts.X, verts.Y, verts.Z));
}
myMeshGeometry3D.Positions = myPositionCollection;
Int32Collection myTriangleIndicesCollection = new Int32Collection();
foreach (var triangle in hexes[j].Tris)
{
myTriangleIndicesCollection.Add(triangle);
}
myMeshGeometry3D.TriangleIndices = myTriangleIndicesCollection;
Material material = new DiffuseMaterial(
new SolidColorBrush(Colors.Black)); ;
if (switcher)
{
material = new DiffuseMaterial(
new SolidColorBrush(Colors.BlueViolet));
}
switcher = !switcher;
GeometryModel3D model = new GeometryModel3D(
myMeshGeometry3D, material);
myGeometryModel.Geometry = myMeshGeometry3D;
myModel3DGroup.Children.Add(model);
myModel3DGroup.Children.Add(myGeometryModel);
}
sw.Stop();
I've tried to make my loop parallel, but myGeometryModel and myModel3DGroup are in the main thread so i can't modify them.

Your code isn't the problem.
I tried it out (thanks for posting a link) and based on what I was seeing in the Visual Studio performance tools tried using the Model3DCollection constructor that takes an IEnumerable<Model3D>, instead of adding them one by one, which I see Andy also suggested. (During the build loop I added all the models to my own List<Model3D> - which took almost no time - and sourced the Model3DCollection with this list - which is where everything ground to a halt.)
While this approximately halved the time, it was still 20+ seconds on my souped up machine in debug mode. The Model3DCollection constructor with the IEnumerable - one line of code - took up nearly all the time.
For what it's worth - and I assume you know this, but for everyone else's benefit - detail level 5 rendered about 4x as fast for me, with 20,485 elements in the collection vs. 81,925 at level 6, while level 4 was essentially instantaneous at 5125 elements. So apparently every detail level quadruples your model count and in turn the time to construct the Model3DCollection. Point being, if you can get away with a lower detail level the render times improve dramatically.
But if you require that detail level, you really need to be looking at another platform IMO. C# itself is not the issue, but the bottleneck is in WPF, so you're rather stuck. If you need to stick with WPF, you might try looking into D3DImage and SharpDX. No C++ is required. Of course it would be a substantial re-write, but you're virtually certain to get the performance you're looking for.

Edit:
Experiment with code.
I added the following code to the end of the generation:
myViewport3D.Children.Add(myModelVisual3D);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(#"e:\temp\3dg.xaml", settings);
XamlWriter.Save(myViewport3D, writer);
This results in a file roughly 79 meg and of course does not speed the generation up at all.
I then load that file instead of generating it:
private void CreateHexaSphere(object sender, RoutedEventArgs e)
{
using (FileStream fs = new FileStream(#"e:\temp\3dg.xaml", FileMode.Open))
{
myViewport3D = (Viewport3D)XamlReader.Load(fs);
}
this.Content = myViewport3D;
return;
That takes roughly 7 seconds to show the sphere. Yep. Bit of a difference there.
If you are happy to just load that file from disk then 7 seconds seems way better than 52 seconds.
If you need to generate this thing on the fly then I would find a way to work with strings and build a similar string to what you get in that file.
Unsuccessful theory:
I think all the things that matter there are freezables.
.Freeze() a freezable and you increase performance but can also pass it from one thread to another.
You could do most of this on multiple parallel background threads.
EG each hex could be built on a set of parallel worker threads. Each building their own myMeshGeometry3D which was frozen and returned to a caller thread.
Loosely, you'd have a Task DoMeshGeom3D(int j) which returns one of your 3d geometries.
You could use plinq or task.whenall or parallel.foreach
Roughly
var taskList = new List<Task<MeshGeometry3D>>();
for (int j = 0; j < hexes.Length; j++)
{
taskList.Add(DoMeshGeom3D(j));
}
var result = await Task.WhenAll(taskList.ToList());
Grab your list of numerous MeshGeometry3D and assemble them all together.
And you could be awaiting that on a background thread in another task.
You can also try constructing your 3dgroup using a list or ienumerable of these things constructed in the separate threads rather than adding one at a time.
https://briancaos.wordpress.com/2020/09/11/run-tasks-in-parallel-using-net-core-c-and-async-coding/
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=netcore-3.1

Related

How to chain together multiple NAudio ISampleProvider Effects

I have some DSP effects coded in the ISampleProvider model. To apply one effect I do this and it works fine.
string filename = "C:\myaudio.mp3";
MediaFoundationReader mediaFileReader = new MediaFoundationReader(filename);
ISampleProvider sampProvider = mediaFileReader.ToSampleProvider();
ReverbSampleProvider reverbSamplr = new ReverbSampleProvider(sampProvider);
IWavePlayer waveOutDevice.Init(reverbSamplr);
waveOutDevice.Play();
How can I apply multiple effects to the same input file simultaneously?
For example, if i have a Reverb effect and Distortion effect providers, how can I chain them together to apply them at the same time to one input file?
Effects can be chained together by passing one as the "source" for the next. So if you wanted your audio to go first through a reverb, and then distortion, you might do something like this, passing the original audio into the Reverb effect, the output of the reverb into the distortion effect and then sending the distortion to the waveOut device.
var reverb = new ReverbSampleProvider(sampProvider);
var distortion = new DistortionSampleProvider(reverb);
waveOutDevice.Init(distortion);
(n.b. NAudio does not come with built in reverb/distortion effects - you must make these yourself or source them from elsewhere)
Mark's answer is correct, but that approach is a pain if you're copy and pasting things around in different orders, because you have to change the variables that you're passing through.
For example, if you start with:
var lpf = new LowPassEffectStream(input);
var reverb = new ReverbEffectStream(lpf);
var stereo = new StereoEffectStream(reverb);
var vol = new VolumeSampleProvider(stereo);
waveOutDevice.Init(vol);
And you want to swap reverb and stereo, a quick copy-paste leaves you with the input variables backwards:
var lpf = new LowPassEffectStream(input);
var stereo = new StereoEffectStream(reverb); // <--
var reverb = new ReverbEffectStream(lpf); // <--
var vol = new VolumeSampleProvider(stereo);
waveOutDevice.Init(vol);
It also makes it easy to fix a parameter but forget to fix another, e.g. fixing the stereo effect to have lpf as its input, but forgetting to fix the reverb effect. This often results in skipped effects in the chain leading to frustrated debugging when the effect appears not to work.
To make things easier and less error-prone when I'm stacking effects together and re-ordering them, I created the following helper class:
class EffectChain : ISampleProvider
{
public EffectChain(ISampleProvider source)
{
this._sourceStream = source;
}
private readonly ISampleProvider _sourceStream;
private readonly List<ISampleProvider> _chain = new List<ISampleProvider>();
public ISampleProvider Head
{
get
{
return _chain.LastOrDefault() ?? _sourceStream;
}
}
public WaveFormat WaveFormat
{
get
{
return Head.WaveFormat;
}
}
public void AddEffect(ISampleProvider effect)
{
_chain.Add(effect);
}
public int Read(float[] buffer, int offset, int count)
{
return Head.Read(buffer, offset, count);
}
}
You can use it like this:
var effectChain = new EffectChain(input);
var lpf = new LowPassEffectStream(effectChain.Head);
effectChain.AddEffect(lpf);
var stereo = new StereoEffectStream(effectChain.Head);
effectChain.AddEffect(stereo);
var reverb = new ReverbEffectStream(effectChain.Head);
effectChain.AddEffect(reverb);
var vol = new VolumeSampleProvider(effectChain.Head);
effectChain.AddEffect(vol);
waveOutDevice.Init(effectChain);
This allows you to quickly re-order effects in the chain, as each effect takes the effect chain's head as an input. If you don't add any effects it just acts as a pass-through. You could easily expand this class to have more methods for managing the contained effects if you wanted to, but as it stands it works quite cleanly.

Code working in one Addin but doing nothing in another

FilteredElementCollector Lincoln = new FilteredElementCollector(doc);
Lincoln.OfCategory(BuiltInCategory.OST_RvtLinks); Autodesk.Revit.DB.View CurrentView = uiDoc.ActiveView;
ICollection<ElementId> Toggle_On = Lincoln.ToElementIds(); Toggle_On.Clear();
ICollection<ElementId> Toggle_Off = Lincoln.ToElementIds(); Toggle_Off.Clear();
List<Element> Processed = new List<Element>();
List<string> Revit_On = new List<string>(); List<string> Revit_Off = new List<string>();
List<string> Revit_Names = new List<string>();
foreach (Element One_Link in Lincoln)
{
string Revit_Name = One_Link.Name;
if (!Revit_Names.Contains(Revit_Name))//prevents processing same link twice;/but does NOT change anyway!!!!
{
Revit_Names.Add(Revit_Name);
Boolean Is_Hidden = One_Link.IsHidden(CurrentView);//
if (Is_Hidden)
{
Toggle_On.Add(One_Link.Id); Revit_On.Add(One_Link.Name);
}//this apparently does detect what is hidden;
else
{
Toggle_Off.Add(One_Link.Id); Revit_Off.Add(One_Link.Name);
}
}
}
Transaction Do_Toggle = new Transaction(doc, "DoToggle");
Do_Toggle.Start();
if (!Toggle_Off.Count.Equals(0)) { CurrentView.HideElements(Toggle_Off); }
if (!Toggle_On.Count.Equals(0)) { CurrentView.UnhideElements(Toggle_On); }
Do_Toggle.Commit();
Is somehow the transaction failing? Undo is not available, so it doesn't think it has done anything that might need to be undone. Note that this EXACT code is used in another one of my addins (in which multiple optional sub-programs are controlled by picking radio options on a form). But when I am trying to use the code in a standalone version, it fails (without errors). Note also that I've inserted multiple TaskDialog entries to verify that it is indeed finding the RvtLinks that are either visible or hidden in the current view. But it is simply refusing to actually change their visibility. If I run the dialog controlled version, everything toggles, but if I immediately run the standalone, nothing toggles (proving it isn't uneditable pinned links). I have made this option available to users by making "Toggle Links" the default so they can call up my collected program and just hit a carriage return, but I need this to be a true standalone.
You code confuses me. For instance, why do you initialise the Toggle_On and Toggle_Off collections with member values, only to clear them immediately afterwards?
In any case, you use of the transaction does not follow the recommended pattern of enclosing it in a using statement.
Please refer to The Building Coder topic group on Handling Transactions and Transaction Groups for more information on using transactions in the Revit API.

Am I reusing OpenCL/Cloo(C#) objects correctly?

I'm experimenting with OpenCL (through Cloo's C# interface). To do so, I'm experimenting with the customary matrix-multiplication-on-the-GPU. The problem is, during my speed tests, the application crashes. I'm trying to be efficient regarding the the re-allocation of various OpenCL objects, and I'm wondering if I'm botching something in doing so.
I'll put the code in this question, but for a bigger picture, you can get the code from github here: https://github.com/kwende/ClooMatrixMultiply
My main program does this:
Stopwatch gpuSw = new Stopwatch();
gpuSw.Start();
for (int c = 0; c < NumberOfIterations; c++)
{
float[] result = gpu.MultiplyMatrices(matrix1, matrix2, MatrixHeight, MatrixHeight, MatrixWidth);
}
gpuSw.Stop();
So I'm basically doing the call NumberOfIterations times, and timing the average execution time.
Within the MultiplyMatrices call, the first time through, I call Initialize to setup all the objects I'm going to reuse:
private void Initialize()
{
// get the intel integrated GPU
_integratedIntelGPUPlatform = ComputePlatform.Platforms.Where(n => n.Name.Contains("Intel")).First();
// create the compute context.
_context = new ComputeContext(
ComputeDeviceTypes.Gpu, // use the gpu
new ComputeContextPropertyList(_integratedIntelGPUPlatform), // use the intel openCL platform
null,
IntPtr.Zero);
// the command queue is the, well, queue of commands sent to the "device" (GPU)
_commandQueue = new ComputeCommandQueue(
_context, // the compute context
_context.Devices[0], // first device matching the context specifications
ComputeCommandQueueFlags.None); // no special flags
string kernelSource = null;
using (StreamReader sr = new StreamReader("kernel.cl"))
{
kernelSource = sr.ReadToEnd();
}
// create the "program"
_program = new ComputeProgram(_context, new string[] { kernelSource });
// compile.
_program.Build(null, null, null, IntPtr.Zero);
_kernel = _program.CreateKernel("ComputeMatrix");
}
I then enter the main body of my function (the part that will be executed NumberOfIterations times).
ComputeBuffer<float> matrix1Buffer = new ComputeBuffer<float>(_context,
ComputeMemoryFlags.ReadOnly| ComputeMemoryFlags.CopyHostPointer,
matrix1);
_kernel.SetMemoryArgument(0, matrix1Buffer);
ComputeBuffer<float> matrix2Buffer = new ComputeBuffer<float>(_context,
ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer,
matrix2);
_kernel.SetMemoryArgument(1, matrix2Buffer);
float[] ret = new float[matrix1Height * matrix2Width];
ComputeBuffer<float> retBuffer = new ComputeBuffer<float>(_context,
ComputeMemoryFlags.WriteOnly | ComputeMemoryFlags.CopyHostPointer,
ret);
_kernel.SetMemoryArgument(2, retBuffer);
_kernel.SetValueArgument<int>(3, matrix1WidthMatrix2Height);
_kernel.SetValueArgument<int>(4, matrix2Width);
_commandQueue.Execute(_kernel,
new long[] { 0 },
new long[] { matrix2Width ,matrix1Height },
null, null);
unsafe
{
fixed (float* retPtr = ret)
{
_commandQueue.Read(retBuffer,
false, 0,
ret.Length,
new IntPtr(retPtr),
null);
_commandQueue.Finish();
}
}
The third or fourth time through (it's somewhat random, which hints at memory access issues), the program crashes. Here is my kernel (I'm sure there are faster implementations, but right now my goal is just to get something working without blowing up):
kernel void ComputeMatrix(
global read_only float* matrix1,
global read_only float* matrix2,
global write_only float* output,
int matrix1WidthMatrix2Height,
int matrix2Width)
{
int x = get_global_id(0);
int y = get_global_id(1);
int i = y * matrix2Width + x;
float value = 0.0f;
// row y of matrix1 * column x of matrix2
for (int c = 0; c < matrix1WidthMatrix2Height; c++)
{
int m1Index = y * matrix1WidthMatrix2Height + c;
int m2Index = c * matrix2Width + x;
value += matrix1[m1Index] * matrix2[m2Index];
}
output[i] = value;
}
Ultimately the goal here is to better understand the zero-copy features of OpenCL (since I'm using Intel's integrated GPU). I have been having trouble getting it to work and so wanted to step back a bit to see if I understood even more basic things...apparently I don't as I can't get even this to work without blowing up.
The only other thing I can think of is it's how I'm pinning the pointer to send it to the .Read() function. But I don't know of an alternative.
Edit:
For what it's worth, I updated the last part of code (the read code) to this, and it still crashes:
_commandQueue.ReadFromBuffer(retBuffer, ref ret, false, null);
_commandQueue.Finish();
Edit #2
Solution found by huseyin tugrul buyukisik (see comment below).
Upon placing
matrix1Buffer.Dispose();
matrix2Buffer.Dispose();
retBuffer.Dispose();
At the end, it all worked fine.
OpenCl resources like buffers, kernels and commandqueues should be released after other resources that they are bound-to are released. Re-creating without releasing depletes avaliable slots quickly.
You have been re-creating arrays in a method of gpu and that was the scope of opencl buffers. When it finishes, GC cannot track opencl's unmanaged memory areas and that causes leaks, which makes crashes.
Many opencl implementations use C++ bindings which needs explicit release commands by C#, Java and other environments.
Also the set-argument part is not needed many times when repeated kernel executions use exact same buffer order as parameters of kernel.

Erasing AutoCAD drawing objects after exploding them, using C#

I am working on a Task in which the code automatically opens a drawing selected by the user [in a UI] and selects all the objects in the drawing and starts to explode all the of them till they cant be exploded anymore. While doing this I face a problem, the original (un-exploded 3D object) is still present in the drawing, super imposed by the Exploded object. Every recursive call of the Explode function creates a new exploded 3D object of that object.
Here is a snippet of the code I working on:
PromptSelectionResult ss = ed.SelectAll();
using (DocumentLock acLckDoc = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
objs = new DBObjectCollection();
foreach (SelectedObject so in ss.Value)
{
Entity ent = (Entity)tr.GetObject(so.ObjectId, OpenMode.ForWrite);
if (!(ent is Solid3d))
{
ent.Explode(objs);
ent.UpgradeOpen();
ent.Erase();
}
}
tr.Commit();
}
}
As soon as the control comes on to the ent.Erase() statement - it throws an exception, eCannotBeErasedByCaller. I cant figure out why? I have unlocked all layers, opened the entity for Write, CommandFlags have been set to Session and UsePickSet (shuffled through all).
Anybody got any suggestions?
Looking at your description, you probably need a recursive explode. Sometime ago I did a code around this, for other type of entities, but you can adjust it.
private List<DBObject> FullExplode(Entity ent)
{
// final result
List<DBObject> fullList = new List<DBObject>();
// explode the entity
DBObjectCollection explodedObjects = new DBObjectCollection();
ent.Explode(explodedObjects);
foreach (Entity explodedObj in explodedObjects)
{
// if the exploded entity is a blockref or mtext
// then explode again
if (explodedObj.GetType() == typeof(BlockReference) ||
explodedObj.GetType() == typeof(MText))
{
fullList.AddRange(FullExplode(explodedObj));
}
else
fullList.Add(explodedObj);
}
return fullList;
}
source: http://adndevblog.typepad.com/infrastructure/2013/04/get-cogopoint-label-text.html
I finally found out the reason why the Original objects werent getting erased.
In the earlier part of the code, a AutoCAD Plant3D dwg is exported to AutoCAD (ExporttoAutoCAD / Saveas), this was creating Proxy items. These cant be deleted manually or via code.
Only way is to explode the PipeLines and Inline assets before exporting the file. This happens automatically if you export the file, but if you use saveas, you will have to explode the Pipe components before you export the file.
Wasted a lot of time understanding the cause, but finally got it!

Reduce memory footprint of File operations

I'm trying to run this method, it works fine but every time after some hundreds of internal iterations I get with an Out of Memory exception:
...
MNDBEntities db = new MNDBEntities();
var regs = new List<DOCUMENTS>();
var query = from reg in db.DOCUMENTS
where reg.TAG_KEYS.Any(p => p.TAG_DATE_VALUES.FirstOrDefault().TAG_DATE_VALUE.HasValue
&& p.TAG_DATE_VALUES.FirstOrDefault().TAG_DATE_VALUE.Value.Year == 2012)
select reg;
var pages = new List<string>();
foreach (var item in query)
{
Document cert = new Document();
var tags = item.TAG_KEYS;
foreach (var tag in tags)
{
// Basic stuff...
}
var pagesS = item.PAGES;
foreach (var page in pagesS)
{
var path = #"C:\Kumquat\" + (int)page.NUMBER + ".vpimg";
File.WriteAllBytes(path, page.IMAGE);
pages.Add(path);
Console.WriteLine(path);
}
//cms.Save(cert, pages.ToArray()).Wait();
foreach (var pageFile in pages)
File.Delete(pageFile);
pagesS = null;
pages.Clear();
}
...
I'm pretty sure problem is related with the File.WriteAllBytes or the File.Delete because if I comment those lines the method runs without exception. What I'm doing is basically get some tags from a DB plus a document image, that image is then saved onto disk then a stored into a cms and then deleted from disk. Honestly don't figure out what I'm doing wrong with that File calls. Any idea?
This is what PerfView shows:
This is what visual studio 2012 profiler shows as the hot point, the thing is: this is all generated code (within the Entity Model) am I doing something wrong maybe with the properties of the model?
Try to use http://www.microsoft.com/en-us/download/details.aspx?id=28567 to profile your code, focusing on GC events, and CLR managed allocation tick events.
page.IMAGE could be the problem. Most likely it will allocate a byte array and never delete it. Best to change the code to:
page.WriteTo(path);
The rest of the code shown does look fine. The only possible problem is large object allocation, which could lead to fragmentation problem in LOH.

Categories

Resources