Need suggestions for speeding up drawing

In profiling my code we discovered that DisplayFoW() is responsible for 32% of the runtime processing.

This is a typical screen shot from my game displaying “Fog of War” (the gray areas are what the user can’t see directly):


DisplayFoW() is called only once from Draw():

  public void DisplayFoW()
        {
            Microsoft.Xna.Framework.Rectangle r;

            for (int x = 0; x < MAP_WIDTH; x++)
            {
                for (int y = 0; y < MAP_HEIGHT; y++)
                {
                    if (!TacticalAILib.FogOfWar.FoWMap[x, y]) // !Visible
                    {
                        r = new Microsoft.Xna.Framework.Rectangle(x + LeftMapOffset, y + TopMapOffset, 1, 1);
                        spriteBatch.Draw(tLine, r, Color.Gray * 0.4f);

                    }
                }
            }
        } // end of DisplayFoW

I can only assume it’s all those

 spriteBatch.Draw(tLine, r, Color.Gray * 0.4f);

calls that’s taking so long.

Any suggestions on speeding this up? Would adjusting the frame rate help? Or creating a bitmap mask in the background and blitting that?

Are you basically drawing a sprite per “pixel” (jugding from screenshot it looks pretty pixelwise) … ? that’s going to be indeed pretty slow and there is nothing you can do about it, except rethinking your way to create Fog of War.

As normally FoW does not change every frame - and even then - create a texture for the FoW and update this when changes happen, if it should be perpixel. plot that texture onto screen every frame.

or use bigger units then “pixel”, separating the screen/map into bigger chunks which will be either in view or not and render the fog of war as an approximation of it (spheres basically or other chunks)

If you’re applying a color to each individual pixel, then maybe it’s worth trying Texture2D.SetData instead of calling spriteBatch.Draw on each pixel. I would guess that spritebatch.Draw has some overhead that’s adding up. After setting Texture2D’s data, you’d just draw your entire texture with a single spritebatch.Draw call.

Instead of doing a pixel FOW rendering, I would do it a little bit different, I would assume you want to be able to render what is visible according to fog of war, every unit has a visibility radius so I will use a render target and render all the visible area as circles , and then use this as a mask.

So, just as an example, I see around 32 red units, assuming those are your units, I will render 32 circles in that render target, and that will be my mask to show units on the map. So 1 render target for FOW, one render target for all units, and 1 render target or back buffer directly for terrain, mix them together and that will be a lot faster than doing x * y spritebatches for the whole screen only for FOW. I am not sure if you have buildings that obstruct visibility, but if that is the case there is a different better approach.

Initially I did this similar approach to render lights visibility in my game, I had hundred of units and each with a light emitting area so the player could only see what my units illuminated, very similar to what a FOW is, and it was running in the many hundreds of FPS. But later I wanted them to cast shadows and also obstruct light, you can see an example of this in my blogpost from 8 years ago with a few units, but it scales pretty good with hundreds of units too. If you want something like that you will need to calculate visibility areas in 2d, I am not 100% sure but this engine used to do that, called Krypton , I used at the end that one for the blogpost before, there isn’t much information now since it is over 11 years old engine that works with XNA, but most of the code should work on Monogame. It actually created geometry based on source points/meshes and based on that you could calculate what is visible and not, it was quite fast even +11 years ago at +60 FPS.

I found some info the the way back machine, check this , you can check in the way back machine for updated info, there are quite many snapshots if you want to check.

I think this is the solution. How would I create a texture in memory from the FoWMap[] (which is a 2D array of bools)?

I think this is the way to go. I think I need to build a Texture2D in memory from the FoWMap[] array of bools and then just blit that to the screen once. Can you point me to any examples of using this method?

The Fog of War calculation is actually pretty fast. It’s the repeated Draw() calls that are slowing everything done. How can I transfer the FoWMap[] which is a 2D array of bools into a Texture2d in memory and just call that Texture2D once?

const int SampleWidth = 1920;
const int SampleHeight = 1080;

//  Create a Texture2D once with your map size
Texture2D FoWTexture = new(GraphicsDevice, SampleWidth, SampleHeight);
//  When your FoW bool[,] is created, convert it to a 1 dimensional Color[]
bool[,] FoW = new bool[SampleWidth, SampleHeight];
Color[] Colors = new Color[SampleWidth * SampleHeight];
Color TrueColor = Color.Gray * 0.4f;
Color FalseColor = Color.Transparent;

for (int x = 0; x < SampleWidth; x++)
{
    for (int y = 0; y < SampleHeight; y++)
    {
        Colors[x * y] = FoW[x, y] ? TrueColor : FalseColor;
    }
}

//  Set the FoW texture's data to the 1-d array of color data
FoWTexture.SetData(Colors);
//  If your FoW data has only slightly changed since the last time you updated the texture's data
//  then you should be able to use the SetData overloads to just set the values of whatever pixels have changed
//  EX: if only the topleft corner 0,0 changed, you can do
Color[] Changed = new Color[1];
Changed[0] = FoW[0, 0] ? TrueColor : FalseColor;
FoWTexture.SetData(Changed, 0, 1);

I may have gotten the indexing wrong (haven’t worked with 2d arrays in a while) but you get the idea

Another option to consider is scaling. If your map is, say, 1200x800, then instead of storing your FoW data as bool[1200, 800], you could do it as bool[600, 400]. So each boolean represents data for a 2x2 chunk of pixels. Then when you draw the FoW Texture, draw it with a 2x scale.

BINGO! THANK YOU! I had to make a couple of small changes:

Color[] Colors = new Color[Map.Width * Map.Height];
                Color TrueColor = Color.Black * 0.4f;
                Color FalseColor = Color.Transparent;

                for (int x = 0; x < Map.Width; x++)
                {
                    for (int y = 0; y < Map.Height; y++)
                    {
                        Colors[x + (y * Map.Width)] = TacticalAILib.FogOfWar.FoWMap[x, y] ? FalseColor : TrueColor;
                    }
                }

Note the increment from the 2D to a 1D array and I had to change the color to 40% Black, not Gray. But, BINGO and THANK YOU! I haven’t done any profiling but I can tell it’s a LOT FASTER!!!