Advice needed about a basic software rasterizer in C# using bitmaps, threads and multiple buffers

0

As an exercise, I am starting to write some kind of software rasterizer.

I try to implement some basic algorithms, like line drawing, triangle rasterization, clipping, gouraud shading, etc...

Then, I will try to code using Unity, or Xenko.

In order to avoid using unsafe directive, I do pixel manipulations on an array, then I copy this array buffer in a PictureBox.

I want to have something smooth, without vsync (I don't think I have access to this with simple c#), and thus, based on a triple buffer, I try to use threads.

I have the main thread, which would be handled by a GUI, so it should be async.

From this thread, I manage three other threads: one for clearing a first buffer, one for drawing on a second buffer, and the last buffer for copying data to the real BMP.

I want to optimize those three steps, to avoid wait states at a maximum.

I would like your advice about this way of doing things.

Following is an implementation of what I have in mind.

Is my code correct? (i don't think so, as I'm a c# beginner).

When testing, I can see that I encounter an error at the LockBits function call, when Sleep() value is too small.

Should I avoid threading?

Is this buffering technic efficient?

Thanks for your help

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private Bitmap _bmp;

        private uint[] _buffer1;

        private uint[] _buffer2;

        private uint[] _buffer3;

        private uint[] _clearBuffer;

        private uint[] _drawBuffer;

        private uint[] _copyBuffer;

        private int _t1;

        private int _t2;

        public Form1()
        {
            InitializeComponent();

            _bmp = new Bitmap(320, 200, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            _buffer1 = new uint[320 * 200];
            _buffer2 = new uint[320 * 200];
            _buffer3 = new uint[320 * 200];
            _clearBuffer = _buffer1;
            _drawBuffer = _buffer2;
            _copyBuffer = _buffer3;
            _t1 = 0;
            _t2 = 0;
        }

        private void DoDisplay()
        {
            Task[] tasks = new Task[3];
            tasks[0] = Task.Factory.StartNew(() => ClearBuffer());
            tasks[1] = Task.Factory.StartNew(() => DrawBuffer());
            tasks[2] = Task.Factory.StartNew(() => CopyBuffer());
            Task.WaitAll(tasks);
            SwapBuffers();
        }

        private void ClearBuffer()
        {
            for (int i = 0; i < _clearBuffer.Length; i++)
                _clearBuffer[i] = 0xFF000000;
        }

        private void DrawBuffer()
        {
            int left = 160 + (int)(100.0 * Math.Sin(Math.PI * _t1 / 180.0));
            int top = 100 - (int)(75.0 * Math.Sin(Math.PI * _t2 / 180.0));
            int pos = left + (top * 320);

            _drawBuffer[pos] = 0xFFFFFFFF;
            _drawBuffer[pos + 1] = 0xFFFFFFFF;
            _drawBuffer[pos + 2] = 0xFFFFFFFF;

            _drawBuffer[pos + 320] = 0xFFFFFFFF;
            _drawBuffer[pos + 321] = 0xFFFFFFFF;
            _drawBuffer[pos + 322] = 0xFFFFFFFF;

            _drawBuffer[pos + 640] = 0xFFFFFFFF;
            _drawBuffer[pos + 641] = 0xFFFFFFFF;
            _drawBuffer[pos + 642] = 0xFFFFFFFF;

            _t1 += 3;
            _t2 += 4;
            if (_t1 > 360)
                _t1 = 0;
            if (_t2 > 360)
                _t2 = 0;
        }

        private void CopyBuffer()
        {
            System.Drawing.Imaging.BitmapData bmpData = _bmp.LockBits(
                new Rectangle(0, 0, 320, 200),
                System.Drawing.Imaging.ImageLockMode.ReadWrite,
                _bmp.PixelFormat);
            System.Runtime.InteropServices.Marshal.Copy((int[])(object)_copyBuffer, 0, bmpData.Scan0, _copyBuffer.Length);
            _bmp.UnlockBits(bmpData);

            pictureBox1.Image = _bmp;
        }

        private void SwapBuffers()
        {
            uint[] temp = _copyBuffer;
            _copyBuffer = _drawBuffer;
            _drawBuffer = _clearBuffer;
            _clearBuffer = temp;
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            await Task.Run(() =>
            {
                while (true)
                {
                    DoDisplay();
                    Thread.Sleep(10);
                }
            });
        }
    }
}
c#
multithreading
animation
bitmap
double-buffering
asked on Stack Overflow Dec 18, 2019 by adellieux • edited Dec 18, 2019 by adellieux

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0