Commits

David Bucciarelli committed df39616

Introduced the first code for an halt condition based on convergence threshold

Comments (0)

Files changed (8)

core/contribution.cpp

 void ContributionBuffer::Buffer::Splat(Film *film, u_int tileIndex)
 {
 	const u_int num_contribs = min(pos, CONTRIB_BUF_SIZE);
-	film->AddTileSamples(contribs, num_contribs, tileIndex);
+	film->AddTileSamples(contribs, num_contribs, tileIndex, film->GetColorSpace());
 	pos = 0;
 }
 
 
 Film::Film(u_int xres, u_int yres, Filter *filt, u_int filtRes, const float crop[4], 
 		   const string &filename1, bool premult, bool useZbuffer,
-		   bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct, int haltspp, int halttime,
+		   bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct,
+		   int haltspp, int halttime, float haltthreshold,
 		   bool debugmode, int outlierk, int tilec) :
 	Queryable("film"),
 	xResolution(xres), yResolution(yres),
 	contribPool(NULL), filter(filt), filterTable(NULL), filterLUTs(NULL),
 	filename(filename1),
 	colorSpace(0.63f, 0.34f, 0.31f, 0.595f, 0.155f, 0.07f, 0.314275f, 0.329411f), // default is SMPTE
+	convergenceBufferReference(NULL), convergenceBufferReferenceCount(NULL),
+	convergenceBufferDelta(NULL),
 	ZBuffer(NULL), use_Zbuf(useZbuffer),
 	debug_mode(debugmode), premultiplyAlpha(premult),
 	writeResumeFlm(w_resume_FLM), restartResumeFlm(restart_resume_FLM), writeFlmDirect(write_FLM_direct),
 	outlierRejection_k(outlierk), haltSamplesPerPixel(haltspp),
-	haltTime(halttime), histogram(NULL), enoughSamplesPerPixel(false)
+	haltTime(halttime), haltThreshold(haltthreshold), haltThresholdComplete(0.f),
+	histogram(NULL), enoughSamplesPerPixel(false)
 {
 	// Compute film image extent
 	memcpy(cropWindow, crop, 4 * sizeof(float));
 	AddBoolAttribute(*this, "enoughSamples", "Indicates if the halt condition been reached", &Film::enoughSamplesPerPixel);
 	AddIntAttribute(*this, "haltSamplesPerPixel", "Halt Samples per Pixel", haltSamplesPerPixel, &Film::haltSamplesPerPixel, Queryable::ReadWriteAccess);
 	AddIntAttribute(*this, "haltTime", "Halt time in seconds", haltTime, &Film::haltTime, Queryable::ReadWriteAccess);
+	AddFloatAttribute(*this, "haltThreshold", "Halt threshold", haltThreshold, &Film::haltThreshold, Queryable::ReadWriteAccess);
+	AddFloatAttribute(*this, "haltThresholdComplete", "Halt threshold complete", &Film::haltThresholdComplete);
 	AddBoolAttribute(*this, "writeResumeFlm", "Write resume file", writeResumeFlm, &Film::writeResumeFlm, Queryable::ReadWriteAccess);
 	AddBoolAttribute(*this, "restartResumeFlm", "Restart (overwrite) resume file", restartResumeFlm, &Film::restartResumeFlm, Queryable::ReadWriteAccess);
 	AddBoolAttribute(*this, "writeFlmDirect", "Write resume file directly to disk", writeFlmDirect, &Film::writeFlmDirect, Queryable::ReadWriteAccess);	
 	delete filterLUTs;
 	delete filter;
 	delete ZBuffer;
+	delete convergenceBufferReference;
+	delete convergenceBufferReferenceCount;
+	delete convergenceBufferDelta;
 	delete histogram;
 	delete contribPool;
 }
 	if (bufferGroups.size() == 0)
 		bufferGroups.push_back(BufferGroup("default"));
 	for (u_int i = 0; i < bufferGroups.size(); ++i)
-		bufferGroups[i].CreateBuffers(bufferConfigs,xPixelCount,yPixelCount);
+		bufferGroups[i].CreateBuffers(bufferConfigs,xPixelCount, yPixelCount);
 
 	// Allocate ZBuf buffer if needed
-	if(use_Zbuf)
-		ZBuffer = new PerPixelNormalizedFloatBuffer(xPixelCount,yPixelCount);
+	if (use_Zbuf)
+		ZBuffer = new PerPixelNormalizedFloatBuffer(xPixelCount, yPixelCount);
+	
+	// Allocate convergence buffers if needed
+	if (haltThreshold > 0.f) {
+		convergenceBufferReference = new BlockedArray<RGBColor>(xPixelCount, yPixelCount);
+		convergenceBufferReferenceCount = new BlockedArray<float>(xPixelCount, yPixelCount);
+		convergenceBufferReferenceCount->Fill(0.f);
+		convergenceBufferDelta = new BlockedArray<float>(xPixelCount, yPixelCount);
+		convergenceBufferDelta->Fill(-1.f);
+		convergenceBufferMap.resize(xPixelCount * yPixelCount, false);
+		convergencePixelCount = 0;
+	}
 
     // Dade - check if we have to resume a rendering and restore the buffers
     if(writeResumeFlm) {
 }
 
 
-void Film::AddTileSamples(const Contribution* const contribs, u_int num_contribs, u_int tileIndex) {
-	
+void Film::AddTileSamples(const Contribution* const contribs, u_int num_contribs,
+		u_int tileIndex, const ColorSystem *colorSpace) {
 	int xTilePixelStart, xTilePixelEnd;
 	int yTilePixelStart, yTilePixelEnd;
 	GetTileExtent(tileIndex, &xTilePixelStart, &xTilePixelEnd, &yTilePixelStart, &yTilePixelEnd);
 		RejectTileOutliers(contribs, num_contribs, tileIndex, yTilePixelStart, yTilePixelEnd);
 	}
 
-
 	for (u_int ci = 0; ci < num_contribs; ci++) {
 		const Contribution &contrib(contribs[ci]);
 
 		if (premultiplyAlpha)
 			xyz *= alpha;
 
+		BufferConfig &currentGroupConfig = bufferConfigs[contrib.bufferGroup];
 		BufferGroup &currentGroup = bufferGroups[contrib.bufferGroup];
 		Buffer *buffer = currentGroup.getBuffer(contrib.buffer);
 
 		const u_int yEnd = static_cast<u_int>(min(y1, yTilePixelEnd));
 
 		for (u_int y = yStart; y < yEnd; ++y) {
-			const int yoffset = (y-y0) * filterLUT.GetWidth();
+			const int yoffset = (y - y0) * filterLUT.GetWidth();
+			const u_int yPixel = y - yPixelStart;
 			for (u_int x = xStart; x < xEnd; ++x) {
 				// Evaluate filter value at $(x,y)$ pixel
 				const int xoffset = x-x0;
 				const float filterWt = lut[yoffset + xoffset];
 				// Update pixel values with filtered sample contribution
-				buffer->Add(x - xPixelStart,y - yPixelStart,
+				const u_int xPixel = x - xPixelStart;
+				buffer->Add(xPixel, yPixel,
 					xyz, alpha, filterWt * weight);
 				// Update ZBuffer values with filtered zdepth contribution
 				if(use_Zbuf && contrib.zdepth != 0.f)
-					ZBuffer->Add(x - xPixelStart, y - yPixelStart, contrib.zdepth, 1.0f);
+					ZBuffer->Add(xPixel, yPixel, contrib.zdepth, 1.0f);
+
+				if ((haltThreshold > 0.f) && (currentGroupConfig.output & BUF_FRAMEBUFFER)) {
+					// Check if we have enough samples to evaluate convergence speed again
+
+					// Merge all buffer results
+					XYZColor c;
+					float newSampleCount = 0.f;
+					for(u_int j = 0; j < bufferGroups.size(); ++j) {
+						if (!bufferGroups[j].enable)
+							continue;
+
+						for(u_int i = 0; i < bufferConfigs.size(); ++i) {
+							const Buffer &buffer = *(bufferGroups[j].buffers[i]);
+							if (!(bufferConfigs[i].output & BUF_FRAMEBUFFER))
+								continue;
+
+							XYZColor p;
+							float a;
+
+							newSampleCount += buffer.GetData(xPixel, yPixel, &p, &a);
+							c += bufferGroups[j].convert.Adapt(p);
+						}
+					}
+
+					float &oldSampleCount = (*convergenceBufferReferenceCount)(xPixel, yPixel);
+
+					if (newSampleCount - oldSampleCount > 2.f) {
+						// We have enough samples, update the convergence map
+
+						const RGBColor newC = colorSpace->ToRGBConstrained(c);
+						RGBColor &oldC = (*convergenceBufferReference)(xPixel, yPixel);
+
+						const float newDelta = max(max(
+								fabs(newC.c[0] - oldC.c[0]),
+								fabs(newC.c[1] - oldC.c[1])),
+								fabs(newC.c[2] - oldC.c[2])) / 256.f;
+						float &oldDelta = (*convergenceBufferDelta)(xPixel, yPixel);
+
+						// Update values
+						oldC = newC;
+						oldSampleCount = newSampleCount;
+						oldDelta = newDelta;
+
+						const size_t mapOffset = xPixel + yPixel * (xPixelCount);
+						if (newDelta < haltThreshold) {
+							// Convergence condition has been satisfied
+							if (!convergenceBufferMap[mapOffset])
+								++convergencePixelCount;
+							convergenceBufferMap[mapOffset] = true;
+						} else {
+							// Convergence condition has been satisfied
+							if (convergenceBufferMap[mapOffset])
+								--convergencePixelCount;
+							convergenceBufferMap[mapOffset] = false;
+						}
+
+						haltThresholdComplete = convergencePixelCount / (float)(xPixelCount * yPixelCount);
+					}
+				}
 			}
 		}
 	}
 void Film::AddSample(Contribution *contrib) {
 	u_int tileIndex0, tileIndex1;
 	u_int tiles = GetTileIndexes(*contrib, &tileIndex0, &tileIndex1);
-	AddTileSamples(contrib, 1, tileIndex0);
+	AddTileSamples(contrib, 1, tileIndex0, GetColorSpace());
 	if (tiles > 1)
-		AddTileSamples(contrib, 1, tileIndex1);
+		AddTileSamples(contrib, 1, tileIndex1, GetColorSpace());
 }
 
 void Film::SetSample(const Contribution *contrib) {
 	Film(u_int xres, u_int yres, Filter *filt, u_int filtRes, const float crop[4],
 		const string &filename1, bool premult, bool useZbuffer,
 		bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct,
-		int haltspp, int halttime, bool debugmode, int outlierk, int tilecount);
+		int haltspp, int halttime, float haltthreshold, bool debugmode, int outlierk, int tilecount);
 
 	virtual ~Film();
 
 	 * @param contribs Array of contributions to add
 	 * @param num_contribs Number of contributions in the contribs array
 	 * @param tileIndex Index of the tile the contributions should be added to
+	 * @param colorSpace Color space used for convergence test (can be NULL)
 	 */
-	virtual void AddTileSamples(const Contribution* const contribs, u_int num_contribs, u_int tileIndex);
+	virtual void AddTileSamples(const Contribution* const contribs, u_int num_contribs,
+		u_int tileIndex, const ColorSystem *colorSpace);
 	virtual void SetSample(const Contribution *contrib);
 	virtual void AddSampleCount(float count);
 	virtual void SaveEXR(const string &exrFilename, bool useHalfFloats, bool includeZBuf, int compressionType, bool tonemapped) {
 	virtual void SetStringParameterValue(luxComponentParameters param, const string& value, u_int index) = 0;
 	virtual string GetStringParameterValue(luxComponentParameters param, u_int index) = 0;
 
+	virtual ColorSystem *GetColorSpace(){ return &colorSpace; }
+
 	/*
 	 * Accessor for samplePerPass
 	 * It is only used by SPPM and may disappears once the Buffer API allows for
 
 	std::vector<BufferConfig> bufferConfigs;
 	std::vector<BufferGroup> bufferGroups;
+
+	BlockedArray<RGBColor> *convergenceBufferReference;
+	BlockedArray<float> *convergenceBufferReferenceCount;
+	BlockedArray<float> *convergenceBufferDelta;
+	vector<bool> convergenceBufferMap;
+	u_int convergencePixelCount;
+
 	PerPixelNormalizedFloatBuffer *ZBuffer;
 	bool use_Zbuf;
 
 	int haltSamplesPerPixel;
 	// Seconds to wait before to stop. Any value <= 0 will never stop the rendering
 	int haltTime;
+	// Convergence threshold to reach before to stop the rendering
+	float haltThreshold;
+	float haltThresholdComplete;
 
 	Histogram *histogram;
 	bool enoughSamplesPerPixel; // At the end to get better data alignment
 			}
 		}
 	}
+	void Fill(const T d) {
+		for (size_t v = 0; v < vRes; ++v) {
+			for (size_t u = 0; u < uRes; ++u)
+				(*this)(u, v) = d;
+		}
+	}
 	size_t BlockSize() const { return 1 << logBlockSize; }
 	size_t RoundUp(size_t x) const {
 		return (x + BlockSize() - 1) & ~(BlockSize() - 1);

core/rendererstatistics.cpp

 	AddDoubleAttribute(*this, "remainingTime", "Remaining rendering time", &RendererStatistics::getRemainingTime);
 	AddDoubleAttribute(*this, "haltTime", "Halt rendering after time", &RendererStatistics::getHaltTime);
 	AddDoubleAttribute(*this, "percentHaltTimeComplete", "Percent of halt time completed", &RendererStatistics::getPercentHaltTimeComplete);
+	AddDoubleAttribute(*this, "haltThreshold", "Halt rendering when convergence speed is under a certain threshold", &RendererStatistics::getHaltThreshold);
+	AddDoubleAttribute(*this, "percentHaltThresholdComplete", "Percent of halt threshold completed", &RendererStatistics::getPercentHaltThresholdComplete);
 	AddDoubleAttribute(*this, "percentComplete", "Percent of render completed", &RendererStatistics::getPercentComplete);
 	AddDoubleAttribute(*this, "efficiency", "Efficiency of renderer", &RendererStatistics::getEfficiency);
 	AddDoubleAttribute(*this, "efficiencyWindow", "Efficiency of renderer", &RendererStatistics::getEfficiencyWindow);
 	return getPercentHaltTimeComplete();
 }
 
+double RendererStatistics::getHaltThreshold() {
+	float haltThreshold = 0;
+
+	Queryable* filmRegistry = Context::GetActive()->registry["film"];
+	if (filmRegistry)
+		haltThreshold = (*filmRegistry)["haltThreshold"].FloatValue();
+
+	return haltThreshold > 0.f ? haltThreshold : std::numeric_limits<double>::infinity();
+}
+
+double RendererStatistics::getPercentHaltThresholdComplete() {
+	double haltThresholdComplete = 0;
+
+	Queryable* filmRegistry = Context::GetActive()->registry["film"];
+	if (filmRegistry)
+		haltThresholdComplete = (*filmRegistry)["haltThresholdComplete"].FloatValue();
+
+	return haltThresholdComplete * 100.0;
+}
+
 u_int RendererStatistics::getSlaveNodeCount() {
 	return Context::GetActive()->GetServerCount();
 }
 	AddStringAttribute(*this, "elapsedTime", "Elapsed rendering time", &RendererStatistics::Formatted::getElapsedTime);
 	AddStringAttribute(*this, "remainingTime", "Remaining rendering time", &RendererStatistics::Formatted::getRemainingTime);
 	AddStringAttribute(*this, "haltTime", "Halt rendering after time", &RendererStatistics::Formatted::getHaltTime);
+	AddStringAttribute(*this, "haltThreshold", "Halt rendering when convergence speed is under a certain threshold", &RendererStatistics::Formatted::getHaltThreshold);
 }
 
 // Helper class for RendererStatistics::Formatted::getStringFromTemplate()
 	return boost::posix_time::to_simple_string(time_duration(0, 0, Round2UInt(rs->getHaltTime()), 0));
 }
 
+std::string RendererStatistics::Formatted::getHaltThreshold() {
+	return boost::str(boost::format("%1$0.0f%%") % rs->getHaltThreshold());
+}
+
 RendererStatistics::FormattedLong::FormattedLong(RendererStatistics* rs)
 	: Formatted(rs, "renderer_statistics_formatted")
 {
 	typedef RendererStatistics::FormattedLong FL;
 
 	AddStringAttribute(*this, "percentHaltTimeComplete", "Percent of halt time completed", &FL::getPercentHaltTimeComplete);
+	AddStringAttribute(*this, "percentHaltThresholdComplete", "Percent of halt threshold completed", &FL::getPercentHaltThresholdComplete);
 	AddStringAttribute(*this, "percentComplete", "Percent of render completed", &FL::getPercentComplete);
 
 	AddStringAttribute(*this, "efficiency", "Efficiency of renderer", &FL::getEfficiency);
 		stringTemplate += " [%remainingTime%]";
 	if (rs->getPercentHaltTimeComplete() != 0.0)
 		stringTemplate += " (%percentHaltTimeComplete%)";
+	if (rs->getHaltThreshold() > 0.0)
+		stringTemplate += " (%percentHaltThresholdComplete%)";
 	stringTemplate += " - %threadCount%";
 	if (rs->getSlaveNodeCount() != 0)
 		stringTemplate += " %slaveNodeCount%";
 	return boost::str(boost::format("%1$0.0f%% Time") % rs->getPercentHaltTimeComplete());
 }
 
+std::string RendererStatistics::FormattedLong::getPercentHaltThresholdComplete() {
+	return boost::str(boost::format("%1$0.2f%% Convergence") % rs->getPercentHaltThresholdComplete());
+}
+
 std::string RendererStatistics::FormattedLong::getEfficiency() {
 	return boost::str(boost::format("%1$0.0f%% Efficiency") % rs->getEfficiency());
 }
 
 	AddStringAttribute(*this, "percentComplete", "Percent of render completed", boost::bind(boost::mem_fn(&FL::getPercentComplete), fl));
 	AddStringAttribute(*this, "percentHaltTimeComplete", "Percent of halt time completed", &FS::getPercentHaltTimeComplete);
+	AddStringAttribute(*this, "percentHaltThresholdComplete", "Percent of halt threshold completed", &FS::getPercentHaltThresholdComplete);
 
 	AddStringAttribute(*this, "efficiency", "Efficiency of renderer", &FS::getEfficiency);
 	AddStringAttribute(*this, "efficiencyWindow", "Efficiency of renderer", &FS::getEfficiencyWindow);
 		stringTemplate += " [%remainingTime%]";
 	if (rs->getPercentHaltTimeComplete() != 0.0)
 		stringTemplate += " (%percentHaltTimeComplete%)";
+	if (rs->getHaltThreshold() > 0.0)
+		stringTemplate += " (%percentHaltThresholdComplete%)";
 	stringTemplate += " - %threadCount%";
 	if (rs->getSlaveNodeCount() != 0)
 		stringTemplate += " %slaveNodeCount%";
 	return boost::str(boost::format("%1$0.0f%% T") % rs->getPercentHaltTimeComplete());
 }
 
+std::string RendererStatistics::FormattedShort::getPercentHaltThresholdComplete() {
+	return boost::str(boost::format("%1$0.2f%% Conv") % rs->getPercentHaltThresholdComplete());
+}
+
 std::string RendererStatistics::FormattedShort::getEfficiency() {
 	return boost::str(boost::format("%1$0.0f%% Eff") % rs->getEfficiency());
 }

core/rendererstatistics.h

 		std::string getElapsedTime();
 		std::string getRemainingTime();
 		std::string getHaltTime();
+		std::string getHaltThreshold();
 	};
 
 	class FormattedShort;	// Forward declaration
 
 		std::string getPercentComplete();
 		std::string getPercentHaltTimeComplete();
+		std::string getPercentHaltThresholdComplete();
 
 		virtual std::string getEfficiency();
 		virtual std::string getEfficiencyWindow();
 		virtual std::string getRecommendedStringTemplate();
 
 		std::string getPercentHaltTimeComplete();
+		std::string getPercentHaltThresholdComplete();
 
 		virtual std::string getEfficiency();
 		virtual std::string getEfficiencyWindow();
 	double getElapsedTime() { return timer.Time(); }
 	double getHaltTime();
 	double getPercentHaltTimeComplete();
+	double getHaltThreshold();
+	double getPercentHaltThresholdComplete();
 	u_int getSlaveNodeCount();
 
 	// These methods must be overridden for renderers

film/fleximage.cpp

 	bool cw_EXR_gamutclamp, bool cw_EXR_ZBuf, ZBufNormalization cw_EXR_ZBuf_normalizationtype, bool cw_EXR_straight_colors,
 	bool cw_PNG, OutputChannels cw_PNG_channels, bool cw_PNG_16bit, bool cw_PNG_gamutclamp, bool cw_PNG_ZBuf, ZBufNormalization cw_PNG_ZBuf_normalizationtype,
 	bool cw_TGA, OutputChannels cw_TGA_channels, bool cw_TGA_gamutclamp, bool cw_TGA_ZBuf, ZBufNormalization cw_TGA_ZBuf_normalizationtype, 
-	bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct, int haltspp, int halttime,
+	bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct, int haltspp, int halttime, float haltthreshold,
 	int p_TonemapKernel, float p_ReinhardPreScale, float p_ReinhardPostScale,
 	float p_ReinhardBurn, float p_LinearSensitivity, float p_LinearExposure, float p_LinearFStop, float p_LinearGamma,
 	float p_ContrastYwa, const string &p_response, float p_Gamma,
 	const float cs_red[2], const float cs_green[2], const float cs_blue[2], const float whitepoint[2],
 	bool debugmode, int outlierk, int tilec) :
 	Film(xres, yres, filt, filtRes, crop, filename1, premult, cw_EXR_ZBuf || cw_PNG_ZBuf || cw_TGA_ZBuf, w_resume_FLM, 
-		restart_resume_FLM, write_FLM_direct, haltspp, halttime, debugmode, outlierk, tilec), 
+		restart_resume_FLM, write_FLM_direct, haltspp, halttime, haltthreshold, debugmode, outlierk, tilec), 
 	framebuffer(NULL), float_framebuffer(NULL), alpha_buffer(NULL), z_buffer(NULL),
 	writeInterval(wI), flmWriteInterval(fwI), displayInterval(dI)
 {
 					framebuffer[3 * i] = static_cast<unsigned char>(Clamp(256 * rgbcolor[i].c[0], 0.f, 255.f));
 					framebuffer[3 * i + 1] = static_cast<unsigned char>(Clamp(256 * rgbcolor[i].c[1], 0.f, 255.f));
 					framebuffer[3 * i + 2] = static_cast<unsigned char>(Clamp(256 * rgbcolor[i].c[2], 0.f, 255.f));
+					
+					// Some debug code used to show the convergence map
+					//framebuffer[3 * i] = framebuffer[3 * i + 1] = framebuffer[3 * i + 2] = convergenceBufferMap[i] ? 255.f : 0.f;
 				}
 			}
 			if ((type & IMAGE_FRAMEBUFFER) && float_framebuffer) {
 
 	const int haltspp = params.FindOneInt("haltspp", -1);
 	const int halttime = params.FindOneInt("halttime", -1);
+	const float haltthreshold = params.FindOneFloat("haltthreshold", -1.f);
 
 	// Color space primaries and white point
 	// default is SMPTE
 		w_EXR, w_EXR_channels, w_EXR_halftype, w_EXR_compressiontype, w_EXR_applyimaging, w_EXR_gamutclamp, w_EXR_ZBuf, w_EXR_ZBuf_normalizationtype, w_EXR_straightcolors,
 		w_PNG, w_PNG_channels, w_PNG_16bit, w_PNG_gamutclamp, w_PNG_ZBuf, w_PNG_ZBuf_normalizationtype,
 		w_TGA, w_TGA_channels, w_TGA_gamutclamp, w_TGA_ZBuf, w_TGA_ZBuf_normalizationtype, 
-		w_resume_FLM, restart_resume_FLM, w_FLM_direct, haltspp, halttime,
+		w_resume_FLM, restart_resume_FLM, w_FLM_direct, haltspp, halttime, haltthreshold,
 		s_TonemapKernel, s_ReinhardPreScale, s_ReinhardPostScale, s_ReinhardBurn, s_LinearSensitivity,
 		s_LinearExposure, s_LinearFStop, s_LinearGamma, s_ContrastYwa, response, s_Gamma,
 		red, green, blue, white, debug_mode, outlierrejection_k, tilecount);
 		bool cw_EXR_gamutclamp, bool cw_EXR_ZBuf, ZBufNormalization cw_EXR_ZBuf_normalizationtype, bool cw_EXR_straight_colors,
 		bool cw_PNG, OutputChannels cw_PNG_channels, bool cw_PNG_16bit, bool cw_PNG_gamutclamp, bool cw_PNG_ZBuf, ZBufNormalization cw_PNG_ZBuf_normalizationtype,
 		bool cw_TGA, OutputChannels cw_TGA_channels, bool cw_TGA_gamutclamp, bool cw_TGA_ZBuf, ZBufNormalization cw_TGA_ZBuf_normalizationtype, 
-		bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct, int haltspp, int halttime,
+		bool w_resume_FLM, bool restart_resume_FLM, bool write_FLM_direct, int haltspp, int halttime, float haltthreshold,
 		int p_TonemapKernel, float p_ReinhardPreScale, float p_ReinhardPostScale,
 		float p_ReinhardBurn, float p_LinearSensitivity, float p_LinearExposure, float p_LinearFStop, float p_LinearGamma,
 		float p_ContrastDisplayAdaptionY, const string &response, float p_Gamma,
 	float m_RGB_X_Blue, d_RGB_X_Blue;
 	float m_RGB_Y_Blue, d_RGB_Y_Blue;
 	float m_Gamma, d_Gamma;
-	int clampMethod, d_clampMethod;	
+	int clampMethod, d_clampMethod;
 
 	int m_TonemapKernel, d_TonemapKernel;
 	float m_ReinhardPreScale, d_ReinhardPreScale;