2014-11-20 13:21:19 +00:00
/ *
* Copyright ( C ) 2010 - 2014 Laurent CLOUET
* Author Laurent CLOUET < laurent . clouet @nopnop.net >
*
2020-05-28 13:28:42 +02:00
* This program is free software ; you can redistribute it and / or
2014-11-20 13:21:19 +00:00
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; version 2
* of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
* /
2024-12-14 13:54:56 +00:00
package com.sheepit.client.rendering ;
2014-11-20 13:21:19 +00:00
2024-12-14 13:54:56 +00:00
import com.sheepit.client.logger.Log ;
import com.sheepit.client.config.Configuration ;
import com.sheepit.client.config.Configuration.ComputeType ;
import com.sheepit.client.config.DirectoryManager ;
import com.sheepit.client.datamodel.client.Error ;
import com.sheepit.client.datamodel.client.Error.Type ;
import com.sheepit.client.network.DownloadItem ;
2021-05-11 15:36:59 +00:00
import com.sheepit.client.os.OS ;
2024-12-14 13:54:56 +00:00
import com.sheepit.client.ui.Gui ;
import com.sheepit.client.utils.Utils ;
2021-05-11 15:36:59 +00:00
import lombok.Data ;
import lombok.Getter ;
2015-05-06 20:34:59 +01:00
import java.io.BufferedReader ;
2014-11-20 13:21:19 +00:00
import java.io.File ;
2015-05-06 20:34:59 +01:00
import java.io.FileWriter ;
import java.io.FilenameFilter ;
import java.io.IOException ;
import java.io.InputStreamReader ;
import java.io.PrintWriter ;
import java.io.StringWriter ;
import java.text.DateFormat ;
import java.text.ParseException ;
import java.text.SimpleDateFormat ;
2022-09-29 14:06:40 +00:00
import java.time.Duration ;
import java.time.Instant ;
2023-05-26 21:05:23 +00:00
import java.time.LocalTime ;
2015-05-06 20:34:59 +01:00
import java.util.ArrayList ;
2023-01-06 14:53:06 +00:00
import java.util.Arrays ;
2015-05-06 20:34:59 +01:00
import java.util.Date ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
2019-08-07 18:40:02 +02:00
import java.util.Observable ;
import java.util.Observer ;
2023-07-30 21:34:11 +00:00
import java.util.Optional ;
2015-05-06 20:34:59 +01:00
import java.util.TimeZone ;
2017-04-25 13:06:23 +02:00
import java.util.Timer ;
import java.util.TimerTask ;
2020-03-19 23:48:06 +01:00
import java.util.regex.Matcher ;
2021-05-11 15:36:59 +00:00
import java.util.regex.Pattern ;
2014-11-30 23:42:57 +00:00
2024-12-14 13:54:56 +00:00
import static com.sheepit.client.rendering.RenderSettings.UPDATE_METHOD_BY_REMAINING_TIME ;
import static com.sheepit.client.rendering.RenderSettings.UPDATE_METHOD_BY_TILE ;
2024-11-20 17:43:26 +00:00
2020-05-28 13:28:42 +02:00
@Data public class Job {
2023-04-26 09:33:53 +00:00
public static final String POST_LOAD_NOTIFICATION = " POST_LOAD_SCRIPT_loaded " ;
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
public static final int SHOW_BASE_ICON = - 1 ;
2020-05-28 13:28:42 +02:00
2024-11-20 16:57:43 +00:00
private DownloadItem projectDownload ;
private DownloadItem rendererDownload ;
2014-11-20 13:21:19 +00:00
private String id ;
2020-04-14 17:35:54 +02:00
private String validationUrl ;
2016-01-08 20:53:58 +01:00
private String name ;
2021-05-11 15:36:59 +00:00
private char [ ] password ;
2024-11-20 17:43:26 +00:00
2014-12-05 15:15:24 +00:00
private boolean synchronousUpload ;
2024-11-20 17:43:26 +00:00
private RenderSettings renderSettings ;
2024-12-14 14:10:52 +00:00
@Getter private RenderProcess renderProcess ;
2024-11-20 17:43:26 +00:00
private RenderOutput renderOutput ;
private RenderState renderState ;
2015-05-06 20:34:59 +01:00
private Gui gui ;
2019-08-07 22:17:59 +02:00
private Configuration configuration ;
2015-05-06 20:34:59 +01:00
private Log log ;
2014-11-20 13:21:19 +00:00
2024-11-20 22:51:06 +00:00
private DirectoryManager directoryManager ;
2020-05-28 13:28:42 +02:00
public Job ( Configuration config_ , Gui gui_ , Log log_ , String id_ , String frame_ , String path_ , boolean use_gpu , String command_ , String validationUrl_ ,
2024-11-20 16:57:43 +00:00
String script_ , DownloadItem projectDownload_ , DownloadItem rendererDownload_ , String name_ , char [ ] password_ , boolean synchronous_upload_ ,
2020-05-28 13:28:42 +02:00
String update_method_ ) {
2019-08-07 22:17:59 +02:00
configuration = config_ ;
2014-11-20 13:21:19 +00:00
id = id_ ;
2024-11-20 17:43:26 +00:00
renderSettings = new RenderSettings ( frame_ , script_ , path_ , command_ , use_gpu , update_method_ ) ;
2020-04-14 17:35:54 +02:00
validationUrl = validationUrl_ ;
2024-11-20 16:57:43 +00:00
projectDownload = projectDownload_ ;
rendererDownload = rendererDownload_ ;
2016-01-08 20:53:58 +01:00
name = name_ ;
2024-06-03 14:02:30 +00:00
password = password_ . clone ( ) ;
2014-12-05 15:15:24 +00:00
synchronousUpload = synchronous_upload_ ;
2015-05-06 20:34:59 +01:00
gui = gui_ ;
log = log_ ;
2024-11-20 22:51:06 +00:00
directoryManager = new DirectoryManager ( configuration ) ;
2024-11-20 17:43:26 +00:00
renderProcess = new RenderProcess ( log_ ) ;
renderState = new RenderState ( ) ;
renderOutput = new RenderOutput ( ) ;
2015-04-27 20:10:36 +01:00
}
2017-02-19 14:19:48 +01:00
public void block ( ) {
2024-11-20 17:43:26 +00:00
renderState . setBlock ( ) ;
2024-12-14 14:10:52 +00:00
RenderProcess process = getRenderProcess ( ) ;
2017-02-19 14:19:48 +01:00
if ( process ! = null ) {
2023-07-15 14:49:03 +00:00
process . kill ( ) ;
2017-02-19 14:19:48 +01:00
}
}
2024-06-08 03:21:33 +00:00
public void incompatibleProcessBlock ( ) {
2024-11-20 17:43:26 +00:00
renderState . setBlockIncompatibleProcess ( ) ;
2024-12-14 14:10:52 +00:00
RenderProcess process = getRenderProcess ( ) ;
2024-06-08 03:21:33 +00:00
if ( process ! = null ) {
process . kill ( ) ;
}
}
2024-06-07 14:30:24 +00:00
@Override
2014-11-20 13:21:19 +00:00
public String toString ( ) {
2020-05-28 13:28:42 +02:00
return String
2023-09-19 17:14:49 +00:00
. format ( " Job (numFrame '%s' archiveChunks %s rendererMD5 '%s' ID '%s' pictureFilename '%s' jobPath '%s' gpu %s name '%s' updateRenderingStatusMethod '%s' render %s) " ,
2024-11-20 17:43:26 +00:00
getRenderSettings ( ) . getFrameNumber ( ) , projectDownload . getMD5 ( ) , rendererDownload . getMD5 ( ) , id , getRenderOutput ( ) . getFullImagePath ( ) ,
getRenderSettings ( ) . getPath ( ) , getRenderSettings ( ) . isUseGPU ( ) , name , getRenderSettings ( ) . getUpdateRenderingStatusMethod ( ) , renderProcess ) ;
2014-11-20 13:21:19 +00:00
}
public String getPrefixOutputImage ( ) {
return id + " _ " ;
}
public String getRendererDirectory ( ) {
2024-11-20 16:57:43 +00:00
return configuration . getWorkingDirectory ( ) . getAbsolutePath ( ) + File . separator + rendererDownload . getMD5 ( ) ;
2014-11-20 13:21:19 +00:00
}
public String getRendererPath ( ) {
2014-11-30 23:42:57 +00:00
return getRendererDirectory ( ) + File . separator + OS . getOS ( ) . getRenderBinaryPath ( ) ;
2014-11-20 13:21:19 +00:00
}
public String getSceneDirectory ( ) {
2023-09-19 17:14:49 +00:00
return configuration . getWorkingDirectory ( ) . getAbsolutePath ( ) + File . separator + this . id ;
2014-11-20 13:21:19 +00:00
}
public String getScenePath ( ) {
2024-11-20 17:43:26 +00:00
return getSceneDirectory ( ) + File . separator + getRenderSettings ( ) . getPath ( ) ;
2014-11-20 13:21:19 +00:00
}
2019-08-07 18:40:02 +02:00
public Error . Type render ( Observer renderStarted ) {
2016-10-12 00:34:51 +02:00
gui . status ( " Rendering " ) ;
2024-12-14 14:10:52 +00:00
RenderProcess process = getRenderProcess ( ) ;
2017-04-25 13:06:23 +02:00
Timer timerOfMaxRenderTime = null ;
2024-06-06 01:05:27 +00:00
String core_script ;
2018-06-02 15:11:45 +02:00
// When sending Ctrl+C to the terminal it also get's sent to all subprocesses e.g. also the render process.
// The java program handles Ctrl+C but the renderer quits on Ctrl+C.
// This script causes the renderer to ignore Ctrl+C.
2020-05-28 13:28:42 +02:00
String ignore_signal_script = " import signal \ n " + " def hndl(signum, frame): \ n " + " pass \ n " + " signal.signal(signal.SIGINT, hndl) \ n " ;
2024-11-20 17:43:26 +00:00
if ( getRenderSettings ( ) . isUseGPU ( ) & & configuration . getGPUDevice ( ) ! = null & & configuration . getComputeMethod ( ) ! = ComputeType . CPU ) {
2020-05-28 13:28:42 +02:00
core_script = " sheepit_set_compute_device( \" " + configuration . getGPUDevice ( ) . getType ( ) + " \" , \" GPU \" , \" " + configuration . getGPUDevice ( ) . getId ( )
+ " \" ) \ n " ;
2017-05-07 21:00:20 +02:00
gui . setComputeMethod ( " GPU " ) ;
2015-05-06 20:34:59 +01:00
}
else {
2020-05-16 18:20:38 +10:00
// Otherwise (CPU), fix the tile size to 32x32px
2017-02-11 16:54:40 +01:00
core_script = " sheepit_set_compute_device( \" NONE \" , \" CPU \" , \" CPU \" ) \ n " ;
2017-05-07 21:00:20 +02:00
gui . setComputeMethod ( " CPU " ) ;
2015-05-06 20:34:59 +01:00
}
2018-06-02 15:11:45 +02:00
core_script + = ignore_signal_script ;
2023-03-27 15:39:09 +00:00
File disableViewportScript = null ;
2015-05-06 20:34:59 +01:00
File script_file = null ;
2024-11-20 17:43:26 +00:00
String [ ] command1 = getRenderSettings ( ) . getCommand ( ) . split ( " " ) ;
2015-05-06 20:34:59 +01:00
int size_command = command1 . length + 2 ; // + 2 for script
2019-08-07 22:17:59 +02:00
if ( configuration . getNbCores ( ) > 0 ) { // user has specified something
2015-05-06 20:34:59 +01:00
size_command + = 2 ;
}
2021-05-11 15:36:59 +00:00
List < String > command = new ArrayList < > ( size_command ) ;
2015-05-06 20:34:59 +01:00
2021-05-11 15:36:59 +00:00
Map < String , String > new_env = new HashMap < > ( ) ;
2015-05-06 20:34:59 +01:00
2021-04-04 17:34:39 +00:00
//make sure the system doesn´ t interfere with the blender runtime, and that blender doesn´ t attempt to load external libraries/scripts.
2023-12-08 09:58:01 +00:00
new_env . put ( " BLENDER_USER_RESOURCES " , " " ) ;
2021-04-04 17:34:39 +00:00
new_env . put ( " BLENDER_USER_CONFIG " , " " ) ;
2024-03-22 10:48:35 +00:00
new_env . put ( " BLENDER_USER_EXTENSIONS " , " " ) ;
2021-04-04 17:34:39 +00:00
new_env . put ( " BLENDER_USER_SCRIPTS " , " " ) ;
new_env . put ( " BLENDER_USER_DATAFILES " , " " ) ;
2023-12-08 09:58:01 +00:00
new_env . put ( " BLENDER_SYSTEM_RESOURCES " , " " ) ;
new_env . put ( " BLENDER_SYSTEM_SCRIPTS " , " " ) ;
2021-04-04 17:34:39 +00:00
new_env . put ( " BLENDER_SYSTEM_DATAFILES " , " " ) ;
2023-12-08 09:58:01 +00:00
new_env . put ( " BLENDER_SYSTEM_PYTHON " , " " ) ;
2021-05-27 22:36:41 +02:00
new_env . put ( " OCIO " , " " ) ; //prevent blender from loading a non-standard color configuration
2023-12-08 09:58:01 +00:00
new_env . put ( " TEMP " , configuration . getWorkingDirectory ( ) . getAbsolutePath ( ) . replace ( " \\ " , " \\ \\ " ) ) ;
new_env . put ( " TMP " , configuration . getWorkingDirectory ( ) . getAbsolutePath ( ) . replace ( " \\ " , " \\ \\ " ) ) ;
2015-05-06 20:34:59 +01:00
2023-12-08 09:58:01 +00:00
new_env . put ( " CORES " , Integer . toString ( configuration . getNbCores ( ) ) ) ;
new_env . put ( " PRIORITY " , Integer . toString ( configuration . getPriority ( ) ) ) ;
2023-07-30 21:34:11 +00:00
// Add own lib folder first, because Steam or other environments may set an LD_LIBRARY_PATH that has priority over the runpath in the Blender excutable,
// but contains incompatible libraries.
String currentLDLibraryPath = Optional . ofNullable ( System . getenv ( " LD_LIBRARY_PATH " ) ) . orElse ( " " ) ;
new_env . put ( " LD_LIBRARY_PATH " , getRendererDirectory ( ) + " /lib " + " : " + currentLDLibraryPath ) ;
2015-05-06 20:34:59 +01:00
for ( String arg : command1 ) {
switch ( arg ) {
case " .c " :
2023-03-27 15:39:09 +00:00
command . add ( " -P " ) ;
try {
2023-04-26 09:33:53 +00:00
disableViewportScript = File . createTempFile ( " pre_load_script_ " , " .py " , configuration . getWorkingDirectory ( ) ) ;
2023-03-27 15:39:09 +00:00
File file = new File ( disableViewportScript . getAbsolutePath ( ) ) ;
FileWriter fwriter ;
fwriter = new FileWriter ( file ) ;
PrintWriter out = new PrintWriter ( fwriter ) ;
out . write ( " import bpy " ) ;
out . write ( " \ n " ) ;
2023-04-26 09:33:53 +00:00
out . write ( " import sys " ) ;
out . write ( " \ n " ) ;
2023-03-27 15:39:09 +00:00
out . write ( " from bpy.app.handlers import persistent " ) ;
out . write ( " \ n " ) ;
out . write ( " @persistent " ) ;
out . write ( " \ n " ) ;
2023-04-26 09:33:53 +00:00
out . write ( " def hide_stuff(hide_dummy): " ) ;
out . write ( " \ n " ) ;
out . write ( " print('PRE_LOAD_SCRIPT_hide_viewport') " ) ;
2023-03-27 15:39:09 +00:00
out . write ( " \ n " ) ;
2023-04-26 09:33:53 +00:00
out . write ( " #Hide collections in the viewport " ) ;
2023-03-27 15:39:09 +00:00
out . write ( " \ n " ) ;
out . write ( " for col in bpy.data.collections: " ) ;
out . write ( " \ n " ) ;
2023-04-26 09:33:53 +00:00
out . write ( " col.hide_viewport = True " ) ;
2023-03-27 15:39:09 +00:00
out . write ( " \ n " ) ;
out . write ( " for obj in bpy.data.objects: " ) ;
out . write ( " \ n " ) ;
out . write ( " #Hide objects in the viewport " ) ;
out . write ( " \ n " ) ;
2024-02-25 18:36:18 +00:00
out . write ( " #obj.hide_viewport = True " ) ;
2023-03-27 15:39:09 +00:00
out . write ( " \ n " ) ;
out . write ( " #Hide modifier in the viewport " ) ;
out . write ( " \ n " ) ;
out . write ( " for mod in obj.modifiers: " ) ;
out . write ( " \ n " ) ;
out . write ( " mod.show_viewport = False " ) ;
out . write ( " \ n " ) ;
2023-04-26 09:33:53 +00:00
out . write ( " sys.stdout.flush() " ) ;
out . write ( " \ n " ) ;
2023-03-27 15:39:09 +00:00
out . write ( " bpy.app.handlers.version_update.append(hide_stuff) " ) ;
out . write ( " \ n " ) ;
out . close ( ) ;
command . add ( disableViewportScript . getAbsolutePath ( ) ) ;
}
catch ( IOException e ) {
StringWriter sw = new StringWriter ( ) ;
e . printStackTrace ( new PrintWriter ( sw ) ) ;
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-03-27 15:39:09 +00:00
log . debug ( logline ) ;
}
log . error ( " Job::render exception on script generation, will return UNKNOWN " + e + " stacktrace " + sw . toString ( ) ) ;
return Error . Type . UNKNOWN ;
}
2015-05-06 20:34:59 +01:00
command . add ( getScenePath ( ) ) ;
command . add ( " -P " ) ;
try {
2023-04-26 09:33:53 +00:00
script_file = File . createTempFile ( " post_load_script_ " , " .py " , configuration . getWorkingDirectory ( ) ) ;
2015-05-06 20:34:59 +01:00
File file = new File ( script_file . getAbsolutePath ( ) ) ;
FileWriter txt ;
txt = new FileWriter ( file ) ;
PrintWriter out = new PrintWriter ( txt ) ;
2024-11-20 17:43:26 +00:00
out . write ( getRenderSettings ( ) . getScript ( ) ) ;
2015-05-06 20:34:59 +01:00
out . write ( " \ n " ) ;
2023-04-26 09:33:53 +00:00
out . write ( " import sys " ) ;
out . write ( " \ n " ) ;
out . write ( " print(' " + POST_LOAD_NOTIFICATION + " ') " ) ;
out . write ( " \ n " ) ;
out . write ( " sys.stdout.flush() " ) ;
out . write ( " \ n " ) ;
2015-05-06 20:34:59 +01:00
out . write ( core_script ) ; // GPU part
out . write ( " \ n " ) ; // GPU part
out . close ( ) ;
command . add ( script_file . getAbsolutePath ( ) ) ;
}
catch ( IOException e ) {
StringWriter sw = new StringWriter ( ) ;
e . printStackTrace ( new PrintWriter ( sw ) ) ;
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-01-06 14:43:14 +00:00
log . debug ( logline ) ;
}
2016-08-29 17:56:21 +02:00
log . error ( " Job::render exception on script generation, will return UNKNOWN " + e + " stacktrace " + sw . toString ( ) ) ;
2015-05-06 20:34:59 +01:00
return Error . Type . UNKNOWN ;
}
script_file . deleteOnExit ( ) ;
break ;
case " .e " :
command . add ( getRendererPath ( ) ) ;
// the number of cores has to be put after the binary and before the scene arg
2019-08-07 22:17:59 +02:00
if ( configuration . getNbCores ( ) > 0 ) {
2015-05-06 20:34:59 +01:00
command . add ( " -t " ) ;
2019-08-07 22:17:59 +02:00
command . add ( Integer . toString ( configuration . getNbCores ( ) ) ) ;
2015-05-06 20:34:59 +01:00
}
break ;
case " .o " :
2019-08-07 22:17:59 +02:00
command . add ( configuration . getWorkingDirectory ( ) . getAbsolutePath ( ) + File . separator + getPrefixOutputImage ( ) ) ;
2015-05-06 20:34:59 +01:00
break ;
case " .f " :
2024-11-20 17:43:26 +00:00
command . add ( getRenderSettings ( ) . getFrameNumber ( ) ) ;
2015-05-06 20:34:59 +01:00
break ;
default :
command . add ( arg ) ;
break ;
}
}
2021-11-16 14:51:53 +00:00
Timer memoryCheck = new Timer ( ) ;
2015-05-06 20:34:59 +01:00
try {
2019-08-07 18:40:02 +02:00
renderStartedObservable event = new renderStartedObservable ( renderStarted ) ;
2015-05-06 20:34:59 +01:00
String line ;
log . debug ( command . toString ( ) ) ;
OS os = OS . getOS ( ) ;
2019-08-07 22:17:59 +02:00
process . setCoresUsed ( configuration . getNbCores ( ) ) ;
2015-05-06 20:34:59 +01:00
process . start ( ) ;
2024-12-14 14:10:52 +00:00
getRenderProcess ( ) . setProcess ( os . exec ( command , new_env ) ) ;
getRenderProcess ( ) . setOsProcess ( OS . operatingSystem . getProcess ( ( int ) getRenderProcess ( ) . getProcess ( ) . pid ( ) ) ) ;
BufferedReader input = new BufferedReader ( new InputStreamReader ( getRenderProcess ( ) . getProcess ( ) . getInputStream ( ) ) ) ;
2021-11-16 14:51:53 +00:00
memoryCheck . scheduleAtFixedRate ( new TimerTask ( ) {
2024-06-03 14:02:30 +00:00
@Override public void run ( ) {
2021-12-21 10:08:59 +00:00
updateProcess ( ) ;
2021-11-16 14:51:53 +00:00
}
2021-12-21 10:08:59 +00:00
} , 0L , 200L ) ;
2015-05-06 20:34:59 +01:00
2020-08-30 00:13:20 +10:00
// Make initial test/power frames ignore the maximum render time in user configuration. Initial test frames have Job IDs below 20
// so we just activate the user defined timeout when the scene is not one of the initial ones.
if ( configuration . getMaxRenderTime ( ) > 0 & & Integer . parseInt ( this . getId ( ) ) > = 20 ) {
2017-04-25 13:06:23 +02:00
timerOfMaxRenderTime = new Timer ( ) ;
timerOfMaxRenderTime . schedule ( new TimerTask ( ) {
2020-05-28 13:28:42 +02:00
@Override public void run ( ) {
2024-12-14 14:10:52 +00:00
RenderProcess process = getRenderProcess ( ) ;
2017-04-25 13:06:23 +02:00
if ( process ! = null ) {
2020-05-28 13:28:42 +02:00
long duration = ( new Date ( ) . getTime ( ) - process . getStartTime ( ) ) / 1000 ; // in seconds
if ( configuration . getMaxRenderTime ( ) > 0 & & duration > configuration . getMaxRenderTime ( ) ) {
2024-11-20 17:43:26 +00:00
getRenderState ( ) . setAskForRendererKill ( true ) ;
2023-07-15 14:49:03 +00:00
log . debug ( " Killing render - exceeding allowed process duration " ) ;
process . kill ( ) ;
2017-04-25 13:06:23 +02:00
}
}
}
2019-08-07 22:17:59 +02:00
} , configuration . getMaxRenderTime ( ) * 1000 + 2000 ) ; // +2s to be sure the delay is over
2017-04-25 13:06:23 +02:00
}
2022-09-29 14:06:40 +00:00
2015-05-06 20:34:59 +01:00
log . debug ( " renderer output " ) ;
try {
2020-03-19 23:48:06 +01:00
int progress = - 1 ;
2020-05-28 13:28:42 +02:00
2022-02-14 11:30:09 +00:00
Pattern progressPattern = Pattern . compile ( " (Rendered|Path Tracing Tile|Rendering|Sample) ( \\ d+) \\ s? \\ / \\ s?( \\ d+)( Tiles| samples|,)* " ) ;
2023-04-26 09:33:53 +00:00
Pattern beginScenePrepPattern = Pattern . compile ( POST_LOAD_NOTIFICATION ) ;
2022-09-29 14:06:40 +00:00
Pattern beginPostProcessingPattern = Pattern . compile ( " ^Fra: \\ d* \\ w*(.)* \\ | (Compositing|Denoising) " ) ;
2023-05-26 21:05:23 +00:00
Pattern savingPattern = Pattern . compile ( " Time: \\ d \\ d: \\ d \\ d. \\ d \\ d \\ (Saving: ( \\ d \\ d: \\ d \\ d. \\ d \\ d) " ) ;
int savingTimeSeconds = - 1 ;
2022-09-29 14:06:40 +00:00
Instant timeStamp = null ;
Duration phaseDuration ; //We divide the job into 3 phases: preparation, rendering, compositing
boolean scenePrepStarted = false ;
boolean renderingStarted = false ;
boolean postProcessingStarted = false ;
2020-05-28 13:28:42 +02:00
2020-06-17 18:24:59 +10:00
// Initialise the progress bar in the icon and the UI (0% completed at this time)
2020-03-19 23:48:06 +01:00
gui . updateTrayIcon ( 0 ) ;
2023-01-05 16:25:29 +01:00
gui . status ( " Preparing project " , 0 ) ;
2020-05-28 13:28:42 +02:00
2015-05-06 20:34:59 +01:00
while ( ( line = input . readLine ( ) ) ! = null ) {
2017-03-29 22:13:04 +02:00
log . debug ( line ) ;
2020-05-28 13:28:42 +02:00
2020-06-03 00:06:03 +10:00
// Process lines until the version is loaded (usually first or second line of log)
2024-11-20 17:43:26 +00:00
if ( getRenderOutput ( ) . getBlenderLongVersion ( ) = = null ) {
2020-06-03 00:06:03 +10:00
Pattern blenderPattern = Pattern . compile ( " Blender (([0-9]{1,3} \\ .[0-9]{0,3}).*)$ " ) ;
Matcher blendDetectedVersion = blenderPattern . matcher ( line ) ;
if ( blendDetectedVersion . find ( ) ) {
2024-11-20 17:43:26 +00:00
getRenderOutput ( ) . setBlenderLongVersion ( blendDetectedVersion . group ( 1 ) ) ;
getRenderOutput ( ) . setBlenderShortVersion ( blendDetectedVersion . group ( 2 ) ) ;
2020-06-03 00:06:03 +10:00
}
}
2022-09-29 14:06:40 +00:00
Matcher scenePrepDetector = beginScenePrepPattern . matcher ( line ) ;
if ( scenePrepStarted = = false & & scenePrepDetector . find ( ) ) {
scenePrepStarted = true ;
timeStamp = Instant . now ( ) ;
}
2022-02-14 11:30:09 +00:00
progress = computeRenderingProgress ( line , progressPattern , progress ) ;
2022-09-29 14:06:40 +00:00
if ( renderingStarted = = false & & progress ! = - 1 ) {
renderingStarted = true ;
if ( timeStamp = = null ) {
timeStamp = new Date ( process . getStartTime ( ) ) . toInstant ( ) ;
}
phaseDuration = Duration . between ( timeStamp , Instant . now ( ) ) ;
timeStamp = Instant . now ( ) ;
process . setScenePrepDuration ( ( int ) phaseDuration . toSeconds ( ) ) ;
}
Matcher postProcessingDetector = beginPostProcessingPattern . matcher ( line ) ;
if ( postProcessingStarted = = false & & postProcessingDetector . find ( ) ) {
postProcessingStarted = true ;
if ( timeStamp = = null ) {
timeStamp = new Date ( process . getStartTime ( ) ) . toInstant ( ) ;
}
phaseDuration = Duration . between ( timeStamp , Instant . now ( ) ) ;
timeStamp = Instant . now ( ) ;
process . setRenderDuration ( ( int ) phaseDuration . toSeconds ( ) ) ;
}
2023-05-26 21:05:23 +00:00
Matcher savingTimeDetector = savingPattern . matcher ( line ) ;
if ( savingTimeDetector . find ( ) ) {
String savingTime = savingTimeDetector . group ( 1 ) ;
if ( savingTime ! = null ) {
savingTimeSeconds = ( int ) Duration . between ( LocalTime . MIN , LocalTime . parse ( " 00: " + savingTime ) ) . toSeconds ( ) ; //add leading hours to comply with ISO time format
}
}
2024-12-14 14:10:52 +00:00
if ( configuration . getMaxAllowedMemory ( ) ! = - 1 & & getRenderProcess ( ) . getMemoryUsed ( ) . get ( ) > configuration . getMaxAllowedMemory ( ) ) {
log . debug ( " Blocking render because process ram used ( " + getRenderProcess ( ) . getMemoryUsed ( ) . get ( ) + " k) is over user setting ( " + configuration
2021-11-16 14:51:53 +00:00
. getMaxAllowedMemory ( ) + " k) " ) ;
2017-03-29 22:13:04 +02:00
process . finish ( ) ;
2022-09-29 14:06:40 +00:00
if ( process . getRenderDuration ( ) = = - 1 ) {
if ( timeStamp = = null ) {
timeStamp = new Date ( process . getStartTime ( ) ) . toInstant ( ) ;
}
phaseDuration = Duration . between ( timeStamp , Instant . now ( ) ) ;
process . setRenderDuration ( ( int ) phaseDuration . toSeconds ( ) ) ;
}
2017-03-29 22:13:04 +02:00
if ( script_file ! = null ) {
script_file . delete ( ) ;
}
2023-03-27 15:39:09 +00:00
if ( disableViewportScript ! = null ) {
disableViewportScript . delete ( ) ;
}
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
// Once the process is finished (either finished successfully or with an error) move back to
// base icon (isolated S with no progress bar)
2024-06-10 16:34:12 +00:00
gui . updateTrayIcon ( SHOW_BASE_ICON ) ;
2020-05-28 13:28:42 +02:00
2017-03-29 22:13:04 +02:00
return Error . Type . RENDERER_OUT_OF_MEMORY ;
}
2015-05-06 20:34:59 +01:00
2024-06-03 14:02:30 +00:00
updateSpeedSamplesRendered ( line ) ;
2022-02-14 11:30:09 +00:00
updateRenderingStatus ( line , progress ) ;
2024-11-20 17:43:26 +00:00
Type error = getRenderOutput ( ) . detectError ( line ) ;
2015-05-06 20:34:59 +01:00
if ( error ! = Error . Type . OK ) {
if ( script_file ! = null ) {
script_file . delete ( ) ;
}
2023-03-27 15:39:09 +00:00
if ( disableViewportScript ! = null ) {
disableViewportScript . delete ( ) ;
}
2022-09-29 14:06:40 +00:00
if ( process . getRenderDuration ( ) = = - 1 ) {
if ( timeStamp = = null ) {
timeStamp = new Date ( process . getStartTime ( ) ) . toInstant ( ) ;
}
phaseDuration = Duration . between ( timeStamp , Instant . now ( ) ) ;
process . setRenderDuration ( ( int ) phaseDuration . toSeconds ( ) ) ;
}
2020-03-19 23:48:06 +01:00
// Put back base icon
2024-06-10 16:34:12 +00:00
gui . updateTrayIcon ( SHOW_BASE_ICON ) ;
2023-07-15 14:49:03 +00:00
process . kill ( ) ;
2024-04-19 18:40:55 +00:00
maybeCleanWorkingDir ( error ) ;
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-01-06 14:43:14 +00:00
log . debug ( logline ) ;
}
2020-05-28 13:28:42 +02:00
2015-05-06 20:34:59 +01:00
return error ;
}
2020-05-28 13:28:42 +02:00
2024-12-14 14:10:52 +00:00
if ( ! event . isStarted ( ) & & ( getRenderProcess ( ) . getMemoryUsed ( ) . get ( ) > 0 & & scenePrepStarted | | process . getRemainingDuration ( ) > 0 ) ) {
2019-08-07 18:40:02 +02:00
event . doNotifyIsStarted ( ) ;
2019-08-07 17:02:52 +02:00
}
2015-05-06 20:34:59 +01:00
}
2022-09-29 14:06:40 +00:00
if ( timeStamp = = null ) {
timeStamp = new Date ( process . getStartTime ( ) ) . toInstant ( ) ;
}
2023-05-26 21:05:23 +00:00
2022-09-29 14:06:40 +00:00
if ( postProcessingStarted = = false ) {
phaseDuration = Duration . between ( timeStamp , Instant . now ( ) ) ;
process . setRenderDuration ( ( int ) phaseDuration . toSeconds ( ) ) ;
2023-05-26 21:05:23 +00:00
//we need to subtract the time to save the frame to disk
if ( savingTimeSeconds > 0 & & process . getRenderDuration ( ) > 0 ) {
process . setRenderDuration ( Math . max ( 0 , process . getRenderDuration ( ) - savingTimeSeconds ) ) ;
}
2022-09-29 14:06:40 +00:00
}
else {
phaseDuration = Duration . between ( timeStamp , Instant . now ( ) ) ;
process . setPostProcessingDuration ( ( int ) phaseDuration . toSeconds ( ) ) ;
}
2015-05-06 20:34:59 +01:00
input . close ( ) ;
2022-09-29 14:06:40 +00:00
2023-05-26 21:05:23 +00:00
log . debug ( String . format ( " render times: %n \ tScene prep: %ds%n \ tRendering: %ds%n \ tPost: %ss%n \ tTotal: %ds%n \ tRendering/Total: %.03f%n " ,
2022-09-29 14:06:40 +00:00
process . getScenePrepDuration ( ) ,
process . getRenderDuration ( ) ,
process . getPostProcessingDuration ( ) ,
process . getDuration ( ) ,
( Math . max ( process . getRenderDuration ( ) , 0 ) * 100 . 0 ) / process . getDuration ( )
) ) ;
2015-05-06 20:34:59 +01:00
}
catch ( IOException err1 ) { // for the input.readline
// most likely The handle is invalid
2016-08-29 17:56:21 +02:00
log . error ( " Job::render exception(B) (silent error) " + err1 ) ;
2015-05-06 20:34:59 +01:00
}
2021-11-16 14:51:53 +00:00
finally {
memoryCheck . cancel ( ) ;
}
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
// Put back base icon
2024-06-10 16:34:12 +00:00
gui . updateTrayIcon ( SHOW_BASE_ICON ) ;
2023-07-15 14:49:03 +00:00
2015-05-06 20:34:59 +01:00
log . debug ( " end of rendering " ) ;
2023-07-15 14:49:03 +00:00
2015-05-06 20:34:59 +01:00
}
catch ( Exception err ) {
if ( script_file ! = null ) {
script_file . delete ( ) ;
}
2023-03-27 15:39:09 +00:00
if ( disableViewportScript ! = null ) {
disableViewportScript . delete ( ) ;
}
2015-05-06 20:34:59 +01:00
StringWriter sw = new StringWriter ( ) ;
err . printStackTrace ( new PrintWriter ( sw ) ) ;
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-01-06 14:43:14 +00:00
log . debug ( logline ) ;
}
2016-08-29 17:56:21 +02:00
log . error ( " Job::render exception(A) " + err + " stacktrace " + sw . toString ( ) ) ;
2015-05-06 20:34:59 +01:00
return Error . Type . FAILED_TO_EXECUTE ;
}
int exit_value = process . exitValue ( ) ;
process . finish ( ) ;
2017-04-25 13:06:23 +02:00
if ( timerOfMaxRenderTime ! = null ) {
timerOfMaxRenderTime . cancel ( ) ;
}
2015-05-06 20:34:59 +01:00
if ( script_file ! = null ) {
script_file . delete ( ) ;
}
2023-03-27 15:39:09 +00:00
if ( disableViewportScript ! = null ) {
disableViewportScript . delete ( ) ;
}
2015-05-06 20:34:59 +01:00
// find the picture file
2024-11-20 17:43:26 +00:00
final String filename_without_extension = getPrefixOutputImage ( ) + getRenderSettings ( ) . getFrameNumber ( ) ;
2015-05-06 20:34:59 +01:00
FilenameFilter textFilter = new FilenameFilter ( ) {
2024-06-07 14:30:24 +00:00
@Override public boolean accept ( File dir , String name ) {
2015-05-06 20:34:59 +01:00
return name . startsWith ( filename_without_extension ) ;
}
} ;
2019-08-07 22:17:59 +02:00
File [ ] files = configuration . getWorkingDirectory ( ) . listFiles ( textFilter ) ;
2020-05-28 13:28:42 +02:00
2024-11-20 17:43:26 +00:00
if ( getRenderState ( ) . isAskForRendererKill ( ) ) {
2016-08-29 18:06:17 +02:00
log . debug ( " Job::render been asked to end render " ) ;
2017-04-25 13:06:23 +02:00
2020-05-28 13:28:42 +02:00
long duration = ( new Date ( ) . getTime ( ) - process . getStartTime ( ) ) / 1000 ; // in seconds
2020-08-30 00:13:20 +10:00
if ( configuration . getMaxRenderTime ( ) > 0 & & duration > configuration . getMaxRenderTime ( ) & & Integer . parseInt ( this . getId ( ) ) > = 20 ) {
2019-08-07 22:17:59 +02:00
log . debug ( " Render killed because process duration ( " + duration + " s) is over user setting ( " + configuration . getMaxRenderTime ( ) + " s) " ) ;
2017-04-25 13:06:23 +02:00
return Error . Type . RENDERER_KILLED_BY_USER_OVER_TIME ;
}
2016-08-29 18:06:17 +02:00
if ( files . length ! = 0 ) {
2023-01-06 14:53:06 +00:00
Arrays . stream ( files ) . forEach ( file - > new File ( file . getAbsolutePath ( ) ) . delete ( ) ) ;
2016-08-29 18:06:17 +02:00
}
2024-11-20 17:43:26 +00:00
if ( getRenderState ( ) . isServerBlock ( ) ) {
2016-11-01 19:44:09 +01:00
return Error . Type . RENDERER_KILLED_BY_SERVER ;
}
2024-11-20 17:43:26 +00:00
if ( getRenderState ( ) . isUserBlock ( ) ) {
2016-08-29 18:06:17 +02:00
return Error . Type . RENDERER_KILLED_BY_USER ;
}
2024-11-20 17:43:26 +00:00
if ( getRenderState ( ) . isIncompatibleProcessKill ( ) ) {
2024-06-08 03:21:33 +00:00
return Error . Type . RENDERER_KILLED_BY_USER_INCOMPATIBLE_PROCESS ;
}
2016-08-29 18:06:17 +02:00
return Error . Type . RENDERER_KILLED ;
}
2015-05-06 20:34:59 +01:00
if ( files . length = = 0 ) {
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-01-06 14:43:14 +00:00
log . debug ( logline ) ;
}
2016-08-29 17:56:21 +02:00
log . error ( " Job::render no picture file found (after finished render (filename_without_extension " + filename_without_extension + " ) " ) ;
2015-05-06 20:34:59 +01:00
String basename = " " ;
try {
2024-11-20 17:43:26 +00:00
basename = getRenderSettings ( ) . getPath ( ) . substring ( 0 , getRenderSettings ( ) . getPath ( ) . lastIndexOf ( '.' ) ) ;
2015-05-06 20:34:59 +01:00
}
catch ( Exception e ) {
e . printStackTrace ( ) ;
}
2019-08-07 22:17:59 +02:00
File crash_file = new File ( configuration . getWorkingDirectory ( ) + File . separator + basename + " .crash.txt " ) ;
2015-05-06 20:34:59 +01:00
if ( crash_file . exists ( ) ) {
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-01-06 14:43:14 +00:00
log . debug ( logline ) ;
}
2016-08-29 17:56:21 +02:00
log . error ( " Job::render crash file found => the renderer crashed " ) ;
2015-05-06 20:34:59 +01:00
crash_file . delete ( ) ;
return Error . Type . RENDERER_CRASHED ;
}
if ( exit_value = = 127 & & process . getDuration ( ) < 10 ) {
2024-11-20 22:51:06 +00:00
for ( String logline : directoryManager . filesystemHealthCheck ( ) ) {
2023-01-06 14:43:14 +00:00
log . debug ( logline ) ;
}
2016-08-29 17:56:21 +02:00
log . error ( " Job::render renderer returned 127 and took " + process . getDuration ( ) + " s, some libraries may be missing " ) ;
2015-05-06 20:34:59 +01:00
return Error . Type . RENDERER_MISSING_LIBRARIES ;
}
return Error . Type . NOOUTPUTFILE ;
}
else {
2023-01-06 14:53:06 +00:00
if ( files . length = = 2 ) {
Arrays . sort ( files ) ; //in case of an exr we end up with 2 images, the output as an exr and the preview as a jpg, we want to ensure the output comes first
String path = files [ 1 ] . getAbsolutePath ( ) ;
String extension = path . substring ( path . lastIndexOf ( " . " ) + 1 ) . toLowerCase ( ) ;
if ( " jpg " . equals ( extension ) ) {
2024-11-20 17:43:26 +00:00
getRenderOutput ( ) . setPreviewImagePath ( files [ 1 ] . getAbsolutePath ( ) ) ;
2023-01-06 14:53:06 +00:00
}
}
2024-11-20 17:43:26 +00:00
getRenderOutput ( ) . setFullImagePath ( files [ 0 ] . getAbsolutePath ( ) ) ;
getRenderOutput ( ) . setFullImageSize ( new File ( getRenderOutput ( ) . getFullImagePath ( ) ) . length ( ) ) ;
log . debug ( String . format ( " Job::render pictureFilename: %s, size: %d' " , getRenderOutput ( ) . getFullImagePath ( ) , getRenderOutput ( ) . getFullImageSize ( ) ) ) ;
2015-05-06 20:34:59 +01:00
}
File scene_dir = new File ( getSceneDirectory ( ) ) ;
long date_modification_scene_directory = ( long ) Utils . lastModificationTime ( scene_dir ) ;
if ( date_modification_scene_directory > process . getStartTime ( ) ) {
scene_dir . delete ( ) ;
}
2023-05-21 15:07:18 +00:00
gui . status ( String . format ( " Render time: %dmin%ds " ,
2022-09-29 14:06:40 +00:00
process . getRenderDuration ( ) / 60 ,
process . getRenderDuration ( ) % 60 )
) ;
2015-05-06 20:34:59 +01:00
return Error . Type . OK ;
}
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
private int computeRenderingProgress ( String line , Pattern tilePattern , int currentProgress ) {
Matcher standardTileInfo = tilePattern . matcher ( line ) ;
int newProgress = currentProgress ;
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
if ( standardTileInfo . find ( ) ) {
2020-06-17 18:24:59 +10:00
int tileJustProcessed = Integer . parseInt ( standardTileInfo . group ( 2 ) ) ;
int totalTilesInJob = Integer . parseInt ( standardTileInfo . group ( 3 ) ) ;
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
newProgress = Math . abs ( ( tileJustProcessed * 100 ) / totalTilesInJob ) ;
}
2020-05-28 13:28:42 +02:00
2020-06-17 18:24:59 +10:00
// Only update the tray icon and the screen if percentage has changed
2020-03-19 23:48:06 +01:00
if ( newProgress ! = currentProgress ) {
gui . updateTrayIcon ( newProgress ) ;
2020-06-17 18:24:59 +10:00
gui . status ( " Rendering " , newProgress ) ;
2020-03-19 23:48:06 +01:00
}
2020-05-28 13:28:42 +02:00
2020-03-19 23:48:06 +01:00
return newProgress ;
}
2020-05-28 13:28:42 +02:00
2024-06-03 14:02:30 +00:00
private void updateSpeedSamplesRendered ( String line ) {
2023-02-02 18:25:53 +01:00
// Looking for "Rendered 1281 samples in 66.319402 seconds"
Pattern pattern = Pattern . compile ( " ^Rendered ( \\ d+) samples in ([ \\ d.]+) seconds$ " ) ;
Matcher matcher = pattern . matcher ( line ) ;
if ( matcher . find ( ) ) {
int amount = Integer . parseInt ( matcher . group ( 1 ) ) ;
float duration = Float . parseFloat ( matcher . group ( 2 ) ) ;
if ( duration ! = 0 & & amount ! = 0 ) {
2024-11-20 17:43:26 +00:00
this . renderOutput . setSpeedSamplesRendered ( amount / duration ) ;
2023-02-02 18:25:53 +01:00
}
}
// should we use this, instead ???
// "Average time per sample: 0.052112 seconds"
}
2022-02-14 11:30:09 +00:00
private void updateRenderingStatus ( String line , int progress ) {
2024-11-20 17:43:26 +00:00
if ( getRenderSettings ( ) . getUpdateRenderingStatusMethod ( ) = = null | | UPDATE_METHOD_BY_REMAINING_TIME . equals ( getRenderSettings ( ) . getUpdateRenderingStatusMethod ( ) ) ) {
2015-05-06 20:34:59 +01:00
String search_remaining = " remaining: " ;
int index = line . toLowerCase ( ) . indexOf ( search_remaining ) ;
if ( index ! = - 1 ) {
String buf1 = line . substring ( index + search_remaining . length ( ) ) ;
index = buf1 . indexOf ( " " ) ;
if ( index ! = - 1 ) {
String remaining_time = buf1 . substring ( 0 , index ) . trim ( ) ;
int last_index = remaining_time . lastIndexOf ( '.' ) ; //format 00:00:00.00 (hr:min:sec)
if ( last_index > 0 ) {
remaining_time = remaining_time . substring ( 0 , last_index ) ;
}
try {
DateFormat date_parse_minute = new SimpleDateFormat ( " m:s " ) ;
DateFormat date_parse_hour = new SimpleDateFormat ( " h:m:s " ) ;
DateFormat date_parse = date_parse_minute ;
if ( remaining_time . split ( " : " ) . length > 2 ) {
date_parse = date_parse_hour ;
}
date_parse . setTimeZone ( TimeZone . getTimeZone ( " GMT " ) ) ;
Date date = date_parse . parse ( remaining_time ) ;
2016-10-12 00:34:51 +02:00
gui . setRemainingTime ( Utils . humanDuration ( date ) ) ;
2024-12-14 14:10:52 +00:00
getRenderProcess ( ) . setRemainingDuration ( ( int ) ( date . getTime ( ) / 1000 ) ) ;
2015-05-06 20:34:59 +01:00
}
catch ( ParseException err ) {
log . error ( " Client::updateRenderingStatus ParseException " + err ) ;
}
}
}
2022-02-14 11:30:09 +00:00
else { //extrapolate remaining time from time rendered & progress
if ( line . contains ( " Time " ) = = true ) {
2024-12-14 14:10:52 +00:00
long timeRendered = new Date ( ) . getTime ( ) - getRenderProcess ( ) . getStartTime ( ) ;
2022-02-14 11:30:09 +00:00
if ( progress > 0 & & timeRendered > 0 ) {
long linearTimeEstimation = ( long ) ( ( 100 . 0 / progress ) * timeRendered ) ;
long timeRemaining = linearTimeEstimation - timeRendered ;
Date date = new Date ( timeRemaining ) ;
gui . setRemainingTime ( Utils . humanDuration ( date ) ) ;
2024-12-14 14:10:52 +00:00
getRenderProcess ( ) . setRemainingDuration ( ( int ) ( date . getTime ( ) / 1000 ) ) ;
2022-02-14 11:30:09 +00:00
}
}
}
2015-05-06 20:34:59 +01:00
}
2024-11-20 17:43:26 +00:00
else if ( UPDATE_METHOD_BY_TILE . equals ( getRenderSettings ( ) . getUpdateRenderingStatusMethod ( ) ) ) {
2018-08-24 19:46:03 +02:00
String search = " Tile " ;
int index = line . lastIndexOf ( search ) ;
if ( index ! = - 1 ) {
String buf = line . substring ( index + search . length ( ) ) ;
String [ ] parts = buf . split ( " / " ) ;
2019-08-05 14:11:24 +02:00
if ( parts . length = = 2 ) {
2018-08-24 19:46:03 +02:00
try {
int current = Integer . parseInt ( parts [ 0 ] ) ;
int total = Integer . parseInt ( parts [ 1 ] ) ;
if ( total ! = 0 ) {
gui . status ( String . format ( " Rendering %s %% " , ( int ) ( 100 . 0 * current / total ) ) ) ;
return ;
}
}
catch ( NumberFormatException e ) {
2024-12-14 09:39:28 +00:00
log . error ( " Exception 94: " + e ) ;
2018-08-24 19:46:03 +02:00
}
}
}
gui . status ( " Rendering " ) ;
}
2015-05-06 20:34:59 +01:00
}
2021-12-21 10:08:59 +00:00
private void updateProcess ( ) {
2024-12-14 14:10:52 +00:00
getRenderProcess ( ) . update ( ) ;
2015-05-06 20:34:59 +01:00
}
2020-05-28 13:28:42 +02:00
2024-04-19 18:40:55 +00:00
private void maybeCleanWorkingDir ( Type error ) {
boolean cleanup = Type . COLOR_MANAGEMENT_ERROR = = error
| | Type . RENDERER_CRASHED_PYTHON_ERROR = = error
| | Type . ENGINE_NOT_AVAILABLE = = error
| | Type . DETECT_DEVICE_ERROR = = error ;
if ( cleanup ) {
2024-11-20 22:51:06 +00:00
directoryManager . cleanWorkingDirectory ( ) ;
2024-04-19 18:40:55 +00:00
}
}
2019-08-07 18:40:02 +02:00
public static class renderStartedObservable extends Observable {
2020-05-28 13:28:42 +02:00
@Getter private boolean isStarted ;
2019-08-07 18:40:02 +02:00
public renderStartedObservable ( Observer observer ) {
super ( ) ;
addObserver ( observer ) ;
}
2020-05-28 13:28:42 +02:00
2019-08-07 18:40:02 +02:00
public void doNotifyIsStarted ( ) {
setChanged ( ) ;
notifyObservers ( ) ;
isStarted = true ;
}
}
2014-11-20 13:21:19 +00:00
}