#!/usr/bin/perl

# farm script
#
# This script drives BMRT in a way that resembles a "poor man's netrender."
#
# Usage:    farm [rib-files]
#
# Prerequisites:
#
# 1. You must have an environment variable called BMRT_FARM which contains
#    a space-separated list of machine names which can be used as a render
#    farm.  Machines with multiple processors should be listed multiple times.
#    For example, in csh, you will need something like this:
#           setenv BMRT_FARM "sneezy doc doc sleepy"
#    (to indicate to use these three machines, and doc can handle two frames
#    rendering at once)
# 2. Each machine needs to be able to find the rendrib binary in the
#    same place.
# 3. Each machine must be able to see the directory and RIB file to render,
#    and be able to access it using the same pathname.
#
#
# How it works:
#
# farm greps through your RIB files, to find the output resolution and
# image file name.  It calculates how many pieces to chop your image
# into (based on resolution and number of machines in your farm), then
# uses "rsh" to send different crop windows to different machines.
# 
# The servers run rendrib with the -of flag to override the output
# filename (so they don't write on top of each other), the -safe flag to
# ensure that no two machines work on the same, and the -maxload flag to
# ensure that machines don't get overloaded.
# 
# Each crop window is rendered to a separate image file, such as
# myimage.tif.001, myimage.tif.002, etc.  Also, as each one completes,
# the server rendrib will leave a "myimage.tif.001.done" turd lying
# around.  When 'farm' sees that all the turds are there and no parts
# are still computing, it will run the "mosaic" program to stitch them
# together and delete all the pieces.
# 
# Author:  Larry Gritz (gritzl@acm.org)
#
# $Revision: 1.1.2.1 $    $Date: 1999-03-16 10:47:38-08 $
#

use Cwd;
use POSIX;

$bmrtargs = "";

$xres = 640;
$yres = 480;

$servercmd = `which rendrib`;
chop $servercmd;

$imagefilename = "ri.tif";


for ($i = 0;  $i <= $#ARGV;  $i = $i + 1) {
    if ($ARGV[$i] eq "-renderer") {
	$i = $i + 1;
	$servercmd = $ARGV[$i];
	next;
    }
    $bmrtargs = "$bmrtargs $ARGV[$i] ";
}

# Find the resolution and display filename
@filelist = split ' ', $bmrtargs;
foreach $filename (@filelist) {
#    print "$filename\n";
    open FILE, "<$filename";
    while (<FILE>) {
        if (/^\s*#/) {   # Skip commented lines
           next;
        }
	if (/Display\s+\"([^\"]+)/) {
	    $imagefilename = $1;
	}
	if (/Format\s+(\d+)\s+(\d+)/) {
	    $xres = $1;
	    $yres = $2;
	}
	break if (/WorldBegin/);
    }
    close FILE;
}


$mydir = $ENV{"PWD"};

@machines = split(' ', $ENV{"BMRT_FARM"});
$nmach = 0;
foreach $m (@machines) {
    $loadlimit{$m} += 1;
    if ($loadlimit{$m} == 1) {
	$loadlimit{$m} -= 0.5;
    }
    $nmach++;
    print "Machine $nmach is $m\n";
} 


$splits_x = floor($xres/64);
$splits_y = floor($yres/64);
$splits = $splits_x * $splits_y;
while ($splits > $nmach) {
    if ($splits_x > $splits_y*$xres/$yres) {
	$splits_x -= 2;
    } else {
	$splits_y -= 2;
    }
    $splits = $splits_x * $splits_y;
}
if ($splits_x < 1) {
    $splits_x = 1;
}
if ($splits_y < 1) {
    $splits_y = 1;
}
print "Splitting into $splits_x x $splits_y\n";

$m = 0;
$done = 0;
$laststatus = "";
while ($done == 0) {
    $done = 1;
    $newspawns = 0;
    $status = "";
    for ($y = 0;  $y < $splits_y;  $y += 1) {
	for ($x = 0;  $x < $splits_x;  $x += 1) {
	    $splitname = sprintf ("$imagefilename.%03d", $y * $splits_x + $x);
	    $donename = $splitname . ".done";
	    if ( -e $donename) {
		$status .= "*";
		next;
	    }
	    $done = 0;
	    # This file is not yet done rendering.  Has it started?
	    if ( -e $splitname) {
		$status .= "-";
		next;
	    }
	    $status .= ".";
	    $newspawns = 1;
	    $crop = sprintf("%g %g %g %g",
			    $x / $splits_x, ($x+1) / $splits_x,
			    $y / $splits_y, ($y+1) / $splits_y);
	    $cmd = "rsh $machines[$m] \"(cd $mydir ; $servercmd -safe -silent "
			 . "-crop $crop -of $splitname "
			 . "-maxload $loadlimit{$machines[$m]} "
			 . "$bmrtargs )\" &";
	    print "$machines[$m] $crop\n";
	    system ($cmd);
	    if ($splits > 4) {
		sleep (5);   # Let's not spawn too quickly
	    }
	    $m += 1;
	    if ($m >= $nmach) {
		$m = 0;
		# If we've exhausted the machine list, wait a bit
		sleep (15);
	    }
	}
    }
    if ($status ne $laststatus) {
	print "$status\n";
	$laststatus = $status;
    }
    # We've made a pass through the segments.  Rest some
    if ($done == 0) {
	sleep (10);
    }
}
print "\n";
$cmd = "mkmosaic -o $imagefilename";
for ($y = 0;  $y < $splits_y;  $y += 1) {
    for ($x = 0;  $x < $splits_x;  $x += 1) {
	$cmd .= " $imagefilename" . sprintf(".%03d", $y * $splits_x + $x);
    }
}
print "$cmd\n";
`$cmd`;
$cmd = "/bin/rm $imagefilename.*";
`$cmd`;

