diff -Naur source.ori/optin.c source/optin.c --- source.ori/optin.c Sat Jun 30 01:49:21 2001 +++ source/optin.c Sat Jun 30 10:01:28 2001 @@ -30,7 +30,8 @@ * * Written by CEY 4/94 based on existing code and INI code from CDW. * -* --- +* Modified Oct. 96 by Deischi for PVM +* Modified Jun 2001 by Agaran for conditional PVM build * * Modification by Thomas Willhalm, March 1999, used with permission. * @@ -56,7 +57,9 @@ #include "targa.h" #include "userio.h" #include "png_pov.h" - +#ifdef USE_PVM +#include "pvm.h" +#endif /***************************************************************************** @@ -187,7 +190,23 @@ { BITS_PER_COLOR_OP, "Bits_Per_Color" }, { BITS_PER_COLOUR_OP, "Bits_Per_Colour" }, +#ifdef USE_PVM + { INCLUDE_INI_OP, "Include_Ini" }, + + /* new options for PVM-Pov */ + { PVM_TASKS_OP, "PVM_Tasks" }, + { PVM_WIDTH_OP, "PVM_Width" }, + { PVM_HEIGHT_OP, "PVM_Height" }, + { PVM_SLAVE_OP, "PVM_Slave" }, + { PVM_ARCH_OP, "PVM_Arch" }, + { PVM_NICE_OP, "PVM_Nice" }, + { PVM_WD_OP, "PVM_WD" }, + { PVM_HOSTS_OP, "PVM_Hosts" }, + { PVM_USE_OP, "PVM" } + +#else { INCLUDE_INI_OP, "Include_Ini" } +#endif }; static char temp_string[3]="\0\0"; @@ -229,7 +248,8 @@ * * CHANGES * -* - +* Oct. 1996: Added options for PVMPOV [Deischi] +* jun 2001: conditional pvm * ******************************************************************************/ @@ -560,6 +580,44 @@ case INCLUDE_INI_OP: value[0] = '\0'; return value; +#ifdef USE_PVM + /* + * PVM Options + */ + case PVM_TASKS_OP: + sprintf(value, "%d", PvmTasks); + return value; + case PVM_WIDTH_OP: + sprintf(value, "%d", PvmChunkWidth); + return value; + case PVM_HEIGHT_OP: + sprintf(value, "%d", PvmChunkHeight); + return value; + case PVM_SLAVE_OP: + strncpy(value, PvmSlavename ? PvmSlavename : "", 128); + return value; + case PVM_ARCH_OP: + sprintf(value, "%s", PvmArch); + return value; + case PVM_NICE_OP: + sprintf(value, "%d", PvmNice); + return value; + case PVM_WD_OP: + strncpy(value, PvmWorkingDir, PATH_MAX); + return value; + case PVM_HOSTS_OP: + if(PvmHosts) { + int i; + for(i=0; i < PvmHostsN; i++) { + strncat(value, PvmHosts[i], 128); + strncat(value, ",", 128); + } + } + return value; + case PVM_USE_OP: + sprintf(value, "%s", (PvmTasks || PvmSlave) ? "On" : "Off"); + return value; +#endif default: Error("Unknown INI option in Write_INI."); @@ -602,6 +660,8 @@ * Sep 1994 : Added options for union splitting, vista/light buffer. [DB] * Jan 1995 : Added options for histogram grid. [CJC] * Feb 1995 : Added options for console/file redirection and .INI writing [SCD] +* Oct 1996 : Added options for PVMPOV [Deischi] +* jun 2001 : added condititional build for pvm * ******************************************************************************/ @@ -1024,13 +1084,67 @@ break; /* "N" option flag is used by networking (multi-processor) options. - - case 'N': - case 'n': - - break; - */ +#ifdef USE_PVM + /* + * PVM-POV + * + */ + case 'N': /* switches used for PVMPOV */ + case 'n': + switch( Option_String[1]) { + case '\0': /* Start one slave/available PVM host */ + process_variable(PVM_USE_OP, Add_Option ? "On" : "Off"); + break; + case 'a': /* Select a particular architecture to run the slaves on */ + case 'A': + process_variable(PVM_ARCH_OP, Option_String+2); + break; + case 'n': /* Sets the niceness (priority) level */ + case 'N': + process_variable(PVM_NICE_OP, Option_String+2); + break; + case 't': /* Sets the number of tasks to run. Default is 1 per host */ + case 'T': + process_variable(PVM_TASKS_OP, Option_String+2); + break; + case 'h': /* The height of a single grid block, in pixels */ + case 'H': + process_variable(PVM_HEIGHT_OP, Option_String+2); + break; + case 'w': /* The width of a single grid block, in pixels */ + case 'W': + process_variable(PVM_WIDTH_OP, Option_String+2); + break; + case 's': /* specify the name of the slave */ + case 'S': + process_variable(PVM_SLAVE_OP, Option_String+2); + break; + case 'd': /* specify working directory for the slaves */ + case 'D': + process_variable(PVM_WD_OP, Option_String+2); + break; + /* Internal sub-option "-nI" indicates a PVM slave task. + * This will be appended to current argv string, so it will override + * the previous -nxx options. */ + case 'i': + case 'I': + PvmTasks = 1; + PvmSlave = 1; + + if (chdir(&Option_String[2]) < 0) { + fprintf(stderr, "Slave cannot cd to %s.\n", &Option_String[2]); + exit(1); + } + break; + + default: + fprintf(stderr, "Illegal +N%c switch setting\n", Option_String[1]); + break; + } /* switch */ + break; +#endif + case 'O': case 'o': @@ -1262,7 +1376,8 @@ * * CHANGES * -* - +* Oct 1996 : Added options for PVMPOV [Deischi] +* jun 2001 : conditional build for pvm * ******************************************************************************/ @@ -1445,21 +1560,36 @@ return; case END_COLUMN_OP: +#ifdef USE_PVM + /* tw */ + if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF) { +#else { /* tw */ if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF) +#endif if(floatval > 0.0 && floatval <= 1.0) { opts.Last_Column = -1; opts.Last_Column_Percent = floatval; } +#ifdef USE_PVM + else { + opts.Last_Column = (int) floatval; } +#else else opts.Last_Column = (int) floatval; +#endif } /* tw */ return; case END_ROW_OP: +#ifdef USE_PVM + /* tw */ + if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF) { +#else { /* tw */ if (sscanf (value, DBL_FORMAT_STRING, &floatval) != SCANF_EOF) +#endif if(floatval > 0.0 && floatval <= 1.0) { opts.Last_Line = -1; @@ -1831,6 +1961,86 @@ Error ("Could not open Include_Ini='%s'.\n", value); } return; + +#ifdef USE_PVM + /* + * Options for PVMPOV + */ + case PVM_TASKS_OP: + if (sscanf(value, "%d", &PvmTasks) != 1 || PvmTasks < 1) { + PvmTasks = 9999; /* Try to start one slave/available PVM host */ + } + return; + + case PVM_WIDTH_OP: + if (sscanf(value, "%d", &PvmChunkWidth) != 1 || PvmChunkWidth < 4) + PvmChunkWidth = PVM_DEFAULT_GRID_WIDTH; + return; + + case PVM_HEIGHT_OP: + if (sscanf(value, "%d", &PvmChunkHeight) != 1 || PvmChunkHeight < 4) + PvmChunkHeight = PVM_DEFAULT_GRID_HEIGHT; + return; + + case PVM_SLAVE_OP: + PvmSlavename = POV_MALLOC( sizeof(char)*(strlen(value)+1), + "PVM Slavename"); + strcpy(PvmSlavename, value); + return; + + case PVM_ARCH_OP: + strncpy(PvmArch,value,19); + PvmArch[19] = '\0'; + + if (PvmTasks < 1) + PvmTasks = 9999; /* Try to start one slave/available PVM host */ + return; + + case PVM_NICE_OP: + if (sscanf(value, "%d", &PvmNice) != 1 || PvmNice < 0) { + PvmNice = PVM_DEFAULT_NICE; + } + return; + + case PVM_WD_OP: + strncpy(PvmWorkingDir, value, PATH_MAX); + return; + + case PVM_HOSTS_OP: { + char * c = value; + char * c2; + int n; + + PvmHostsN = 1; /* count number of names */ + while((c = strchr(c, ',')) != NULL) { + c ++; + PvmHostsN ++; + } + PvmHosts = (char**)POV_MALLOC( sizeof(char*) * PvmHostsN, "PvmHosts"); + + c = value; + c2 = strchr(c,','); + for(i=0; i < PvmHostsN; i++) { + n = (c2) ? c2 - c : strlen(c); + PvmHosts[i] = (char*)POV_MALLOC( sizeof(char) * (n + 1), "PvmHosts[i]"); + strncpy(PvmHosts[i], c, n); + PvmHosts[i][n] = '\0'; + + if(c2) c = c2 + 1; + c2 = strchr(c, ','); + } + + PvmTasks = PvmHostsN; + + return; + + } + case PVM_USE_OP: + PvmTasks = istrue(value) ? 9999 : 0; + return; + +#endif + default: Warning(0.0,"Unimplemented INI '%s'.\n",Option_Variable[variable].Token_Name); diff -Naur source.ori/optin.h source/optin.h --- source.ori/optin.h Sat Jun 30 01:49:21 2001 +++ source/optin.h Sat Jun 30 03:43:53 2001 @@ -19,6 +19,10 @@ * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * +* +* Oct 1996 : Added options for PVMPOV [Deischi] +* Jun 2001 : Added conditional build for PVM Support [Agaran] +* *****************************************************************************/ @@ -131,6 +135,19 @@ BITS_PER_COLOR_OP, BITS_PER_COLOUR_OP, INCLUDE_INI_OP, + +#ifdef USE_PVM + /* new options for PVMPOV */ + PVM_TASKS_OP, + PVM_WIDTH_OP, + PVM_HEIGHT_OP, + PVM_SLAVE_OP, + PVM_ARCH_OP, + PVM_NICE_OP, + PVM_WD_OP, + PVM_HOSTS_OP, + PVM_USE_OP, +#endif MAX_OPTION } INI_OP; diff -Naur source.ori/optout.c source/optout.c --- source.ori/optout.c Sat Jun 30 01:49:21 2001 +++ source/optout.c Sat Jun 30 09:25:02 2001 @@ -19,6 +19,9 @@ * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * +* Oct 1996 : Added options for PVMPOV [Deischi] +* jun 2001 : added conditional build for pvm +* *****************************************************************************/ #include @@ -65,8 +68,9 @@ #include "povray.h" #include "optin.h" #include "optout.h" - - +#ifdef USE_PVM +#include "pvm.h" +#endif /***************************************************************************** * Local preprocessor defines @@ -611,6 +615,9 @@ * Dec 1994 : Changed to show options depending on parameter n. [DB] * * Feb 1995 : Changed to terminate only if f != 0. [DB] +* +* Oct 1996 : Added options from PVMPOV [Deischi] +* Jun 2001 : conditional build for pvm * ******************************************************************************/ @@ -640,7 +647,12 @@ Banner(" 4 Output Options - file related\n"); Banner(" 5 Tracing Options\n"); Banner(" 6 Animation Options\n"); +#ifdef USE_PVM + Banner(" 7 PVM Options\n"); + Banner(" 8 Redirecting Options\n"); +#else Banner(" 7 Redirecting Options\n"); +#endif break; @@ -767,6 +779,40 @@ break; +#ifdef USE_PVM + /* PVM options. */ + + case 7: + + Banner("\n"); + Banner("PVM options\n"); + Banner("\n"); + Banner(" N = Turn on PVM\n"); + Banner(" NTnnn = Spawn number of tasks\n"); + Banner(" NAs = Spawn only on architecture\n"); + Banner(" NWnnn = Width of each chunk\n"); + Banner(" NHnnn = Height of each chunk\n"); + Banner(" NNnnn = Niceness of slaves\n"); + Banner(" NSs = Name of slaves (executable)\n"); + Banner(" NDnnn = Name of the working directory\n"); + Banner(" pvm_hosts = n,n,.. = List of hostnames to use\n"); + Banner("\n"); + + /* Redirecting options. */ + + Banner("\n"); + Banner("Redirecting options\n"); + Banner("\n"); + Banner(" GI= write all .INI parameters to file name\n"); + Banner(" Gx= write stream x to console and/or file name\n"); + Banner(" GA - All streams (except status)\n"); + Banner(" GD - Debug stream\n"); + Banner(" GF - Fatal stream\n"); + Banner(" GR - Render stream\n"); + Banner(" GS - Statistics stream\n"); + Banner(" GW - Warning stream\n"); +#else + /* Redirecting options. */ case 7: @@ -782,6 +828,7 @@ Banner(" GR - Render stream\n"); Banner(" GS - Statistics stream\n"); Banner(" GW - Warning stream\n"); +#endif break; @@ -867,7 +914,8 @@ * * CHANGES * -* - +* Oct 1996 : Added options from PVMPOV [Deischi] +* Jun 2001 : Added conditional build for PVM Support [Agaran] * ******************************************************************************/ @@ -1089,6 +1137,26 @@ } Render_Info("\n"); +#if USE_PVM + if(PvmTasks) { + int i; + Render_Info("PVM Options\n"); + Render_Info(" Block Width....%8d", PvmChunkWidth); + Render_Info(" Block Height...%8d\n", PvmChunkHeight); + Render_Info(" PVM Tasks......%8d\n", PvmTasks); + Render_Info(" PVM Nice.......%8d\n", PvmNice); + Render_Info(" PVM Arch....... %s\n", PvmArch); + Render_Info(" PVM Slave...... %s\n", PvmSlavename ? PvmSlavename : ""); + Render_Info(" PVM WorkingDir. %s\n", PvmWorkingDir); + if(PvmHosts) { + Render_Info(" Pvm Hosts......"); + for(i=0; i < PvmHostsN; i++) { + Render_Info(" %s", PvmHosts[i]); + } + Render_Info("\n"); + } + } +#endif /* Print redirecting options. */ diff -Naur source.ori/optout.h source/optout.h --- source.ori/optout.h Sat Jun 30 01:49:21 2001 +++ source/optout.h Sat Jun 30 02:17:45 2001 @@ -19,6 +19,10 @@ * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * +* Oct 1996 : Added options for PVMPOV [Deischi] +* May 1999 : Changed code to work with POVRAY 3.1e [Flierl] +* Jun 2001 : conditional build for PVM [agaran] +* *****************************************************************************/ @@ -36,12 +40,19 @@ #define POV_RAY_VERSION "3.1g" #define DISTRIBUTION_MESSAGE_1 "This is an unofficial version compiled by:" +#ifdef USE_PVM +#define DISTRIBUTION_MESSAGE_2 "Jakob Flierl - PVMPOV Version 3.1e.2" +#else #define DISTRIBUTION_MESSAGE_2 "FILL IN NAME HERE........................." +#endif #define DISTRIBUTION_MESSAGE_3 "The POV-Ray Team(tm) is not responsible for supporting this version." /* Number of help pages (numbered 0 to MAX_HELP_PAGE). */ - +#ifdef USE_PVM +#define MAX_HELP_PAGE 8 +#else #define MAX_HELP_PAGE 7 +#endif diff -Naur source.ori/povray.c source/povray.c --- source.ori/povray.c Sat Jun 30 01:49:21 2001 +++ source/povray.c Sat Jun 30 09:55:32 2001 @@ -20,6 +20,10 @@ * DKBTrace was originally written by David K. Buck. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins. * +* Oct 1996 : Added options for PVMPOV [Deischi] +* May 1999 : Changed code to work with POVRAY 3.1e [Flierl] +* Jun 2001 : Added conditional build for PVM [Agaran] +* *****************************************************************************/ #include @@ -69,7 +73,9 @@ #include "userio.h" /*Error,Warning,Init_Text_Streams*/ #include "lbuffer.h" #include "vbuffer.h" - +#ifdef USE_PVM +#include "pvm.h" +#endif /***************************************************************************** * Local preprocessor defines @@ -157,9 +163,15 @@ static void fix_up_rendering_window (void); static void fix_up_animation_values (void); static void fix_up_scene_name (void); +#ifdef USE_PVM +void set_output_file_handle (void); +void setup_output_file_name (void); +void open_output_file (void); +#else static void set_output_file_handle (void); static void setup_output_file_name (void); static void open_output_file (void); +#endif static void FrameRender (void); static void init_statistics (COUNTER *); static void sum_statistics (COUNTER *, COUNTER *); @@ -192,7 +204,8 @@ * * CHANGES * -* - +* Oct 1996 : Added PVM Support [Deischi] +* Jun 2001 : Added conditional build for PVM Support [Agaran] * ******************************************************************************/ @@ -262,17 +275,31 @@ } #endif +#ifdef USE_PVM + /* If slave, turn off certain optiongs */ + if ( PvmSlave) + opts.Options &= ~(DISPLAY | VERBOSE); +#endif + /* Strip path and extension off input name to create scene name */ fix_up_scene_name (); /* Redirect text streams [SCD 2/95] */ Open_Text_Streams(); +#ifdef USE_PVM + if ( !PvmSlave) { + /* Write .INI file [SCD 2/95] */ + Write_INI_File(); + + ALT_WRITE_INI_FILE + } +#else /* Write .INI file [SCD 2/95] */ Write_INI_File(); ALT_WRITE_INI_FILE - +#endif /* Make sure clock is okay, validate animation parameters */ fix_up_animation_values(); @@ -282,9 +309,18 @@ /* Set output file handle for options screen. */ set_output_file_handle(); +#ifdef USE_PVM + /* Initialize PVM master */ + if ( !PvmSlave && PvmTasks) + Pvm_Master_Init(argc, argv); + + /* Print options used. */ + if ( !PvmSlave) + Print_Options(); +#else /* Print options used. */ Print_Options(); - +#endif /* BEGIN SECTION */ /* VARIOUS INITIALIZATION THAT ONLY NEEDS TO BE DONE 1/EXECUTION */ @@ -306,6 +342,32 @@ /* END SECTION */ +#ifdef USE_PVM + /* PVM Slave */ + if (PvmSlave ) { + if( Pvm_Slave_Init() < 0) + return; + + Pvm_Slave_Control(); /* get the first block to do */ + while(PvmNextFrame <= opts.FrameSeq.FinalFrame) { + /* set frame number */ + opts.FrameSeq.FrameNumber = PvmNextFrame; + /* calculate clock value */ + opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock + + ((DBL)(opts.FrameSeq.FrameNumber - opts.FrameSeq.InitialFrame)) * + Clock_Delta; + + FrameRender(); + } + + Pvm_Slave_Exit(); + + Terminate_POV(0); + + MAIN_RETURN_STATEMENT + } +#endif + /* Execute the first shell-out command */ Pre_Scene_Result=(POV_SHELLOUT_CAST)POV_SHELLOUT(PRE_SCENE_SHL); @@ -314,6 +376,26 @@ if (Pre_Scene_Result != ALL_SKIP_RET) { if (Pre_Scene_Result != SKIP_ONCE_RET) +#ifdef USE_PVM + if(!PvmSlave && PvmTasks) { + START_TIME + Pvm_Master_Control(); /* distribute work, + and collect results, + call shellout commands */ + STOP_TIME + trender = TIME_ELAPSED + + PRINT_STATS(totalstats); + } else { + for (opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame, + opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock; + + opts.FrameSeq.FrameNumber <= opts.FrameSeq.FinalFrame; + + opts.FrameSeq.FrameNumber++, + opts.FrameSeq.Clock_Value += Clock_Delta) + { +#else { for (opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame, opts.FrameSeq.Clock_Value = opts.FrameSeq.InitialClock; @@ -323,6 +405,7 @@ opts.FrameSeq.FrameNumber++, opts.FrameSeq.Clock_Value += Clock_Delta) { +#endif setup_output_file_name(); /* Execute a shell-out command before tracing */ @@ -348,7 +431,6 @@ } } } - /* Print total stats ... */ if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) @@ -399,6 +481,8 @@ * CHANGES * * Feb 1996: Make sure we are displaying when doing a mosaic preview [AED] +* Oct 1996: Added PVM support [Deischi] +* Jun 2001: Added conditional build for PVM Support [agaran] * ******************************************************************************/ @@ -483,6 +567,30 @@ /* Save variable values. */ variable_store(STORE); +#ifdef USE_PVM + if( !PvmSlave) { + + /* Open output file and if we are continuing an interrupted trace, + * read in the previous file settings and any data there. This has to + * be done before any image-size related allocations, since the settings + * in a resumed file take precedence over that specified by the user. [AED] + */ + open_output_file(); + + /* Start the display. */ + if (opts.Options & DISPLAY) + { + Status_Info ("\nDisplaying..."); + + POV_DISPLAY_INIT(Frame.Screen_Width, Frame.Screen_Height); + + Display_Started = TRUE; + + /* Display vista tree. */ + Draw_Vista_Buffer(); + } + } /* !PvmTasks */ +#else /* Open output file and if we are continuing an interrupted trace, * read in the previous file settings and any data there. This has to * be done before any image-size related allocations, since the settings @@ -502,15 +610,45 @@ /* Display vista tree. */ Draw_Vista_Buffer(); } - +#endif /* Get things ready for ray tracing (misc init, mem alloc) */ Initialize_Renderer(); +#ifdef USE_PVM + /* If a PVM slave, re-assign Write_Line to PVM */ + if (PvmSlave) { + Output_File_Handle->Write_Line_p = Pvm_Write_Line; + } +#endif /* This had to be taken out of open_output_file() because we don't have * the final image size until the output file has been opened, so we can't * initialize the display until we know this, which in turn means we can't * read the rendered part before the display is initialized. [AED] */ +#ifdef USE_PVM + if (!PvmSlave) { + if ((opts.Options & DISKWRITE) && (opts.Options & CONTINUE_TRACE)) + { + Read_Rendered_Part(Actual_Output_Name); + + if (opts.Last_Line > Frame.Screen_Height) + opts.Last_Line = Frame.Screen_Height; + + if (opts.Last_Column > Frame.Screen_Width) + opts.Last_Column = Frame.Screen_Width; + } + } /* !PvmSlave */ + if(PvmSlave || !PvmTasks) { + /* Get parsing time. */ + STOP_TIME + tparse = TIME_ELAPSED + + /* Get total parsing time. */ + tparse_total += tparse; + + } /* PvmSlave || !PvmTasks */ + +#else if ((opts.Options & DISKWRITE) && (opts.Options & CONTINUE_TRACE)) { Read_Rendered_Part(Actual_Output_Name); @@ -529,7 +667,7 @@ /* Get total parsing time. */ tparse_total += tparse; - +#endif /* Store start time for trace. */ START_TIME @@ -547,7 +685,65 @@ POV_PRE_RENDER Status_Info ("\nRendering...\r"); +#ifdef USE_PVM + do { + /* Macro for setting up any special FP options */ + CONFIG_MATH + + /* Ok, go for it - trace the picture. */ + + /* If radiosity preview has been done, we are continuing a trace, so it + * is important NOT to do the preview, even if the user requests it, as it + * will cause discontinuities in radiosity shading by (probably) calculating + * a few more radiosity values. + */ + if ( !opts.Radiosity_Preview_Done ) + { + if ( opts.Options & RADIOSITY ) + { + /* Note that radiosity REQUIRES a mosaic preview prior to main scan */ + + Start_Tracing_Mosaic_Smooth(opts.PreviewGridSize_Start, opts.PreviewGridSize_End); + } + else + { + if (opts.Options & PREVIEW && opts.Options & DISPLAY) + { + Start_Tracing_Mosaic_Preview(opts.PreviewGridSize_Start, opts.PreviewGridSize_End); + } + } + } + + switch (opts.Tracing_Method) + { + case 2 : + + Start_Adaptive_Tracing(); + + break; + + case 1 : + default: + + Start_Non_Adaptive_Tracing(); + } + } while(PvmSlave ? Pvm_Slave_Control() : 0); /* ask for more work */ + + /* We're done. */ + + /* Record time so well spent before file close so it can be in comments */ + STOP_TIME + trender = TIME_ELAPSED + + if(!PvmSlave) { + /* Close out our file */ + if (Output_File_Handle) + { + Close_File(Output_File_Handle); + } + } +#else /* Macro for setting up any special FP options */ CONFIG_MATH @@ -600,7 +796,7 @@ { Close_File(Output_File_Handle); } - +#endif Stage = STAGE_SHUTDOWN; POV_PRE_SHUTDOWN @@ -626,6 +822,27 @@ /* Get total render time. */ trender_total += trender; +#ifdef USE_PVM + if(!PvmSlave) { + POV_DISPLAY_FINISHED + + if ((opts.Options & DISPLAY) && Display_Started) + { + POV_DISPLAY_CLOSE + + Display_Started = FALSE; + } + + if (opts.histogram_on) + write_histogram (opts.Histogram_File_Name) ; + } + Status_Info("\nDone Tracing"); + + /* Print stats ... */ + if(!PvmSlave) + PRINT_STATS(stats); + +#else POV_DISPLAY_FINISHED if ((opts.Options & DISPLAY) && Display_Started) @@ -642,7 +859,7 @@ /* Print stats ... */ PRINT_STATS(stats); - +#endif if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) { /* Add them up */ @@ -1030,8 +1247,11 @@ * NULL function pointer. * ******************************************************************************/ - +#ifdef USE_PVM +void set_output_file_handle() +#else static void set_output_file_handle() +#endif { char *def_ext = NULL; char temp[FILE_NAME_LENGTH]; @@ -1110,7 +1330,11 @@ * function appends a path separator on output. * ******************************************************************************/ +#ifdef USE_PVM +void setup_output_file_name() +#else static void setup_output_file_name() +#endif { char number_string[10]; char separator_string[2] = {FILENAME_SEPARATOR, 0} ; @@ -1219,8 +1443,11 @@ * - * ******************************************************************************/ - +#ifdef USE_PVM +void open_output_file() +#else static void open_output_file() +#endif { int Buffer_Size; @@ -1378,12 +1605,18 @@ opts.Radiosity_Nearest_Count = 6; opts.Radiosity_Recursion_Limit = 1; opts.Radiosity_Quality = 6; /* Q-flag value for light gathering */ +#ifdef USE_PVM + opts.Radiosity_File_ReadOnContinue = PvmSlave ? 0 : 1; + opts.Radiosity_File_SaveWhileRendering = PvmSlave ? 0 : 1; /* Don't save radiostiry cache with PVM */ + opts.Radiosity_File_AlwaysReadAtStart = 0; + opts.Radiosity_File_KeepOnAbort = PvmSlave ? 0 : 1; +#else opts.Radiosity_File_ReadOnContinue = 1; opts.Radiosity_File_SaveWhileRendering = 1; opts.Radiosity_File_AlwaysReadAtStart = 0; opts.Radiosity_File_KeepOnAbort = 1; +#endif opts.Radiosity_File_KeepAlways = 0; - init_statistics(stats); init_statistics(totalstats); diff -Naur source.ori/pvm/PVMPOV.Changelog source/pvm/PVMPOV.Changelog --- source.ori/pvm/PVMPOV.Changelog Thu Jan 1 01:00:00 1970 +++ source/pvm/PVMPOV.Changelog Sun Sep 12 00:25:35 1999 @@ -0,0 +1,17 @@ + * 3.1e.2 [Jakob Flierl] + - fixed problem with the statistics collection in line 1331 of "pvm.c" + - minor changes in "sources/pvm/Makefile.aimk" + - updated documentation + - added "PVMPOV.Changelog" and "PVMPOV.benchmark" + * 3.1e.1 [Jakob Flierl] + - applied the patch to POV-Ray version 3.1e + - fixed at least partially a memory bug in "render.c". Thanks to Michael + Eilers for solving this problem at least partially. + * 3.0x [Harald Deischinger] + - applied the patch to POV-Ray 3.01 and 3.01 + - several command line enhancements. + * 2.2 to 2.9 [Andreas Dilger] + - changes in optin.h, optin.c, optout.h, optout.c, + povray.c, render.h, render.c + - added pvm/Makefile.aimk, pvm/pvm.h, pvm/pvm.c + * Initial Version [Brad Kline, Cray Inc.] diff -Naur source.ori/pvm/PVMPOV.benchmark source/pvm/PVMPOV.benchmark --- source.ori/pvm/PVMPOV.benchmark Thu Jan 1 01:00:00 1970 +++ source/pvm/PVMPOV.benchmark Sun Sep 12 00:25:35 1999 @@ -0,0 +1 @@ +Surf to http://www.haveland.com/povbench/ to look at some render statistics. diff -Naur source.ori/pvm/PVMPOV.example source/pvm/PVMPOV.example --- source.ori/pvm/PVMPOV.example Thu Jan 1 01:00:00 1970 +++ source/pvm/PVMPOV.example Sun Sep 12 00:25:35 1999 @@ -0,0 +1,283 @@ +This file describes the command line options of PVMPOV, as well as the general +theory behind how it operates. + +Theory of Operation +------------------- +Using the PVM model, there is one master and many slave tasks. The master has +the responsibility of issuing the work sections to the slaves, and receiving +portions of the rendered sections back. The master does not render anything +by itself. The PVM component is only active if the user gives the "+N" option +to POV. Otherwise, PVMPOV behaves the same as regular POV-Ray and runs a +single task only on the local machine. + +Valid POV-Ray command-line options relating to PVM are: + + pvm=on + +N This is the default for starting PVMPOV. One slave will be + started on each available host, regardless of architecture, and + the blocks will be 32x32 pixels in size. The slaves will be + started with a nice value of 5, which means they will run at a + lower priority than other user jobs. + pvm=off + -N Turns of PVM support. PVMPOV now runs exactly as normal POV-Ray. + You should do this when there are still errors in your pov-file. + PVMPOV will not display error message otherwise. + + pvm_tasks=xx + +NTxx Start xx tasks on the available PVM hosts. This is usually only + useful if debugging on a single machine, or for starting more + than 1 task on multi-processor hosts. If, for example, you have + 10 machines with 4 CPUs each, you could specify +nt40 to start 4 + processes on each host (and the OS will hopefully run 1 on each + CPU). + + Note that PVM is stupid in the way it starts tasks, so if, + in the previous example, one of the hosts has only one CPU, it will + still have 4 slaves started on it. You can use the "pvm_hosts" + option (see below) to control on which machines the tasks are + started. + + Starting multiple tasks on a single processor will always be less + efficient than a single task because of context switching and extra + message passing. + + pvm_arch=arch + +NAarch Start the tasks only on the PVM architecture "arch". If +NT is not + given, one task will be started on each of the hosts of the given + architecture. + + pvm_nice=xx + +NNxx Run the slaves at a niceness factor of xx. The default niceness + is 5. In general, changing the niceness value will not affect + performance very much, but may get others upset with you. The + nicest setting for pvmpov is 20, while the least nice setting is + 0. Note that these values are always used even on systems that + use nice values from 20-40. See the installation document and + the man page for nice for more information. + + pvm_width=xx + +NWxx Change the width of the blocks to xx pixels. The default width + is 32 pixels. + + pvm_height=xx + +NHxx Change the height of the blocks to xx pixels. The default height + is 32 pixels. + + It is important to note that by varying the size of the grid + sections, you can affect the performance of the rendering. If you + have particular renderings that are very complex in a small portion + of the display, then a finer grain may help. In this way, more of + the tasks are able to migrate towards the grid sections that are + more complex. Conversely, if you have a shorter render or a + slower network, it may be advantageous to have larger blocks to + reduce network overhead, as well as ensure the slaves are not idle + waiting for blocks to render. + + You must also consider overhead if using anti-aliasing. Anti- + aliasing requires the line segment above and below the grid section + to be traced so that super-sampling may occur. If the height of the + grid is reduced in size, and anti-aliasing is turned on, your + percentage of overhead goes up. For example, setting a height of + four (-NH4) using anti-aliasing would have more than 25% overhead. + If the image size is not an integer multiple of the grid size, the + edge blocks are smaller (ie extra pixels aren't rendered), so it + is not necessary to evenly divide the image into blocks. + + pvm_slave=slave + +NSslave Uses slave as filename for the slave tasks. If you do not + specify this option PVMPOV will use the current executable name + also for the slaves. + + Using this option you can for example run the X11 version + (x-pvmpov) as master (to display the results) and a version + without any display support (pvmpov) as slaves. + + pvm_wd=dir + +NDdir Set the working directory for the slaves. By default PVMPOV + tries to run the slaves in the same directory, as the master. + But sometimes 'getcwd' gives misleading output (when automounting + is used), or you simply want to run the slaves in some other + directory, then you can use this option. + + pvm_hosts=name1,name2,... + Set the names of the hosts to use for slaves. Note, that there are + not spaces allowed between the names! + + By default PVM distributes the processes in some order to the + available machines. Sometimes the choice of PVM is not the best, + so you can specify explicitly which machines to use. + + You can use more tasks, then you specify here. You can use the + options in this way: pvm_hosts=darkstar,darkstar,baby +nt6 + This will start 6 tasks (+nt6 must come after the pvm_hosts), + 4 on darkstar and 2 on baby. + + +For example, if I wanted to distribute an 1024x768 rendering and use eight PVM +tasks to do the work, I would use the +NT8 option, and let the PVM master +define 768 32x32 sections. Eight slave tasks would be launched, and when ready, +they would request work to do from the master. The master then issues a column +and row range back to the requesting slave. + +% pvmpov +nt8 +w1024 +h768 +iskyvase.pov +oskyvase.tga + +As each quarter of a grid is completed, the slave sends it back to the +master, which buffers the incoming blocks until an entire line recieved, at +which time it writes the line to disk with the specified file format. If +all slaves did perfectly equal work in this example, they would be issued 96 +sections of work, and would have returned 384 messages of 32x8 pixels each. + +Note that the work issues are done essentially one at a time. As each slave +starts up it requests a single block, and as it recieves a work request, it +requests the next block. In this way, effective load balancing can occur. +Tasks rendering the "easy" sections will eventually all migrate towards the +harder sections. Seriously imbalanced distributions will be seen by widely +varying percentages shown at the end of the rendering, assuming all of the +hosts are identical. Since each slave always has a block to be rendered, it +will be kept busy without waiting for the master to recieve its work request +and reply with a new block assignment. + +If for some reason any of the slaves are very slow, or they are interrupted +during their operation, their blocks are reassigned to other slaves after +all of the blocks have been assigned a first time. This means that the render +can be completed quickly even if some of the slaves have problems, or if some +blocks have been assigned to slower slaves while the faster slaves are idle. +This will show up as some percentage of late blocks for that machine in the +statistics at the end. Note that blocks will NOT be reassigned to slaves that +are significantly slower than average, in preference to reassigning it to a +faster slave when it becomes available. + +Since only complete lines are written out to disk, interrupted traces will +be restarted at the last complete line that was written to disk, regardless +of how many blocks were buffered by the master at the time. However, it is +possible to re-start an interrupted trace in the same method as POV-Ray. + +Known bugs (and other limitations) +------------------------------------- +All of the slaves must to share the same file system as the master process, +and the source file must be mounted in the same location on each one. This +may limit the number of hosts that can be used in some circumstances. + +The command line arguments for specifying hosts is not cumulative, so + +% pvmpov +naSUNMP +nt24 +n +iskyvase.pov +oskyvase.tga + +will not start 24 slaves on the multi-processor SUN system(s) (which may have +4 CPUs each), and then start 1 slave on each of the remaining systems. + +The statistics for the image being rendered may not be accurate if any of the +slaves is too slow, or stops for some reason. As well, since some sections +may be rendered twice if the block is reassigned, the statistics will not be +exactly the same as if it were rendered on a single host. The master only +waits a few seconds for statistics to arrive before printing them. + +The master does not parse the input file. Because of that it displays +no error messages concerning the input file. As long as your input file +still contains error you should disable PVM support (-N or pvm=off) + +Execution example (from version 2.9): +------------------------------------- +You first must have a PVM daemon launched on each machine that will be +participating in the rendering. Refer to the PVM 3.3 documentation. The +following is an example is from Jason Hough, and was generated on a group +of six Solaris based 4-processor SPARCstation 20s. His home directory is +auto-mounted to all of these hosts. + +He keeps the pvm daemon installed in a directory called "bin", given by +"dx=./bin/pvmd3" relative to his home directory, and pvmpov is in various +subdirectories under "bin" (ie bin/SUN4, bin/SUNMP, bin/LINUX, etc.), given +by the executable path "ex=./bin", so his pvm.hosts file looks like: + +% cat pvm.hosts +glee dx=./bin/pvmd3 ep=./bin +elation dx=./bin/pvmd3 ep=./bin +ecstasy dx=./bin/pvmd3 ep=./bin +bliss dx=./bin/pvmd3 ep=./bin +delight dx=./bin/pvmd3 ep=./bin +rapture dx=./bin/pvmd3 ep=./bin + +Note that, by default, PVM looks for executables in $HOME/pvm3/bin/$PVM_ARCH, +and pvmd should be in $PVM_ROOT/lib, so if this is the case on your systems, +you don't need to have anything in your pvm.hosts file except the hostnames, +one per line. An exception is RS6K, which needs the dx= no matter what. See +the PVM documentation and/or the pvmd3 man page for more information on what +all of this means if you don't understand it. + +The following command launches the PVM daemons. + +% pvm pvm.hosts +3.3.7 +t40001 +pvm> conf +6 hosts, 1 data format + HOST DTID ARCH SPEED + glee 40000 SUNMP 1000 + elation 80000 SUNMP 1000 + ecstasy c0000 SUNMP 1000 + bliss 100000 SUNMP 1000 + delight 140000 SUNMP 1000 + rapture 180000 SUNMP 1000 +pvm> quit + +pvmd still running. + +Now that the PVM daemons are up and waiting for work to do, we can render. +Note that for these MP machines he forces pvmpov to start more tasks than +the default 1 per host, and uses a 64x64 block size: + +% pvmpov -Iskyvase.pov -Oskyvase.tga +nt24 +nw64 +nh64 +v + +POV-Ray Options in effect: +v1 +ft +mb25 +nt24 +nn5 +nw64 +nh64 +a0.300 ++j1.000 +b999 +r3 -q9 -w1024 -h768 -s1 -e768 +-k0.000 -mv2.0 -iskyvase.pov -oskyvase.tga + ...at least 13 tasks successfully spawned in time. + ...Don't worry, more are on the way, I'm just not waiting +PVM Task Distribution: Tasks-24 Grid width-64 Grid height-64 Sections-192 + +Waiting for slave stats. + +PVM Task Distribution Statistics: + host name [ done ] [ late ] host name [ done ] [ late ] + glee [ 4.17%] [ 0.00%] glee [ 4.17%] [ 0.00%] + glee [ 4.17%] [ 0.00%] glee [ 4.17%] [ 0.00%] + elation [ 4.69%] [ 0.00%] elation [ 4.17%] [ 0.00%] + elation [ 4.17%] [ 0.00%] elation [ 4.17%] [ 0.00%] + ecstasy [ 3.65%] [ 0.00%] ecstasy [ 4.69%] [ 0.00%] + ecstasy [ 4.69%] [ 0.00%] ecstasy [ 4.17%] [ 0.00%] + bliss [ 3.65%] [ 0.00%] bliss [ 4.17%] [ 0.00%] + bliss [ 4.69%] [ 0.00%] bliss [ 3.65%] [ 0.00%] + delight [ 3.65%] [ 0.00%] delight [ 4.17%] [ 0.00%] + delight [ 4.17%] [ 0.00%] delight [ 4.17%] [ 0.00%] + rapture [ 4.69%] [ 0.00%] rapture [ 4.17%] [ 0.00%] + rapture [ 4.17%] [ 0.00%] rapture [ 3.65%] [ 0.00%] + + +skyvase.pov statistics +-------------------------------------- +Resolution 1024 x 768 +# Rays: 3773743 # Pixels: 798720 # Pixels supersampled: 17381 + Ray->Shape Intersection Tests: + Type Tests Succeeded Percentage + ----------------------------------------------------------- + Sphere 6304452 1170727 18.57 + Plane 63822062 35385552 55.44 + Quadric 6304452 2770858 43.95 + Cone 5918163 4839298 81.77 + Bounds 5918163 3152226 53.26 + Calls to Noise: 4327871 + Calls to DNoise: 5141872 + Shadow Ray Tests: 10498615 Blocking Objects Found: 254807 + Reflected Rays: 2818594 + Time For Trace: 0 hours 0 minutes 47.00 seconds + +------ +NB: Note that for comparison purposes with other skyvase benchmarks that this + is rendered at 1024x768 instead of the usual 640x480! + +Enjoy! + +Andreas Dilger +adilger@enel.ucalgary.ca + +Harald Deischinger +k3096e5@c210.edvz.uni-linz.ac.at diff -Naur source.ori/pvm/PVMPOV.general source/pvm/PVMPOV.general --- source.ori/pvm/PVMPOV.general Thu Jan 1 01:00:00 1970 +++ source/pvm/PVMPOV.general Sun Sep 12 00:25:35 1999 @@ -0,0 +1,98 @@ + + - PVMPOV - written by Andreas Dilger, June 1 1995 + and Harald Deischinger, 1997 + modified by Jakob Flierl, 1999 + + +***** NOTE - This is an UNOFFICIAL modification to POV-Ray 3.1e ***** + +** DO NOT SEND PROBLEM REPORTS TO THE POV TEAM REGARDING THESE MODIFICATIONS. ** + +The author disclaims all warranties with regard to this software, including +all implied warranties of merchant-ability and fitness. The code is simply +distributed as it is. + + +General information +------------------- + +"I only ask that credit (or blame) is attributed to me for the original work." + - Brad Kline + +The original author of the PVM POV-Ray patch on which this is based is Brad +Kline, of Cray Research, Inc. Because of problems running the original code at +my site, and a desire to learn more about PVM for my Master's work, I have +changed large portions to not only be more user-friendly about reporting +errors, but also hopefully more robust and faster. + - Andreas Dilger + +Because Andreas has other things to do, I have taken over the part of +PVMing POV-Ray 3.1. If you have problems with this program, don't be afraid +to ask me for help. + +Please send me a mail, if you're using PVMPOV Version 3.1e.2. + - Jakob Flierl +Contact +------- +email : flierl@luga.de + +Important information/Problems +------------------------------ + +* This version is not tested very vell, so it is very likely, that you + find some errors - please tell me about them. + + Especially the very time consuming new features (radiosity) are not + really tested. + +* Shellout commands are always done by the master process. + +* Now PVMPOV also supports animations. PVMPOV computes multiply frames + simultaniously, so it is not possible to do "recursive animations" + (like the desk example in povscn/level3). You have to run PVMPOV extra + for each frame. + + All the pre-scene shellouts are done in the correct order, and the same + for the post-scene shellouts. BUT for example the pre-scene shellout for + frame 2, will be executed before the post-scene shellout for frame 1! + + Field Rendering (+UF) is not working yet. + +* The X11 display now displays everything. + There are no longer "forgotten blocks". + +* The master process of PVMPOV does not parse the input file. So you will + NOT see errors in the .pov file - the slaves will simply not start. + So you should turn off PVM support (pvm=off or -N) while developing your + scene file. + + You will also get no warning, if the slave can not find the input file. + The slave will simply never ask for work. Check "/tmp/pvml." for + errors with the slaves. + +* You might get errors, that pvm can not find the executables for the slaves. + You can solve this in lot's of ways. + - Start pvmpov with an absolute filename, + e.g. '/usr/bin/pvmpov ...' + - Specify the absolute path for the slaves, + e.g. 'pvmpov -NS/usr/bin/pvmpov ...' + You can set this option in you '.povrayrc' file. + - Move your executables into ~/pvm3/bin/ARCH + - Use the 'ep=PATH' option in your hostfile + Read the PVM documentation for more information. + +* When using automount you might have the problem, that the slaves can't + change to the working directory. Use the +ND (pvm_wd) option. + Calling pvmpov with "+ND$PWD" should solve the problem. + +* For more information see "PVMPOV.1st_scene" + +Changed files +------------- + optin.c Accept new options for PVMPOV + optin.h + optout.c Print new options and help texts for PVMPOV + optout.h + povray.c Major changes + render.c Minor changes + render.h diff -Naur source.ori/pvm/PVMPOV.install source/pvm/PVMPOV.install --- source.ori/pvm/PVMPOV.install Thu Jan 1 01:00:00 1970 +++ source/pvm/PVMPOV.install Sun Sep 12 00:25:35 1999 @@ -0,0 +1,39 @@ +How to compile and install PVMPOV. + +COMPILE +------- +Before compiling PVMPOV you must me sure to have PVM installed correctly. +PVM is not included with this package. You have to download and install it +manually: http://www.epm.ornl.gov/pvm/pvm_home.html + +After that you can compile and install PVMPOV: + + tar xvfz pvmpov-3.1e.?.tgz <- unpack the PVMPOV distfiles + cd pvmpov3_1e_? + tar xvfz ~/povuni_s.tgz <- unpack the POV source from ftp://ftp.povray.org/ +[POV-Ray versions 3.1e - 3.1g are reported to work fine with PVMPOV 3.e1.2.] + + ./inst-pvm <- apply the PVMPOV patch + +[This script applies the patch and searches for any rejected files. +If there are any .rej-files the changes are not done correctly - +you can try to do the changes by hand.] + + cd pvmpov3_1e_?/povray31/source/pvm + +[You can choose:] + aimk newunix <- to create the UNIX text only binaries + aimk newsvga <- to create binaries for svgalib (Linux only) + aimk newxwin <- to create binaries using X11 to display + +This should create the executables 'pvmpov', 's-pvmpov' or 'x-pvmpov'. + +INSTALL +------ +If your are running PVMPOV on a homogenous cluster, you can copy the +executables to "/usr/bin" and "/usr/X11R6/bin". + +Read "PVMPOV.example" to get started. + +Jakob Flierl, flierl@luga.de +May 1999 diff -Naur source.ori/pvm/PVMPOV.radiosity source/pvm/PVMPOV.radiosity --- source.ori/pvm/PVMPOV.radiosity Thu Jan 1 01:00:00 1970 +++ source/pvm/PVMPOV.radiosity Sun Sep 12 00:25:35 1999 @@ -0,0 +1,39 @@ +[...] PVMPOV and radiosity: + +Radiosity is currently not working with PVMPOV; the resulting images look like +mosaics. There's a currently only a workaround suggested by Andreas Dilger: + +You must create a .rca file for the whole image [when rendering with the +radiosity option turned on], which I did manually by running +"pvmpov -n +qr +d +i .pov" and stopping the rendering when the mosaic +preview is complete, to give me .rca. Then I started a new +"pvmpov +c +qr +d +i .pov +nt2" on my 2 CPU system, and it rendered +the same image as letting "pvmpov -n +qr +d +i .pov" finish on 1 CPU. +The .rca file was readable by both slave systems because they were local, +but it would also be possible to use NFS for this, like normal PVMPOV. +This is some work to do manually, but it will ensure that the radiosity +will work properly with PVMPOV. + +[for programmers only:] +What needs to be done now is to have the PVMPOV master run the mosaic +preview for radiosity before the slave tasks start. It looks like this +could be done in pvm.c:PvmStartFrame(), but the current design of the +master doesn't allow it to do any rendering because it does not parse +the scene, so it would need to be reworked to do this. + +In the old PVMPOV 2.2 design, the master did the parsing of the scene +file to ensure that the syntax was correct. I think this is the better +design. If radiosity is used, the master will render the preview before +the rendering is started on the slaves, so the .rca file is available. +If the master does the parsing, then it can also send the data to the +slaves via the POB binary file format through PVM (no NFS needed) so we +don't waste the time spent parsing the scene at the master. + +I think what needs to be done is modify povray.c so it doesn't call +Pvm_Master_Control() instead of FrameRender() in povray.c:398. Then +FrameRender() should be modified to work with PVMPOV code, replacing +PvmStartFrame() entirely. This will mean that PVMPOV will render one +frame at a time, which is OK I think, because it means that some of +the problems with PVMPOV 3.x will be fixed, like post-scene shellouts. +I think it will also make PVMPOV less complex also, because it only +has to work on 1 frame at a time. diff -Naur source.ori/pvm.c source/pvm.c --- source.ori/pvm.c Thu Jan 1 01:00:00 1970 +++ source/pvm.c Sun Sep 12 00:25:35 1999 @@ -0,0 +1,1556 @@ +/**************************************************************************** +* pvm.c +* +* This module implements the PVM control routines for PVM'd POVRAY. +* +* This file was written by Brad Kline. April 1994. +* modified by Andreas Dilger Jan - May 1995 +* modified by Harald Deischinger, 1996 - 1997 +* +* NOTE - PVM is not supplied here. +* +* You need the PVM 3.x libraries, include files, and PVMD daemons. +* +* PVM is available from the University of Tennessee, Knoxville, TN. +* +* The author disclaims all warranties with regard to this software, +* including all implied warranties of merchant-ability and fitness. +* The code is simply distributed as it is. +* +* 970610 - New option PVM_WD. To specify a working directory for +* the slaves. [Deischi] +* New option PVM_Hosts. To give names for hosts to use +* as slaves. [Deischi] +* +* 9704.. - changed to to animations, lots of changes +* simplifications to the whole thing [Deischi] +* +* 96.... - Changed code to work with POVRAY 3.0 [Deischi] +* +* 950508 - Changed code that allocates blocks to be more robust. +* Changed the write_line routines to hook into the existing routines. +* Changed the slave startup processes to be more informative. +* +* 940511 - Remove code that tried to fit grid sections into whole parts. The +* code now supports odd sized images. +* +*****************************************************************************/ +#include +#include +#include "frame.h" +#include "vector.h" +#include "povproto.h" +#include "frame.h" +#include "povray.h" +#include "render.h" +#include "optout.h" +#include "pvm3.h" +#include "pvm.h" + + +/* This distribution scheme breaks down the image into to two dimensional + * areas. Each area has a designated grid control point. The grid area + * is up to PvmChunkWidth (-NWxx) pixels wide by PvmChunkHeight (-NHxx) + * high. If one uses the default, then a grid section would be 32x32, or + * 1024 pixels. In a 640x480 image, this would create 300 grid control + * points, and therefore 300 different sections to do work on (not lines.). + */ + +/* + * clock value in shellout command is not working correctly + * timings are not displayed (and done) correctly + * statistics can be wrong + * continue trace might produce wrong results -- needs testing + */ + +/* + * Global variables + */ +char PvmArch[20] = { 0 }; +int PvmChunkHeight = PVM_DEFAULT_GRID_HEIGHT; /* height of a block */ +int PvmChunkWidth = PVM_DEFAULT_GRID_WIDTH; /* widht of a block */ +int PvmRowsToSend = 1; +int PvmSlave = 0; +int PvmTasks = 9999; +int PvmNice = PVM_DEFAULT_NICE; +int PvmMTid; +int PvmBlockNum; +char * PvmSlavename = NULL; +int PvmNextFrame = 0; +char PvmWorkingDir[PATH_MAX+1] = ""; /* PVM Working Directory */ +char ** PvmHosts = NULL; +int PvmHostsN = 0; + +/* + * local functions + */ +static void PvmSpawnError PARAMS((int)); +static void PvmSpawnTidError PARAMS((int)); +static void PvmSpawn PARAMS((void)); +static void PvmCheckFirst PARAMS((void)); +static void PvmInitSlaveStat PARAMS((void)); +static void PvmInitFrameStat PARAMS((void)); + +static int PvmMasterReceive PARAMS((void)); +static void PvmIdentifySlave PARAMS((void)); +static void PvmSendWork PARAMS((int block)); +static void PvmReassignWork PARAMS((int block)); +static void PvmAssignWork PARAMS((void)); +static void PvmMasterEnd PARAMS((void)); +static int PvmStartFrame PARAMS((void)); +static void PvmFinishFrame PARAMS((void)); +static void PvmReceiveData PARAMS((void)); +static void PvmWrite PARAMS((void)); + +static void PvmUnpackStats PARAMS((void)); + + +static int grid_points; /* number of grid blocks for one frame */ +static int grid_ppline; /* Number of grid blocks in a row */ +static int frames; /* num. of frames to do */ +static int frame = -1; /* the current frame, start with 0 to count */ +static int next_frame_finish; +static int frame_display; /* the frame that is displayed */ + +static int grid_lines_done = 0; /* num. of PvmCthunkWidth lines done (only statistics) */ + +static int npixels_done = 0; /* Number of pixels done, by all processors */ + +static int row_start; /* First row in the block */ +static int row_end; /* Last row in the block */ +static int col_start; /* First column in the block */ +static int col_end; /* Last column in the block */ +static int msgtag; + +static struct timeval wait_time; /* Time to wait for slave stats */ +static struct pvmhostinfo *hostinfo; /* Info on each PVM host */ +static int minions; /* Number of slave tasks */ +static int nhost; /* Number of available pvm hosts */ +static pvm_slave_stat *slave_stat; /* Info on the slaves */ +static pvm_frame_stat *frame_stat; +static int rtid; /* tid of the message sender */ +static int *tids; /* tids of all the slaves */ +static int slave_num; /* Relative host number of sender */ +static char *slave_name; /* Hostname of sending slave */ +static int nstat = 0; /* Number of slaves returning stats */ + +static int bitmask; /* Start tasks on a specific arch */ +static int bufid; /* pvm recieve buffer id */ +static int bytesin; /* Size of message */ +static char **sargs; /* argv for the slaves */ +static char slave_option[PATH_MAX+4]; /* the directory argument */ + + +/* in povray.c */ +extern char Actual_Output_Name[FILE_NAME_LENGTH]; +void setup_output_file_name PARAMS((void)); +void open_output_file PARAMS((void)); +void set_output_file_handle(); + + + +/******************************************************************************** + * Initialize Master Process + *******************************************************************************/ + + +/* + * Spawn the slaves, and check for errors + */ + +/* handle an error from pvm_spawn */ +static void PvmSpawnError(m) + int m; +{ + /* Only quit if we can't start many tasks, or an error condition. */ + Error_Line( "...error spawning tasks because \n"); + switch (m) { + case PvmBadParam: + Error_Line("of bad parameter.\n"); + break; + case PvmNoHost: + Error_Line("host not in PVM.\n"); + break; + case PvmNoFile: + Error_Line("executable not found.\n"); + break; + case PvmNoMem: + Error_Line("no memory on host.\n"); + break; + case PvmSysErr: + Error_Line("pvmd not responding.\n"); + break; + case PvmOutOfRes: + Error_Line("lack of resources.\n"); + break; + default: + Error_Line("of unknown error.\n"); + } + pvm_perror(""); + pvm_exit(); + close_all(); + exit(1); +} + +static void PvmSpawnTidError(tid) + int tid; +{ + + Warning(0.0, "...spawn failure because "); + switch (tid) { + case PvmBadParam: + Warning(0.0, "of bad parameter.\n"); + break; + case PvmNoHost: + Warning(0.0, "host not in PVM.\n"); + break; + case PvmNoFile: + Warning(0.0, "executable not found.\n"); + break; + case PvmNoMem: + Warning(0.0, "no memory on host.\n"); + break; + case PvmSysErr: + Warning(0.0, "pvmd not responding.\n"); + break; + case PvmOutOfRes: + Warning(0.0, "lack of resources.\n"); + break; + default: + Warning(0.0, "of unknown error.\n"); + } +} + +static void PvmSpawn() { + int i; + + /* Start up the slave processes */ + if (PvmArch[0] != '\0') { + Status_Info(" Spawning %s with %d PVM tasks on arch %s...\n", + PvmSlavename, PvmTasks,PvmArch); + } else { + Status_Info(" Spawning %s with %d PVM tasks on %d hosts...\n", + PvmSlavename, PvmTasks,nhost); + } + + if(PvmHosts) { /* start at specified hosts */ + int m; + minions = 0; + for(i=0; i < PvmTasks; i++) { /* start one by one */ + m = pvm_spawn(PvmSlavename, sargs, PvmTaskHost, PvmHosts[i % PvmHostsN], + 1, tids+minions); + if( m < 0) { /* spawn failed */ + PvmSpawnError(m); + } else if (m==0) { /* failed a little bit */ + PvmSpawnTidError(tids[minions]); + } + minions += m; + } + + } else { /* start all at once somwhere */ + minions = pvm_spawn(PvmSlavename, sargs, bitmask, PvmArch, PvmTasks, tids); + + if(minions < 0) /* check for total failure */ + PvmSpawnError(minions); + for (i = minions; i < PvmTasks; i++) /* errors/process */ + PvmSpawnTidError(tids[i]); + } + + /* Check if "few" tasks could be started */ + if (minions < (PvmTasks + 1) / 2) { + if(minions==0) { + Error_Line("...No tasks"); + } else { + Error_Line("...only %d of %d tasks", minions, PvmTasks); + } + Error_Line(" spawned! Quitting.\n"); + pvm_exit(); + close_all(); + exit(1); + } else { + Status_Info(" ...%d PVM tasks successfully spawned.\n", minions); + } + + PvmTasks = minions; +} + + +/* + * Wait up to 120 seconds for the first to check in + */ +static void PvmCheckFirst() { + wait_time.tv_sec = 120; + wait_time.tv_usec = 0; + + Status_Info(" Waiting up to %lds for first slave to start...\n", + wait_time.tv_sec); + minions = 0; + + if( pvm_trecv(-1, PVM_INIT_SLAVE, &wait_time) <= 0 ) { + /* No slave checked in. Something is wrong */ + Error_Line("First slave did not start in time! Quitting.\n"); + Error_Line("See /tmp/pvml. on the master PVM host for\n"); + Error_Line("more info on why the slave didn't start correctly.\n"); + pvm_exit(); + close_all(); + exit(1); + } else { + minions++; + } + + if (opts.Options & VERBOSE) { + Status_Info(" Slave 0 successfully started.\n"); + } +} + + +/* + * Get a status struct for each slave + */ +static void PvmInitSlaveStat() { + + slave_stat = (pvm_slave_stat *)POV_CALLOC(PvmTasks, sizeof(pvm_slave_stat), "slave_stat"); + + /* Set up pointers to all the host names and the slave status */ + for (slave_num = 0; slave_num < PvmTasks; slave_num++) { + int j; + int dtid; + + dtid = pvm_tidtohost(tids[slave_num]); + slave_stat[slave_num].name = "unknown"; + + for (j = 0; j < nhost; j++) { + if (dtid == hostinfo[j].hi_tid) { + slave_stat[slave_num].name = (char*)POV_CALLOC(strlen(hostinfo[j].hi_name)+1, + sizeof(char), "hostnames"); + strcpy(slave_stat[slave_num].name,hostinfo[j].hi_name); + break; + } + } + slave_stat[slave_num].frame_assigned = PVM_SLAVE_FREE; + slave_stat[slave_num].block_reassigned = PVM_SLAVE_FREE; + } +} + +/* + * Get a status struct for each frame + */ +static void PvmInitFrameStat() { + int i,j; + + frames = (opts.FrameSeq.FrameType == FT_MULTIPLE_FRAME) ? + opts.FrameSeq.FinalFrame - opts.FrameSeq.InitialFrame + 1 : + 1; + frame_stat = (pvm_frame_stat *)POV_CALLOC(frames, sizeof(pvm_frame_stat), "frame_stat"); + frame_display = -1; + + for(i=0; i < frames; i++) { + frame_stat[i].block_to_assign = 0; + frame_stat[i].block_to_reassign = 0; + + frame_stat[i].block_to_write = 0; + + frame_stat[i].row_to_write = opts.First_Line; + frame_stat[i].row_to_malloc = opts.First_Line; + + frame_stat[i].block_stat = + (pvm_block_stat *)POV_CALLOC(grid_points, sizeof(pvm_block_stat), "block_stat"); + + for(j=0; j < MaxStat; j++) + Init_Counter(frame_stat[i].stats[j]); + } +} + +/* + * Initialize PVM Master + */ +void +Pvm_Master_Init(argc, argv) + int argc; + char **argv; +{ + int i; + + Status_Info("Initializing PVMPOV\n"); + + /* Get a table for the TIDs */ + tids = (int *)POV_CALLOC(PvmTasks, sizeof(int), "tids"); + + /* Build a new arg list for the sub-tasks - add -ni in front */ + sargs = (char **)POV_CALLOC(argc + 1, sizeof(char *), "sargs"); + + /* Move argument pointers to new list array, keep position 0 free for -ni option */ + for (i = 1; i < argc; i++) { + sargs[i] = argv[i]; + } + + /* Get current directory for the slave tasks */ + if(PvmWorkingDir[0] == '\0') + if ( GETCWD(PvmWorkingDir) == NULL) { + Error("PVM Master cannot get current directory.\n"); + return; + } + + /* Add slave flag */ + sprintf(slave_option, "-nI%s", PvmWorkingDir); + sargs[0] = slave_option; + + /* Enroll in PVM */ + if (pvm_mytid() < 0) { + Error("Error starting master task.\n"); + } + + if (pvm_config(&nhost, (int *)NULL, &hostinfo) < 0) { + pvm_exit(); + Error("Error getting PVM machine status.\n"); + } + + bitmask = PvmTaskDefault; + + /* If PvmArch is specified, use that architecture */ + if (PvmArch[0] != '\0') { + bitmask = PvmTaskArch; + + /* If arch is psecified, but not the number of tasks, go and + * count how many hosts of that type there are. + */ + if (PvmTasks >= 9999) { + PvmTasks = 0; + + for (i = 0; i < nhost; i++) { + if (strcmp(PvmArch,hostinfo[i].hi_arch) == 0) + PvmTasks++; + } + } + } + + /* If PvmTasks is specified, use that many tasks, otherwise use + * as many hosts as possible. + */ + if (PvmTasks >= 9999) { + PvmTasks = nhost; + } + + /* if no slavename was given, use name of current executable */ + if(PvmSlavename == NULL) + PvmSlavename = argv[0]; + + PvmSpawn(); /* spawn the slaves */ + PvmCheckFirst(); /* wait for first slave */ + + pvm_setopt(PvmRoute, PvmRouteDirect); + + /* Max # of pixels in a grid row */ + PvmChunkWidth = min(PvmChunkWidth, opts.Last_Column - opts.First_Column); + + /* Grid points per line - rounded up */ + grid_ppline = (opts.Last_Column - opts.First_Column + PvmChunkWidth - 1) / PvmChunkWidth; + + /* Establish grid section height */ + PvmChunkHeight = min(PvmChunkHeight, opts.Last_Line - opts.First_Line); + + /* Total grid points in the image - round up PvmChunkHeight */ + grid_points = ((opts.Last_Line - opts.First_Line + PvmChunkHeight - 1) / PvmChunkHeight) * + grid_ppline; + + PvmInitSlaveStat(); + PvmInitFrameStat(); + + /* initialize total stats */ + for(i=0; i < MaxStat; i++) { + Init_Counter(totalstats[i]); + } +} + + + + + +/******************************************************************************** + * Master Control + ********************************************************************************/ + +/* + * Receive a message + */ +static int PvmMasterReceive() { + + /* wait for a message from the slaves */ + if ((bufid = pvm_trecv(-1, -1, &wait_time)) < 0) { + Error_Line( "PVM Master cannot receive.\n"); + pvm_perror(""); + pvm_exit(); + exit(1); + } + + /* We timed out waiting for any messages */ + if (bufid == 0) { + Status_Info("."); + fflush(stderr); + + return 1; + } + + return 0; +} + +/* + * Find name and number of slave, of current message + */ +static void PvmIdentifySlave() { + + /* get info about this msg. */ + if (pvm_bufinfo(bufid, &bytesin, &msgtag, &rtid) < 0) { + Error_Line( "PVM Master cannot bufinfo.\n"); + pvm_perror(""); + pvm_exit(); + return; + } + + /* find the name of the slave, sending the msg. */ + slave_name = "unknown"; + for (slave_num = 0; slave_num < PvmTasks; slave_num++) { + if (tids[slave_num] == rtid) { + slave_name = slave_stat[slave_num].name; + break; + } + } +} + +/* + * Send a PVM_WORK message + * Set block to PVM_GRID_POINT_ASSIGNED + */ +static void PvmSendWork(block) + int block; +{ + int row_start, row_end, col_start, col_end; +#ifdef DEBUG + Status_Info("Assigning block: %d, frame: %d of %d to %s.\n", block, frame, frames, slave_name); +#endif /* DEBUG */ + + frame_stat[frame].block_stat[block].status = PVM_GRID_POINT_ASSIGNED; + frame_stat[frame].block_stat[block].assigned_tid = rtid; + + row_start = (block/grid_ppline) * PvmChunkHeight + opts.First_Line; + row_end = (row_start + PvmChunkHeight) - 1; + + if (row_end >= opts.Last_Line) + row_end = opts.Last_Line - 1; + + col_start = (block%grid_ppline) * PvmChunkWidth + opts.First_Column; + col_end = (col_start + PvmChunkWidth) - 1; + + if (col_end >= opts.Last_Column) + col_end = opts.Last_Column - 1; + + pvm_initsend(PvmDataDefault); + pvm_pkint(&frame,1,1); + pvm_pkint(&block,1,1); + pvm_pkint(&row_start,1,1); + pvm_pkint(&row_end,1,1); + pvm_pkint(&col_start,1,1); + pvm_pkint(&col_end,1,1); + if (pvm_send(rtid, PVM_WORK) < 0) { + Error_Line( "PVM Master can't send to slave on %s.\n",slave_name); + pvm_perror(""); + pvm_exit(); + exit(1); + } + + frame_stat[frame].block_to_assign ++; +} + +/* + * Send a PVM_WORK message + * Set block to PVM_GRID_POINT_REASSIGNED + */ +static void PvmReassignWork(block) + int block; +{ +#ifdef DEBUG + Status_Info("Reassigning %d to %s.\n", block, + slave_name); +#endif /* DEBUG */ + + frame_stat[frame].block_stat[block].status=PVM_GRID_POINT_REASSIGNED; + frame_stat[frame].block_stat[block].reassigned_tid = rtid; + slave_stat[slave_num].block_reassigned = block; + + if (frame_stat[frame].block_stat[block].wr_line == 0) { + row_start = (block / grid_ppline)*PvmChunkHeight + opts.First_Line; + } else { + row_start = frame_stat[frame].block_stat[block].wr_line; + } + row_end = (block/grid_ppline + 1) * PvmChunkHeight - 1; + + if (row_end >= opts.Last_Line) + row_end = opts.Last_Line - 1; + + col_start = (block % grid_ppline) * PvmChunkWidth + + opts.First_Column; + col_end = (col_start + PvmChunkWidth) - 1; + + if (col_end >= opts.Last_Column) + col_end = opts.Last_Column - 1; + + pvm_initsend(PvmDataDefault); + pvm_pkint(&frame,1,1); + pvm_pkint(&block,1,1); + pvm_pkint(&row_start,1,1); + pvm_pkint(&row_end,1,1); + pvm_pkint(&col_start,1,1); + pvm_pkint(&col_end,1,1); + if (pvm_send(rtid, PVM_WORK) < 0) { + Error_Line( "PVM Master can't send to slave on %s.\n",slave_name); + pvm_perror(""); + pvm_exit(); + exit(1); + } + + frame_stat[frame].block_to_reassign++; +} + + +/* + * Do all the stuff, that is necessary for a new frame + * This is called before the PVM_WORK is sent + * + * A lot of this is taken from 'FrameRender' + * + * ret: 0 .. OK send the PVMWOR + * 1 .. Shellout gave SKIP_ONCE_RET -> this frame is done + */ +static int PvmStartFrame() { + int Frame_Result; + + if(frame_stat[frame].status != PVM_FRAME_FREE) { + Warning(0.0, "Frame %d already started.\n", frame); + return 1; + } + + if(opts.Options & VERBOSE) + Status_Info("\nStarting frame %d...", opts.FrameSeq.InitialFrame + frame); + + frame_stat[frame].status = PVM_FRAME_WORKING; + + /* allocate row pointers */ + frame_stat[frame].row_ptr = (COLOUR **)POV_CALLOC(opts.Last_Line, sizeof(COLOUR *), "row_ptr"); + + opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame + frame; + setup_output_file_name(); + + /* Execute a shell-out command before tracing */ + Frame_Result = POV_SHELLOUT(PRE_FRAME_SHL); + + if (Frame_Result == ALL_SKIP_RET) { + /* stop completely */ + int i; + for(i=frame; i < frames; i++) { + frame_stat[i].block_to_assign = grid_points; + frame_stat[i].block_to_reassign = grid_points; + frame_stat[i].status = PVM_FRAME_DONE_SHELLOUT; + } + /* continue searching, frames before this one, should be finished */ + return 1; + } + + if (Frame_Result == SKIP_ONCE_RET) { + /* mark frame as done */ + frame_stat[frame].block_to_assign = grid_points; + frame_stat[frame].block_to_reassign = grid_points; + frame_stat[frame].status = PVM_FRAME_DONE_SHELLOUT; + + /* continue searching */ + return 1; + } + + /* + * open output file + */ + set_output_file_handle(); + open_output_file(); + + Initialize_Renderer(); + + /* + * Start displaying + */ + if( (opts.Options & DISPLAY) && !Display_Started) { + Status_Info("\nDisplaying frame %d...", opts.FrameSeq.InitialFrame + frame); + POV_DISPLAY_INIT(Frame.Screen_Width, Frame.Screen_Height); + Display_Started = TRUE; + frame_display = frame; + Status_Info("\n"); + } + + + /* + * read already rendered parts + */ + if ((opts.Options & DISKWRITE) && (opts.Options & CONTINUE_TRACE)) { + int i; + int fl = opts.First_Line; /* remember start of image */ + + Read_Rendered_Part(Actual_Output_Name); /* read -> changes opts.First_line */ + + frame_stat[frame].row_to_write = + frame_stat[frame].row_to_malloc = opts.First_Line; + + frame_stat[frame].block_to_write = + frame_stat[frame].block_to_assign = (opts.First_Line/PvmChunkHeight)*grid_ppline; + /* blocks above the new start are already finished */ + for(i=0; i < frame_stat[frame].block_to_assign; i++) { + frame_stat[frame].block_stat[i].status = PVM_GRID_POINT_WRITTEN; + } + if(opts.Options & VERBOSE) + Status_Info("Continuing with block %d in frame %d\n", + frame_stat[frame].block_to_assign, frame); + + opts.First_Line = fl; /* set back the old value */ + + + /* this should not be necessary */ + if (opts.Last_Line > Frame.Screen_Height) + opts.Last_Line = Frame.Screen_Height; + + if (opts.Last_Column > Frame.Screen_Width) + opts.Last_Column = Frame.Screen_Width; + } + + /* + * make a copy of the Output_File_Handle + */ + frame_stat[frame].output_file = Output_File_Handle; + if(Output_File_Handle->filename) { + char * n = POV_MALLOC(strlen(Output_File_Handle->filename)+1, "filename"); + strcpy(n, Output_File_Handle->filename); + frame_stat[frame].output_file->filename = n; + } + + if(opts.Options & VERBOSE) + Status_Info("\n"); + + return 0; +} + + +/* + * test, if the next frame in the sequence finished, + * do all the necessary stuff to close the frame (shellout, ...) + */ +static void PvmFinishFrame() { + + /* check, if frame has really finished */ + if( frame_stat[frame].row_to_write < opts.Last_Line) + return; + + /* if not already closed */ + if( frame_stat[frame].status < PVM_FRAME_DONE) { + + if(opts.Options & VERBOSE) + Status_Info("\nFinishing frame %d...", opts.FrameSeq.InitialFrame + frame); + Status_Info("rtw. %d\n", frame_stat[frame].row_to_write); + + frame_stat[frame].status = PVM_FRAME_DONE; + + POV_FREE(frame_stat[frame].row_ptr); + + /* Close output file */ + if (frame_stat[frame].output_file) { + Output_File_Handle = frame_stat[frame].output_file; + Close_File(frame_stat[frame].output_file); + } + + if( frame == frame_display) { + POV_DISPLAY_FINISHED + + if ((opts.Options & DISPLAY) && Display_Started) { + POV_DISPLAY_CLOSE + + Display_Started = FALSE; + } + + frame_display = -1; + } + + if (opts.histogram_on) + write_histogram (opts.Histogram_File_Name); + } + + if( frame_stat[frame].status < PVM_FRAME_DONE_SHELLOUT) { + /* the shellouts are done in the correct order */ + if( frame == next_frame_finish) { + int Frame_Result; + + frame_stat[frame].status = PVM_FRAME_DONE_SHELLOUT; + + opts.FrameSeq.FrameNumber = opts.FrameSeq.InitialFrame + frame; + setup_output_file_name(); + + Frame_Result = POV_SHELLOUT(POST_FRAME_SHL); + + if ((Frame_Result==SKIP_ONCE_RET) || (Frame_Result==ALL_SKIP_RET)) { + next_frame_finish = frames; + return; + } + next_frame_finish ++; /* one more frame finished */ + + frame ++; /* check next frame (might have finished earlier) */ + if( frame < frames) + PvmFinishFrame(); + } + } + if(opts.Options & VERBOSE) + Status_Info("\n"); +} + + +/* + * Assign Work + * + * A slaved requested work, now find free block (or a block to reassign) + */ +static void PvmAssignWork() { + int min_assigned; + int found; + int f; + + /* The slave wants work, but has already been reassigned something, + * then tell it to ask again when it really wants work. + */ + if( slave_stat[slave_num].block_reassigned != PVM_SLAVE_FREE) { +#ifdef DEBUG + Status_Info("%s already assigned on %d. Not reassigning.\n", + slave_name, + slave_stat[slave_num].block_reassigned); +#endif /* DEBUG */ + pvm_initsend(PvmDataDefault); + pvm_send(rtid, PVM_INIT_SLAVE); + + return; + } + + + /* + * search for a free block, take a frame with minimal + * number of assigned blocks + */ + found = 0; + min_assigned = grid_points; + + frame = slave_stat[slave_num].frame_assigned; + /* if slave was already assigned and that frame has still free blocks */ + if ( (frame >= 0) && + (frame_stat[frame].block_to_assign < grid_points) ) { + + min_assigned = 0; + found = 1; + } + + /* find free frame with minimal number of assigned frames */ + for(f = 0; (f < frames) && (min_assigned > 0); f ++) { + if( (frame_stat[f].block_to_assign < min_assigned) && + (frame_stat[f].block_to_assign < grid_points) ) { + +#ifdef DEBUG + Status_Info("\nfound: frame %d, ass: %d, min: %d\n", + f, frame_stat[f].block_to_assign, + min_assigned); +#endif + + min_assigned = frame_stat[f].block_to_assign; + frame = f; + found = 1; + + } + } + + if( found ) { + /* first block for that frame */ + if( frame_stat[frame].block_to_assign == 0) { + /* Do the necessary preparations for a new frame */ + if( PvmStartFrame() ) { + /* Shellout gave SKIP_ONCE_REt -> search next block */ + PvmAssignWork(); + return; + } + } + + slave_stat[slave_num].frame_assigned = frame; + PvmSendWork(frame_stat[frame].block_to_assign); + return; + } + + + /* + * found no free block + * search for a block to reassign + */ + + /* if this slave was slow, and there are enough other slaves left, + don't use this anymore */ + if ( (slave_stat[slave_num].pixels_done < npixels_done*9/10/PvmTasks) && + (minions >= (PvmTasks + 3) / 4) ) { + + Status_Info("Not using %s for reassignment (%d%%)\n", slave_name, + 100*slave_stat[slave_num].pixels_done*PvmTasks/npixels_done); + + /* tell slave to finish work */ + pvm_initsend(PvmDataDefault); + pvm_send(rtid, PVM_STOP_SLAVE); + } + + found = 0; + + frame = slave_stat[slave_num].frame_assigned; + /* if slave was already assigned and that frame has free blocks */ + if ( (frame >= 0) && + (frame_stat[frame].block_to_reassign < grid_points) ) { + + min_assigned = 0; + found = 1; + } + + /* find first frame with minimal number of reassigned frames */ + min_assigned = grid_points + 1; + for(f = 0; (f < frames) && (min_assigned > 0); f ++) { + if( frame_stat[f].block_to_reassign < min_assigned) { + + min_assigned = frame_stat[frame].block_to_reassign; + frame = f; + found = 1; + } + } + + if( found ) { + /* find block in frame */ + int block; + for(block = 0; block < grid_points; block ++) { + if( frame_stat[frame].block_stat[block].status < PVM_GRID_POINT_DONE) { + slave_stat[slave_num].frame_reassigned = frame; + PvmReassignWork(block); + return; + } + } + } + + /* + * there is nothing left to reassign + */ + Status_Info("All blocks are assigned. Stopping %s.\n", slave_name); + + /* tell slave to finish work */ + pvm_initsend(PvmDataDefault); + pvm_send(rtid, PVM_STOP_SLAVE); + +} + +/* + * display a part from one line + */ +static void PvmDisplayPlot(row_ptr, row, col, ncols) + COLOUR * row_ptr; + int row, col, ncols; +{ + unsigned char Red, Green, Blue, Alpha; + DBL grey; + int i; + + /* draw line */ + for (i = col; i < col+ncols; i++) { + extract_colors(row_ptr[i], &Red, &Green, &Blue, &Alpha, &grey); + POV_DISPLAY_PLOT(i, row, Red, Green, Blue, Alpha); + } + /* force draw of the whole line (with X11) */ + i = opts.Last_Column - 1; + extract_colors(row_ptr[i], &Red, &Green, &Blue, &Alpha, &grey); + POV_DISPLAY_PLOT(i, row, Red, Green, Blue, Alpha); +} + + +/* + * one of the slaves sends a part of it's computed data + */ +static void PvmReceiveData() { + int ncols; + int i; + int block; + + pvm_upkint(&frame, 1, 1); + pvm_upkint(&block, 1, 1); + pvm_upkint(&row_start, 1, 1); + pvm_upkint(&col_start, 1, 1); + pvm_upkint(&ncols, 1, 1); + + row_end = row_start + (bytesin - 3*sizeof(int))/(5*ncols*sizeof(COLC)); + col_end = col_start + ncols; + +#ifdef DEBUG + Status_Info("receiving frame: %d, row: %d-%d, col: %d-%d\n", + frame, row_start, row_end, col_start, col_end); +#endif + + /* Make some checks to see if the data is not corrupted or invalid */ + if( (row_start < opts.First_Line) || (col_start < opts.First_Column) || + (row_end > opts.Last_Line) || (col_end > opts.Last_Column) || + (frame < 0) || (frame >= frames) || + (ncols > PvmChunkWidth) || (row_end - row_start > PvmChunkHeight) ) { + + Warning(0.0, "Bad block recieved from %s\n",slave_name); + Warning(0.0, "Frame %d, Row %d - %d, Column %d - %d.\n", frame, row_start, row_end, + col_start, col_end); + + return; + } + + /* get one row after the other */ + for (i = row_start; i < row_end; i++) { + if (i >= frame_stat[frame].block_stat[block].wr_line) { /* Is incoming data current? */ + + /* allocate memory, to receive data */ + if (frame_stat[frame].row_ptr[i] == NULL) { /* Starting a new row */ + + frame_stat[frame].row_ptr[i] = (COLOUR *)POV_MALLOC(opts.Last_Column*sizeof(COLOUR), + "row_ptr"); + + while(frame_stat[frame].row_ptr[frame_stat[frame].row_to_malloc] != NULL && + frame_stat[frame].row_to_malloc < opts.Last_Line) { + frame_stat[frame].row_to_malloc++; + } + } + + /* get color data */ + pvm_upkCOL(&(frame_stat[frame].row_ptr[i][col_start][0]),5*ncols,1); + + frame_stat[frame].block_stat[block].wr_line = i + 1; + + if (frame == frame_display) { + PvmDisplayPlot(frame_stat[frame].row_ptr[i], i, col_start, ncols); + } + + /* Statistics - Attribute this grid section */ + grid_lines_done++; + slave_stat[slave_num].pixels_done += ncols; + + npixels_done += ncols; + } else { + /* another slave already sent this data */ + + COLOUR *dummy = (COLOUR *)POV_MALLOC(ncols * sizeof(COLOUR), "dummy row"); + pvm_upkCOL(&(dummy[0][0]),5*ncols,1); + + /* Statistics - Attribute this grid section */ + slave_stat[slave_num].pixels_late += ncols; + POV_FREE(dummy); + } + } + /* Has this entire block been rendered? */ + if (frame_stat[frame].block_stat[block].wr_line >= + (block/grid_ppline + 1)*PvmChunkHeight + opts.First_Line - 1 || + frame_stat[frame].block_stat[block].wr_line >= opts.Last_Line) { +#ifdef DEBUG + Status_Info("Block completed by %s\n", slave_name); +#endif /* DEBUG */ + frame_stat[frame].block_stat[block].status = PVM_GRID_POINT_DONE; + if (slave_stat[slave_num].block_reassigned == block) { +#ifdef DEBUG + Status_Info(" reassigned block cleared.\n"); +#endif /* DEBUG */ + slave_stat[slave_num].block_reassigned = PVM_SLAVE_FREE; + } +#ifdef DEBUG + else + { + Status_Info(" reassigned block is %d.\n", + slave_stat[slave_num].block_reassigned); + } +#endif /* DEBUG */ + } + + if ((opts.Options & VERBOSE) && (grid_lines_done % PvmChunkHeight) == 0) { + Status_Info("\r%5.2f of blocks complete.", + (float)100*grid_lines_done/PvmChunkHeight/grid_points/frames); + } +} + +/* + * Write out us many lines as possible + */ +static void PvmWrite() { + int i; + + do { + /* check, if there is a complete line to write */ + for (i = frame_stat[frame].block_to_write; + i < frame_stat[frame].block_to_write + grid_ppline; i++) { + if (frame_stat[frame].block_stat[i].wr_line <= frame_stat[frame].row_to_write) { +#ifdef DEBUG + Status_Info("needing block %d in frame %d\n", i, frame); +#endif + return; + } + } + + /* OK, we can write a whole line */ + if (opts.Options & DISKWRITE) { +#ifdef DEBUG + Status_Info("write: %d %d\n", frame, frame_stat[frame].row_to_write); +#endif + Write_Line(frame_stat[frame].output_file, + frame_stat[frame].row_ptr[frame_stat[frame].row_to_write], + frame_stat[frame].row_to_write); + } + + /* Still lines to allocate so */ + if (frame_stat[frame].row_to_malloc < opts.Last_Line) { + /* move this row buffer down, so we avaoid one malloc/free */ + frame_stat[frame].row_ptr[frame_stat[frame].row_to_malloc] = + frame_stat[frame].row_ptr[frame_stat[frame].row_to_write]; + frame_stat[frame].row_ptr[frame_stat[frame].row_to_write] = NULL; + frame_stat[frame].row_to_malloc++; + } else { /* Otherwise free the row buffer */ + POV_FREE(frame_stat[frame].row_ptr[frame_stat[frame].row_to_write]); + frame_stat[frame].row_ptr[frame_stat[frame].row_to_write]= NULL; + } + + frame_stat[frame].row_to_write++; + +#ifndef DEBUG + if ((opts.Options & VERBOSE) && (frame_stat[frame].row_to_write % 8) == 0) { + Status_Info("\r%5.2f of blocks complete.", + (float)100*grid_lines_done/PvmChunkHeight/grid_points/frames); + Status_Info(" %4d of %4d lines finished (in frame %d).", + frame_stat[frame].row_to_write - opts.First_Line, + opts.Last_Line - opts.First_Line, frame); + } +#endif /* not DEBUG */ + + /* Finished a whole row of blocks */ + if ((frame_stat[frame].row_to_write - opts.First_Line) % PvmChunkHeight == 0) { + for (i = frame_stat[frame].block_to_write; + i < frame_stat[frame].block_to_write + grid_ppline; i++) { + frame_stat[frame].block_stat[i].status = PVM_GRID_POINT_WRITTEN; + } + frame_stat[frame].block_to_write += grid_ppline; + } + + } while(frame_stat[frame].block_to_write < grid_points); +} + +/* + * Handle slave requests, distribute and collect work + */ +void Pvm_Master_Control() { + + /* Wait five minutes at a time between messages */ + wait_time.tv_sec = 300; + + next_frame_finish = 0; /* next frame that finishes */ + + /* There are incomplete frames and/or incoming messages, and there are any slaves left*/ + while ( (next_frame_finish < frames) || + ((pvm_probe(-1, -1) > 0) && (minions > 0))) { + + /* check for user abort */ + TEST_ABORT; + if (Stop_Flag) { + Render_Info("\nAborting render...\n"); + + PvmMasterEnd(); + + if ((opts.Options & DISPLAY) && Display_Started) { + POV_DISPLAY_CLOSE; + } + if (opts.Do_Stats) { + PRINT_STATS(stats); + } + Error("User abort.\n"); + + return; + } + + /* receive a request from a slave */ + if(PvmMasterReceive()) + continue; + + PvmIdentifySlave(); + +#ifdef DEBUG + Status_Info("Received msg %d from %s\n", msgtag, slave_name); +#endif + + /* a slaves requests work */ + switch(msgtag) { + case PVM_INIT_SLAVE: /* Another slave started ok */ + if (opts.Options & VERBOSE) { + Status_Info(" Slave %d at %s successfully started.\n", minions, slave_name); + } + minions++; + break; + + case PVM_NEED_WORK: /* a slave wants work */ + PvmAssignWork(); + break; + + case PVM_SLAVE_RESULTS: /* slaves sends us some data */ + PvmReceiveData(); + PvmWrite(); + PvmFinishFrame(); + break; + + case PVM_SLAVE_STATS: /* Too many stats, eh? */ +#ifdef DEBUG + Status_Info("Getting stats from %s.\n", slave_name); +#endif /* DEBUG */ + PvmUnpackStats(); + nstat++; + break; + + case PVM_SLAVE_EXIT: /* Slave has exited. */ + Status_Info("\nSlave at %s has exited.\n",slave_name); + + if (--minions <= 0) { + Error_Line( "Error - All slave tasks have exited!\n"); + pvm_exit(); + return; + } + break; + + default: /* something strange happend */ + Warning(0.0, "Bad block recieved from %s - message type %4X.\n", + slave_name, msgtag); + } + } /* incomplete frames */ + + /* Finished calculating everything */ + PvmMasterEnd(); +} + +/* + * Stop PvmMaster + */ +static void PvmMasterEnd() { + int i; + + /* tell all slave to stop work */ + for(i=0; i < PvmTasks; i++) { + pvm_initsend(PvmDataDefault); + pvm_send(tids[i], PVM_STOP_SLAVE); + } + + /* Wait a few seconds for lingering stats */ + wait_time.tv_sec = 4; + wait_time.tv_usec = 0; + + if (opts.Options & VERBOSE) + Status_Info("\nWaiting for remaining slave stats.\n"); + + /* some incoming stats */ + while ((bufid = pvm_trecv(-1, PVM_SLAVE_STATS, &wait_time)) > 0) { +#ifdef DEBUG + int msgtag; + if (pvm_bufinfo(bufid, &bytesin, &msgtag, &rtid) < 0) { + Error_Line( "PVM Master cannot bufinfo.\n"); + pvm_perror(""); + pvm_exit(); + return; + } + + PvmIdentifySlave(); + + Status_Info("Getting stats from %s.\n", slave_name); +#endif /* DEBUG */ + + PvmUnpackStats(); + nstat++; + } + + /* Dump stats for the tasks */ + Statistics( "\n\nPVM Task Distribution Statistics:\n"); + Statistics( "%20s [ done ] [ late ]","host name"); + Statistics( "%20s [ done ] [ late ]\n","host name"); + + for (i = 0; i < PvmTasks; i++) { + int dtid; + int j; + + dtid = pvm_tidtohost(tids[i]); + + for (j = 0; j < PvmTasks; j++) { + if (dtid == hostinfo[j].hi_tid) + break; + } + + Statistics("%20s [%5.2f%%] [%5.2f%%]", hostinfo[j].hi_name, + (DBL)slave_stat[i].pixels_done * 100 / npixels_done / frames, + (DBL)slave_stat[i].pixels_late * 100 / npixels_done / frames); + + if (i % 2 == 1) + Statistics("\n"); + } + + pvm_exit(); + + Statistics("\n\nPOV-Ray statistics for finished frames:"); + if(opts.FrameSeq.FrameType==FT_MULTIPLE_FRAME) { + for(i=0; i < next_frame_finish; i++) { + Statistics("\nFrame %d:\n", opts.FrameSeq.InitialFrame + i); + PRINT_STATS(frame_stat[i].stats); + } + if (opts.Do_Stats) { + Statistics("\n\nTotal statistics:"); + } + } + +} + +/* + * A slave sent some stats + */ +static void PvmUnpackStats() { + COUNTER tmpStat[MaxStat]; + + pvm_upkint(&frame, 1, 1); + + if( pvm_upkSTAT((long*)tmpStat, MaxStat*sizeof(COUNTER)/sizeof(long), 1) >= 0) { + int i; + COUNTER tmp; + + for(i=0; i < MaxStat; i++) { + /* stats per frame */ + Add_Counter(tmp,tmpStat[i],frame_stat[frame].stats[i]); + frame_stat[frame].stats[i] = tmp; + + /* total stats */ + Add_Counter(tmp,tmpStat[i],totalstats[i]); + totalstats[i] = tmp; + + if(opts.FrameSeq.FrameType != FT_MULTIPLE_FRAME) { + Add_Counter(tmp,tmpStat[i],stats[i]); + stats[i] = tmp; + } + } + } else { + Error_Line("Unpack Stats.\n"); + pvm_perror(""); + } +} + + + +/******************************************************************************** + * Slave Intialization + ********************************************************************************/ + +int Pvm_Slave_Init() { + /* Get master TID */ + PvmMTid = pvm_parent(); + + /* Have PVM notify us if the host POV process is killed + * or the local PVM daemon dies */ + pvm_notify(PvmTaskExit, PVM_KILL_SLAVE, 1, &PvmMTid); + pvm_setopt(PvmAutoErr, 2); + + /* Set direct routing */ + pvm_setopt(PvmRoute, PvmRouteDirect); + + /* Tell the master we started successfully */ + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid, PVM_INIT_SLAVE); + + /* Send an initial work request to the master, so that there is always + * an outstanding block assignment, and we don't have to wait for the + * master to respond to our request. + */ + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid, PVM_NEED_WORK); + + NICE(PvmNice); + + return 0; +} + + +/******************************************************************************** + * Slave Control + ********************************************************************************/ + +static void PvmSendStats() { + pvm_initsend(PvmDataDefault); + pvm_pkint( &frame, 1, 1); + pvm_pkSTAT( (long*)stats, MaxStat*sizeof(COUNTER)/sizeof(long), 1); + pvm_send(PvmMTid, PVM_SLAVE_STATS); +} + +/* + * Request a new block from the master, + * handle all messages sent by the master + * ret: 0 .. continue with new frame (or exit) + * 1 .. continue with this frame (but new region) + */ +int Pvm_Slave_Control() { + + struct timeval wait_time; + int bufid; + unsigned int msgtag; + int f; + static int first = 1; + + /* Wait up to 5 minutes for a message, otherwise exit */ + wait_time.tv_sec = 300; + wait_time.tv_usec = 0; + + if((bufid = pvm_trecv(PvmMTid, -1, &wait_time)) == 0) { + Error_Line( "Slave timed out waiting for master\n"); + pvm_exit(); + exit(1); + } else if (bufid < 0) { + Error_Line( "Cannot receive message from master."); + pvm_perror(""); + pvm_exit(); + exit(1); + } + + /* We have a block assignment from the master - IGOR SERVES! */ + if(pvm_bufinfo(bufid, (int *)0, &msgtag, &PvmMTid) < 0) { + Error_Line("Cannot get PVM bufinfo\n"); + pvm_perror(""); + pvm_exit(); + exit(1); + } + + switch( msgtag) { + case PVM_INIT_SLAVE: + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid, PVM_NEED_WORK); + break; + + case PVM_WORK: + /* Ask master for some more block assignments in advance + * to avoid waiting for them to be sent later + */ + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid, PVM_NEED_WORK); + + /* Unpack the message */ + pvm_upkint(&f, 1, 1); + pvm_upkint(&PvmBlockNum, 1, 1); + pvm_upkint(&opts.First_Line, 1, 1); + pvm_upkint(&opts.Last_Line, 1, 1); + pvm_upkint(&opts.First_Column, 1, 1); + pvm_upkint(&opts.Last_Column, 1, 1); + + /* Last_Line is really "number of lines to do" */ + opts.Last_Line++; + + /* Send the data every 1/4 of a block */ + PvmRowsToSend = (opts.Last_Line - opts.First_Line + 3) / 4; + + /* Last_Column is really "number of columns to do" */ + opts.Last_Column++; + + /* check, if a new frame starts */ + if(frame != f) { + if(!first) + PvmSendStats(); + first = 0; + PvmNextFrame = opts.FrameSeq.InitialFrame + f; + frame = f; + return 0; /* stop with this frame */ + } + + /* We need to do this for every block */ + Initialize_Renderer(); + + return 1; /* still the same frame */ + + case PVM_STOP_SLAVE: /* Everything is done, send stats and exit */ + PvmSendStats(); + PvmNextFrame = opts.FrameSeq.FinalFrame + 1; + return 0; + + case PVM_KILL_SLAVE: + Error_Line("\nSlave killed because of dead master.\n"); + pvm_exit(); + exit(1); + + default: /* Unknown message */ + Error_Line("\nUnknown control message from master - %X\n",msgtag); + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid,PVM_NEED_WORK); /* Request work again */ + } + return 0; +} + +void Pvm_Slave_Exit() { + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid, PVM_SLAVE_EXIT); + pvm_exit(); +} + +void Pvm_Write_Line(handle, line_data, line_number) + FILE_HANDLE *handle; + COLOUR *line_data; + int line_number; +{ + static int in_buffer = 0; + static int ncols; + + if (in_buffer == 0) /* No lines written yet. Initialize buffer */ + { + ncols = opts.Last_Column - opts.First_Column; + pvm_initsend(PvmDataDefault); + + /* Insert block info into message */ + pvm_pkint(&frame, 1, 1); + pvm_pkint(&PvmBlockNum, 1, 1); + pvm_pkint(&line_number, 1, 1); + pvm_pkint(&opts.First_Column, 1, 1); + pvm_pkint(&ncols, 1, 1); + } + + if (pvm_pkCOL(&(line_data[opts.First_Column][0]),5*ncols, 1) < 0) { + Error_Line( "Cannot pack in Pvm_Write_Line\n"); + pvm_perror(""); + if (in_buffer > 0) { + pvm_send(PvmMTid, PvmBlockNum); /* Send any lines we have done. */ + } + pvm_initsend(PvmDataDefault); + pvm_send(PvmMTid, PVM_SLAVE_EXIT); /* Tell the master we're dying */ + pvm_exit(); + return; + } + + in_buffer++; + + if (in_buffer >= PvmRowsToSend || line_number >= opts.Last_Line - 1) { + if (pvm_send(PvmMTid, PVM_SLAVE_RESULTS) < 0) { + Error_Line( "Cannot send block in Pvm_Write_Line\n"); + pvm_perror(""); + pvm_exit(); + return; + } + + in_buffer = 0; + } +} + + + + + + + + + + + + + + + + + + + diff -Naur source.ori/pvm.h source/pvm.h --- source.ori/pvm.h Thu Jan 1 01:00:00 1970 +++ source/pvm.h Sun Sep 12 00:25:35 1999 @@ -0,0 +1,164 @@ +/**************************************************************************** +* pvm.h +* +* This include module is used by PVM control routines in the PVM'd PovRAY. +* +* This file was written by Brad Kline. April 1994. +* modified by Andreas Dilger, Feb 1995. +* modified by Harald Deischinger, 1996 - April 1997 +* modified by Jakob Flierl, May 1999 +* +* All the defines that need modification are at the top. +* +* The author disclaims all warranties with regard to this software, +* including all implied warranties of merchant-ability and fitness. +* The code is simply distributed as it is. +* +* +*****************************************************************************/ + +/* If setpriority() doesn't exist on your system, you should change + * "your_pvm_arch" on the following line to whatever your actual PVM_ARCH + * is (ie ALPHA or SCO), and please email me to tell me about it, so I can + * add it in permanently. + */ + +#include + +#if defined(SUN4SOL2) || defined(your_pvm_arch) + +#define NICE(x) nice(x) +extern int nice PARAMS((int prio)); + +#else + +/* This should work for the majority of systems */ + +#define NICE(x) setpriority(PRIO_PROCESS,0,getpriority(PRIO_PROCESS,0)+(x)) + +#if !(defined __GLIBC__ && __GLIBC__ >= 2) +extern int setpriority PARAMS((int which, int who, int prio)); +extern int getpriority PARAMS((int which, int who)); +#endif +#endif + +/* How nice to be by default. 0 is the regular job priority, while is 20 very + * nice. The nice value can be changed on the command line, so there is + * probably little reason to change it here. + */ +#define PVM_DEFAULT_NICE 5 + +/* On BSD systems, use getwd instead of getcwd. Change "your_pvm_arch" on + * the following line to whatever your actual PVM_ARCH is (ie MIPS or CRAY), + * and please email me to tell me about it, so I can add it in permanently. + */ +#if defined(NEXT) || defined(your_pvm_arch) + +#define GETCWD(path) getwd(path) +extern char *getwd PARAMS((char *)); + +#else + +#define GETCWD(path) getcwd(path,PATH_MAX+1) +extern char *getcwd PARAMS((char *, size_t )); + +#endif + +extern int chdir PARAMS((const char *path)); + +/* Grid decomposition defaults */ +#define PVM_DEFAULT_GRID_HEIGHT 32 +#define PVM_DEFAULT_GRID_WIDTH 32 + +/* what type of encoding to use whan sending pixels */ +#define pvm_upkCOL pvm_upkfloat +#define pvm_pkCOL pvm_pkfloat +#define pvm_upkSTAT pvm_upklong +#define pvm_pkSTAT pvm_pklong + + +/* The maximum path length. _POSIX_PATH_MAX_ = 255 in my limits.h file, but + * not all systems have this defined, and my base SUN4 install didn't have + * PATH_MAX in .*/ +#ifndef PATH_MAX +#ifdef _POSIX_PATH_MAX +#define PATH_MAX _POSIX_PATH_MAX +#else /* _POSIX_PATH_MAX doesn't exist. A safe guess. */ +#define PATH_MAX 1024 +#endif /* _POSIX_PATH_MAX */ +#endif /* PATH_MAX */ + +/* Structure to hold info about each slave process */ +typedef struct { + char *name; + int pixels_done; /* Number of pixels completed */ + int pixels_late; /* Number of pixels finished after another slave did it */ + int block_reassigned; /* Block number reassigned to this slave */ + int frame_assigned; /* assigned to that frame */ + int frame_reassigned; /* reassigned to that frame */ +} pvm_slave_stat; +#define PVM_SLAVE_FREE -1 + +/* Structure to hold info about each grid section */ +typedef struct { + int status; /* Current status of block */ + int wr_line; /* Line to be written next */ + int assigned_tid; /* Tid of the machine assigned to this block */ + int reassigned_tid; /* Tid of the machine reassigned to this block */ +} pvm_block_stat; +/* Status indicators */ +#define PVM_GRID_POINT_FREE 0 +#define PVM_GRID_POINT_ASSIGNED 1 +#define PVM_GRID_POINT_REASSIGNED 2 +#define PVM_GRID_POINT_DONE 3 +#define PVM_GRID_POINT_WRITTEN 4 + + +/* Structure to hold info about each frame */ +typedef struct { + int status; + COLOUR ** row_ptr; + int row_to_write; + int row_to_malloc; + int block_to_assign; + int block_to_reassign; + int block_to_write; + COUNTER stats[MaxStat]; /* PovRay Status */ + pvm_block_stat * block_stat; + FILE_HANDLE* output_file; +} pvm_frame_stat; +#define PVM_FRAME_FREE 0 +#define PVM_FRAME_WORKING 1 +#define PVM_FRAME_DONE 2 +#define PVM_FRAME_DONE_SHELLOUT 3 + +/* Define some message IDs to be used */ +#define PVM_INIT_SLAVE 1 +#define PVM_STOP_SLAVE 2 +#define PVM_KILL_SLAVE 3 +#define PVM_NEED_WORK 4 +#define PVM_SLAVE_EXIT 5 +#define PVM_SLAVE_STATS 6 +#define PVM_SLAVE_RESULTS 7 +#define PVM_WORK 8 + +/* Prototypes for public functions defined in pvm.c */ +void Pvm_Master_Init PARAMS((int argc, char **argv)); +void Pvm_Master_Control PARAMS((void)); +void Pvm_Write_Line PARAMS((FILE_HANDLE *handle, COLOUR *line, int row)); +int Pvm_Slave_Init PARAMS((void)); +int Pvm_Slave_Control PARAMS((void)); +void Pvm_Slave_Exit PARAMS((void)); + +extern char PvmArch[]; +extern int PvmChunkWidth; +extern int PvmChunkHeight; +extern int PvmSlave; +extern int PvmTasks; +extern int PvmNice; +extern char * PvmSlavename; +extern int PvmNextFrame; +extern int PvmBlockNum; +extern char PvmWorkingDir[]; +extern char ** PvmHosts; +extern int PvmHostsN; diff -Naur source.ori/render.c source/render.c --- source.ori/render.c Sat Jun 30 01:49:21 2001 +++ source/render.c Sat Jun 30 10:08:01 2001 @@ -46,7 +46,9 @@ #include "texture.h" #include "vbuffer.h" #include "userio.h" - +#ifdef USE_PVM +#include "pvm.h" +#endif /***************************************************************************** * Local preprocessor defines @@ -65,6 +67,10 @@ #define SUB_PIXEL_GRID_SIZE 16 +#ifdef USE_PVM +DBL maxclr; +#endif + /***************************************************************************** * Local typedefs ******************************************************************************/ @@ -104,7 +110,9 @@ static int SuperSampleCount, RadiosityCount; +#ifndef USE_PVM static DBL maxclr; +#endif /* Jitter values are taken from [-0.5*JitterScale, 0.5*JitterScale]. */ @@ -314,7 +322,9 @@ static int create_ray (RAY *ray, DBL x, DBL y, int ray_number); static void supersample (COLOUR result, int x, int y); static void gamma_correct (COLOUR Colour); +#ifndef USE_PVM static void extract_colors (COLOUR Colour, unsigned char *Red, unsigned char *Green, unsigned char *Blue, unsigned char *Alpha, DBL *grey); +#endif static void trace_pixel (int x, int y, COLOUR Colour); static void initialise_histogram (void) ; static void accumulate_histogram (int x, int y, int on); @@ -349,7 +359,8 @@ * * CHANGES * -* - +* Oct 1996: Added support for PVM [Deischi] +* Jun 2001: Added conditional build for PVM Support [Agaran] * ******************************************************************************/ @@ -368,6 +379,12 @@ size = (Frame.Screen_Width + 1) * sizeof(COLOUR); +#ifdef USE_PVM + /* looks like normal mem leak, maybe that should be always enabled? */ + if (Previous_Line!=NULL) POV_FREE(Previous_Line); + if (Current_Line!=NULL) POV_FREE(Current_Line); +#endif + Previous_Line = (COLOUR *)POV_MALLOC(size, "previous line buffer"); Current_Line = (COLOUR *)POV_MALLOC(size, "current line buffer"); @@ -381,6 +398,12 @@ { size = (Frame.Screen_Width + 1) * sizeof(char); +#ifdef USE_PVM + /* again looks like memleak fixup... */ + if (Previous_Line_Antialiased_Flags!=NULL) POV_FREE(Previous_Line_Antialiased_Flags); + if (Current_Line_Antialiased_Flags!=NULL) POV_FREE(Current_Line_Antialiased_Flags); +#endif + Previous_Line_Antialiased_Flags = (char *)POV_MALLOC(size, "previous line flags"); Current_Line_Antialiased_Flags = (char *)POV_MALLOC(size, "current line flags"); @@ -390,6 +413,11 @@ Current_Line_Antialiased_Flags[i] = 0; } } +#ifdef USE_PVM + /* If PVM, this is already enough */ + if(PvmTasks && !PvmSlave) + return; +#endif Assign_Vector(Camera_Ray.Initial, Frame.Camera->Location); @@ -3154,8 +3182,11 @@ * Jun 1995 : Alpha channel support -CEY * ******************************************************************************/ - +#ifdef USE_PVM +void extract_colors(COLOUR Colour, unsigned char *Red, unsigned char *Green, unsigned char *Blue, unsigned char *Alpha, DBL *grey) +#else static void extract_colors(COLOUR Colour, unsigned char *Red, unsigned char *Green, unsigned char *Blue, unsigned char *Alpha, DBL *grey) +#endif { if (opts.PaletteOption == GREY) { diff -Naur source.ori/render.h source/render.h --- source.ori/render.h Sat Jun 30 01:49:21 2001 +++ source/render.h Sat Jun 30 03:18:56 2001 @@ -59,6 +59,9 @@ extern unsigned long *histogram_grid ; extern unsigned long max_histogram_value ; extern FILE_HANDLE *Histogram_File_Handle ; +#ifdef USE_PVM +extern DBL maxclr; +#endif /***************************************************************************** * Global functions @@ -74,6 +77,10 @@ DBL Trace (RAY *Ray, COLOUR Colour, DBL Weight); void Check_User_Abort (int Do_Stats); void write_histogram (char *filename); +#ifdef USE_PVM +void extract_colors (COLOUR Colour, unsigned char *Red, unsigned char *Green, unsigned char *Blue, unsigned char *Alpha, DBL *grey); +#endif + void destroy_histogram (void); #endif diff -Naur source.ori/unix/makefile source/unix/makefile --- source.ori/unix/makefile Sat Jun 30 03:02:03 2001 +++ source/unix/makefile Sat Jun 30 11:54:57 2001 @@ -53,7 +53,7 @@ #CFLAGS = -O6 -finline-functions -ffast-math -c -ansi -m386 -DCPU=586 -DCOMPILER_VER=\".`uname`.$(CC)\" -DPOV_LIB_DIR=\"$(POVLIBDIR)\" $(SRCINC) $(LIBPNGINC) $(ZLIBINC) # Linux compiler flags, Pentium II optimized -CFLAGS = $(OPT_FLAGS) -finline-functions -ffast-math -c -ansi -DCOMPILER_VER=\".`uname`.$(CC)\" -DPOV_LIB_DIR=\"$(POVLIBDIR)\" $(SRCINC) +CFLAGS = $(OPT_FLAGS) $(PVMFLAGS) -finline-functions -ffast-math -c -ansi -DCOMPILER_VER=\".`uname`.$(CC)\" -DPOV_LIB_DIR=\"$(POVLIBDIR)\" $(SRCINC) # HPUX compiler flags #CFLAGS = +O2 -finline-functions -c -Aa -D_HPUX_SOURCE -DCOMPILER_VER=\".`uname`.$(CC)\" $(SRCINC) $(LIBPNGINC) $(ZLIBINC) @@ -149,12 +149,15 @@ # UTARGET is the name of the text-only UNIX version UTARGET=povray +UTARGET_PVM=pvmpov # XTARGET is the name of the X-Windows executable. XTARGET=x-povray +XTARGET_PVM=x-pvmpov # STARGET is the name of the SVGA executable. STARGET=s-povray +STARGET_PVM=s-pvmpov # This is the suffix for object files. OBJ = .o @@ -167,6 +170,13 @@ SRCINC = -I. -I$(SRCDIR) +# not needed +#PVMINC = -I$(PVM_ROOT)/include +#PVMLIB = /usr/lib/libpvm3.a /usr/lib/libgpvm3.a +PVMLIB = -lpvm3 +# dunno for what that is used +XDIR = $(HOME)/pvm3/bin/$(PVM_ARCH) + # # End of user specific options # @@ -1526,6 +1536,12 @@ xpovmask.xbm \ $(SRCDIR)/povproto.h +pvmDEP = $(SRCDIR)/pvm.c \ + config.h \ + $(SRCDIR)/pvm.h +# $(UNIXDIR)/unixconf.h + + config.h: @echo @echo 'You need to select which executable version you want' @@ -1549,46 +1565,89 @@ unix: $(UTARGET) @echo +unix_pvm: $(UTARGET_PVM) + @echo + $(UTARGET): $(POVOBJS) $(ODIR)/unix$(OBJ) $(CC) $(POVOBJS) $(ODIR)/unix$(OBJ) $(LFLAGS) -o $(UTARGET) +$(UTARGET_PVM): $(POVOBJS) $(ODIR)/pvm$(OBJ) $(ODIR)/unix$(OBJ) + $(CC) $(POVOBJS) $(ODIR)/unix$(OBJ) $(ODIR)/pvm$(OBJ) $(PVMLIB) $(LFLAGS) -o $(UTARGET_PVM) + svga: $(STARGET) @echo +svga_pvm: $(STARGET_PVM) + @echo + + $(STARGET): $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/unix$(OBJ) $(CC) $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/unix$(OBJ) $(LFLAGS) $(SLIBLIB) -o $(STARGET) +$(STARGET_PVM): $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/pvm$(OBJ) $(ODIR)/unix$(OBJ) + $(CC) $(POVOBJS) $(ODIR)/svga$(OBJ) $(ODIR)/unix$(OBJ) $(ODIR)/pvm$(OBJ) $(PVMLIB) $(LFLAGS) $(SLIBLIB) -o $(STARGET_PVM) + xwin: $(XTARGET) @echo +xwin_pvm: $(XTARGET_PVM) + @echo + $(XTARGET): $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/unix$(OBJ) $(CC) $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/unix$(OBJ) $(LFLAGS) $(XLIBLIB) -o $(XTARGET) +$(XTARGET_PVM): $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/pvm$(OBJ) $(ODIR)/unix$(OBJ) + $(CC) $(POVOBJS) $(ODIR)/xwindows$(OBJ) $(ODIR)/unix$(OBJ) $(ODIR)/pvm$(OBJ) $(PVMLIB) $(LFLAGS) $(XLIBLIB) -o $(XTARGET_PVM) + + # Only these files need to be rebuilt if the display type was changed DISPOBJS = $(ODIR)/povray$(OBJ) $(ODIR)/render$(OBJ) \ $(ODIR)/userio$(OBJ) $(ODIR)/vbuffer$(OBJ) +PVMOBJ = $(ODIR)/optin$(OBJ) $(ODIR)/optout$(OBJ) $(ODIR)/povray$(OBJ) $(ODIR)render$(OBJ) + newunix: - -@$(RM) $(DISPOBJS) + -@$(RM) $(DISPOBJS) $(PVMOBJ) -@cp unixconf.h config.h -@touch -c $(ODIR)/*$(OBJ) -@$(MAKE) unix +newunix_pvm: + -@$(RM) $(DISPOBJS) $(PVMOBJ) + -@cp unixconf.h config.h + -@touch -c $(ODIR)/*$(OBJ) + -@$(MAKE) PVMFLAGS="-DUSE_PVM" unix_pvm + + newsvga: - -@$(RM) $(DISPOBJS) + -@$(RM) $(DISPOBJS) $(PVMOBJ) -@cp svgaconf.h config.h -@touch -c $(ODIR)/*$(OBJ) -@$(MAKE) svga +newsvga_pvm: + -@$(RM) $(DISPOBJS) $(PVMOBJ) + -@cp svgaconf.h config.h + -@touch -c $(ODIR)/*$(OBJ) + -@$(MAKE) PVMFLAGS="-DUSE_PVM" svga_pvm + newxwin: - -@$(RM) $(DISPOBJS) + -@$(RM) $(DISPOBJS) $(PVMOBJ) -@cp xwinconf.h config.h -@touch -c $(ODIR)/*$(OBJ) -@$(MAKE) xwin +newxwin_pvm: + -@$(RM) $(DISPOBJS) $(PVMOBJ) + -@cp xwinconf.h config.h + -@touch -c $(ODIR)/*$(OBJ) + -@$(MAKE) PVMFLAGS="-DUSE_PVM" xwin_pvm + + clean: - -@$(RM) $(POVOBJS) $(ODIR)/unix$(OBJ) $(ODIR)/xwindows$(OBJ) - -@$(RM) $(ODIR)/svga$(OBJ) $(UTARGET) $(XTARGET) $(STARGET) + -@$(RM) $(POVOBJS) pvm$(OBJ) $(ODIR)/unix$(OBJ) $(ODIR)/xwindows$(OBJ) + -@$(RM) $(ODIR)/svga$(OBJ) $(UTARGET) $(XTARGET) $(STARGET) $(UTARGET_PVM) + -@$(RM) $(XTARGET_PVM) $(STARGET_PVM) install: -@cp povray.1 $(POVPATH)/man/man1 @@ -1821,17 +1880,7 @@ $(ODIR)/xwindows$(OBJ) : $(xwindowsDEP) $(CC) $(CFLAGS) $(XLIBINC) xwindows.c - - - - - - - - - - - - +$(ODIR)/pvm$(OBJ) : $(pvmDEP) + $(CC) $(CFLAGS) $(SRCDIR)/pvm.c