简体   繁体   English

C#WPF ModelVisual3D创建时间过长,无法在单独的线程上完成

[英]C# WPF ModelVisual3D creation takes too long and cannot be done on separate thread

I have a WPF project ( VS2010 , .NET4.0 ) in which I create a rather big ModelVisual3D object (read from custom format STL file, process info, create mesh, etc.) This takes about 3-4 sec. 我有一个WPF项目( VS2010.NET4.0 ),在其中创建了一个很大的ModelVisual3D对象(从自定义格式的STL文件读取,处理信息,创建网格等),大约需要3-4秒。 to be created and another 2-3 sec. 要创建,另外2-3秒。 to do a mainViewport.Children.Add(ModelVisual3D) . 做一个mainViewport.Children.Add(ModelVisual3D) I do this all in a custom class and call this method: 我在自定义类中完成所有操作,然后调用此方法:

 class My3DModel
{
...
        public MyModelVisual3D createModelVisual3D(MyTypes tType, int tNumber)
            {
                this.myModelVisual3D = new MyModelVisual3D(tType, tNumber);
                for (int i = 0, j = 0; i < this.Triangles.Length; i++)
                {
                    this.mesh.Positions.Add(this.Triangles[i].Vertex1);
                    this.mesh.Positions.Add(this.Triangles[i].Vertex2);
                    this.mesh.Positions.Add(this.Triangles[i].Vertex3);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.TriangleIndices.Add(j++);
                    this.mesh.TriangleIndices.Add(j++);
                    this.mesh.TriangleIndices.Add(j++);
                }
                this.model3DGroup.Children.Add(new GeometryModel3D(this.mesh, material));
                this.myModelVisual3D.Content = this.model3DGroup;
                return this.myModelVisual3D;
            }
}

The return value is also a custom class I created: 返回值也是我创建的一个自定义类:

class ToothModelVisual3D : ModelVisual3D
{
    //VARIABLES
    private MyTypes myType;
    private int number;

    //OPERATORS
    public MyTypes MyType
    {get { return myType; } set { myType = value; }}

    public int Number
    {get { return number; } set { number = value;}}

    public ToothModelVisual3D() { }

    public ToothModelVisual3D(MyTypes tType, int tNumber) { MyType = tType; Number = tNumber; }
}

All I want to do is the following once in the beginning of the program: 我只想在程序开始时执行以下操作:

{
        My3DModel myModel;
        myModel = new My3DModel();
        myModel.readFileBytes("C:\\registered\\" + 1 + ".stl");
        myModel.loadTriangles();
        mainViewport.Children.Add(myModel.createModelVisual3D(MyTypes.Sometype, 1);
}

If I do it on the main thread the UI hangs. 如果我在主线程上执行此操作,则UI会挂起。 If I do it on a worker thread and invoke mainViewport.Children.Add(...) it says it cannot access the resourses created on that worker thread. 如果我在工作线程上执行此操作并调用mainViewport.Children.Add(...)它说它无法访问在该工作线程上创建的资源。 Help?! 救命?!

From what I understand I've reached a point where I have two threads and resources belonging to each of them ( mainViewport => UIThread & myModel => WorkerThread ). 据我了解,我已经到达了两个线程和属于它们的资源( mainViewport => UIThread & myModel => WorkerThread )。 Neither thread can access directly the other's resource but creating and using myModel on the UIThread makes it hang... All I want to do is have enough responsiveness from the UI, so the user may minimize the program while waiting for it to load the models, nothing more. 两个线程都不能直接访问另一个线程的资源,但是在UIThread上创建并使用myModel会使该线程挂起...我只想从UI上获得足够的响应度,因此用户可以在等待程序加载模型的同时将其最小化,仅此而已。 How can I do that? 我怎样才能做到这一点? Is there a way to do all the CPU heavy work on the UIThread, so no resource conflicts arise and have a worker thread that only handles UI for that time? 有没有一种方法可以在UIThread上完成所有CPU繁重的工作,因此不会出现资源冲突,并且有一个只能在该时间处理UI的工作线程?

PS: I've tried with Thread, BackgroundWorker & Task<TResult> classes. PS:我已经尝试过Thread, BackgroundWorkerTask<TResult>类。 Results were similar if not to say the same. 如果不说相同,结果是相似的。

PPS: The full version will load massive models which will load more than 30-40 sec... PPS:完整版将加载海量模型,加载时间将超过30-40秒...

I recently came across the same issue when porting an XNA application to WPF. 在将XNA应用程序移植到WPF时,我最近遇到了相同的问题。 In my case I partially resolved this by using a background thread to load the positions, normals, and indices from file. 就我而言,我通过使用后台线程从文件加载位置,法线和索引来部分解决此问题。 Then in that same thread, construct a memory stream containing XAML for the Model3DGroup with the GeometryModel3D and MeshGeometry3D. 然后在同一线程中,使用GeometryModel3D和MeshGeometry3D为Model3DGroup构造一个包含XAML的内存流。

Then, in the UI thread, once the memory stream is available, load the model... 然后,在UI线程中,一旦内存流可用,就加载模型...

Model3DGroup model = System.Windows.Markup.XamlReader.Load(memoryStream) as Model3DGroup;

There is still a delay, but as file access is done in a background thread, it is not as severe. 仍然存在延迟,但是由于文件访问是在后台线程中完成的,因此并不那么严重。

Sorry for the late answer, but I actually managed to workaround the problem long time ago the following way: 很抱歉,您的回答很晚,但是很久以前,我实际上可以通过以下方法解决该问题:

    delegate void myDelegate();

    private void fileOpenButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            Thread ViewportLoaderThread = new Thread(loadViewportItemsAsync);
            ViewportLoaderThread.IsBackground = true;
            ViewportLoaderThread.Start();
        }
        catch (Exception err) { UtilsProgram.writeErrorLog(err.ToString()); }
    }

    private void loadViewportItemsAsync()
    {
        try
        {
            //TRY to browse for a file
            if (!browseForFile()) return;

            Dispatcher.Invoke(new Action(() => { myStatusBar.Visibility = System.Windows.Visibility.Visible; menuItemHelpDemo.IsEnabled = false; }), null);

            //Load file, unpack, decrypt, load STLs and create ModelGroup3D objects
            UtilsDen.DenModel = new DenLoader(UtilsDen.Filename, UtilsDen.Certificate, UtilsDen.PrivateKey, this);

            //Load the models to viewport async
            myDelegate asyncDel = new myDelegate(sendModelsToViewportAsync);
            this.Dispatcher.BeginInvoke(asyncDel, null);
        }
        catch (Exception err) { MessageBox.Show(UtilsProgram.langDict["msg18"]); UtilsProgram.writeErrorLog(err.ToString()); }
    }

    private void sendModelsToViewportAsync()
    {
        for (int i = 0; i < UtilsDen.DenModel.StlFilesCount; i++)
        {
            //Add the models to MAIN VIEWPORT
            ModelVisual3D modelVisual = new ModelVisual3D();
            GeometryModel3D geometryModel = new GeometryModel3D();
            Model3DGroup modelGroup = new Model3DGroup();

            geometryModel = new GeometryModel3D(UtilsDen.DenModel.StlModels[i].MeshGeometry, UtilsDen.Material);

            modelGroup.Children.Add(geometryModel);
            modelVisual.Content = modelGroup;
            mainViewport.Children.Add(toothModelVisual);
        }
    }

The key was to use the this.Dispatcher.BeginInvoke(asyncDel, null); 关键是使用this.Dispatcher.BeginInvoke(asyncDel, null); as it works on the main thread, but does not lag it, because it is executed asynchronously. 因为它是异步执行的,所以它在主线程上工作,但不会滞后。

Using a delegate still appears to introduce a lag on the UI, a better solution is create the model in a worker thread and then freeze it. 使用委托似乎仍然会在UI上引入滞后,更好的解决方案是在工作线程中创建模型,然后冻结它。 The model can then be cloned by the UI thread without the annoying exception. 然后可以通过UI线程克隆模型,而不会产生烦人的异常。 This works for me with models which take 25 seconds or more to load. 这适用于需要25秒或更长时间加载的模型。 The only issue I've found with this is that it doesn't work if the model contains a texture. 我发现的唯一问题是,如果模型包含纹理,则它不起作用。

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

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