Time.realtimeSinceStartup for debugging use.
512x512 Resize Time : 0.38~ 8 Channels : 8.6 seconds
1024x1024 Resize Time : 1.5~ 8 Channels : 36 seconds (I want to use 1024, but taking this long nulls the choice out)
I am generating Perlin Noise (great for terrain) at game init. Trying to optimize startup time I checked what functions were taking longest : with my resize function being the absolute biggest by a long shot. It’s called many times per each channel as well.
So my question is : What can I do to make this faster?
public static function ResizeRGBA(tex : Texture2D , width : int , height : int ) : Texture2D{
//BILLINEAR RESIZING.
var resizedImage : Texture2D = Texture2D(width, height, TextureFormat.ARGB32 , false);
var bTemp : Texture2D = Instantiate(tex, Vector3.zero, Quaternion.identity);
var fraction_x : float;
var fraction_y : float;
var one_minus_x : float;
var one_minus_y : float;
//----
var ceil_x : int ;
var ceil_y : int;
var floor_x : int;
var floor_y : int;
//----
var c1 = Color();
var c2 = Color();
var c3 = Color();
var c4 = Color();
var col = Color();
var f : float;
//-----
var b1 : float;
var b2 : float;
var tempw : float = bTemp.width * 1.0;
var temph : float = width * 1.0;
var nXFactor : float = tempw / temph ;
tempw = bTemp.height * 1.0;
temph = height * 1.0;
var nYFactor : float = tempw / temph;
for (x = 0 ; x <= width ; ++x){
for (y = 0 ; y <= height ; ++y){
floor_x = Mathf.Floor(x * nXFactor);
floor_y = Mathf.Floor(y * nYFactor);
ceil_x = floor_x + 1;
ceil_y = floor_y + 1;
//Loop all 4 channels.
fraction_x = x * nXFactor - floor_x;
fraction_y = y * nYFactor - floor_y;
one_minus_x = 1.0 - fraction_x ;
one_minus_y = 1.0 - fraction_y ;
//-----
c1 = bTemp.GetPixel(floor_x, floor_y); //neighboring pixels
c2 = bTemp.GetPixel(ceil_x, floor_y);
c3 = bTemp.GetPixel(floor_x, ceil_y);
c4 = bTemp.GetPixel(ceil_x, ceil_y);
for (i = 0; i <= 3 ; ++i){ //For each channel.
b1 = one_minus_x * c1[i] + fraction_x * c2[i];
b2 = one_minus_x * c3[i] + fraction_x * c4[i];
col[i] = one_minus_y * b1 + fraction_y * b2;
}
resizedImage.SetPixel(x,y, col);
}
}
// resizedImage.Apply();
// Destroy (bTemp);
// Destroy (tex);
return resizedImage;
}
There was a thread recently on how to use a camera to render a texture instead of using getpixel / setpixel for a giant speed increase. You might want to poke around for it.
I wrote this multithreaded bilinear scaling function a while ago. I was going to put it in the wiki but never got around to it; I think I was going to add nearest neighbor scaling as well or something. Anyway, scaling with rendertextures is way faster, but needs Pro, and also might not be 100% deterministic (I seem to recall reading something years ago about ATI and nVidia not doing bilinear scaling exactly the same way).
In any case the SetPixels/Apply lines take as long as the entire scaling routine so I’m not sure the multithreading stuff really makes much difference in practice, but hey… Not counting those SetPixels/Apply lines, it’s 3.4X faster on my quad-core machine vs. using just one thread.
The function scales the texture that’s passed in rather than returning a new texture, so to use it you’d call the script TextureScale and then do “TextureScale.Bilinear(myTexture, newWidth, newHeight);”.
–Eric
import System.Threading;
// Only works on ARGB32, RGB24 and Alpha8 textures that are marked readable
class ThreadData {
var start : int;
var end : int;
function ThreadData (s : int, e : int) {
start = s;
end = e;
}
}
private static var texColors : Color[];
private static var newColors : Color[];
private static var w : int;
private static var ratioX : float;
private static var ratioY : float;
private static var w2 : int;
private static var finishCount : int;
static function Bilinear (tex : Texture2D, newWidth : int, newHeight : int) {
texColors = tex.GetPixels();
newColors = new Color[newWidth * newHeight];
w = tex.width;
ratioX = 1.0 / (parseFloat(newWidth)/(w-1));
ratioY = 1.0 / (parseFloat(newHeight)/(tex.height-1));
w2 = newWidth;
var cores = Mathf.Min(SystemInfo.processorCount, newHeight);
var slice = newHeight/cores;
finishCount = 0;
if (cores > 1) {
for (i = 0; i < cores-1; i++) {
var threadData = new ThreadData(slice*i, slice*(i+1));
var thread = new Thread(BilinearScale);
thread.Start(threadData);
}
threadData = new ThreadData(slice*i, newHeight);
BilinearScale(threadData);
while (finishCount < cores);
}
else {
threadData = new ThreadData(0, newHeight);
BilinearScale(threadData);
}
tex.Resize(newWidth, newHeight);
tex.SetPixels(newColors);
tex.Apply();
}
private static function BilinearScale (threadData : ThreadData) {
for (y = threadData.start; y < threadData.end; y++) {
var yFloor = Mathf.Floor(y*ratioY);
var y1 = yFloor*w;
var y2 = (yFloor+1)*w;
yw = y*w2;
for (x = 0; x < w2; x++) {
var xFloor = Mathf.Floor(x*ratioX);
var xLerp = x*ratioX-xFloor;
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor+1], xLerp),
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor+1], xLerp),
y*ratioY-yFloor);
}
}
finishCount++;
}
private static function ColorLerpUnclamped (c1 : Color, c2 : Color, value : float) : Color {
return new Color (c1.r + (c2.r - c1.r)*value,
c1.g + (c2.g - c1.g)*value,
c1.b + (c2.b - c1.b)*value,
c1.a + (c2.a - c1.a)*value);
}
C# version of above script plz? @Eric5h5