简体   繁体   English

WPF 3D 图形循环时间过长

[英]WPF 3D graphic loop takes too long

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)我正在尝试创建 goldbergs 多面体,但是应该在我的屏幕上绘制它的代码运行速度太慢(绘制第 6 级 detalization 大约需要 22 秒)

            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.我试图使我的循环并行,但myGeometryModelmyModel3DGroup在主线程中,所以我无法修改它们。

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.这会产生一个大约 79 兆的文件,当然根本不会加快生成速度。

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.显示球体大约需要 7 秒。 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.如果您乐于从磁盘加载该文件,那么 7 秒似乎比 52 秒要好得多。

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. .Freeze() 是可冻结的,可以提高性能,但也可以将它从一个线程传递到另一个线程。

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.每个构建自己的 myMeshGeometry3D,它被冻结并返回到调用者线程。

Loosely, you'd have a Task DoMeshGeom3D(int j) which returns one of your 3d geometries.松散地,您将有一个 Task DoMeshGeom3D(int j) 返回您的 3d 几何图形之一。

You could use plinq or task.whenall or parallel.foreach您可以使用 plinq 或 task.whenall 或 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.抓住您的众多 MeshGeometry3D 列表并将它们组装在一起。

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.您还可以尝试使用在单独的线程中构建的列表或可枚举的这些东西来构建您的 3dgroup,而不是一次添加一个。

https://briancaos.wordpress.com/2020/09/11/run-tasks-in-parallel-using.net-core-c-and-async-coding/ https://briancaos.wordpress.com/2020/09/11/run-tasks-in-parallel-using.net-core-c-and-async-coding/

https://learn.microsoft.com/en-us/do.net/api/system.threading.tasks.task.whenall?view.netcore-3.1 https://learn.microsoft.com/en-us/do.net/api/system.threading.tasks.task.whenall?view.netcore-3.1

            foreach (var figure in hexes)
        {
            var mesh = new MeshGeometry3D();
            mesh.Positions = new Point3DCollection(figure.Vericies);
            mesh.TriangleIndices = new Int32Collection(figure.Tris);
            mesh.Normals = new Vector3DCollection(figure.Normals);


            Material material = new DiffuseMaterial(
                            new SolidColorBrush(Colors.Black)); ;

            if (switcher)
            {
                material = new DiffuseMaterial(
                            new SolidColorBrush(Colors.BlueViolet));
            }

            switcher = !switcher;

            //var material = new DiffuseMaterial(new SolidColorBrush(Colors.Blue));
            var model = new GeometryModel3D(mesh, material);
            myModel3DGroup.Children.Add(model);
        }

I tried to ask chat GPT and got this solution (render time 191ms)我试着询问聊天 GPT 并得到了这个解决方案(渲染时间 191 毫秒)

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.我尝试了它(感谢发布链接)并根据我在 Visual Studio 性能工具中看到的内容尝试使用采用IEnumerable<Model3D>Model3DCollection构造函数,而不是一个一个地添加它们,我也看到 Andy建议。 (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.) (在构建循环期间,我将所有模型添加到我自己的List<Model3D> - 这几乎没有时间 - 并使用此列表Model3DCollection - 这是一切都停止的地方。)

While this approximately halved the time, it was still 20+ seconds on my souped up machine in debug mode.虽然这大约将时间减半,但在调试模式下我的增强型机器上仍然需要 20 多秒。 The Model3DCollection constructor with the IEnumerable - one line of code - took up nearly all the time.带有IEnumerableModel3DCollection构造函数——一行代码——几乎占据了所有时间。

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.对于它的价值 - 我假设你知道这一点,但为了其他人的利益 - 细节级别 5 对我来说渲染速度大约是 4 倍,集合中有 20,485 个元素,而级别 6 为 81,925,而级别 4 基本上是瞬时的 5125元素。 So apparently every detail level quadruples your model count and in turn the time to construct the Model3DCollection .因此,显然每个细节级别都会使您的 model 计数增加四倍,进而使构建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.但是如果你需要那个细节级别,你真的需要考虑另一个平台 IMO。 C# itself is not the issue, but the bottleneck is in WPF, so you're rather stuck. C# 本身不是问题,但瓶颈在 WPF,所以你相当卡住了。 If you need to stick with WPF, you might try looking into D3DImage and SharpDX .如果您需要坚持使用 WPF,您可以尝试查看D3DImageSharpDX No C++ is required.不需要 C++。 Of course it would be a substantial re-write, but you're virtually certain to get the performance you're looking for.当然,这将是一次实质性的重写,但您几乎可以肯定会获得您正在寻找的性能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM