Author Topic: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)  (Read 1163 times)

0 Members and 1 Guest are viewing this topic.

Offline mammon

  • ZX 81
  • *
  • Posts: 4
  • Karma: 1
  • Y = x (G^P)
    • View Profile
So, before you even consider just copy/paste the following code - you should know this:
this code is beyond flawed, and is slow as hell!
And with that in mind, I'm here asking for this board's help in achieving a better way to make this tunnel animated...

I have tried using both jagged arrays, and multidimension arrays (which you can clearly see) - but it still gives this "lag" feature no matter what I change :/
I suspect very much that it is due to the pixel manipulation, which seem to be too slow :(

Oh yes, I have also tried using Parallel.For - but that didn't even work at all (it just crashes due to memory being inaccesible!)...
And yes, I have also considered using every OpenGL and DirectX solution instead (and tried a few, but still slow as shit!)...

So here it is, no more nagging about me failing - hopefully someone in here can aid me in making this possible :D

Oh by the way, the code is derived from "Lode's Computer Graphics Tutorial" - found here: http://lodev.org/cgtutor/tunnel.html
Code: [Select]
//Using FastBitmap.cs by boogop which is found on PSC ;)
//http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=5653&lngWId=10

    public partial class Form1 : Form
    {
BackgroundWorker bw;
        Bitmap bmp;
        int fHeight;
        int fWidth;
        Graphics gForm;
        Graphics gBmp;

/*Multi-dimension array:*/
int[,] distanceTable;
/*Jagged array:*/
        int[][] distanceTable2;
/*Multi-dimension array:*/
        int[,] angleTable;
/*Jagged array:*/
        int[][] angleTable2;

Bitmap myTexture;

int animation = 0;
        int rotation = 0;

        public Form1()
        {
            InitializeComponent();
            InitPictureBox();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer | ControlStyles.Opaque | ControlStyles.ResizeRedraw, true);
        }

        void InitPictureBox()
        {
            bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            gForm = pictureBox1.CreateGraphics();
            gBmp = Graphics.FromImage(bmp);

            fWidth = bmp.Width;
            fHeight = bmp.Height;
        }

        void DoXorTunnel()
        {
            while (true)
            {
                animation += 3;
                rotation += 1;

                Application.DoEvents();

                var myBitmap = new Bitmap(bmp.Width, bmp.Height);
                var shiftX = myTexture.Width + animation;
                var shiftY = myTexture.Height + rotation;

                var a = new FastBitmap(myTexture);
                var b = new FastBitmap(myBitmap);
                for (int y = 0; y < bmp.Height; y++)
                {
                    Application.DoEvents();
                    for (int x = 0; x < bmp.Width; x++)
                    {
                        /*Multi-dimension array:*/
                        //b.SetPixel(x, y, (a.GetPixel((distanceTable[x, y] + shiftX) % myTexture.Width, (angleTable[x, y] + shiftY) % myTexture.Height)));

                        /*Jagged array:*/
                        b.SetPixel(x, y, (a.GetPixel((distanceTable2[x][y] + shiftX) % myTexture.Width, (angleTable2[x][y] + shiftY) % myTexture.Height)));
                    }
                }

                a.Release();
                b.Release();
                gForm.DrawImageUnscaled(myBitmap, 0, 0, fWidth, fHeight);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            myTexture = XorTexture(128, 128);

            CreateTables();

            bw = new BackgroundWorker();
            bw.DoWork += Bw_DoWork;
        }

        private void Bw_DoWork(object sender, DoWorkEventArgs e)
        {
            InitPictureBox();
            DoXorTunnel();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bw.RunWorkerAsync();
        }

        Bitmap XorTexture(int texHeight, int texWidth)
        {
            var myB = new Bitmap(texWidth, texHeight);
            var b = new FastBitmap(myB);

            for (var y = 0; y < texHeight; y++)
            {
                for (var x = 0; x < texWidth; x++)
                {
                    var c = (x ^ y) % 256;
                    b.SetPixel(x, y, Color.FromArgb(c, c, c));
                }
            }
            b.Release();
            return myB;
        }

        void CreateTables()
        {
            distanceTable = new int[bmp.Width, bmp.Height];
            distanceTable2 = new int[bmp.Width][];
            for (var i = 0; i < bmp.Width; i++)
                distanceTable2[i] = new int[bmp.Height];

            angleTable = new int[bmp.Width, bmp.Height];
            angleTable2 = new int[bmp.Width][];
            for (var i = 0; i < bmp.Width; i++)
                angleTable2[i] = new int[bmp.Height];

            for (var y = 0; y < bmp.Height; y++)
            {
                for (var x = 0; x < bmp.Width; x++)
                {
                    int angle, distance;
                    float ratio = 32.0f;
                    distance = (int)(ratio * myTexture.Height / Math.Sqrt((x - bmp.Width / 2.0) * (x - bmp.Width / 2.0) + (y - bmp.Height / 2.0) * (y - bmp.Height / 2.0))) % myTexture.Height;
                    angle = (int)(0.5 * myTexture.Width * Math.Atan2(y - bmp.Height / 2.0, x - bmp.Width / 2.0) / Math.PI);
                    distanceTable[x, y] = distance;
                    angleTable[x, y] = angle;

                    distanceTable2[x][y] = distance;
                    angleTable2[x][y] = angle;
                }
            }
        }
    }

Any suggestions to make this pixel manipulation work faster?
"Attempt the absurd, to achieve the impossible!"

Offline Rbz

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 2723
  • Karma: 485
    • View Profile
    • http://www.rbraz.com/
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #1 on: April 09, 2017 »
Hi  :hi:

I don't know much about C#, but for what I can see, you are doing it mostly correctly - pre calculating the xor texture  and tunnel table.

But, I would probably not recreating a new Bitmap and FastBitmap everytime and it could be the problem you are having.

So remove those from the while loop, init them on Form1_Load, and see what happens:
Code: [Select]
var myBitmap = new Bitmap(bmp.Width, bmp.Height);
var a = new FastBitmap(myTexture);
var b = new FastBitmap(myBitmap);
Challenge Trophies Won:

Offline mammon

  • ZX 81
  • *
  • Posts: 4
  • Karma: 1
  • Y = x (G^P)
    • View Profile
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #2 on: April 10, 2017 »
Heya, and thanks for the tip - however, its no use in here... I've made a small modification on the FastBitmap class to actually able one todo such operation  however, to no gain at all :(
Seems GDI+ (your CPU), is not able to work with anything bigger than something tiny in the end... Pherhaps SFML or SDL is the working way to go in the end...
"Attempt the absurd, to achieve the impossible!"

Offline boogop

  • C= 64
  • **
  • Posts: 68
  • Karma: 42
    • View Profile
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #3 on: April 11, 2017 »
Glad to see some c#! All my contest entries so far have been in c#.

Two items jump out at me
1. Multi-dimension arrays. The compiler has to do bounds checks on those and it will tool your performance.
2. Getpixel. Even with fastbitmap these are bad. They make flame effects problematic in this language.

I can't tell what size your picturebox is but using gdi 640x480 is going to be about the limit. I find 350x350 or thereabouts works pretty good but above that starts really slowing down.

The game in c# is all about optimization. When I've done contest entries I sh1t you not the last couple months are spent optimizing.
Challenge Trophies Won:

Offline boogop

  • C= 64
  • **
  • Posts: 68
  • Karma: 42
    • View Profile
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #4 on: April 11, 2017 »
Note: much discussion on bounds checking exists and unless you read the IL it's not easy to tell what the compiler is doing.
Challenge Trophies Won:

Offline mammon

  • ZX 81
  • *
  • Posts: 4
  • Karma: 1
  • Y = x (G^P)
    • View Profile
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #5 on: April 11, 2017 »
True that - I've already had my fair share of optimizing the code to the point of my brain breaking down... And as of IL, well - my nickname isn't chosen by random (my nickname is known a lil' bit in the scene so to speak), I have reversed IL binaries for over a decade now :P

But truth be told, IL is not very efficient when it comes to actual optimization due to the limitations of running ANY code behind some obscured VM :(

That being said, I have now come to a conclusion: only way to make this work at all, is by trying to utilize SDL or something equal... Surely OpenTk/Tao would be a suggestion, but thats even more barebone in comparison to SDL (requires alot of work to make a simple "engine" so to speak) - so I'm gonna attempt to make something with SDL afaik :)

Anyways, I'll keep you guys posted with updates when I make some progress ;)
"Attempt the absurd, to achieve the impossible!"

Offline boogop

  • C= 64
  • **
  • Posts: 68
  • Karma: 42
    • View Profile
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #6 on: April 11, 2017 »
Got a little more speed out of it. I love c# but eventually we run into the limitations of the language    :telloff:

Code: [Select]
        FastBitmap a = new FastBitmap(myTexture);
        FastBitmap b = new FastBitmap(myBitmap);
        for (int x = 0; x < bmp.Width; x++)
        {
          fixed (int* pDistance = &distanceTable[x, 0])
          {
            fixed (int* pAngle = &angleTable[x, 0])
            {
              for (int y = 0; y < bmp.Height; y++)
              {
                int xp = *(pDistance + y);
                int xx = (xp + shiftX) % 128;
                int ap = *(pAngle + y);
                int yy = (ap + shiftY) % 128;
                Color c = a.GetPixel(xx, yy);

                b.SetPixel(x, y, c);
              }
            }
          }
        }
Challenge Trophies Won:

Offline hellfire

  • Sponsor
  • Pentium
  • *******
  • Posts: 1289
  • Karma: 466
    • View Profile
    • my stuff
Re: [C#/GDI+] Xor Texture Tunnel (aka Fake 3D)
« Reply #7 on: May 10, 2017 »
Maybe a bit late but what the heck...

Code: [Select]
b.SetPixel(x, y, (a.GetPixel((distanceTable2[x][y] + shiftX) % myTexture.Width, (angleTable2[x][y] + shiftY) % myTexture.Height)));In order of relevance:
Modulo (%) is a division and expensive. You can use bit-masking if your texture-size is a power-of-2 (e.g. use "& 255" if your texture is 256x256 pixels wide).
Try not to call SetPixel for every pixel - if you fill scanlines from left to right, the next pixel is always one dword further.
Try not to call GetPixel for every pixel - with 256x256 textures your texel is at [( v<<8 )+u]
"[ x ][ y ] " reads your memory from top to bottom which makes bad use of the cache.

About the cache:
For every pixel you read, the cache fetches 64 consecutive bytes (in the hope that you want to access the following bytes, too).
But instead you're reading another pixel in the next row (x+1) and the cache reads another 64 bytes.
So when you reach the bottom of your table, there are height*64 bytes in the cache.
When start to read from the next column (y+1), you would fetch data was already read into the cache - but is it still there?
The L1 Cache is 32kB so the height of your table must be smaller than 512 before the cache is full.
But you actually want to keep the cache for your texture reads (which happen in a much more random order).
And since other tasks are also using the cache you can't expect to get much more than half of it.
Of course when L1 fails, there's still L2 and L3 and you'll probably see significant diffiferences when you consume more than a megabyte of cache space.
« Last Edit: May 11, 2017 by hellfire »
Challenge Trophies Won: