Help optimizing function (Billinear Resize, uscript)

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