TLDR: Get DirectBitmap from https://stackoverflow.com/a/34801225 and add
public void SetPixel(int x, int y, int argb)
{
int index = x + (y * Width);
Bits[index] = argb;
}
then change GetStaticNoise to
readonly static int white = Color.White.ToArgb();
readonly static int black = Color.Black.ToArgb();
readonly static Random NoiseRnd = new Random();
private static DirectBitmap GetStaticNoise(int width, int height)
{
var bitmap = new DirectBitmap(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
var rnd = NoiseRnd.Next(0, 2);
bitmap.SetPixel(x, y, rnd % 2 == 0 ? white : black);
}
}
return bitmap;
}
Long story: I have done stupid things in the past and know how to optimize your code
To start with, there is no need to guess when you can just ask code profiler to tell why your code is slow :)
Roughly 96% of CPU time is spent in GetStaticNoise
in which major culprit is, unsurprisingly, SetPixel
.
Before dealing with big fat pixel setting, lets take a look at other lines with surprisingly high CPU time.
- bitmap.Width and bitmap.Height does bunch of stuff under the hood.
- preset colors isn't a simple integer, but a fat object.
- Random.Shared creates Random object for each call. It is also thread-safe, which means there will be some performance cost hidden inside
- Ternary conditional operator generates different bytecode than full IF, and can be faster for conditional assignments
Few seconds of refactor later you get about +10% performance for "free".
You could optimize further by reusing Bitmap, but for that size would have to be either constant or hook into OnSizeChanged
event.
Knowing that SetPixel
is a performance hog, you can just search for "c# fast SetPixel" and just copy&paste ready to use solution from another SE post. Make sure to read through the comments on why it is not a perfect solution.
Lo and behold, performance increased tenfold just by not using SetPixel unnecessary safety logic.
But that ain't the end, let's run profiler again to get reminded that Color
object sucks for game development.
With color already being prefetched, we can preconvert it as well and suddenly code is so fast that property getter becomes performance hog, taking whole 5%!
Chopping properties away you can reach spot where Random numbers takes longer to generate than SetPixel.
But at this point we have pretty much reached limits of software rendering on windows desktop. For faster performance you'll need to use GPU.
To clarify on using Ternary Operator as performance optimization: Two-element array is twice as fast as full If statement, and Ternary Operator is ~30% faster than array. Original code used modulo 2 to check parity, but with Random already returning only 0 or 1 it can be used without modulo for small performance gain. Have in mind that (ab)using array indexes as logic (pointers) should work better in native code, without all the fancy memory management of .NET. Or it might not, depending on performance tricks done by CPU's themselves. Micro optimizations are tricky.