简体   繁体   中英

How do i reduce memory usage in my C# program?

I'm working on a 3d engine, I recently added a list so that i can sort the triangles of my mesh and render them from furthest away to closest in order to avoid overlapping triangles that shouldn't be there. Ever since i've added this memory usage has shot way up, I'm not sure what to do to fix this however.

I've already tried converting the list to an array and converting it to a linked list but neither gave me the performance i needed.

//#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());
        }
    }
}

I expect to render a 3D object to my screen without wasting too much memory.

I've worked out the source of the problem

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

and

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

The reason it used so much memory was because i forgot to clear the list each time... Sometimes i question my intelligence.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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