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);
}
});
}
}
}
User contributions licensed under CC BY-SA 3.0