Remove passes.

Fix some table dimensions.
Proper locale for normalized weight.
This commit is contained in:
mfeemster
2014-11-02 23:16:34 -08:00
parent 6b2b6ede7f
commit b2600f0fff
28 changed files with 257 additions and 394 deletions

View File

@ -93,7 +93,6 @@ public:
m_OrigFinalRasH = ember.m_OrigFinalRasH;
m_OrigPixPerUnit = ember.m_OrigPixPerUnit;
m_Supersample = ember.m_Supersample;
m_Passes = ember.m_Passes;
m_TemporalSamples = ember.m_TemporalSamples;
m_Symmetry = ember.m_Symmetry;
@ -186,7 +185,6 @@ public:
m_OrigFinalRasH = 1080;
m_OrigPixPerUnit = 240;
m_Supersample = 1;
m_Passes = 1;
m_TemporalSamples = 1000;
m_Symmetry = 0;
m_Quality = 100;
@ -778,7 +776,6 @@ public:
InterpT<&Ember<T>::m_CamDepthBlur>(embers, coefs, size);
InterpX<m3T, &Ember<T>::m_CamMat>(embers, coefs, size);
InterpT<&Ember<T>::m_Rotate>(embers, coefs, size);
InterpI<&Ember<T>::m_Passes>(embers, coefs, size);
InterpI<&Ember<T>::m_TemporalSamples>(embers, coefs, size);
InterpT<&Ember<T>::m_MaxRadDE>(embers, coefs, size);
InterpT<&Ember<T>::m_MinRadDE>(embers, coefs, size);
@ -1309,7 +1306,6 @@ public:
m_MinRadDE = 0;
m_CurveDE = T(0.4);
m_GammaThresh = T(0.01);
m_Passes = 1;
m_TemporalSamples = 1000;
m_SpatialFilterType = GAUSSIAN_SPATIAL_FILTER;
m_AffineInterp = INTERP_LOG;
@ -1341,7 +1337,6 @@ public:
m_MinRadDE = -1;
m_CurveDE = -1;
m_GammaThresh = -1;
m_Passes = 0;
m_TemporalSamples = 0;
m_SpatialFilterType = GAUSSIAN_SPATIAL_FILTER;
m_AffineInterp = INTERP_LOG;
@ -1381,7 +1376,6 @@ public:
<< "Original Raster Width: " << m_OrigFinalRasW << endl
<< "Original Raster Height: " << m_OrigFinalRasH << endl
<< "Supersample: " << m_Supersample << endl
<< "Passes: " << m_Passes << endl
<< "Temporal Samples: " << m_TemporalSamples << endl
<< "Symmetry: " << m_Symmetry << endl
@ -1468,11 +1462,7 @@ public:
//The multiplier in size of the histogram and DE filtering buffers. Must be at least one, preferrably never larger than 4, only useful at 2.
//Xml field: "supersample" or "overample (deprecated)".
size_t m_Supersample;
//Times to run the algorithm while clearing the histogram, but not the filter. Almost always set to 1 and may even be deprecated.
//Xml field: "passes".
size_t m_Passes;
//When animating, split each pass into this many pieces, each doing a fraction of the total iterations. Each temporal sample
//will render an interpolated instance of the ember that is a fraction of the current ember and the next one.
//When rendering a single image, this field is always set to 1.

View File

@ -36,7 +36,7 @@ namespace EmberNs
extern void sincos(float x, float *s, float *c);
#endif
#define EMBER_VERSION "0.4.1.3"
#define EMBER_VERSION "0.4.1.4"
#define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
#define ISAAC_SIZE 4

View File

@ -155,7 +155,6 @@ public:
os << " temporal_filter_width=\"" << ember.m_TemporalFilterWidth << "\"";
os << " quality=\"" << ember.m_Quality << "\"";
os << " passes=\"" << ember.m_Passes << "\"";
os << " temporal_samples=\"" << ember.m_TemporalSamples << "\"";
os << " background=\"" << ember.m_Background.r << " " << ember.m_Background.g << " " << ember.m_Background.b << "\"";
os << " brightness=\"" << ember.m_Brightness << "\"";

View File

@ -265,14 +265,13 @@ bool Renderer<T, bucketT>::CreateTemporalFilter(bool& newAlloc)
//Use intelligent testing so it isn't created every time a new ember is passed in.
if ((!m_TemporalFilter.get()) ||
(m_Ember.m_Passes != m_TemporalFilter->Passes()) ||
(m_Ember.m_TemporalSamples != m_TemporalFilter->TemporalSamples()) ||
(m_Ember.m_TemporalFilterType != m_TemporalFilter->FilterType()) ||
(m_Ember.m_TemporalFilterWidth != m_TemporalFilter->FilterWidth()) ||
(m_Ember.m_TemporalFilterExp != m_TemporalFilter->FilterExp()))
{
m_TemporalFilter = unique_ptr<TemporalFilter<T>>(
TemporalFilterCreator<T>::Create(m_Ember.m_TemporalFilterType, m_Ember.m_Passes, m_Ember.m_TemporalSamples, m_Ember.m_TemporalFilterWidth, m_Ember.m_TemporalFilterExp));
TemporalFilterCreator<T>::Create(m_Ember.m_TemporalFilterType, m_Ember.m_TemporalSamples, m_Ember.m_TemporalFilterWidth, m_Ember.m_TemporalFilterExp));
newAlloc = true;
}
@ -287,15 +286,13 @@ bool Renderer<T, bucketT>::CreateTemporalFilter(bool& newAlloc)
/// future-proofs the algorithm for GPU-based renderers.
/// If the caller calls Abort() at any time, or the progress function returns 0,
/// the entire rendering process will exit as soon as it can.
/// The concept of passes from flam3 has been removed as it was never used.
/// The loop structure is:
/// {
/// Passes (Default 1)
/// Temporal Samples (Default 1 for single image)
/// {
/// Temporal Samples (Default 1 for single image)
/// Iterate (Either to completion or to a specified number of iterations)
/// {
/// Iterate (Either to completion or to a specified number of iterations)
/// {
/// }
/// }
/// }
///
@ -326,11 +323,11 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<unsigned char>& finalImage, doubl
m_InRender = true;
EnterRender();
m_Abort = false;
bool filterAndAccumOnly = (m_ProcessAction == FILTER_AND_ACCUM && Passes() == 1);
bool filterAndAccumOnly = m_ProcessAction == FILTER_AND_ACCUM;
bool accumOnly = m_ProcessAction == ACCUM_ONLY;
bool resume = m_ProcessState != NONE;
bool newFilterAlloc;
size_t temporalSample = 0, pass;
size_t temporalSample = 0;
T deTime;
eRenderStatus success = RENDER_OK;
//double iterationTime = 0;
@ -349,7 +346,6 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<unsigned char>& finalImage, doubl
if (!resume)//Beginning, reset everything.
{
m_LastPass = 0;
m_LastTemporalSample = 0;
m_LastIter = 0;
m_LastIterPercent = 0;
@ -360,9 +356,8 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<unsigned char>& finalImage, doubl
m_Background.Clear();
}
//User requested an increase in quality after finishing.
else if (m_ProcessState == ITER_STARTED && m_ProcessAction == KEEP_ITERATING && TemporalSamples() == 1 && Passes() == 1)
else if (m_ProcessState == ITER_STARTED && m_ProcessAction == KEEP_ITERATING && TemporalSamples() == 1)
{
m_LastPass = 0;
m_LastTemporalSample = 0;
m_LastIter = m_Stats.m_Iters;
m_LastIterPercent = 0;//Might skip a progress update, but shouldn't matter.
@ -372,10 +367,7 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<unsigned char>& finalImage, doubl
m_Background.Clear();
}
pass = (resume ? m_LastPass : 0);
//Make sure values are within valid range.
ClampGteRef(m_Ember.m_Passes, size_t(1));
ClampGteRef(m_Ember.m_Supersample, size_t(1));
//Make sure to get most recent update since loop won't be entered to call Interp().
@ -426,199 +418,176 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<unsigned char>& finalImage, doubl
if (!resume)
ResetBuckets(true, false);//Only reset hist here and do accum when needed later on.
//Passes, outermost loop 1.
for (; (pass < Passes()) && !m_Abort;)
{
deTime = T(time) + m_TemporalFilter->Deltas()[pass * m_Ember.m_TemporalSamples];
deTime = T(time) + m_TemporalFilter->Deltas()[0];
//Interpolate and get an ember for DE purposes.
//Additional interpolation will be done in the temporal samples loop.
//Interpolate and get an ember for DE purposes.
//Additional interpolation will be done in the temporal samples loop.
//it.Tic();
if (m_Embers.size() > 1)
Interpolater<T>::Interpolate(m_Embers, deTime, 0, m_Ember);
//it.Toc("Interp 2");
ClampGte<T>(m_Ember.m_MinRadDE, 0);
ClampGte<T>(m_Ember.m_MaxRadDE, 0);
if (!CreateDEFilter(newFilterAlloc))
{
m_ErrorReport.push_back("Density filter creation failed, aborting.\n");
success = RENDER_ERROR;
goto Finish;
}
//Temporal samples, loop 1.
temporalSample = resume ? m_LastTemporalSample : 0;
for (; (temporalSample < TemporalSamples()) && !m_Abort;)
{
T colorScalar = m_TemporalFilter->Filter()[temporalSample];
T temporalTime = T(time) + m_TemporalFilter->Deltas()[temporalSample];
//Interpolate again.
//it.Tic();
if (m_Embers.size() > 1)
Interpolater<T>::Interpolate(m_Embers, deTime, 0, m_Ember);
//it.Toc("Interp 2");
Interpolater<T>::Interpolate(m_Embers, temporalTime, 0, m_Ember);//This will perform all necessary precalcs via the ember/xform/variation assignment operators.
ClampGte<T>(m_Ember.m_MinRadDE, 0);
ClampGte<T>(m_Ember.m_MaxRadDE, 0);
//it.Toc("Interp 3");
if (!CreateDEFilter(newFilterAlloc))
if (!resume && !AssignIterator())
{
m_ErrorReport.push_back("Density filter creation failed, aborting.\n");
m_ErrorReport.push_back("Iterator assignment failed, aborting.\n");
success = RENDER_ERROR;
goto Finish;
}
//Temporal samples, loop 2.
temporalSample = resume ? m_LastTemporalSample : 0;
for (; (temporalSample < TemporalSamples()) && !m_Abort;)
ComputeCamera();
//For each temporal sample, the palette m_Dmap needs to be re-created with color scalar. 1 if no temporal samples.
MakeDmap(colorScalar);
//The actual number of times to iterate. Each thread will get (totalIters / ThreadCount) iters to do.
//This is based on zoom and scale calculated in ComputeCamera().
//Note that the iter count is based on the final image dimensions, and not the super sampled dimensions.
size_t itersPerTemporalSample = ItersPerTemporalSample();//The total number of iterations for this temporal sample without overrides.
size_t sampleItersToDo;//The number of iterations to actually do in this sample, considering overrides.
if (subBatchCountOverride > 0)
sampleItersToDo = subBatchCountOverride * SubBatchSize() * ThreadCount();//Run a specific number of sub batches.
else
sampleItersToDo = itersPerTemporalSample;//Run as many iters as specified to complete this temporal sample.
sampleItersToDo = min(sampleItersToDo, itersPerTemporalSample - m_LastIter);
EmberStats stats = Iterate(sampleItersToDo, temporalSample);//The heavy work is done here.
//If no iters were executed, something went catastrophically wrong.
if (stats.m_Iters == 0)
{
T colorScalar = m_TemporalFilter->Filter()[pass * TemporalSamples() + temporalSample];
T temporalTime = T(time) + m_TemporalFilter->Deltas()[pass * TemporalSamples() + temporalSample];
//Interpolate again.
//it.Tic();
if (m_Embers.size() > 1)
Interpolater<T>::Interpolate(m_Embers, temporalTime, 0, m_Ember);//This will perform all necessary precalcs via the ember/xform/variation assignment operators.
//it.Toc("Interp 3");
if (!resume && !AssignIterator())
{
m_ErrorReport.push_back("Iterator assignment failed, aborting.\n");
success = RENDER_ERROR;
goto Finish;
}
ComputeCamera();
//For each temporal sample, the palette m_Dmap needs to be re-created with color scalar. 1 if no temporal samples.
MakeDmap(colorScalar);
//The actual number of times to iterate. Each thread will get (totalIters / ThreadCount) iters to do.
//This is based on zoom and scale calculated in ComputeCamera().
//Note that the iter count is based on the final image dimensions, and not the super sampled dimensions.
size_t itersPerTemporalSample = ItersPerTemporalSample();//The total number of iterations for this temporal sample in this pass without overrides.
size_t sampleItersToDo;//The number of iterations to actually do in this sample in this pass, considering overrides.
if (subBatchCountOverride > 0)
sampleItersToDo = subBatchCountOverride * SubBatchSize() * ThreadCount();//Run a specific number of sub batches.
else
sampleItersToDo = itersPerTemporalSample;//Run as many iters as specified to complete this temporal sample.
sampleItersToDo = min(sampleItersToDo, itersPerTemporalSample - m_LastIter);
EmberStats stats = Iterate(sampleItersToDo, pass, temporalSample);//The heavy work is done here.
//If no iters were executed, something went catastrophically wrong.
if (stats.m_Iters == 0)
{
m_ErrorReport.push_back("Zero iterations ran, rendering failed, aborting.\n");
success = RENDER_ERROR;
Abort();
goto Finish;
}
if (m_Abort)
{
success = RENDER_ABORT;
goto Finish;
}
//Accumulate stats whether this batch ran to completion or exited prematurely.
m_LastIter += stats.m_Iters;//Sum of iter count of all threads, reset each temporal sample.
m_Stats.m_Iters += stats.m_Iters;//Sum of iter count of all threads, cumulative from beginning to end.
m_Stats.m_Badvals += stats.m_Badvals;
m_Stats.m_IterMs += stats.m_IterMs;
//After each temporal sample, accumulate these.
//Allow for incremental rendering by only taking action if the iter loop for this temporal sample is completely done.
if (m_LastIter >= itersPerTemporalSample)
{
m_Vibrancy += m_Ember.m_Vibrancy;
m_Gamma += m_Ember.m_Gamma;
m_Background.r += m_Ember.m_Background.r;
m_Background.g += m_Ember.m_Background.g;
m_Background.b += m_Ember.m_Background.b;
m_VibGamCount++;
m_LastIter = 0;
temporalSample++;
}
m_LastTemporalSample = temporalSample;
if (subBatchCountOverride > 0)//Don't keep going through this loop if only doing an incremental render.
break;
}//Temporal samples.
//If we've completed all temporal samples and all passes, then it was a complete render, so report progress.
if ((Passes() == 1 || pass == Passes() - 1) && (temporalSample >= TemporalSamples()))
{
m_ProcessState = ITER_DONE;
if (m_Callback && !m_Callback->ProgressFunc(m_Ember, m_ProgressParameter, 100.0, 0, 0))
{
Abort();
success = RENDER_ABORT;
goto Finish;
}
m_ErrorReport.push_back("Zero iterations ran, rendering failed, aborting.\n");
success = RENDER_ERROR;
Abort();
goto Finish;
}
FilterAndAccum:
if (filterAndAccumOnly || temporalSample >= TemporalSamples() || forceOutput)
if (m_Abort)
{
//t.Toc("Iterating and accumulating");
//Compute k1 and k2.
eRenderStatus fullRun = RENDER_OK;//Whether density filtering was run to completion without aborting prematurely or triggering an error.
T passFilter = T(1) / T(Passes());//Original used an array, but every element in the array had the same value, so just use a single value here.
T area = FinalRasW() * FinalRasH() / (m_PixelsPerUnitX * m_PixelsPerUnitY);//Need to use temps from field if ever implemented.
m_K1 = (Brightness() * T(268.0) * passFilter) / 256;
//When doing an interactive render, force output early on in the render process, before all iterations are done.
//This presents a problem with the normal calculation of K2 since it relies on the quality value; it will scale the colors
//to be very dark. Correct it by pretending the number of iters done is the exact quality desired and then scale according to that.
if (forceOutput)
{
T quality = ((T)m_Stats.m_Iters / (T)FinalDimensions()) * (m_Scale * m_Scale);
m_K2 = (Supersample() * Supersample() * Passes()) / (area * quality * m_TemporalFilter->SumFilt());
}
else
m_K2 = (Supersample() * Supersample() * Passes()) / (area * m_ScaledQuality * m_TemporalFilter->SumFilt());
if (filterAndAccumOnly || pass == 0)
ResetBuckets(false, true);//Only the histogram was reset above, now reset the density filtering buffer.
//t.Tic();
//Apply appropriate filter if iterating is complete.
if (filterAndAccumOnly || temporalSample >= TemporalSamples())
{
fullRun = m_DensityFilter.get() ? GaussianDensityFilter() : LogScaleDensityFilter();
}
else
{
//Apply requested filter for a forced output during interactive rendering.
if (m_DensityFilter.get() && m_InteractiveFilter == FILTER_DE)
fullRun = GaussianDensityFilter();
else if (!m_DensityFilter.get() || m_InteractiveFilter == FILTER_LOG)
fullRun = LogScaleDensityFilter();
}
//Only update state if iterating and filtering finished completely (didn't arrive here via forceOutput).
if (fullRun == RENDER_OK && m_ProcessState == ITER_DONE && (Passes() == 1 || pass == Passes() - 1))
m_ProcessState = FILTER_DONE;
//Take special action if filtering exited prematurely.
if (fullRun != RENDER_OK)
{
if (Passes() > 1)//Since all filtering is cummulative with passes > 1, must restart the entire process.
{
m_ProcessState = NONE;
m_ProcessAction = FULL_RENDER;
}
ResetBuckets(false, true);//Reset the accumulator, come back and try again on the next call.
success = fullRun;
goto Finish;
}
if (m_Abort)
{
success = RENDER_ABORT;
goto Finish;
}
//t.Toc("Density estimation filtering time: ", true);
success = RENDER_ABORT;
goto Finish;
}
//Only increment pass if the temporal samples loop has been completed, which could have been done incrementally.
//Also skip if rendering jumped straight here after completely finishing beforehand.
if (!filterAndAccumOnly && temporalSample >= TemporalSamples())//This may not work if filtering was prematurely exited.
pass++;
//Accumulate stats whether this batch ran to completion or exited prematurely.
m_LastIter += stats.m_Iters;//Sum of iter count of all threads, reset each temporal sample.
m_Stats.m_Iters += stats.m_Iters;//Sum of iter count of all threads, cumulative from beginning to end.
m_Stats.m_Badvals += stats.m_Badvals;
m_Stats.m_IterMs += stats.m_IterMs;
if (!filterAndAccumOnly)
m_LastPass = pass;
//After each temporal sample, accumulate these.
//Allow for incremental rendering by only taking action if the iter loop for this temporal sample is completely done.
if (m_LastIter >= itersPerTemporalSample)
{
m_Vibrancy += m_Ember.m_Vibrancy;
m_Gamma += m_Ember.m_Gamma;
m_Background.r += m_Ember.m_Background.r;
m_Background.g += m_Ember.m_Background.g;
m_Background.b += m_Ember.m_Background.b;
m_VibGamCount++;
m_LastIter = 0;
temporalSample++;
}
m_LastTemporalSample = temporalSample;
if (subBatchCountOverride > 0)//Don't keep going through this loop if only doing an incremental render.
break;
}//Passes.
}//Temporal samples.
//If we've completed all temporal samples, then it was a complete render, so report progress.
if (temporalSample >= TemporalSamples())
{
m_ProcessState = ITER_DONE;
if (m_Callback && !m_Callback->ProgressFunc(m_Ember, m_ProgressParameter, 100.0, 0, 0))
{
Abort();
success = RENDER_ABORT;
goto Finish;
}
}
FilterAndAccum:
if (filterAndAccumOnly || temporalSample >= TemporalSamples() || forceOutput)
{
//t.Toc("Iterating and accumulating");
//Compute k1 and k2.
eRenderStatus fullRun = RENDER_OK;//Whether density filtering was run to completion without aborting prematurely or triggering an error.
T area = FinalRasW() * FinalRasH() / (m_PixelsPerUnitX * m_PixelsPerUnitY);//Need to use temps from field if ever implemented.
m_K1 = (Brightness() * T(268.0)) / 256;
//When doing an interactive render, force output early on in the render process, before all iterations are done.
//This presents a problem with the normal calculation of K2 since it relies on the quality value; it will scale the colors
//to be very dark. Correct it by pretending the number of iters done is the exact quality desired and then scale according to that.
if (forceOutput)
{
T quality = ((T)m_Stats.m_Iters / (T)FinalDimensions()) * (m_Scale * m_Scale);
m_K2 = (Supersample() * Supersample()) / (area * quality * m_TemporalFilter->SumFilt());
}
else
m_K2 = (Supersample() * Supersample()) / (area * m_ScaledQuality * m_TemporalFilter->SumFilt());
ResetBuckets(false, true);//Only the histogram was reset above, now reset the density filtering buffer.
//t.Tic();
//Apply appropriate filter if iterating is complete.
if (filterAndAccumOnly || temporalSample >= TemporalSamples())
{
fullRun = m_DensityFilter.get() ? GaussianDensityFilter() : LogScaleDensityFilter();
}
else
{
//Apply requested filter for a forced output during interactive rendering.
if (m_DensityFilter.get() && m_InteractiveFilter == FILTER_DE)
fullRun = GaussianDensityFilter();
else if (!m_DensityFilter.get() || m_InteractiveFilter == FILTER_LOG)
fullRun = LogScaleDensityFilter();
}
//Only update state if iterating and filtering finished completely (didn't arrive here via forceOutput).
if (fullRun == RENDER_OK && m_ProcessState == ITER_DONE)
m_ProcessState = FILTER_DONE;
//Take special action if filtering exited prematurely.
if (fullRun != RENDER_OK)
{
ResetBuckets(false, true);//Reset the accumulator, come back and try again on the next call.
success = fullRun;
goto Finish;
}
if (m_Abort)
{
success = RENDER_ABORT;
goto Finish;
}
//t.Toc("Density estimation filtering time: ", true);
}
AccumOnly:
if (m_ProcessState == FILTER_DONE || forceOutput)
@ -816,57 +785,29 @@ eRenderStatus Renderer<T, bucketT>::LogScaleDensityFilter()
//Timing t(4);
//Original didn't parallelize this, doing so gives a 50-75% speedup.
//If there is only one pass, the value can be directly assigned, which is quicker than summing.
if (Passes() == 1)
//The value can be directly assigned, which is quicker than summing.
parallel_for(startRow, endRow, [&] (size_t j)
{
parallel_for(startRow, endRow, [&] (size_t j)
size_t row = j * m_SuperRasW;
//__m128 logm128;//Figure out SSE at some point.
//__m128 bucketm128;
//__m128 scaledBucket128;
for (size_t i = startCol; (i < endCol) && !m_Abort; i++)
{
size_t row = j * m_SuperRasW;
//__m128 logm128;//Figure out SSE at some point.
//__m128 bucketm128;
//__m128 scaledBucket128;
size_t index = row + i;
for (size_t i = startCol; (i < endCol) && !m_Abort; i++)
//Check for visibility first before doing anything else to avoid all possible unnecessary calculations.
if (m_HistBuckets[index].a != 0)
{
size_t index = row + i;
T logScale = (m_K1 * log(1 + m_HistBuckets[index].a * m_K2)) / m_HistBuckets[index].a;
//Check for visibility first before doing anything else to avoid all possible unnecessary calculations.
if (m_HistBuckets[index].a != 0)
{
T logScale = (m_K1 * log(1 + m_HistBuckets[index].a * m_K2)) / m_HistBuckets[index].a;
//Original did a temporary assignment, then *= logScale, then passed the result to bump_no_overflow().
//Combine here into one operation for a slight speedup.
m_AccumulatorBuckets[index] = (m_HistBuckets[index] * (bucketT)logScale);
}
//Original did a temporary assignment, then *= logScale, then passed the result to bump_no_overflow().
//Combine here into one operation for a slight speedup.
m_AccumulatorBuckets[index] = m_HistBuckets[index] * (bucketT)logScale;
}
});
}
else//Passes > 1, so sum.
{
parallel_for(startRow, endRow, [&] (size_t j)
{
size_t row = j * m_SuperRasW;
for (size_t i = startCol; (i < endCol) && !m_Abort; i++)
{
size_t index = row + i;
//Check for visibility first before doing anything else to avoid all possible unnecessary calculations.
if (m_HistBuckets[index].a != 0)
{
//Figure out SSE at some point.
//__declspec(align(16))
T logScale = (m_K1 * log(1 + m_HistBuckets[index].a * m_K2)) / m_HistBuckets[index].a;
//logm128 = _mm_load1_ps(&logScale);
//bucketm128 = _mm_load_ps(m_HistBuckets[index].Channels);
//scaledBucket128 = _mm_mul_ps(logm128, bucketm128);
m_AccumulatorBuckets[index] += (m_HistBuckets[index] * bucketT(logScale));
}
}
});
}
}
});
//t.Toc(__FUNCTION__);
return m_Abort ? RENDER_ABORT : RENDER_OK;
@ -1216,11 +1157,10 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(unsigned char* pixel
/// which by default is 10,000 iterations.
/// </summary>
/// <param name="iterCount">The number of iterations to run</param>
/// <param name="pass">The pass this is running for</param>
/// <param name="temporalSample">The temporal sample within the current pass this is running for</param>
/// <param name="temporalSample">The temporal sample this is running for</param>
/// <returns>Rendering statistics</returns>
template <typename T, typename bucketT>
EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t pass, size_t temporalSample)
EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample)
{
//Timing t2(4);
m_IterTimer.Tic();
@ -1245,7 +1185,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t pass, size_t t
m_BadVals[threadIndex] = 0;
//Sub batch iterations, loop 3.
//Sub batch iterations, loop 2.
for (m_SubBatch[threadIndex] = 0; (m_SubBatch[threadIndex] < totalItersPerThread) && !m_Abort; m_SubBatch[threadIndex] += subBatchSize)
{
//Must recalculate the number of iters to run on each sub batch because the last batch will most likely have less than m_SubBatchSize iters.
@ -1262,7 +1202,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t pass, size_t t
//Finally, iterate.
//t.Tic();
//Iterating, loop 4.
//Iterating, loop 3.
m_BadVals[threadIndex] += m_Iterator->Iterate(m_Ember, subBatchSize, fuse, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
//iterationTime += t.Toc();
@ -1278,21 +1218,19 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t pass, size_t t
if (m_Callback && threadIndex == 0)
{
percent = 100.0 *
double
(
double
(
double
(
double
(
double
(
//Takes progress of current thread and multiplies by thread count.
//This assumes the threads progress at roughly the same speed.
double(m_LastIter + (m_SubBatch[threadIndex] * m_ThreadsToUse)) / double(ItersPerTemporalSample())
) + temporalSample
) / (double)TemporalSamples()
) + (double)pass
) / (double)Passes();
//Takes progress of current thread and multiplies by thread count.
//This assumes the threads progress at roughly the same speed.
double(m_LastIter + (m_SubBatch[threadIndex] * m_ThreadsToUse)) / double(ItersPerTemporalSample())
) + temporalSample
) / (double)TemporalSamples()
);
double percentDiff = percent - m_LastIterPercent;
double toc = m_ProgressTimer.Toc();
@ -1406,7 +1344,6 @@ template <typename T, typename bucketT> ePaletteMode Renderer<T, bucketT>::
/// Virtual ember wrappers overridden from RendererBase, getters only.
/// </summary>
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::Passes() const { return m_Ember.m_Passes; }
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::TemporalSamples() const { return m_Ember.m_TemporalSamples; }
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::FinalRasW() const { return m_Ember.m_FinalRasW; }
template <typename T, typename bucketT> size_t Renderer<T, bucketT>::FinalRasH() const { return m_Ember.m_FinalRasH; }

View File

@ -74,7 +74,7 @@ protected:
virtual eRenderStatus GaussianDensityFilter();
virtual eRenderStatus AccumulatorToFinalImage(vector<unsigned char>& pixels, size_t finalOffset);
virtual eRenderStatus AccumulatorToFinalImage(unsigned char* pixels, size_t finalOffset);
virtual EmberStats Iterate(size_t iterCount, size_t pass, size_t temporalSample);
virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);
public:
//Non-virtual render properties, getters and setters.
@ -131,7 +131,6 @@ public:
inline ePaletteMode PaletteMode() const;
//Virtual ember wrappers overridden from RendererBase, getters only.
virtual size_t Passes() const override;
virtual size_t TemporalSamples() const override;
virtual size_t FinalRasW() const override;
virtual size_t FinalRasH() const override;

View File

@ -23,7 +23,6 @@ RendererBase::RendererBase()
ThreadCount(Timing::ProcessorCount());
m_Callback = nullptr;
m_ProgressParameter = nullptr;
m_LastPass = 0;
m_LastTemporalSample = 0;
m_LastIter = 0;
m_LastIterPercent = 0;
@ -65,7 +64,7 @@ void RendererBase::ChangeVal(std::function<void(void)> func, eProcessAction acti
//new and old quality values.
else if (action == KEEP_ITERATING)
{
if (m_ProcessState == ACCUM_DONE && TemporalSamples() == 1 && Passes() == 1)
if (m_ProcessState == ACCUM_DONE && TemporalSamples() == 1)
{
m_ProcessState = ITER_STARTED;
m_ProcessAction = KEEP_ITERATING;
@ -84,11 +83,11 @@ void RendererBase::ChangeVal(std::function<void(void)> func, eProcessAction acti
m_ProcessState = NONE;
m_ProcessAction = FULL_RENDER;
}
//If passes == 1, set the state to ITER_DONE and the next process action to FILTER_AND_ACCUM.
//Set the state to ITER_DONE and the next process action to FILTER_AND_ACCUM.
else
{
m_ProcessState = Passes() == 1 ? ITER_DONE : NONE;
m_ProcessAction = Passes() == 1 ? FILTER_AND_ACCUM : FULL_RENDER;//Cannot just filter if passes > 1 because filtering is done with each pass.
m_ProcessState = ITER_DONE;
m_ProcessAction = FILTER_AND_ACCUM;
}
}
//Run accum only.
@ -283,7 +282,7 @@ size_t RendererBase::PixelSize() const { return NumChannels() * BytesP
size_t RendererBase::GutterWidth() const { return m_GutterWidth; }
size_t RendererBase::DensityFilterOffset() const { return m_DensityFilterOffset; }
size_t RendererBase::TotalIterCount(size_t strips) const { return (size_t)((size_t)Round(ScaledQuality()) * FinalRasW() * FinalRasH() * strips); }//Use Round() because there can be some roundoff error when interpolating.
size_t RendererBase::ItersPerTemporalSample() const { return (size_t)ceil(double(TotalIterCount(1)) / double(Passes() * TemporalSamples())); }//Temporal samples is used with animation, which doesn't support strips, so pass 1.
size_t RendererBase::ItersPerTemporalSample() const { return (size_t)ceil(double(TotalIterCount(1)) / double(TemporalSamples())); }//Temporal samples is used with animation, which doesn't support strips, so pass 1.
eProcessState RendererBase::ProcessState() const { return m_ProcessState; }
eProcessAction RendererBase::ProcessAction() const { return m_ProcessAction; }
EmberStats RendererBase::Stats() const { return m_Stats; }

View File

@ -165,7 +165,6 @@ public:
virtual eRendererType RendererType() const;
//Abstract render properties, getters only.
virtual size_t Passes() const = 0;
virtual size_t TemporalSamples() const = 0;
virtual size_t HistBucketSize() const = 0;
virtual size_t FinalRasW() const = 0;

View File

@ -872,7 +872,6 @@ public:
adjustedEmber.m_FinalRasW = (size_t)(ember.m_FinalRasW * scalar);
adjustedEmber.m_FinalRasH = (size_t)(ember.m_FinalRasH * scalar);
adjustedEmber.m_PixelsPerUnit *= scalar;
adjustedEmber.m_Passes = 1;
adjustedEmber.m_TemporalSamples = 1;
m_Renderer->SetEmber(adjustedEmber);
@ -1181,9 +1180,6 @@ public:
if (templ.m_Quality > 0)
ember.m_Quality = templ.m_Quality;
if (templ.m_Passes > 0)
ember.m_Passes = templ.m_Passes;
if (templ.m_TemporalSamples > 0)
ember.m_TemporalSamples = templ.m_TemporalSamples;
@ -1209,9 +1205,6 @@ public:
if (templ.m_GammaThresh >= 0)
ember.m_GammaThresh = templ.m_GammaThresh;
if (templ.m_Passes > 0)
ember.m_Passes = templ.m_Passes;
if (templ.m_SpatialFilterType > 0)
ember.m_SpatialFilterType = templ.m_SpatialFilterType;

View File

@ -44,14 +44,12 @@ public:
/// Derived class constructors will complete the final part of filter setup.
/// </summary>
/// <param name="filterType">Type of the filter.</param>
/// <param name="passes">The number of passes used in the ember being rendered</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
TemporalFilter(eTemporalFilterType filterType, size_t passes, size_t temporalSamples, T filterWidth)
TemporalFilter(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth)
{
size_t i, steps = passes * temporalSamples;
size_t i, steps = temporalSamples;
m_Passes = passes;
m_TemporalSamples = temporalSamples;
m_FilterWidth = filterWidth;
m_Deltas.resize(steps);
@ -98,7 +96,6 @@ public:
{
if (this != &filter)
{
m_Passes = filter.m_Passes;
m_TemporalSamples = filter.m_TemporalSamples;
m_FilterWidth = filter.m_FilterWidth;
m_FilterExp = filter.m_FilterExp;
@ -146,7 +143,6 @@ public:
/// Accessors.
/// </summary>
size_t Size() const { return m_Filter.size(); }
size_t Passes() const { return m_Passes; }
size_t TemporalSamples() const { return m_TemporalSamples; }
T FilterWidth() const { return m_FilterWidth; }
T FilterExp() const { return m_FilterExp; }
@ -176,7 +172,6 @@ protected:
T m_SumFilt;//The sum of all filter values.
T m_FilterWidth;
T m_FilterExp;
size_t m_Passes;
size_t m_TemporalSamples;
vector<T> m_Deltas;//Delta vector.
vector<T> m_Filter;//Filter vector.
@ -194,12 +189,11 @@ public:
/// <summary>
/// Constructor to create an Exp filter.
/// </summary>
/// <param name="passes">The number of passes used in the ember being rendered</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
/// <param name="filterExp">The filter exp.</param>
ExpTemporalFilter(size_t passes, size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, passes, temporalSamples, filterWidth)
ExpTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, temporalSamples, filterWidth)
{
if (Size() > 1)
{
@ -237,11 +231,10 @@ public:
/// <summary>
/// Constructor to create a Gaussian filter.
/// </summary>
/// <param name="passes">The number of passes used in the ember being rendered</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
GaussianTemporalFilter(size_t passes, size_t temporalSamples, T filterWidth)
: TemporalFilter<T>(GAUSSIAN_TEMPORAL_FILTER, passes, temporalSamples, filterWidth)
GaussianTemporalFilter(size_t temporalSamples, T filterWidth)
: TemporalFilter<T>(GAUSSIAN_TEMPORAL_FILTER, temporalSamples, filterWidth)
{
if (Size() > 1)
{
@ -273,11 +266,10 @@ public:
/// <summary>
/// Constructor to create a Box filter.
/// </summary>
/// <param name="passes">The number of passes used in the ember being rendered</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
BoxTemporalFilter(size_t passes, size_t temporalSamples, T filterWidth)
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, passes, temporalSamples, filterWidth)
BoxTemporalFilter(size_t temporalSamples, T filterWidth)
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, temporalSamples, filterWidth)
{
if (Size() > 1)
{
@ -300,23 +292,22 @@ public:
/// Creates the specified filter type based on the filterType enum parameter.
/// </summary>
/// <param name="filterType">Type of the filter</param>
/// <param name="passes">The number of passes used in the ember being rendered</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter</param>
/// <param name="filterExp">The filter exp, only used with Exp filter, otherwise ignored.</param>
/// <returns>A pointer to the newly created filter object</returns>
static TemporalFilter<T>* Create(eTemporalFilterType filterType, size_t passes, size_t temporalSamples, T filterWidth, T filterExp = 1)
static TemporalFilter<T>* Create(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp = 1)
{
TemporalFilter<T>* filter = nullptr;
if (filterType == BOX_TEMPORAL_FILTER)
filter = new BoxTemporalFilter<T>(passes, temporalSamples, filterWidth);
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth);
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
filter = new GaussianTemporalFilter<T>(passes, temporalSamples, filterWidth);
filter = new GaussianTemporalFilter<T>(temporalSamples, filterWidth);
else if (filterType == EXP_TEMPORAL_FILTER)
filter = new ExpTemporalFilter<T>(passes, temporalSamples, filterWidth, filterExp);
filter = new ExpTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
else
filter = new BoxTemporalFilter<T>(passes, temporalSamples, filterWidth);//Default to box if bad enum passed in.
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth);//Default to box if bad enum passed in.
return filter;
}

View File

@ -584,8 +584,6 @@ public:
outPoint->m_VizAdjusted = m_VizAdjusted;
iterHelper.m_Color.x = outPoint->m_ColorX = m_ColorSpeedCache + (m_OneMinusColorCache * inPoint->m_ColorX);
//This modification returns the affine transformed points if no variations are present.
//Note this differs from flam3, which would just return zero in that scenario.
if (m_HasPreOrRegularVars)
{
//Compute the pre affine portion of the transform.
@ -637,6 +635,8 @@ public:
outPoint->m_Z = iterHelper.m_TransZ;
}
}
//Return the affine transformed points if no variations are present.
//Note this differs from flam3, which would just return zero in that scenario.
else
{
//There are no variations, so the affine transformed points can be assigned directly to the output points.