Oops, I misread. Wow, thatās really bad. Must be either the hardware is really that old or drivers suck.
It will. FXAA basically checks for edges based on depth and then blurs them a little.
Oops, I misread. Wow, thatās really bad. Must be either the hardware is really that old or drivers suck.
It will. FXAA basically checks for edges based on depth and then blurs them a little.
Nope, I mismatched my numbers, sorry
So :
Is it normal to have āonlyā 200 FPS at 1080p without MSAA when drawing 100 squares on low-end graphic card (here a Intel UHD Graphics 630) ?
Or it could be me mismatching my numbers, sorry
Okay, I actually didnāt take a look at your code before⦠The way you draw the squares is very inefficient. You should not have separate graphics objects for them.
Preferably youād have only 1 effect, 1 vertex buffer and index buffer so you can draw all your squares in one call to DrawIndexedPrimitives. You can do that by regenerating the vertices every frame for the right position. In fact thatās exactly what SpriteBatch does, so you may want to use just that. Performance will be tons better. If you write your own solution with a custom shader you can do even better, but really I think SpriteBatch will be good enough for this.
Haha, in fact, this is what I was doing before (sending all vertices and indexes in one buffer), but I had some performance issues, and after some research I assumed it was because I was recomputing all vertices every frame, instead of sending them once and using Effect.World on each shape I will try your suggestion and see if it improves my game FPS.
Thanks once again for your time answering my questions
So I tried what seems like an implementation of what you suggested @Jjagg, and I get only around 150FPS
Full working example :
using System;
using System.Collections.Generic;
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(GraphicsDevice device, int screenWidth, int screenHeight)
{
Generate(device, screenWidth, screenHeight);
}
public void MoveTo(Vector2 position)
{
_position = position;
}
public void Generate(GraphicsDevice device, int screenWidth, int screenHeight)
{
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] = 0;
_indexes[4] = 2;
_indexes[5] = 3;
}
}
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 VertexPositionColor[] GetVertices()
{
Matrix worldMatrix = GetWorldMatrix();
return _vertices.Select(v => new VertexPositionColor(Vector3.Transform(v, worldMatrix), Color.Red))
.ToArray();
}
public short[] GetIndexes()
{
return _indexes;
}
public void Draw(GraphicsDevice device)
{
}
}
public class Game1 : Game
{
GraphicsDeviceManager _graphics;
private Square[] _squares;
private BasicEffect _basicEffect;
private VertexBuffer _vertexBuffer;
private IndexBuffer _indexBuffer;
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()
{
//_graphics.GraphicsProfile = GraphicsProfile.HiDef;
//_graphics.PreferMultiSampling = true;
//_graphics.ApplyChanges();
_basicEffect = new BasicEffect(GraphicsDevice);
_basicEffect.World = Matrix.Identity;
_basicEffect.View = Matrix.CreateLookAt(new Vector3(0, 0, 1), Vector3.Zero, Vector3.Up);
_basicEffect.Projection = Matrix.CreateOrthographic(1920, 1080, 0, 1);
_basicEffect.VertexColorEnabled = true;
_vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor),
short.MaxValue, BufferUsage.WriteOnly);
_indexBuffer = new IndexBuffer(GraphicsDevice, typeof(short), short.MaxValue, BufferUsage.WriteOnly);
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(GraphicsDevice, _graphics.PreferredBackBufferWidth,
_graphics.PreferredBackBufferHeight);
_squares[i].MoveTo(
new Vector2(
rand.Next(-_graphics.PreferredBackBufferWidth / 2, _graphics.PreferredBackBufferWidth / 2),
rand.Next(-_graphics.PreferredBackBufferHeight / 2, _graphics.PreferredBackBufferHeight / 2)));
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.White);
VertexPositionColor[] vertices = new VertexPositionColor[short.MaxValue];
short[] indexes = new short[short.MaxValue];
int vertexIndex = 0;
int indexIndex = 0;
short lastVertexCount = 0;
for (int i = 0; i < _squares.Length; i++)
{
short indexOffset = lastVertexCount;
foreach (var vertex in _squares[i].GetVertices())
{
vertices[vertexIndex++] = vertex;
lastVertexCount++;
}
foreach (short index in _squares[i].GetIndexes())
{
indexes[indexIndex++] = (short) (index + indexOffset);
}
}
_vertexBuffer.SetData(vertices);
_indexBuffer.SetData(indexes);
GraphicsDevice.BlendState = BlendState.AlphaBlend;
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.SetVertexBuffer(_vertexBuffer);
GraphicsDevice.Indices = _indexBuffer;
foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _indexBuffer.IndexCount / 3);
}
base.Draw(gameTime);
}
}
}
Screenshot of a profiling :
Am I missing something ? It seems like the SwapChain.Present is eating most of the consumed time, but maybe I missed some optimizations ?
Thatās still fairly different from what I meant. Iāll try and set up an example tonight if I manage
Btw, do you need basiceffect? Or would SpriteEffect do the trick (i.e. do you need 3D lighting)?
Well, I donāt get what mistakes Iāve made, so that would be very helpfull
No, I donāt necessarily need lighting (for now ?), I will try using SpriteEffect to see if itās better.
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 ), 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
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 :
GraphicsDevice.Clear(Color.CornflowerBlue);
: ~3000 FPSGraphicsDevice.Clear(Color.CornflowerBlue);
: ~300FPSGraphicsDevice.Clear(Color.CornflowerBlue);
: ~400FPSOn 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
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.
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 ).
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
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 :
I canāt get screen capture to work in Fullscreen, but here is a full summary :
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 !