/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/


/*
 * NAME
 *	workpool.c
 *
 * DESCRIPTION
 *	This file contains the private data definitions and code for the ray
 *	job work pool.	Each processor has its own workpool.
 *
 *	The workpool consists of a stack of pixel bundles.  Each bundle
 *	contains jobs for primary rays for a contiguous 2D pixel screen
 *	region.
 *
 */


#include <stdio.h>
#include <math.h>
#include "rt.h"



/*
 * NAME
 *	PutJob - put another job into pid's work pool
 *
 * SYNOPSIS
 *	VOID	PutJob(xs, ys, xe, ye, xbe, ybe, pid)
 *	INT	xs,  ys;		// Start of block.
 *	INT	xe,  ye;		// Extent of block.
 *	INT	xbe, ybe;		// Extent of bundle.
 *	INT	pid;			// Process id.
 *
 *  DESCRIPTION
 *	Given a block of image screen pixels, this routine generates pixel
 *	bundle entries that are inserted into pid's work pool stack.
 *
 *	A block includes the starting pixel address, the block size in x and y
 *	dimensions and the bundle size for making pixel jobs.
 *
 *	Pixel addresses are 0 relative.
 *
 *	Locking of workpools for job insertion is not required since a given
 *	process only inserts into its own pool and since we use a barrier
 *	before jobs can be removed from workpools.
 *
 * RETURNS
 *	Nothing.
 */

VOID	PutJob(xs, ys, xe, ye, xbe, ybe, pid)
INT	xs,  ys;
INT	xe,  ye;
INT	xbe, ybe;
INT	pid;
	{
	INT	i, j;
	INT	xb_addr, yb_addr;		/* Bundle pixel address.     */
	INT	xb_end,  yb_end;		/* End bundle pixels.	     */
	INT	xb_size, yb_size;		/* Bundle size. 	     */
	WPJOB	*wpentry;			/* New work pool entry.      */

	/* Starting block pixel address (upper left pixel). */

	xb_addr = xs;
	yb_addr = ys;

	/* Ending block pixel address (lower right pixel). */

	xb_end = xb_addr + xe - 1;
	yb_end = yb_addr + ye - 1;

	for (i = 0; i < ye; i += ybe)
		{
		for (j = 0; j < xe; j += xbe)
			{
			/* Determine bundle size. */

			if ((xb_addr + xbe - 1) <= xb_end)
				xb_size = xbe;
			else
				xb_size = xb_end - xb_addr + 1;

			if ((yb_addr + ybe - 1) <= yb_end)
				yb_size = ybe;
			else
				yb_size = yb_end - yb_addr + 1;


			/* Initialize work pool entry. */

			wpentry = GlobalMalloc(sizeof(WPJOB), "workpool.c");

			wpentry->xpix = xb_addr;
			wpentry->ypix = yb_addr;
			wpentry->xdim = xb_size;
			wpentry->ydim = yb_size;


			/* Add to top of work pool stack. */

			if (!gm->workpool[pid][0])
				wpentry->next = NULL;
			else
				wpentry->next = gm->workpool[pid][0];

			gm->workpool[pid][0] = wpentry;
			xb_addr += xbe;
			}

		xb_addr  = xs;
		yb_addr += ybe;
		}
	}



/*
 * NAME
 *	GetJob - get next job from pid's work pool
 *
 * SYNOPSIS
 *	INT	GetJob(job, pid)
 *	RAYJOB	*job;			// Ray job description.
 *	INT	pid;			// Process id.
 *
 * DESCRIPTION
 *	Return a primary ray job bundle from the top of the stack.  A ray job
 *	bundle consists of the starting primary ray pixel address and the size
 *	of the pixel bundle.
 *
 * RETURNS
 *	Work pool status code.
 */

INT	GetJob(job, pid)
RAYJOB	*job;
INT	pid;
	{
	WPJOB	*wpentry;			/* Work pool entry.	     */

	ALOCK(gm->wplock, pid)
	wpentry = gm->workpool[pid][0];

	if (!wpentry)
		{
		gm->wpstat[pid][0] = WPS_EMPTY;
		AULOCK(gm->wplock, pid)
		return (WPS_EMPTY);
		}

	gm->workpool[pid][0] = wpentry->next;
	AULOCK(gm->wplock, pid)

	/* Set up ray job information. */

	job->x	   = wpentry->xpix;
	job->y	   = wpentry->ypix;
	job->xcurr = wpentry->xpix;
	job->ycurr = wpentry->ypix;
	job->xlen  = wpentry->xdim;
	job->ylen  = wpentry->ydim;

	GlobalFree(wpentry);
	return (WPS_VALID);
	}



/*
 * NAME
 *	GetJobs - get next job for pid (with job stealing)
 *
 * SYNOPSIS
 *	INT	GetJobs(job, pid)
 *	RAYJOB	*job;			// Ray job pointer from work pool.
 *	INT	pid;			// Process id.
 *
 * DESCRIPTION
 *
 * RETURNS
 *	Workpool status.
 */

INT	GetJobs(job, pid)
RAYJOB	*job;
INT	pid;
	{
	INT	i;

	i = pid;

	/* First, try to get job from pid's own pool (or pool 0). */

	if (gm->wpstat[i][0] == WPS_VALID)
		 if (GetJob(job, i) == WPS_VALID)
			{
			return (WPS_VALID);
			}


	/*
	 *	If that failed, try to get job from another pid's work
	 *	pool.
	 */

		i = (pid + 1) % gm->nprocs;

		while (i != pid)
			{
			if (gm->wpstat[i][0] == WPS_VALID)
				if (GetJob(job, i) == WPS_VALID)
					{
					return (WPS_VALID);
					}

			i = (i + 1) % gm->nprocs;
			}

	return (WPS_EMPTY);
	}



/*
 * NAME
 *	PrintWorkPool - print out the work pool entries for a given process
 *
 * SYNOPSIS
 *	VOID	PrintWorkPool(pid)
 *	INT	pid;			// Process id.
 *
 * RETURNS
 *	Nothing.
 */

VOID	PrintWorkPool(pid)
INT	pid;
	{
	WPJOB	*j;

	j = gm->workpool[pid][0];

	while (j)
		{
		printf("Workpool entry:    pid=%3ld    xs=%3ld    ys=%3ld    xe=%3ld    ye=%3ld\n", pid, j->xpix, j->ypix, j->xdim, j->ydim);
		j = j->next;
		}
	}



/*
 * NAME
 *	InitWorkPool - fill pid's work pool with jobs
 *
 * SYNOPSIS
 *	VOID	InitWorkPool(pid)
 *	INT	pid;			// Process id to initialize.
 *
 * RETURNS
 *	Nothing.
 */

VOID	InitWorkPool(pid)
INT	pid;
	{
	INT	i;
	INT	x, y;
	INT	xe, ye;
	INT	xsize, ysize;

	gm->wpstat[pid][0]   = WPS_VALID;
	gm->workpool[pid][0] = NULL;

	i      = 0;
	xsize  = Display.xres/blockx;
	ysize  = Display.yres/blocky;

	for (y = 0; y < Display.yres; y += ysize)
		{
		if (y + ysize > Display.yres)
			ye = Display.yres - y;
		else
			ye = ysize;

		for (x = 0; x < Display.xres; x += xsize)
			{
			if (x + xsize > Display.xres)
				xe = Display.xres - x;
			else
				xe = xsize;

			if (i == pid)
				PutJob(x, y, xe, ye, bundlex, bundley, pid);

			i = (i + 1)%gm->nprocs;
			}
		}

	}

