- Home /
For different texture sizes, which is faster? SetPixel or SetPixels?
I happen to generate quite a few textures (of fairly small size; less than < 300 most of the time) and was just wondering if there was some significant difference in space or cost between using SetPixel and SetPixels?
int size = 300;
// No mip-maps because this is a 1-to-1 for the GUI usually
Texture2D texture = new Texture2D(size, size, TextureFormat.ARGB32, false);
for(int a = 0; a < size; a++) {
for(int b = 0; b < size; b++) {
texture.SetPixel(a, b, Color.white);
}
}
// Down here I would actually change the texture (above makes a "blank" canvas)
And for SetPixels, it looks like:
int size = 300;
Texture2D texture = new Texture2D(size, size, TextureFormat.ARGB32, false);
Color[] colors = new Color[size * size];
for(int a = 0; a < Mathf.Pow(size, 2); a++) {
texture.SetPixels(colors);
}
// Do stuff to change texture
So which is faster? Is there any significant difference between them, and when does it happen (in 1000x1000 texture sizes)?
EDIT: Tried benchmarking this and I found that SetPixel is actually slower than SetPixels even at 1x1 texture sizes. SetPixels32 is faster than SetPixels for any texture larger than 2x2.
SetPixel, size: 1, time: 0.003963776
SetPixels, size: 1, time: 0.0005910769
SetPixels32, size: 1, time: 0.0005298704
SetPixel, size: 100, time: 0.003605723
SetPixels, size: 100, time: 0.002427459
SetPixels32, size: 100, time: 0.001961112
SetPixel, size: 1000, time: 0.05630431
SetPixels, size: 1000, time: 0.2315033
SetPixels32, size: 1000, time: 0.1754332
EDIT 2: Wolfram actually benchmarked the functions and s/he is obviously better at benchmarking than me (benchmark noobs, unite!), so I'll post those benchmarks in here...
4096x4096 512x512 64x64 8x8 1x1
SetPixel 21.1s 20.0s 20.6s 19.9s 20.4s
SetPixels+fillArray 7.7s 7.5s 7.4s 7.9s 32.9s
SetPixels 5.9s 5.3s 5.3s 5.7s 29.2s
SetPixels32 1.8s 0.59s 0.62s 1.2s 34.1s
SetPixel:SetPixels x3.6 x3.8 x3.9 x3.5 x0.7
SetPixel:SetPixels32 x11.5 x33 x33 x16.3 x0.6
SetPixels:SetPixels32 x3.22 x9.0 x8.5 x4.7 x0.9
I think your benchmarks are flawed. You have to loop a significant number of times to get results that mean anything (like, at least a million times, so that the time is greater than .1 second), plus you should run the tests multiple times and average the results. When setting a single pixel, SetPixel is faster than SetPixels, which makes sense because SetPixels has some overhead for dealing with arrays.
Also, note that your current example for SetPixels() is incorrect and does way too much work. You'd need to change your code to this:
for(int a = 0; a < size*size; a++) {
colors[a]=Color.white;
}
texture.SetPixels(colors);
In addition, try to avoid $$anonymous$$athf.Pow(), it is unbelievably expensive.
@Eric5h5: I realize the benchmarks are flawed since I didn't really want to blow time attempting to let my graphics card do a million Texture2D.Apply()'s (it's in pretty bad shape already). It gives a consistently faster time for SetPixels, especially on higher sizes though, so I think it's good enough for a general feel.
@Wolfram: That's actually how it looks now. I removed the $$anonymous$$athf.Pow() to have it not affect the times. I put texture.Apply() into each benchmark though, so I think that's eating most of the time.
Overall, I'm not a benchmarking person, and this is my first time actually trying. I now know that I suck at it.
Well, you don't want to put Apply() in there, that's not the issue here...we already know Apply() is slow. ;)
See the last comment in my answer you accepted, I added a table with some benchmarking results.
Answer by Wolfram · Jun 13, 2012 at 12:23 AM
Your second code example is wrong, you are assigning 90000 times the same pixel array to your texture, which doesn't make sense.
To answer your question, SetPixel() will be significantly slower, possibly by an order of magnitude or more. You are setting each of the single pixels individually, 90000 times in total, calling a function that needs to access the texture data, compute the exact location of that pixel, and replace it, plus there is the loop overhead of 300x executing the inner loop.
On the other hand, SetPixels() is just one call, assinging the whole array, which can just memcopied into the texture data.
EDIT:
If your question is about how to best initialize the array, a compromize might be feasible: With your first approach you'll need to execute a statement 90000x in two nested loops. With your second approach you'll need to execute a statement 90000x (=filling the color array with its initial value (note, in your current code, the color will be black, since it is not initialized)).
Instead, the fastest method (*) will be to create a Color array of size 300, initialize that, and then call SetPixels 300 times, setting one row at a time:
for(int a = 0; a < size; a++)
texture.SetPixels(0,a,size,1,colors);
(*) EDIT: After doing some benchmarks, you don't gain anything by splitting the initialization into a "one row at a time" loop. So simply create a Color32[size*size] array, initialize it, and use a single call to texture.SetPixels32(). This will be fastest for most texture resolutions, as @Eric5h5 explained.
SetPixel actually isn't that much slower than SetPixels, maybe 3X for the same number of pixels. A lot of the overhead is converting Color, and the texture isn't actually changed until you use Apply(). It's better to use SetPixels32 than SetPixels, not only because SetPixels32 is around 5X faster then SetPixels, but because it uses 4X less RA$$anonymous$$ for the array. That makes SetPixels32 about 15X faster than SetPixel for the same number of pixels; there isn't a SetPixel32.
However, in his question he is filling the texture with an initial color, which would mean 90000 calls to SetPixel() vs. 1 call to SetPixels() (plus filling the Color array, of course).
Also, the difference in speed will highly depend on the number of pixels in the block accessed simultaneously by SetPixels(). Unless by "3X slower" you mean the actual execution time of the same number of calls to SetPixel() vs. SetPixels()?
So in general, SetPixels is faster, but is that still true for small numbers (300 was an example)? I assume that SetPixels doesn't internally call SetPixel. Also, sorry about the code quality, it was just a quick example and I was tired (/excuse). ...Also never used SetPixels because I assumed it wouldn't have any significant difference for the texture sizes I was making.
Well, it should be simple enough to setup a little benchmark for that. Use the two variants of the code, and maybe my per-row example as another variant, and maybe also a SetPixels32() variant, and wrap each into something like this:
float startTime=Time.realtimeSinceStartup;
for(int benchmarkLoop=0;benchmarkLoop<maxLoopAmount;benchmarkLoop){
// place the code you want to benchmark here
}
Debug.Log("Execution took "+(Time.realtimeSinceStartup-startTime)+" seconds.");
Then you can experiment with "size" and so on.
@Wolfram: yes, if you're filling, say, a 512X512 texture with SetPixel, using SetPixels would be 3X faster. Regardless of 90000 calls vs 1 call, all of those pixels still have to be converted from Color to the "real" byte values. But SetPixels32 would be 15X faster, primarily because that conversion no longer has to be done.
@$$anonymous$$izuho: see my answer for when you'd use SetPixel. If you are filling an entire texture (or a significant rectangular portion of one), use SetPixels. If you're drawing some arbitrary pixels, use SetPixel. It's not really anything to do with the size of the texture.
Answer by Eric5h5 · Jun 13, 2012 at 12:28 AM
The times when it makes sense to use SetPixel instead of SetPixels is if you're only changing a relatively few individual pixels in different places, and you don't want the overhead of getting an array, making a few changes in the array, then copying the array back.
I was kinda like "What?" when I first read this answer because of the GetPixel
ins$$anonymous$$d of SetPixel
. It makes sense though, but the other answer has all the numbers for speed differences.