How to create basic shapes without getting sharp edges ?

I get around 200FPS, so a bit better than with BasicEffect, but more or less what I had with the previous version (1 BasicEffect per Square).
Once again, I might have missed some optimizations, here is the full source code :

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

namespace Patapits.Framework.RawTests
{
    public class Square
    {
        private static Vector3[] _vertices;
        private static short[] _indexes;

        private Vector2 _position;

        public Square()
        {
            Generate();
        }

        public void MoveTo(Vector2 position)
        {
            _position = position;
        }

        public void Generate()
        {
            if (_vertices == null)
            {
                _vertices = new Vector3[4];

                _vertices[0] = new Vector3(-0.5f, -0.5f, 0.0f);
                _vertices[1] = new Vector3(0.5f, -0.5f, 0.0f);
                _vertices[2] = new Vector3(-0.5f, 0.5f, 0.0f);
                _vertices[3] = new Vector3(0.5f, 0.5f, 0.0f);

                _indexes = new short[6];

                _indexes[0] = 0;
                _indexes[1] = 1;
                _indexes[2] = 2;
                _indexes[3] = 1;
                _indexes[4] = 3;
                _indexes[5] = 2;
            }
        }

        public Matrix GetWorldMatrix()
        {
            return Matrix.CreateRotationZ(MathHelper.Pi / 3) *
                   Matrix.CreateScale(100.0f, 100.0f, 1.0f) *
                   Matrix.CreateTranslation(new Vector3(_position, 0.0f));
        }

        public VertexPositionColorTexture[] GetVertices()
        {
            Matrix worldMatrix = GetWorldMatrix();
            return _vertices.Select(v =>
                    new VertexPositionColorTexture(Vector3.Transform(v, worldMatrix), Color.Red, new Vector2(0.0f)))
                .ToArray();
        }

        public short[] GetIndexes()
        {
            return _indexes;
        }

        public void Draw(GraphicsDevice device)
        {
        }
    }

    public class Game1 : Game
    {
        GraphicsDeviceManager _graphics;

        private Square[] _squares;

        private SpriteEffect _effect;
        
        private Texture2D _whiteTexture;

        public Game1()
        {
            _graphics = new GraphicsDeviceManager(this) {SynchronizeWithVerticalRetrace = false};
            _graphics.PreferredBackBufferWidth = 1920;
            _graphics.PreferredBackBufferHeight = 1080;

            IsFixedTimeStep = false;
            Content.RootDirectory = "Content";

            Window.AllowUserResizing = true;
        }

        protected override void Initialize()
        {
            _effect = new SpriteEffect(GraphicsDevice);
            
            _whiteTexture = new Texture2D(GraphicsDevice, 1, 1);
            _whiteTexture.SetData(new[] {Color.White});

            base.Initialize();
        }

        protected override void LoadContent()
        {
            int squaresCount = 100;
            _squares = new Square[squaresCount];

            Random rand = new Random();

            for (int i = 0; i < squaresCount; i++)
            {
                _squares[i] = new Square();
                _squares[i].MoveTo(
                    new Vector2(
                        rand.Next(0, _graphics.PreferredBackBufferWidth),
                        rand.Next(0, _graphics.PreferredBackBufferHeight)));
            }
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            VertexPositionColorTexture[][] squaresVertices = new VertexPositionColorTexture[_squares.Length][];
            short[][] squaresIndexes = new short[_squares.Length][];

            for (int i = 0; i < _squares.Length; i++)
            {
                squaresVertices[i] = _squares[i].GetVertices();
                squaresIndexes[i] = _squares[i].GetIndexes();
            }

            VertexPositionColorTexture[] vertices =
                new VertexPositionColorTexture[squaresVertices.Sum(array => array.Length)];
            short[] indexes = new short[squaresIndexes.Sum(array => array.Length)];

            int currentVerticesIndex = 0;
            int currentIndexesIndex = 0;

            for (int i = 0; i < _squares.Length; i++)
            {
                int currentIndexesOffset = currentVerticesIndex;
                Array.Copy(squaresVertices[i], 0, vertices, currentVerticesIndex, squaresVertices[i].Length);
                currentVerticesIndex += squaresVertices[i].Length;

                Array.Copy(squaresIndexes[i].Select(s => (short) (s + currentIndexesOffset)).ToArray(), 0, indexes,
                    currentIndexesIndex, squaresIndexes[i].Length);
                currentIndexesIndex += squaresIndexes[i].Length;
            }
            
            GraphicsDevice.BlendState = BlendState.AlphaBlend;

            foreach (EffectPass pass in _effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsDevice.Textures[0] = _whiteTexture;
                GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length,
                    indexes, 0, indexes.Length / 3);
            }

            base.Draw(gameTime);
        }
    }
}

OK, the performance problem probably lies elsewhere. With an ā€œemptyā€ game (without any Effect nor SpriteBatch, only GraphicsDevice.Clear(Color.CornflowerBlue); in the overrided Draw()), I get around 300FPS at 1080p, which I found suspicious. Without GraphicsDevice.Clear(Color.CornflowerBlue);, I get ā€œonlyā€ 400FPS (for a black screen :unamused:), with 96,35% of the run time spent in SwapChain.Present.
Iā€™ll try to update the Intel Graphics drivers to see if it helps.

EDIT : after updating the drivers, I get around the same FPS without anyting (~400FPS in Debug). But in Release, I got ~200FPSā€¦ Iā€™m losing hope :sob:

EDIT 2 : nevermind, in Release mode I was using my dedicated graphic card with some settings on it, after forcing my integrated graphic card, I get the same result : around 400FPS

You could create a new empty project and do nothing but measure fps.
At least youā€™d see if your code has a problem or maybe drivers.
I have a Radeon r7 350x which suprisingly seems to have roughly the same performance as your Intel HD 630. With a new empty project (Desktop GL, Debug) I get roughly 4000 fps on average.
This is the code I used:

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

namespace Game1
{
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.SynchronizeWithVerticalRetrace = false;
            graphics.ApplyChanges();

            IsFixedTimeStep = false;

            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            var framerate = (1 / gameTime.ElapsedGameTime.TotalSeconds);
            Console.WriteLine(framerate);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            base.Draw(gameTime);
        }
    }
}


Running a new empty project on my Intel HD 630 :

  • At default resolution with or without GraphicsDevice.Clear(Color.CornflowerBlue); : ~3000 FPS
  • At 1080p with GraphicsDevice.Clear(Color.CornflowerBlue); : ~300FPS
  • At 1080p without GraphicsDevice.Clear(Color.CornflowerBlue); : ~400FPS

On my dedicated graphic card (NVIDIA GeForce GTX 1050), I get suprisingly low FPS, but maybe itā€™s due to some settings or some hardware limitation (since I have a laptop, I think the display screen is internally connected to the Intel graphic card, so when using the NVIDIA one, some informations might pass from one card to another, explaining these low FPS values).

In summary, if an empty project without drawing anything is getting around 400FPS, Iā€™m not surprised to have such ā€œlowā€ FPS when starting to draw some geometry. But I perhaps Iā€™m exaggerating the geometry draw cost, I donā€™t knowā€¦

Anyway, thanks for your time figuring out these numbers @Finnhax :smile:

Well i looked up that card and then i looked up a comparison.
To see how it compared to a older 2016 100$ amd card.
Which curb stomped the hell out of it in every single test.

https://gpu.userbenchmark.com/Compare/AMD-RX-460-vs-Intel-UHD-Graphics-630-Desktop-Coffee-Lake-i5-i7/3641vsm356797

1080 p with that card is wishful thinking.

Whats a empty opengl project run at in lower screen resolution?.

With an empty OpenGL project, I got more or less the same values, except with graphics.IsFullScreen = true;, with which I got ~1000FPS for the OpenGL project VS ~400FPS for the DirectX one. Interestingā€¦
My game will be veeeeery basic, no effects, not lots of textures (maybe not a single one), it should be graphically comparable to Sokobond. And this is why I think targeting 144FPS at 1080p on these kind of cards should be possible without some tricky magic. But once again, Iā€™m just starting game development and understanding how things work behind the scene, so I might be completely wrong (but if thatā€™s the case, I donā€™t get why :sweat_smile:).

A series of articles about AA, what kinds will be helpful to you, and what performance/methods they use:

https://blogs.msdn.microsoft.com/shawnhar/2011/04/29/texture-aliasing/

Those numbers look fine to me considering your gpu.
Why are you targeting 144 fps? In a 2D game like that itā€™s overkill.
Considering the average User has a 60hz display anyway 60 fps is easily enough.

Thanks for the link, very usefull !

Iā€™m targetting 144 FPS because it seems like thatā€™s the higher display refresh rate widely available (thatā€™s a feeling from what Iā€™ve seen 4 months ago while looking for a new computer).
My current laptop has a refresh rate of 120Hz, I tried running my game at 60FPS : itā€™s stuttery.
Furthermore, Iā€™m targetting such ā€œhighā€ FPS because I think itā€™ll lead to a better quality game code : if something is not optimized, I will quickly notice it and I will be able to fix it early. In my experience with performance optimization, the application will then have way less bugs when released. Of course, lots of bugs can still exist, but probably way less than without setting this kind of constraint.

That being said, maybe thatā€™s impossible (or only possible after spending a tremendous amount of time) and I will need to move on. But before moving on, I would like to know if Iā€™m doing something wrong or if thatā€™s expected to have such a FPS drop when simply drawing some vertices :sweat_smile:

Humm try running this particle instancing test and see what kind of fps you get.
Iā€™ve got it set to 5k particles which are actually instanced quads for this i donā€™t think you can draw anything faster then with that type of draw call.
Try to set the back buffer to a lower resolution then hit f11 to go into fullscreen.
Try it with and without multisampling on.
I can get massive particles on screen even on this old laptop in the 100k + range in lower resolutions.
Or just post a screen shot back its got most of the info printing to the screen.

My old laptopat 1500 x 500 gets about 100fps with 4x multisampling
(as high as this old thing will go in fullscreen).
I can set the backbuffer to 1080p and run it i get about 32 to 60 fps as this card doesnā€™t even go up to 1920 x 1080. Pretty sure that my desktop can run this with a few hundred thousand + particles with everything on though.

It seems like we donā€™t have the same version of MonoGame. I donā€™t have access to GraphicsDevice.PresentationParameters.HardwareModeSwitch and when trying to run it, I got an Exception : Failed to create graphics device! with the StackTrace :

Ć  Microsoft.Xna.Framework.GraphicsDeviceManager.CreateDevice()
Ć  Microsoft.Xna.Framework.GraphicsDeviceManager.Microsoft.Xna.Framework.IGraphicsDeviceManager.CreateDevice()
Ć  Microsoft.Xna.Framework.Game.DoInitialize()
Ć  Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
Ć  Microsoft.Xna.Framework.Game.Run()
Ć  DxParticleTestTrimed.Program.Main() dans D:\Users\Patapits\Downloads\ParticleInstancingTestTrimedDxJan2019\ParticleInstancingTestTrimedDx\Program.cs:ligne 18

I will update my MonoGame version to see if it helps.

EDIT : MonoGame updated, itā€™s running, I will post the results when available.

EDIT 2 : Results :

  • At 1500x500, MSAA on :

  • At 1500x500, MSAA off :

  • At 1920x1080, MSAA on :

  • At 1920x1080, MSAA off :

I canā€™t get screen capture to work in Fullscreen, but here is a full summary :

  • At 1500x500 :
  • MSAA on :
    • Windowed : 167 FPS
    • Fullscreen : 192 FPS
  • MSAA off :
    • Windowed : 828 FPS
    • Fullscreen : 900 FPS
  • At 1920x1080 :
  • MSAA on :
    • Windowed : 66 FPS
    • Fullscreen : 25 FPS (!)
  • MSAA off :
    • Windowed : 250 FPS
    • Fullscreen : 880 FPS (!)

I donā€™t get some results (the ! ones), but maybe thatā€™s due to Windows doing some things behind my backā€¦
Anyway, thatā€™s an interesting result, because I can get 250 FPS at 1920x1080 with that much particles, so I guess 144 FPS for my game is definitely achievable. I will do some more tests with my squares example to see if I can improve it.
Thanks @willmotil !

1 Like

Yep try this.

Add this to that test in update
Replace the code block were you go into full screen with the below code.
Increase the number of particles by a lot i did it to 50 k.
run it.

            if (Keyboard.GetState().IsKeyDown(Keys.F11))
            {
                //https://en.wikipedia.org/wiki/List_of_common_resolutions THESE ARE VALID ASPECT RATIOS  16/9
                // Full HD (FHD)     1080 HDTV (1080i, 1080p)     1920     Ɨ     1080     16āˆ¶9     16āˆ¶9     1āˆ¶1     2,073,600
                // HD+     900p     1600     Ɨ     900     16āˆ¶9     16āˆ¶9     1āˆ¶1     1,440,000 
                // Wide XGA (WXGA-H)     Minimum, 720p HDTV     1280     Ɨ     720     16āˆ¶9     16āˆ¶9     1āˆ¶1     921,600
                //ā€”     Apple iPhone 5, iPhone 5S, iPhone 5C, iPhone SE     1136     Ɨ     640     71āˆ¶40     16āˆ¶9     1.001     727,040 
                //Wide SVGA(WSVGA)       1024    Ɨ     600     128āˆ¶75  16āˆ¶9    1.041   614,400
                //PAL 16:9     1024     Ɨ     576     16āˆ¶9     16āˆ¶9     1āˆ¶1     589,824 
                graphics.IsFullScreen = true;
                graphics.PreferredBackBufferWidth = 1280;
                graphics.PreferredBackBufferHeight = 720;
                graphics.ApplyChanges();
            }

Its the backbuffer size which is the determining factor not so much the number of draws.
Doing the above will make the hardware screen scaler scale it up causing edges but then thatā€™s what the msaa is for.

One note though at that resolution you are probably better off just using high detail textures and far less aliasing. And a lot of people wont be able to run it in the opposite case unless you offer a minimum settings option that upscales.

I tried that in Fullscreen :

  • MSAA off : 336 FPS
  • MSAA on : 35 FPS

What all these numbers are telling me is that :

  • Itā€™s better to use ā€œrealā€ Fullscreen (IsFullscreen = true) than ā€œfakeā€ Fullscreen (borderless window at screen resolution), except in some weird cases with MSAA on
  • With the kind of graphics my game will be composed of, MSAA seems like too demanding, Iā€™ll try to see if I can make some less GPU-expensive AA alternatives working as suggested by some people above

Out of curiosity, I tried running my game (1 BasicEffect per shape, with about 200 different shapes) at 1980x1080 :

  • Without MSAA and ā€œfakeā€ fullscreen : 150 FPS
  • Without MSAA and ā€œtrueā€ fullscreen, : 250 FPS
  • With MSAA on and ā€œfakeā€ fullscreen : 50 FPS
  • With MSAA on and ā€œtrueā€ fullscreen : 20 FPS

Well you have a instancing example thats about the fastest way to draw anything. Remember that each of those 5000 dots are actually quads. i.e. rectangles.

On my rx vega i run that with msaa off with a million particles drawn well over 60fps.

The human eye doesnā€™t really detect much past 60fps some fighter piliots tested being able to detect slight changes at 180 fps but thats pushing the limit of extreme.

Iā€™ve looked into hardware instancing when facing my initial performance problem, but your example is top notch, thanks for that ! I will probably not need it for now, but it will be a useful ressource for me at one point or another :smile:

IIRC, human eyes donā€™t analyze and process static frames, but rather movement. And being used to 60 Hz display, now that I have a 120 Hz one, I can easily tell that there is a big difference in smoothness. I donā€™t know how I get this feeling, but itā€™s blatant (and the same happened when I switched from 25/30 Hz to 60 Hz).

I tried an implementation of FXAA (with the help of DigitalRune Engine source code), and the result is impressively fast (no changes in FPS with or without it), but the rendering might need some tweaks.

Without FXAA :

With FXAA :


EDIT : OK, actually these numbers are valid only for my NVIDIA GeForce GTX 1050ā€¦
I also tried a SMAA (still extracted from the DigitalRune source code), full results for my Intel Graphics HD 630 below (at 960x540) :

  • Without any filter :

  • With FXAA 1x :

  • With FXAA 4x :

  • With SMAA 1x :

  • With SMAA 4x :

  • With ā€œnativeā€ MSAA :

So FXAA definitely seems to be the only viable solution, but itā€™s still looking a bit ā€œsharpyā€ (especially when compared with MSAA which gives the best result IMO).

EDIT 2 : At fullscreen (1080p), none of these techniques gives a satisfactory result, Iā€™m around 100 FPS or lower. Back to R&D :sob:

1 Like

I might have found a way to achieve what I want without any performance drop.
I created a thin outline around my squares, with the outer vertices having a transparent color, the inner vertices being Color.Red. So pixels inbetween square edge and outline will be interpolated, attenuating the sharpness of the square.
Some screenshots :

  • Without outline :

  • With outline :

But Iā€™m not fully convinced it will be a good solution.

  • PROS
  • Minimal FPS impact (maybe when drawing millions of polygons it can have an impact, but my game will not be composed of more than 10000 polygons at once)
  • Itā€™s looking great ! (can be compared to MSAA in my opinion)
  • CONS
  • Outlines need to be computed (probably with inward/outward polygon offseting algorithm, already found a C# implementation with Clipper), and it has a pretty high performance cost, so it can not be done each frame => every shape needs to be pre-computed
  • If I want to scale some shape, outline needs to be recomputed many times to match the scaling progress (otherwise itā€™ll be either blurry - too thick outline - or sharpy - too thin outline)
  • Outlines thickness needs to be picked wisely, and will depend on screen current resolution (the ratio between game world unit and screen pixel unit change at different resolution) => since the player will be able to resize the game window as he wants, all outlines computation will be to be refreshed at each screen size change, so it can have an minor impact

Does that seems like a good solution ? Isnā€™t that too ā€œconvolutedā€ ? Isnā€™t there any other approachs to achieve something similar (softening polygon edges by changing the color alpha value) at a low performance cost ? I will happily take any thought on this :slight_smile:

Oh, and @Jjagg, did you manage to set up an example of an efficient way to draw these squares ? I absolutely want to improve my game development skills but canā€™t find what mistakes Iā€™ve done :worried:

Oops, forgot to post back. I messed up, the code you posted is what I meant.

Have you tried using SpriteBatch? Iā€™m curious how it compares performance-wise.

Haha ok that makes sense to me :wink:
Iā€™m using SpriteEffect now, Iā€™m at 250 fps for drawing 100 squares at 1080p on fake fullscreen, instead of 150 fps with BasicEffect, so a bit better (but Iā€™ve not run them one in front of the other, so this measure might not be ā€œscientificallyā€ valid :sweat_smile:). Iā€™ll try to use SpriteEffect in my game to see if I can get a significant FPS improvement.