--User changes

-Add new variations: bubbleT3D, crob, hexaplay3D, hexcrop, hexes, hexnix3D, loonie2, loonie3, nBlur, octapol and synth.
 -Allow for pre/post versions of dc_bubble, dc_cylinder and dc_linear whereas before they were omitted.
 -When saving a file with multiple embers in it, detect if time values are all the same and if so, start them at zero and increment by 1 for each ember.
 -Allow for numerous quality increases to be coalesced into one. It will pick up at the end of the current render.
 -Show selection highlight on variations tree in response to mouse hover. This makes it easier to see for which variation or param the current mouse wheel action will apply.
 -Make default temporal samples be 100, whereas before it was 1000 which was overkill.
 -Require the shift key to be held with delete for deleting an ember to prevent it from triggering when the user enters delete in the edit box.
  -This wasn't otherwise fixable without writing a lot more code.

--Bug fixes
 -EmberGenome was crashing when generating a sequence from a source file with more than 2 embers in it.
 -EmberGenome was improperly handling the first frame of a merge after the last frame of the loop.
  -These bugs were due to a previous commit. Revert parts of that commit.
 -Prevent a zoom value of less than 0 when reading from xml.
 -Slight optimization of the crescents, and mask variations, if the compiler wasn't doing it already.
 -Unique file naming was broken because it was looking for _# and the default names ended with -#.
 -Disallow renaming of an ember in the library tree to an empty string.
 -Severe bug that prevented some variations from being read correctly from params generated outside this program.
 -Severe OpenCL randomization bug. The first x coordinates of the first points in the first kernel call of the first ember of a render since the OpenCL renderer object was created were not random and were mostly -1.
 -Severe bug when populating xform selection distributions that could sometimes cause a crash due to roundoff error. Fix by using double.
 -Limit the max number of variations in a random ember to MAX_CL_VARS, which is 8. This ensures they'll look the same on CPU and GPU.
 -Prevent user from saving stylesheet to default.qss, it's a special reserved filename.

--Code changes
 -Generalize using the running sum output point inside of a variation for all cases: pre, reg and post.
 -Allow for array variables in variations where the address of each element is stored in m_Params.
 -Qualify all math functions with std::
 -No longer use our own Clamp() in OpenCL, instead use the standard clamp().
 -Redesign how functions are used in the variations OpenCL code.
 -Add tests to EmberTester to verify some of the new functionality.
 -Place more const and override qualifiers on functions where appropriate.
 -Add a global rand with a lock to be used very sparingly.
 -Use a map instead of a vector for bad param names in Xml parsing.
 -Prefix affine interpolation mode defines with "AFFINE_" to make their purpose more clear.
 -Allow for variations that change state during iteration by sending a separate copy of the ember to each rendering thread.
 -Implement this same functionality with a local struct in OpenCL. It's members are the total of all variables that need to change state within an ember.
 -Add Contains() function to Utils.h.
 -EmberRender: print names of kernels being printed with --dump_kernel option.
 -Clean up EmberTester to handle some of the recent changes.
 -Fix various casts.
 -Replace % 2 with & 1, even though the compiler was likely doing this already.
 -Add new file Variations06.h to accommodate new variations.
 -General cleanup.
This commit is contained in:
mfeemster
2015-11-22 14:15:07 -08:00
parent 04e72c27de
commit 330074cfb2
62 changed files with 8176 additions and 1877 deletions

View File

@ -114,7 +114,7 @@ public:
{
if (i != j && m_Embers[i].m_Name == m_Embers[j].m_Name)
{
m_Embers[j].m_Name = m_Embers[j].m_Name + "_" + ToString(++x).toStdString();
m_Embers[j].m_Name = IncrementTrailingUnderscoreInt(QString::fromStdString(m_Embers[j].m_Name)).toStdString();
j = 0;
}
}
@ -130,6 +130,33 @@ public:
return "Flame_" + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
}
/// <summary>
/// Return a copy of the string which ends with _# where # is the
/// previous number at that position incremented by one.
/// If the original string did not end with _#, the returned
/// string will just have _1 appended to it.
/// </summary>
/// <param name="str">The string to process</param>
/// <returns>The original string with the number after the final _ character incremented by one</returns>
static QString IncrementTrailingUnderscoreInt(const QString& str)
{
bool ok = false;
size_t num = 0;
QString endSection;
QString ret = str;
int lastUnderscore = str.lastIndexOf('_');
if (lastUnderscore != -1)
{
endSection = str.section('_', -1);
num = endSection.toULongLong(&ok);
ret.chop(str.size() - lastUnderscore);
}
ret += "_" + QString::number(num + 1);
return ret;
}
/// <summary>
/// Ensures a given input filename is unique by appending a count to the end.
/// </summary>
@ -146,10 +173,11 @@ public:
QString path = original.absolutePath() + '/';
QString base = original.completeBaseName();
QString extension = original.suffix();
do
{
newPath = path + base + "_" + ToString(counter++) + "." + extension;
base = IncrementTrailingUnderscoreInt(base);
newPath = path + base + "." + extension;
}
while (QFile::exists(newPath));
@ -164,7 +192,7 @@ public:
/// <returns>The default ember name</returns>
static QString DefaultEmberName(uint i)
{
return DefaultFilename() + "-" + ToString(i);
return DefaultFilename() + "_" + ToString(i);
}
QString m_Filename;

View File

@ -331,6 +331,8 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e)
}
else if (QKeyEvent* ke = dynamic_cast<QKeyEvent*>(e))
{
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F32)
{
int val = ke->key() - (int)Qt::Key_F1;
@ -340,7 +342,8 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e)
}
else if (o == ui.LibraryTree)
{
if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease)
//Require shift for deleting to prevent it from triggering when the user enters delete in the edit box.
if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease && shift)
{
auto p = GetCurrentEmberIndex();

View File

@ -5046,6 +5046,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
@ -5065,10 +5068,10 @@
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="indentation">
<number>12</number>

View File

@ -213,6 +213,12 @@ void FractoriumEmberController<T>::EmberTreeItemChanged(QTreeWidgetItem* item, i
if (emberItem)
{
if (emberItem->text(0).isEmpty())//Prevent empty string.
{
emberItem->UpdateEditText();
return;
}
string oldName = emberItem->GetEmber()->m_Name;//First preserve the previous name.
tree->blockSignals(true);

View File

@ -65,10 +65,10 @@ void FractoriumEmberController<T>::NewFlock(size_t count)
for (size_t i = 0; i < count; i++)
{
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<intmax_t>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0);
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<intmax_t>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0, MAX_CL_VARS);
ParamsToEmber(ember);
ember.m_Index = i;
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "-" + ToString(i + 1ULL).toStdString();
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "_" + ToString(i + 1ULL).toStdString();
m_EmberFile.m_Embers.push_back(ember);
}
@ -126,7 +126,7 @@ void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
Ember<T> ember;
StopPreviewRender();
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0);
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0, MAX_CL_VARS);
ParamsToEmber(ember);
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
ember.m_Index = m_EmberFile.Size();

View File

@ -79,7 +79,7 @@ void Fractorium::InitParamsUI()
SetupCombo(table, this, row, 1, m_TemporalFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnTemporalFilterTypeComboCurrentIndexChanged(const QString&)));
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DEFilterMinRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMinRadiusWidthChanged(double)), true, 0, 0, 0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DEFilterMaxRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMaxRadiusWidthChanged(double)), true, 9.0, 9.0, 0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DEFilterMaxRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMaxRadiusWidthChanged(double)), true, 0.0, 9.0, 0);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_DECurveSpin, spinHeight, 0.15, 5, 0.1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterCurveWidthChanged(double)), true, 0.4, 0.4, 0.4);
//Iteration.
@ -89,7 +89,7 @@ void Fractorium::InitParamsUI()
SetupSpinner<SpinBox, int>( table, this, row, 1, m_FuseSpin, spinHeight, 1, 1000, 5, SIGNAL(valueChanged(int)), SLOT(OnFuseChanged(int)), true, 15, 15, 15);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, 10, 10, 10);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 1, 1, 1);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_TemporalSamplesSpin, spinHeight, 1, 5000, 50, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_TemporalSamplesSpin, spinHeight, 1, 5000, 1, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000);
comboVals.clear();
comboVals.push_back("Step");
@ -412,8 +412,8 @@ void Fractorium::OnFuseChanged(int d) { m_Controller->FuseChanged(d); }
/// the rendering process is continued, else it's reset.
/// </summary>
/// <param name="d">The quality in terms of iterations per pixel</param>
template <typename T> void FractoriumEmberController<T>::QualityChanged(double d) { Update([&] { m_Ember.m_Quality = d; }, true, d > m_Ember.m_Quality ? KEEP_ITERATING : FULL_RENDER); }
void Fractorium::OnQualityChanged(double d) { m_Controller->QualityChanged(d); }
template <typename T> void FractoriumEmberController<T>::QualityChanged(double d) { /*Update([&] { m_Ember.m_Quality = d; }, true, d > m_Ember.m_Quality ? KEEP_ITERATING : FULL_RENDER);*/ }
void Fractorium::OnQualityChanged(double d) { /*m_Controller->QualityChanged(d);*/ }
/// <summary>
/// Set the supersample.
@ -452,11 +452,11 @@ void FractoriumEmberController<T>::AffineInterpTypeChanged(int i)
Update([&]
{
if (i == 0)
m_Ember.m_AffineInterp = INTERP_LINEAR;
m_Ember.m_AffineInterp = AFFINE_INTERP_LINEAR;
else if (i == 1)
m_Ember.m_AffineInterp = INTERP_LOG;
m_Ember.m_AffineInterp = AFFINE_INTERP_LOG;
else
m_Ember.m_AffineInterp = INTERP_LINEAR;
m_Ember.m_AffineInterp = AFFINE_INTERP_LINEAR;
}, true, NOTHING);
}

View File

@ -301,7 +301,33 @@ bool FractoriumEmberController<T>::Render()
bool success = true;
GLWidget* gl = m_Fractorium->ui.GLDisplay;
RendererCL<T, float>* rendererCL = nullptr;
eProcessAction action = CondenseAndClearProcessActions();
eProcessAction qualityAction, action;
//Quality is the only parameter we update inside the timer.
//This is to allow the user to rapidly increase the quality spinner
//without fully resetting the render. Instead, it will just keep iterating
//where it last left off in response to an increase.
T d = T(m_Fractorium->m_QualitySpin->value());
if (d < m_Ember.m_Quality)//Full restart if quality decreased.
{
m_Ember.m_Quality = d;
qualityAction = eProcessAction::FULL_RENDER;
}
else if (d > m_Ember.m_Quality && ProcessState() == ACCUM_DONE)//If quality increased, keep iterating after current render finishes.
{
m_Ember.m_Quality = d;
qualityAction = eProcessAction::KEEP_ITERATING;
}
else if (IsClose(d, m_Ember.m_Quality))
qualityAction = eProcessAction::NOTHING;
if (qualityAction == eProcessAction::FULL_RENDER)
Update([&] {});//Stop the current render, a full restart is needed.
else if (qualityAction == eProcessAction::KEEP_ITERATING)
m_ProcessActions.push_back(qualityAction);//Special, direct call to avoid resetting the render inside Update() because only KEEP_ITERATING is needed.
action = CondenseAndClearProcessActions();//Combine with all other previously requested actions.
if (m_Renderer->RendererType() == OPENCL_RENDERER)
rendererCL = dynamic_cast<RendererCL<T, float>*>(m_Renderer.get());
@ -391,7 +417,7 @@ bool FractoriumEmberController<T>::Render()
//Only certain stats can be reported with OpenCL.
if (m_Renderer->RendererType() == OPENCL_RENDERER)
{
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Total time: " + QString::fromStdString(renderTime));
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Total time: " + QString::fromStdString(renderTime) + ".");
}
else
{
@ -399,7 +425,7 @@ bool FractoriumEmberController<T>::Render()
QString badVals = ToString<qulonglong>(stats.m_Badvals);
QString badPercent = QLocale::system().toString(percent * 100, 'f', 2);
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Bad values: " + badVals + " (" + badPercent + "%). Total time: " + QString::fromStdString(renderTime));
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Bad values: " + badVals + " (" + badPercent + "%). Total time: " + QString::fromStdString(renderTime) + ".");
}
if (m_LastEditWasUndoRedo && (m_UndoIndex == m_UndoList.size() - 1))//Traversing through undo list, reached the end, so put back in regular edit mode.

View File

@ -18,8 +18,8 @@ FractoriumOptionsDialog::FractoriumOptionsDialog(FractoriumSettings* settings, Q
m_Settings = settings;
QTableWidget* table = ui.OptionsXmlSavingTable;
ui.ThreadCountSpin->setRange(1, Timing::ProcessorCount());
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlTemporalSamplesSpin, spinHeight, 1, 1000, 100, "", "", true, 1000);
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlQualitySpin, spinHeight, 1, 200000, 50, "", "", true, 1000);

View File

@ -70,7 +70,6 @@ QssDialog::QssDialog(Fractorium* parent) :
connect(ui->QssLoadButton, SIGNAL(clicked()), this, SLOT(LoadButton_clicked()), Qt::QueuedConnection);
connect(ui->QssSaveButton, SIGNAL(clicked()), this, SLOT(SaveButton_clicked()), Qt::QueuedConnection);
connect(ui->QssSaveDefaultButton, SIGNAL(clicked()), this, SLOT(SaveDefaultButton_clicked()), Qt::QueuedConnection);
connect(ui->QssBasicButton, SIGNAL(clicked()), this, SLOT(BasicButton_clicked()), Qt::QueuedConnection);
connect(ui->QssMediumButton, SIGNAL(clicked()), this, SLOT(MediumButton_clicked()), Qt::QueuedConnection);
connect(ui->QssAdvancedButton, SIGNAL(clicked()), this, SLOT(AdvancedButton_clicked()), Qt::QueuedConnection);
@ -310,7 +309,7 @@ void QssDialog::accept()
if (m_Theme)
m_Parent->m_Settings->Theme(m_Theme->objectName());
SaveDefaultButton_clicked();
SaveAsDefault();
QDialog::accept();
}
@ -506,11 +505,20 @@ void QssDialog::LoadButton_clicked()
/// <summary>
/// Save the stylesheet to disk.
/// Called when the user clicks the save button.
/// The user cannot save to default.qss, as it's a special placeholder.
/// When they exit the dialog by clicking OK, the currently displayed stylesheet
/// will be saved to default.qss.
/// </summary>
void QssDialog::SaveButton_clicked()
{
auto path = SaveFile();
if (path.toLower().endsWith("default.qss"))
{
QMessageBox::critical(this, "File save error", "Stylesheet cannot be saved to default.qss. Save it to a different file name, then exit the dialog by clicking OK which will set it as the default.");
return;
}
if (!path.isEmpty())
{
ofstream of(path.toStdString());
@ -522,16 +530,16 @@ void QssDialog::SaveButton_clicked()
of.close();
}
else
QMessageBox::critical(this, "File open error", "Failed to open " + path + ", style will not be set as default");
QMessageBox::critical(this, "File save error", "Failed to save " + path + ", style will not be set as default");
}
}
/// <summary>
/// Save the stylesheet to the default.qss on disk.
/// This will be loaded the next time Fractorium runs.
/// Called when the user clicks the save as default button.
/// Called when the user clicks ok.
/// </summary>
void QssDialog::SaveDefaultButton_clicked()
void QssDialog::SaveAsDefault()
{
auto path = m_Parent->m_SettingsPath + "/default.qss";
ofstream of(path.toStdString());
@ -543,7 +551,7 @@ void QssDialog::SaveDefaultButton_clicked()
of.close();
}
else
QMessageBox::critical(this, "File open error", "Failed to open " + path + ", style will not be set as default");
QMessageBox::critical(this, "File save error", "Failed to save " + path + ", style will not be set as default");
}
/// <summary>

View File

@ -107,12 +107,12 @@ private slots:
void LoadButton_clicked();
void SaveButton_clicked();
void SaveDefaultButton_clicked();
void BasicButton_clicked();
void MediumButton_clicked();
void AdvancedButton_clicked();
private:
void SaveAsDefault();
void InsertCssProperty(const QString &name, const QString &value);
void SetupFileDialog();
QString OpenFile();

View File

@ -47,13 +47,6 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssSaveDefaultButton">
<property name="text">
<string>Save as default</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="QssBasicButton">
<property name="text">

View File

@ -39,7 +39,6 @@
**
****************************************************************************/
#include "FractoriumPch.h"
#include "qcssparser.h"
#include "qcssscanner.h"
/// <summary>