如何减少C#程序中的内存使用量?

时间:2019-07-10 07:59:09

标签: c# performance writeablebitmapex

我正在使用3D引擎,最近添加了一个列表,以便可以对网格的三角形进行排序,并从最远的三角形渲染到最接近的三角形,以免出现重叠的三角形。自从我添加了这种内存使用量后,我不确定该如何解决。

我已经尝试过将列表转换为数组并将其转换为链接列表,但是都没有给我所需的性能。

//#define wireframe
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using static System.Diagnostics.Process;

public class Ref<T> // Way around using pointers.
{
    private readonly Action<T> setter;
    private readonly Func<T> getter;
    public Ref(Action<T> setter, Func<T> getter)
    {
        this.setter = setter;
        this.getter = getter;
    }
    public T Value { get { return getter(); } set { setter(value); } }
}

public class Vector3
{
    public float x, y, z;

    public Vector3(float xp = 0, float yp = 0, float zp = 0)
    {
        x = xp;
        y = yp;
        z = zp;
    }

    public static Vector3 operator +(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x + vec2.x, vec1.y + vec2.y, vec1.z + vec2.z);

    public static Vector3 operator -(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x - vec2.x, vec1.y - vec2.y, vec1.z - vec2.z);

    public static Vector3 operator *(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x * vec2.x, vec1.y * vec2.y, vec1.z * vec2.z);

    public static Vector3 operator /(Vector3 vec1, Vector3 vec2) => new Vector3(vec1.x / vec2.x, vec1.y / vec2.y, vec1.z / vec2.z);

    public static Vector3 operator +(Vector3 vec1, float val) => new Vector3(vec1.x + val, vec1.y + val, vec1.z + val);

    public static Vector3 operator -(Vector3 vec1, float val) => new Vector3(vec1.x - val, vec1.y - val, vec1.z - val);

    public static Vector3 operator *(Vector3 vec1, float val) => new Vector3(vec1.x * val, vec1.y * val, vec1.z * val);

    public static Vector3 operator /(Vector3 vec1, float val) => new Vector3(vec1.x / val, vec1.y / val, vec1.z / val);


};

public class Trig : IComparable<Trig>
{
    public List<Vector3> points;
    public Color col = Colors.Orange;

    public Trig(Vector3 vec1, Vector3 vec2, Vector3 vec3, Color colr)
    {
        points = new List<Vector3> { vec1, vec2, vec3 };
        col = colr;
    }
    public Trig(Vector3 vec1, Vector3 vec2, Vector3 vec3)
    {
        points = new List<Vector3> { vec1, vec2, vec3 };
    }
    public int CompareTo(Trig t2)
    {
        return (int)((this.points[0].z + this.points[1].z + this.points[2].z) / 3.0f) - (int)((t2.points[0].z + t2.points[1].z + t2.points[2].z) / 3.0f);
    }

    public Color GetColour(float lum, Color orig) => orig * lum;
};

public class Mesh
{
    public List<Trig> tris;

    public Mesh(List<Trig> trigs = null)
    {
        tris = trigs;
    }

    public void MakeCube(float width = 1, float height = 1, float length = 1)
    {
        tris = new List<Trig>{
                // SOUTH
                new Trig(new Vector3(0.0f * width, 0.0f * height, 0.0f * length), new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length)),
                new Trig(new Vector3(0.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 0.0f * height, 0.0f * length)),

                // EAST                                                      
                new Trig(new Vector3(1.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length)),
                new Trig(new Vector3(1.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length), new Vector3(1.0f * width, 0.0f * height, 1.0f * length)),

                // NORTH                                                     
                new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length)),
                new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 1.0f * length)),

                // WEST                                                      
                new Trig(new Vector3(0.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 0.0f * length)),
                new Trig(new Vector3(0.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(0.0f * width, 0.0f * height, 0.0f * length)),

                // TOP                                                       
                new Trig(new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(0.0f * width, 1.0f * height, 1.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length)),
                new Trig(new Vector3(0.0f * width, 1.0f * height, 0.0f * length), new Vector3(1.0f * width, 1.0f * height, 1.0f * length), new Vector3(1.0f * width, 1.0f * height, 0.0f * length)),

                // BOTTOM                                                    
                new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 0.0f * length)),
                new Trig(new Vector3(1.0f * width, 0.0f * height, 1.0f * length), new Vector3(0.0f * width, 0.0f * height, 0.0f * length), new Vector3(1.0f * width, 0.0f * height, 0.0f * length)),
            };
    }

    public bool LoadObjectFromFile(string path)
    {

        if (!File.Exists(path))
        {
            return false;
        }
        else
        {
            tris = new List<Trig>();
            StreamReader file = new StreamReader(path);
            List<Vector3> vertCache = new List<Vector3>(); // Cache of verts
            string line; 

            while ((line = file.ReadLine()) != null)
            {
                if (line != "")
                {
                    if (line[0] == "v"[0])
                    {
                        Vector3 v = new Vector3(); // temporary vertex
                        string[] temp = line.Split(" "[0]); // Split string into array of strings seperated by space
                        v.x = float.Parse(temp[1]); v.y = float.Parse(temp[2]); v.z = float.Parse(temp[3]); // set temp vertex to the values specified in string.
                        vertCache.Add(v);
                    }

                    if (line[0] == "f"[0])
                    {
                        string[] temp = line.Split(" "[0]); // Split string into array of strings seperated by space
                        tris.Add(new Trig(vertCache[int.Parse(temp[1]) - 1], vertCache[int.Parse(temp[2]) - 1], vertCache[int.Parse(temp[3]) - 1])); // Add a triangle with values from the index specified in string.
                    }
                }
            }

            Console.WriteLine(tris[0].points[0].x);
            file.Close();
            return true;
        }
    }

    public void Resize(float x)
    {
        int counter = 0;
        foreach(Trig tri in tris)
        {
            tris[counter] = new Trig(tri.points[0] * x, tri.points[1] * x, tri.points[2] * x);
            counter++;
        }
    }

    public void Resize(float x, float y, float z)
    {
        int counter = 0;
        foreach (Trig tri in tris)
        {
            Vector3 tempVec = new Vector3(x,y,z);
            tris[counter] = new Trig(tri.points[0] * tempVec, tri.points[1] * tempVec, tri.points[2] * tempVec);
            counter++;
        }
    }
};

public class Mat4x
{
    public float[,] matrix = new float[4, 4];
    public Mat4x()
    {
        matrix.Initialize();
    }
}

public class Camera
{
    public Vector3 position = new Vector3();

    public float fNear;
    public float fFar;
    public float fFov;
    public float fAspectRatio; // = (float)width / (float)height
    public float fFovRad;

    public Camera(Vector3 pos, float near = 0.1f, float far = 1000.0f, float fov = 90.0f, float width = 640, float height = 640)
    {
        position = pos;
        fNear = near;
        fFar = far;
        fFov = fov;
        fAspectRatio = width / height;
        fFovRad = 1.0f / (float)Math.Tan(fFov * 0.5f / 180.0f * 3.14159f);
    }

    public float x {get{ return position.x; } set{ position.x = value; }}
    public float y { get { return position.y; } set { position.y = value; } }
    public float z { get { return position.z; } set { position.z = value; } }
}

public class Light
{
    public Vector3 position = new Vector3();
    private Vector3 _direction = new Vector3();

    public bool isInfinite = false;
    public float radius = 1.0f;
    public Color col;

    public float Normaliser(Vector3 vec)
    {
        return (float)Math.Sqrt((vec.x * vec.x) + (vec.y * vec.y) + (vec.z * vec.z));
    }

    public Light(Vector3 dirc, Vector3 pos, Color colr, bool infin = false, float rad = 1.0f)
    {
        position = pos;
        direction = dirc;
        isInfinite = infin;
        radius = rad;
        col = colr;
        _direction = new Vector3(dirc.x / Normaliser(dirc), dirc.y / Normaliser(dirc), dirc.z / Normaliser(dirc));
    }

    public Light(Vector3 dirc, Vector3 pos)
    {
        position = pos;
        direction = dirc;
        isInfinite = true;
        radius = 0.0f;
        col = Colors.WhiteSmoke;
        _direction = new Vector3(dirc.x / Normaliser(dirc), dirc.y / Normaliser(dirc), dirc.z / Normaliser(dirc));
    }

    public Light(Vector3 dirc)
    {
        direction = dirc;
        isInfinite = true;
        radius = 0.0f;
        col = Colors.WhiteSmoke;
        _direction = new Vector3(dirc.x / Normaliser(dirc), dirc.y / Normaliser(dirc), dirc.z / Normaliser(dirc));
    }

    public float x { get { return position.x; } set { position.x = value; } }
    public float y { get { return position.y; } set { position.y = value; } }
    public float z { get { return position.z; } set { position.z = value; } }

    public Vector3 direction { get => _direction; set => _direction = new Vector3 (value.x / Normaliser(value), value.y / Normaliser(value), value.z / Normaliser(value)); }
}

namespace _2dEngine
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        int height, width;
        WriteableBitmap wrBmp;
        Mesh cube;
        Mat4x matproj, matRotZ, matRotX;
        float fTheta, fElapsedTime;
        Camera maincam;
        Light mainlight;
        List<Trig> trigsToDraw = new List<Trig>();

        void MultiplyMatrixVector(Vector3 i, Ref<Vector3> o, Mat4x m)
        { // multiply a vector and a 4x matrix to get a vector which we store in o.
            Vector3 newVec = new Vector3
            {
                x = (i.x * m.matrix[0, 0]) + (i.y * m.matrix[1, 0]) + (i.z * m.matrix[2, 0]) + m.matrix[3, 0],
                y = (i.x * m.matrix[0, 1]) + (i.y * m.matrix[1, 1]) + (i.z * m.matrix[2, 1]) + m.matrix[3, 1],
                z = (i.x * m.matrix[0, 2]) + (i.y * m.matrix[1, 2]) + (i.z * m.matrix[2, 2]) + m.matrix[3, 2]
            };

            float w = (i.x * m.matrix[0, 3]) + (i.y * m.matrix[1, 3]) + (i.z * m.matrix[2, 3]) + m.matrix[3, 3];

            if (w != 0.0f)
            {
                newVec.x /= w; newVec.y /= w; newVec.z /= w;
            }
            o.Value = newVec;
            //return newVec;
        }

        void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, Color col)
        {
            wrBmp.DrawLine(x1, y1, x2, y2, col);
            wrBmp.DrawLine(x2, y2, x3, y3, col);
            wrBmp.DrawLine(x3, y3, x1, y1, col);
        }

        void FillTriangle(int x1, int y1, int x2, int y2, int x3, int y3, Color col) => wrBmp.FillTriangle(x1, y1, x2, y2, x3, y3, col);

        private void ViewPort_Loaded(object sender, RoutedEventArgs e)
        {
            matproj = new Mat4x();
            matRotZ = new Mat4x();
            matRotX = new Mat4x();
            width = (int)this.ViewPortContainer.ActualWidth;
            height = (int)this.ViewPortContainer.ActualHeight;
            wrBmp = BitmapFactory.New(width, height);
            ViewPort.Source = wrBmp;
            maincam = new Camera(BlankVector3(), width: width, height: height);
            mainlight = new Light(new Vector3(0, 0, -1));

            cube = new Mesh();
            cube.LoadObjectFromFile(@"D:\Users\Electrode\Desktop\Cylinder.obj");

            float fNear = 0.1f;
            float fFar = 1000.0f;
            float fFov = 90.0f;
            float fAspectRatio = (float)width / (float)height;
            float fFovRad = 1.0f / (float)Math.Tan((double)(fFov * 0.5f / 180.0f * 3.14159f));
            fTheta = 1.0f;

            matproj.matrix[0, 0] = fAspectRatio * fFovRad;
            matproj.matrix[1, 1] = fFovRad;
            matproj.matrix[2, 2] = fFar / (fFar - fNear);
            matproj.matrix[3, 2] = (-fFar * fNear) / (fFar - fNear);
            matproj.matrix[2, 3] = 1.0f;
            matproj.matrix[3, 3] = 0.0f;

            CompositionTarget.Rendering += CompostitionTarget_Rendering;
        }

        private void CompostitionTarget_Rendering(object sender, EventArgs e)
        {
            wrBmp.Clear(Colors.Black);
            Trig triRotatedZ, triRotatedZX, triTranslated;

            // Rotation Z
            matRotZ.matrix[0, 0] = (float)Math.Cos(fTheta);
            matRotZ.matrix[0, 1] = (float)Math.Sin(fTheta);
            matRotZ.matrix[1, 0] = (float)-Math.Sin(fTheta);
            matRotZ.matrix[1, 1] = (float)Math.Cos(fTheta);
            matRotZ.matrix[2, 2] = 1;
            matRotZ.matrix[3, 3] = 1;

            // Rotation X
            matRotX.matrix[0, 0] = 1;
            matRotX.matrix[1, 1] = (float)Math.Cos(fTheta * 0.5f);
            matRotX.matrix[1, 2] = (float)Math.Sin(fTheta * 0.5f);
            matRotX.matrix[2, 1] = 0 - (float)Math.Sin(fTheta * 0.5f);
            matRotX.matrix[2, 2] = (float)Math.Cos(fTheta * 0.5f);
            matRotX.matrix[3, 3] = 1;

            fElapsedTime = (float)TimeSpan.FromTicks(DateTime.UtcNow.Ticks - GetCurrentProcess().StartTime.ToUniversalTime().Ticks).TotalSeconds; // Time open in ticks.

            // Alternate theta
            // fTheta += 1.0f;
            fTheta += (1.5f * fElapsedTime / 600) / 4; // Rotate our cube

            /// Debug shit
            /// Console.WriteLine(fTheta);

            foreach (Trig tri in cube.tris) // Iterate through each triangle and render it correctly
            {
                // setup trigs
                triRotatedZ = BlankTrig();
                triRotatedZX = BlankTrig();

                // Rotate in Z-Axis
                MultiplyMatrixVector(tri.points[0], new Ref<Vector3>( // First point rotation
                    setter: yes => { triRotatedZ.points[0] = yes; }, // our setter for the variable
                    getter: () => triRotatedZ.points[0]), matRotZ); // our getter
                MultiplyMatrixVector(tri.points[1], new Ref<Vector3>( // Second point
                    setter: yes => { triRotatedZ.points[1] = yes; },
                    getter: () => triRotatedZ.points[1]), matRotZ);
                MultiplyMatrixVector(tri.points[2], new Ref<Vector3>( // Etc
                    setter: yes => { triRotatedZ.points[2] = yes; },
                    getter: () => triRotatedZ.points[2]), matRotZ);

                // Rotate in X-Axis
                MultiplyMatrixVector(triRotatedZ.points[0], new Ref<Vector3>( // See above
                    setter: yes => { triRotatedZX.points[0] = yes; },
                    getter: () => triRotatedZX.points[0]), matRotX);

                MultiplyMatrixVector(triRotatedZ.points[1], new Ref<Vector3>(
                    setter: yes => { triRotatedZX.points[1] = yes; },
                    getter: () => triRotatedZX.points[1]), matRotX);

                MultiplyMatrixVector(triRotatedZ.points[2], new Ref<Vector3>(
                    setter: yes => { triRotatedZX.points[2] = yes; },
                    getter: () => triRotatedZX.points[2]), matRotX);

                // Offset into the screen
                triTranslated = triRotatedZX;
                triTranslated.points[0].z = triRotatedZX.points[0].z + 120.0f;
                triTranslated.points[1].z = triRotatedZX.points[1].z + 120.0f;
                triTranslated.points[2].z = triRotatedZX.points[2].z + 120.0f;

                // Use Cross-Product to get surface normal
                Vector3 normal, line1, line2;
                normal = line1 = line2 = BlankVector3();

                line1 = triTranslated.points[1] - triTranslated.points[0];

                line2 = triTranslated.points[2] - triTranslated.points[0];

                normal.x = (line1.y * line2.z) - (line1.z * line2.y);
                normal.y = (line1.z * line2.x) - (line1.x * line2.z);
                normal.z = (line1.x * line2.y) - (line1.y * line2.x);

                // It's normally normal to normalise the normal
                float l = (float)Math.Sqrt((normal.x * normal.x) + (normal.y * normal.y) + (normal.z * normal.z));
                normal /= l;

                if (normal.x * (triTranslated.points[0].x - maincam.x) +
                    normal.y * (triTranslated.points[0].y - maincam.y) +
                    normal.z * (triTranslated.points[0].z - maincam.z) < 0.0f)
                {
                    float dp = normal.x * mainlight.direction.x + normal.y * mainlight.direction.y + normal.z * mainlight.direction.z;
                    triTranslated.col = triTranslated.GetColour(dp, triTranslated.col);

                    // Project triangle from 3d to 2d
                    Trig triProj = new Trig(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.0f))
                    {
                        col = triTranslated.col
                    }; // make a blank triangle

                    MultiplyMatrixVector(triTranslated.points[0], new Ref<Vector3>( // First point projection
                        setter: yes => { triProj.points[0] = yes; },
                        getter: () => triProj.points[0]), matproj);

                    MultiplyMatrixVector(triTranslated.points[1], new Ref<Vector3>( // Second point
                        setter: yes => { triProj.points[1] = yes; },
                        getter: () => triProj.points[1]), matproj);

                    MultiplyMatrixVector(triTranslated.points[2], new Ref<Vector3>( // Etc
                        setter: yes => { triProj.points[2] = yes; },
                        getter: () => triProj.points[2]), matproj);


                    //Scale
                    triProj.points[0].x += 1.0f; triProj.points[0].y += 1.0f;
                    triProj.points[1].x += 1.0f; triProj.points[1].y += 1.0f;
                    triProj.points[2].x += 1.0f; triProj.points[2].y += 1.0f;
                    triProj.points[0].x *= 0.5f * width;
                    triProj.points[0].y *= 0.5f * height;
                    triProj.points[1].x *= 0.5f * width;
                    triProj.points[1].y *= 0.5f * height;
                    triProj.points[2].x *= 0.5f * width;
                    triProj.points[2].y *= 0.5f * height;

                    trigsToDraw.Add(triProj); // Either this,

                }
            }

            Array.Sort(trigsToDraw.ToArray()); // This,

            foreach(Trig triDraw in trigsToDraw) // Or This is causing a performance drop
            {
                // Draw triangles
                FillTriangle(
                    (int)triDraw.points[0].x,
                    (int)triDraw.points[0].y,
                    (int)triDraw.points[1].x,
                    (int)triDraw.points[1].y,
                    (int)triDraw.points[2].x,
                    (int)triDraw.points[2].y,
                    triDraw.col);

#if (wireframe)
                DrawTriangle( // Draw wireframe for debug reasons, if you want.
                    (int)triDraw.points[0].x,
                    (int)triDraw.points[0].y,
                    (int)triDraw.points[1].x,
                    (int)triDraw.points[1].y,
                    (int)triDraw.points[2].x,
                    (int)triDraw.points[2].y,
                    Color.FromRgb(0, 255, 0));
#endif
            }
        }

        private static Vector3 BlankVector3()
        {
            return new Vector3();
        }

        private static Trig BlankTrig()
        {
            return new Trig(new Vector3(), new Vector3(), new Vector3());
        }
    }
}

我希望在不浪费过多内存的情况下将3D对象渲染到屏幕上。

1 个答案:

答案 0 :(得分:0)

我已经找出问题的根源

trigsToDraw.Add(triProj); // part 1 of the problem

foreach(Trig triDraw in trigsToDraw)
            {
                // Draw triangles
                FillTriangle(
                    (int)triDraw.points[0].x,
                    (int)triDraw.points[0].y,
                    (int)triDraw.points[1].x,
                    (int)triDraw.points[1].y,
                    (int)triDraw.points[2].x,
                    (int)triDraw.points[2].y,
                    triDraw.col);

#if (wireframe)
                DrawTriangle( // Draw wireframe for debug reasons, if you want.
                    (int)triDraw.points[0].x,
                    (int)triDraw.points[0].y,
                    (int)triDraw.points[1].x,
                    (int)triDraw.points[1].y,
                    (int)triDraw.points[2].x,
                    (int)triDraw.points[2].y,
                    Color.FromRgb(0, 255, 0));
#endif
            }// Part  2 of the problem

它使用大量内存的原因是因为我每次都忘记清除列表... 有时我会怀疑我的智力。