• Advertisement
Sign in to follow this  

C# SFML and fast pixel drawing in C#

Recommended Posts

Hi. It's been a while since I posted here, and my last posts are almost about this exact same subject. Just saying to demonstrate how annoying this is to me.

Here is the problem : I'm trying to make a decent raycaster in C#. The main issue is that for this to happen, I need pixel by pixel drawing. My previous raycaster used VS GDI+, and trough several tricks involving pointers and filling a bitmap byte by byte, I was able to obtain half decent results, and make an online server-client style 3d engine complete with a map builder and several really cool features. I unfortunately wasn't able to expand the project further due to poorly written code (I am an hobbyist, I study Business Administration at Uni) and the fact that my quick hack for performance was barely able to carry the bare minimum of what I needed to make a very bare bone raycaster possible. This came with very real sadness, the realization that the project I spent almost 2 years on was essentially useless, bloated and impossible to expand on. 

Enough background. Now, starting anew, my main concern is to find a way to gain fast pixel by pixel control over the screen. I'm using SFML and C#. My current testbench is pretty simple, I'm using a method I found on the internet written for C++. I Adapted it for C#. I'm filling a Color[,] array (each color is a pixel) and then I copy the RGB values inside a byte[] array before moving them inside the texture buffer. I then display the texture on the screen. I'm not sure what the bottleneck is, the application is faster than my previous one, but it's still too slow for my liking. Raycasters work by redrawing stuff ontop of other stuff, and I fear that adding more stuff would creep it to an halt. I'm posting what I have as a testbench right now, any help would be greatly appreciated. Keep in mind I am not a professional programmer by any mean, I am pretty uncomfortable with pointers and true 3d stuff, but I will use them if I must. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SFML.Audio;
using SFML.Graphics;
using SFML.System;
using SFML.Window;

namespace RayCastFF
{
    class Program
    {
        public static int TestCounter = 0;
        public static Color[,] ScreenBuffer = new Color[640, 360]; //an array containing the color of all the pixel, this is intended to be the main target of all manipulation and draw call
        public static Texture MainViewPort = new Texture(640, 360);//main screen texture

        unsafe static void Main(string[] args)
        {
            //MAINWINDOW SETUP
            RenderWindow window = new RenderWindow(new VideoMode(640, 360), "RayCaster");

            while (window.IsOpen)//MAIN GAME LOOP
            {
                //CALL FOR UPDATE
                Update();

                //DRAW
                window.Clear();
                window.DispatchEvents();

                Sprite mainviewport = new Sprite(MainViewPort);
                window.Draw(mainviewport);//draw the texture over the screen

                window.Display();



                //TAKE INPUT


            }
        }
        static void Update()
        {
            TestCounter++;
            if (TestCounter > 639) { TestCounter = 0; }
            //RESET THE BUFFER (COULD BE REMOVED LATER I GUESS)
            for (int x = 0; x < 640; x++)
            {
                for (int y = 0; y < 360; y++)
                {
                    ScreenBuffer[x, y] = Color.Black;
                }
            }

            //DO STUFF
            DrawLine(Color.Red, TestCounter, 200, 100); //(for this test, i simply draw a moving line)

            //WRITING THE BUFFER INTO THE IMAGE
            //THIS SHOULD ALWAYS BE THE LAST STEP OF THE UPDATE METHOD
            byte[] pixels = new byte[640 * 360 * 4]; //640 x 360 pixels x 4 bytes per pixel
            Color[] cpixels = new Color[640 * 360];//intermediary step to keep everything clear
            for (int x = 0; x < 640; x++)
            {
                for (int y = 0; y < 360; y++)
                {
                    cpixels[x+(640*y)] = ScreenBuffer[x, y];//make an intermediary array the correct dimention and arrange the pixels in the correct position to be drawn (separate step to keep everything clean, I find this operation incredibly confusing mainly because I had no idea how the pixels are supposed to be arrenged in the first place(still kind of dont))
                }
            }
            for (int i = 0; i < 640 * 360 * 4; i += 4)//fill the byte array
            {
                
                pixels[i + 0] = cpixels[i / 4].R;
                pixels[i + 1] = cpixels[i / 4].G;
                pixels[i + 2] = cpixels[i / 4].B;
                pixels[i + 3] = cpixels[i / 4].A;
            }

            MainViewPort.Update(pixels);//update the texture with the array
        }

        //[X , Y]
        static void DrawLine(Color color, int Wpos, int Ytop, int Ybottom)//simple draw method making a vertical line
        {

            for (int y = Ybottom; y < Ytop; y++)
            {
                ScreenBuffer[Wpos, y] = color;
            }
        }
    }
}

What I'd like to end up with is a very fast way to draw the pixels on the window by the abstraction of a single 2d array of 640x360 unit that I could easily and simply manipulate. However, while being simple, it's also somewhat slow. It's also using 30% GPU load for some reason on a 1070GTX 8GB. Again, any help would be greatly appreciated.

Thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement

See those massive 'new <huge array>' operations in the middle of your Update loop?  Remove those.  Those allocations are going to be causing GC collections too frequently.  They're also going to be automatically filling the entire array with zeroes which you don't need since you immediately fill them yourself.

The part where you clear ScreenBuffer to black is eating a bunch of time.  In old programs, instead of clearing the screen and redrawing everything, we would update only the parts of the image that were changing.  Your line moves, so the old-school technique would be to 'erase' ONLY the pixels where the line was last time only, instead of setting the entire screen black first.  For mouse cursors in old apps, what I would do was save a backup copy of all of the pixels underneath where I was about to render the mouse cursor, then render the cursor, then on the next frame render the backup pixels to 'undo' rendering the mouse.

You have two full-array copy loops (ScreenBuffer -> cpixels, cpixels -> pixels), but you only need one ScreenBuffer -> pixels).  Get rid of the cpixels array as well.

In fact you should probably get rid of ScreenBuffer and only use pixels.  Just write to pixels and nothing else.  You will have to know which bytes are red/green/blue/etc but you want high performance so you've gotta remove as many "worthless" operations as possible to get it fast.

If SFML allows you to use uint[] instead of byte[], one assignment to a uint will be faster than four assignments to bytes.  This can be useful as one uint will be one pixel anyway.

C# *should* know how to optimize the constant subexpressions within inner loops such as (y*640) but you can try hand optimizing them yourself to see if it makes any difference.

Make sure you are allowing JIT to fully optimize your code.  If you're using Visual Studio this means using the Release configuration (or enabling optimization on the debug configuration) as well as {launching without the debugger attached or disabling Tools -> Options -> Debugging -> Suppress JIT optimization on module load}.

Edited by Nypyren

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By Allagia X
      An original fantasy RP game needs dedicated, self-motivated, and chill individuals! We have a story and general plot already set up, ready to be expanded upon. 

      Miasma: Twilight Decree is a 2D roleplay adventure game. It’s set in a unique fantasy world with a vast map containing continents and oceans alike. Players are given one objective: to endure the troublous environments Allagia have to offer and successfully progress through time to reach the Age of Technology. The stakes are high, and every character’s actions can alter the world – or reset everything back to the beginning ages. MTD features a blend of survival aspects, dark themes, with the ability to make a mark in the history books.

      What we're currently looking for:
       
      • Writers - Super creative individuals who have experience in lore-making, world-building, and know their way around fantasy writing. All of the general elements are here [setting, plot, etc.] and need some "fluffing out"[quest lines, clans/ factions/ families, etc.]. Bonus points to those who can whip up spells and skills.
      • Artists - Mainly those who specialize in pixelated art, or people who can make concept art [since we lack pictures]. 
      • Project Manager - Someone who is organized and can keep this project on the rails. As thorough as I am, it's difficult to cover all the bases on my own. 
      • Other Positions - Anything else to fill in the gaps. We currently use Wikidot for our wiki; someone with CSS and syntax experience to polish it up would be awesome. A musician/ composer for all things musical. Way later down the road, we'll need community managers, DMs, and the such, though it isn't necessary at the moment. 

      Other information:

      I've been working on this project since the beginning of 2017 with a group of friends. Life basically prohibited a lot of us from continuing on with it, and it went on hiatus for a while. I'm making an attempt to bring this back from the dead since plenty of time and effort went into it beforehand. It goes without saying that I also have a passion for roleplaying. 

      I cannot stress enough that anyone interested should be into fantasy settings or D&D. Otherwise, you're probably not going to have fun with helping!

      We do have a Patreon with a few supporters, and Discord. Until things really start moving, we'll be using Discord to collaborate. 

      For any questions, comments, or concerns, feel free to comment below or add me on discord @ Allagia X#9174 [best method of contact] for more info about this project.
    • By Alexander Nazarov
      Hello. I'm newby in Unity and just start learning basics of this engine. I want to create a game like StackJump (links are below). And now I wondering what features do I have to use to create such my game. Should I use Physics engine or I can move objects changing transform manually in Update().
      If I should use Physics can you in several words direct me how can I implement and what I have to use. Just general info, no need for detailed description of developing process.
      Game in PlayMarket
      Video of the game
    • By Manuel Berger
      Hello fellow devs!
      Once again I started working on an 2D adventure game and right now I'm doing the character-movement/animation. I'm not a big math guy and I was happy about my solution, but soon I realized that it's flawed.
      My player has 5 walking-animations, mirrored for the left side: up, upright, right, downright, down. With the atan2 function I get the angle between player and destination. To get an index from 0 to 4, I divide PI by 5 and see how many times it goes into the player-destination angle.

      In Pseudo-Code:
      angle = atan2(destination.x - player.x, destination.y - player.y) //swapped y and x to get mirrored angle around the y axis
      index = (int) (angle / (PI / 5));
      PlayAnimation(index); //0 = up, 1 = up_right, 2 = right, 3 = down_right, 4 = down

      Besides the fact that when angle is equal to PI it produces an index of 5, this works like a charm. Or at least I thought so at first. When I tested it, I realized that the up and down animation is playing more often than the others, which is pretty logical, since they have double the angle.

      What I'm trying to achieve is something like this, but with equal angles, so that up and down has the same range as all other directions.

      I can't get my head around it. Any suggestions? Is the whole approach doomed?

      Thank you in advance for any input!
       
    • By Terry Jin
      Hi everyone! 

      I am from an indie studio that has received funding for our concept and is ready to create the next generation 2D Pokemon-inspired MMORPG called Phantasy World. This ad is for a volunteer position but hopefully will transition into something more. Our vision is to create a game that draws inspiration from the series but is dramatically different in both aesthetics and gameplay as the work would be our own.
       
      We are hoping that you can help us make this a reality and are looking for game developers familiar with the unreal engine and would be happy to work on a 2D top down game. Sprite artists are also welcome as we are in desperate need of talented artists! Join our discord and let's have a chat! https://discord.gg/hfDxwDX

      Here's some of our in game sprites for playable characters while moving around the game world! Hope to see you soon!
       


  • Advertisement