Search code examples
c#3dmonogame

Monogame program keeps randomly dropping a ton of frames


I'm working with 3D graphics in Monogame and I'm creating 100 instances of a Rectangle class I made:

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BlockMonglerFixed1
{
    public class Rectangle
    {
        /// <summary>
        /// Width of the rectangle
        /// </summary>
        public int Width { get; set; }

        /// <summary>
        /// Height of the rectangle
        /// </summary>
        public int Height { get; set; }

        /// <summary>
        /// Length of the rectangle
        /// </summary>
        public int Length { get; set; }

        /// <summary>
        /// Position of the rectangle
        /// </summary>
        public Vector3 Position { get; set; }

        /// <summary>
        /// Rotation vector3 of the rectangle. X = pitch. Y = yaw. Z = roll
        /// </summary>
        public Vector3 Rotation { get; set; }

        /// <summary>
        /// GraphicsDevice used to draw the rectangle
        /// </summary>
        public GraphicsDevice GraphicsDevice { get; set; }

        /// <summary>
        /// BasicEffect used to to draw the rectangle
        /// </summary>
        public BasicEffect Effect { get; set; }

        /// <summary>
        /// Color of the rectangle. Not used if a texture is being used
        /// </summary>
        public Color Color { get; set; }

        /// <summary>
        /// Texture of the rectangle. Not used if a color is being used.
        /// </summary>
        public Texture3D Texture { get; set; }

        /// <summary>
        /// Determines if the rectangle should move based on its rotation
        /// </summary>
        public bool MoveBasedOnRotation { get; set; }

        int _vertexLength;

        VertexBuffer _buffer;

        /// <summary>
        /// Cube constructor. Generates a new rectangle
        /// </summary>
        /// <param name="width">Width of rectangle</param>
        /// <param name="height">Height of rectangle</param>
        /// <param name="length">Length of rectangle</param>
        /// <param name="position">Position of the rectangle></param>
        /// <param name="rotation">Rotation of the rectangle. This value rotates the rectangle</param>
        /// <param name="graphicsDevice">GraphicsDevice used to draw the rectangle</param>
        /// <param name="effect">Effect of rectangle</param>
        /// <param name="color">Color of rectangle</param>
        /// <param name="moveBasedOnRotation">Determines if the rectangle should move based on its rotation</param>
        public Rectangle(int width, int height, int length, Vector3 position,
            Vector3 rotation, GraphicsDevice graphicsDevice, BasicEffect effect, Color color, bool moveBasedOnRotation)
        {
            //Dimensions of the rectangle
            Width = width;
            Height = height;
            Length = length * 2;

            //Position setup
            Position = position;

            //Rotation setup
            Rotation = rotation;

            MoveBasedOnRotation = moveBasedOnRotation;

            //Effect setup
            Effect = effect;

            //Color setup
            Color = color;

            //Graphics device setup.
            GraphicsDevice = graphicsDevice;

            //Cube setup
            VertexPositionColor[] vertexPositionColors =
            {
                //Face 1
                new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, -Height, 0), Color),

                new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, -Height, 0), Color),

                //Face 2
                new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),

                new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),

                //Side 1
                new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),

                new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
                new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),

                //Side 2
                new VertexPositionColor(new Vector3(Width, Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),

                new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, Height, 0), Color),

                //Bottom
                new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, -Height, 0), Color),

                new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, -Height, 0), Color),

                //Top
                new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                new VertexPositionColor(new Vector3(Width, Height, 0), Color),

                new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
                new VertexPositionColor(new Vector3(Width, Height, 0), Color),
            };

            //Length setup
            _vertexLength = vertexPositionColors.Length;

            //Buffer setup
            _buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), _vertexLength,
                BufferUsage.WriteOnly);

            //Set data of the buffer to the vertexList
            _buffer.SetData(vertexPositionColors);
        }

        public Rectangle(int width, int height, int length, Matrix worldMatrix, Matrix projectionMatrix,
            Matrix viewMatrix,
            GraphicsDevice graphicsDevice, BasicEffect effect, Texture3D texture)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sees if the rectangle collides with another object
        /// </summary>
        /// <param name="otherRectangle">Rectangle to detect collision with</param>
        public bool IsColliding(Rectangle otherRectangle)
        {
            //Get the minimum values of our position
            float minX = Position.X - Width;
            float minY = Position.Y - Height;
            float minZ = Position.Z - Length / 2f;

            //Get the maximum values of our position
            float maxX = Position.X + Width;
            float maxY = Position.Y + Height;
            float maxZ = Position.Z + Length / 2f;

            //Get the minimum values of the other rectangle's position
            float otherMinX = otherRectangle.Position.X - otherRectangle.Width;
            float otherMinY = otherRectangle.Position.Y - otherRectangle.Height;
            float otherMinZ = otherRectangle.Position.Z - otherRectangle.Length / 2f;

            //Get the maximum values of the other rectangle's positions
            float otherMaxX = otherRectangle.Position.X + otherRectangle.Width;
            float otherMaxY = otherRectangle.Position.Y + otherRectangle.Height;
            float otherMaxZ = otherRectangle.Position.Z + otherRectangle.Length / 2f;

            //We can calculate if one rectangle is in another by seeing if one of our sides is inside another rectangle
            return minX <= otherMaxX && maxX >= otherMinX &&
                   minY <= otherMaxY && maxY >= otherMinY &&
                   minZ <= otherMaxZ && maxZ >= otherMinZ;
        }

        /// <summary>
        /// Draws the rectangle to the screen
        /// </summary>
        public void Draw()
        {
            //Hold the current position
            Vector3 tempPosition = Position;

            //Create a new matrix with the position and rotation
            Matrix effectWorldMatrix = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up) *
                                       Matrix.CreateTranslation(-new Vector3(0, 0, -Length / 2f)) *
                                       Matrix.CreateRotationX(Rotation.X) *
                                       Matrix.CreateRotationY(Rotation.Y) *
                                       Matrix.CreateRotationZ(Rotation.Z) *
                                       Matrix.CreateTranslation(Position);

            //If MoveBasedOnRotation is true, set the translation of our new matrix to the previous translation
            //if (!MoveBasedOnRotation) effectWorldMatrix.Translation = tempPosition;

            //Set the effect to our respective matrices;
            Effect.World = effectWorldMatrix;
            Effect.View = Camera.ViewMatrix;
            Effect.Projection = Camera.ProjectionMatrix;

            //Apply our vertex buffer to the graphics device
            GraphicsDevice.SetVertexBuffer(_buffer);

            //Disable culling so that we can see our object on both sides
            RasterizerState rasterizerState = new RasterizerState {CullMode = CullMode.None};
            GraphicsDevice.RasterizerState = rasterizerState;

            foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
            {
                //Apply the pass
                pass.Apply();

                //Use the graphics device to draw the triangle
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, _vertexLength);
            }
        }
    }
}

The problem I am having is that the program runs smoothly at 60 fps, but then the fps randomly drops to near 0. The frequency of frame drops seems to be related to the number of rectangles.

Here is the Draw code that is called every frame (blocks is the rectangle array)

/// <summary>
/// Draw logic. Activates every frame.
/// </summary>
/// <param name="gameTime">How long the program has been running for</param>
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Chocolate);

    foreach (var a in blocks)
        a.Draw();

     fps++;

     if (gameTime.TotalGameTime.Milliseconds % 1000 == 0 && enableFpsCounter)
     {
         Console.WriteLine(fps);

         fps = 0;
     }

     base.Draw(gameTime);
}

The problem only happens when there is a sizable amount of rectangles (~25 rectangles)

Here is is a gif that showcases this problem: https://gyazo.com/ec41cacd6c89a41c330e151108590050

Thank you for reading and I appreciate any help I can get.


Solution

  • I managed to solve this problem by making it so that the RasterizerState is initiated inside the constructor rather than the Draw method:

    using System;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    
    namespace BlockMonglerFixed1
    {
        public class Rectangle
        {
            /// <summary>
            /// Width of the rectangle
            /// </summary>
            public int Width { get; set; }
    
            /// <summary>
            /// Height of the rectangle
            /// </summary>
            public int Height { get; set; }
    
            /// <summary>
            /// Length of the rectangle
            /// </summary>
            public int Length { get; set; }
    
            /// <summary>
            /// Position of the rectangle
            /// </summary>
            public Vector3 Position { get; set; }
    
            /// <summary>
            /// Rotation vector3 of the rectangle. X = pitch. Y = yaw. Z = roll
            /// </summary>
            public Vector3 Rotation { get; set; }
    
            /// <summary>
            /// GraphicsDevice used to draw the rectangle
            /// </summary>
            public GraphicsDevice GraphicsDevice { get; set; }
    
            /// <summary>
            /// BasicEffect used to to draw the rectangle
            /// </summary>
            public BasicEffect Effect { get; set; }
    
            /// <summary>
            /// Color of the rectangle. Not used if a texture is being used
            /// </summary>
            public Color Color { get; set; }
    
            /// <summary>
            /// Texture of the rectangle. Not used if a color is being used.
            /// </summary>
            public Texture3D Texture { get; set; }
    
            /// <summary>
            /// Determines if the rectangle should move based on its rotation
            /// </summary>
            public bool MoveBasedOnRotation { get; set; }
    
            int _vertexLength;
    
            VertexBuffer _buffer;
    
            RasterizerState _rasterizerState;
    
            /// <summary>
            /// Cube constructor. Generates a new rectangle
            /// </summary>
            /// <param name="width">Width of rectangle</param>
            /// <param name="height">Height of rectangle</param>
            /// <param name="length">Length of rectangle</param>
            /// <param name="position">Position of the rectangle></param>
            /// <param name="rotation">Rotation of the rectangle. This value rotates the rectangle</param>
            /// <param name="graphicsDevice">GraphicsDevice used to draw the rectangle</param>
            /// <param name="effect">Effect of rectangle</param>
            /// <param name="color">Color of rectangle</param>
            /// <param name="moveBasedOnRotation">Determines if the rectangle should move based on its rotation</param>
            public Rectangle(int width, int height, int length, Vector3 position,
                Vector3 rotation, GraphicsDevice graphicsDevice, BasicEffect effect, Color color, bool moveBasedOnRotation)
            {
                //Dimensions of the rectangle
                Width = width;
                Height = height;
                Length = length * 2;
    
                //Position setup
                Position = position;
    
                //Rotation setup
                Rotation = rotation;
    
                MoveBasedOnRotation = moveBasedOnRotation;
    
                //Effect setup
                Effect = effect;
    
                //Color setup
                Color = color;
    
                //Graphics device setup.
                GraphicsDevice = graphicsDevice;
    
                //Cube setup
                VertexPositionColor[] vertexPositionColors =
                {
                    //Face 1
                    new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                    new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
    
                    new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
    
                    //Face 2
                    new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
    
                    new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
    
                    //Side 1
                    new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                    new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
    
                    new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                    new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
                    new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
    
                    //Side 2
                    new VertexPositionColor(new Vector3(Width, Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
    
                    new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, Height, 0), Color),
    
                    //Bottom
                    new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                    new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
    
                    new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
    
                    //Top
                    new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
                    new VertexPositionColor(new Vector3(Width, Height, 0), Color),
    
                    new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
                    new VertexPositionColor(new Vector3(Width, Height, 0), Color),
                };
    
                //Length setup
                _vertexLength = vertexPositionColors.Length;
    
                //Buffer setup
                _buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), _vertexLength,
                    BufferUsage.WriteOnly);
    
                //Set data of the buffer to the vertexList
                _buffer.SetData(vertexPositionColors);
    
                _rasterizerState = new RasterizerState {CullMode = CullMode.None};
                GraphicsDevice.RasterizerState = _rasterizerState;
            }
    
            public Rectangle(int width, int height, int length, Matrix worldMatrix, Matrix projectionMatrix,
                Matrix viewMatrix,
                GraphicsDevice graphicsDevice, BasicEffect effect, Texture3D texture)
            {
                throw new NotImplementedException();
            }
    
            /// <summary>
            /// Sees if the rectangle collides with another object
            /// </summary>
            /// <param name="otherRectangle">Rectangle to detect collision with</param>
            public bool IsColliding(Rectangle otherRectangle)
            {
                //Get the minimum values of our position
                float minX = Position.X - Width;
                float minY = Position.Y - Height;
                float minZ = Position.Z - Length / 2f;
    
                //Get the maximum values of our position
                float maxX = Position.X + Width;
                float maxY = Position.Y + Height;
                float maxZ = Position.Z + Length / 2f;
    
                //Get the minimum values of the other rectangle's position
                float otherMinX = otherRectangle.Position.X - otherRectangle.Width;
                float otherMinY = otherRectangle.Position.Y - otherRectangle.Height;
                float otherMinZ = otherRectangle.Position.Z - otherRectangle.Length / 2f;
    
                //Get the maximum values of the other rectangle's positions
                float otherMaxX = otherRectangle.Position.X + otherRectangle.Width;
                float otherMaxY = otherRectangle.Position.Y + otherRectangle.Height;
                float otherMaxZ = otherRectangle.Position.Z + otherRectangle.Length / 2f;
    
                //We can calculate if one rectangle is in another by seeing if one of our sides is inside another rectangle
                return minX <= otherMaxX && maxX >= otherMinX &&
                       minY <= otherMaxY && maxY >= otherMinY &&
                       minZ <= otherMaxZ && maxZ >= otherMinZ;
    
            }
    
            /// <summary>
            /// Draws the triangle to the screen
            /// </summary>
            public void Draw()
            {
                //Hold the current position
                Vector3 tempPosition = Position;
    
                //Create a new matrix with the position and rotation
                Matrix effectWorldMatrix = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up) *
                                           Matrix.CreateTranslation(-new Vector3(0, 0, -Length / 2f)) *
                                           Matrix.CreateRotationX(Rotation.X) *
                                           Matrix.CreateRotationY(Rotation.Y) *
                                           Matrix.CreateRotationZ(Rotation.Z) *
                                           Matrix.CreateTranslation(Position);
    
                //If MoveBasedOnRotation is true, set the translation of our new matrix to the previous translation
                //if (!MoveBasedOnRotation) effectWorldMatrix.Translation = tempPosition;
    
                //Set the effect to our respective matrices;
                Effect.World = effectWorldMatrix;
                Effect.View = Camera.ViewMatrix;
                Effect.Projection = Camera.ProjectionMatrix;
    
                //Apply our vertex buffer to the graphics device
                GraphicsDevice.SetVertexBuffer(_buffer);
    
                foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
                {
                    //Apply the pass
                    pass.Apply();
    
                    //Use the graphics device to draw the triangle
                    GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, _vertexLength);
                }
            }
        }
    }