mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-01-21 21:20:07 -05:00
90ec5b8246
-Show common folder locations such as documents, downloads, pictures in the sidebar in all file dialogs. -Warning message about exceeding memory in final render dialog now suggests strips as the solution to the problem. -Strips now has a tooltip explaining what it does. -Allow more digits in the spinners on the color section the flame tab. -Add manually adjustable size spinners in the final render dialog. Percentage scale and absolute size are fully synced. -Default prefix in final render is now the filename when doing animations (coming from sequence section of the library tab). -Changed the elliptic variation back to using a less precise version for float, and a more precise version for double. The last release had it always using double. -New applied xaos table that shows a read-only view of actual weights by taking the base xform weights and multiplying them by the xaos values. -New table in the xaos tab that gives a graphical representation of the probability that each xform is chosen, with and without xaos. -Add button to transpose the xaos rows and columns. -Add support for importing .chaos files from Chaotica. --Pasting back to Chaotica will work for most, but not all, variations due to incompatible parameter names in some. -Curves are now splines instead of Bezier. This adds compatibility with Chaotica, but breaks it for Apophysis. Xmls are still pastable, but the color curves will look different. --The curve editor on the palette tab can now add points by clicking on the lines and remove points by clicking on the points themselves, just like Chaotica. --Splines are saved in four new xml fields: overall_curve, red_curve, green_curve and blue_curve. -Allow for specifying the percentage of a sub batch each thread should iterate through per kernel call when running with OpenCL. This gives a roughly 1% performance increase due to having to make less kernel calls while iterating. --This field is present for interactive editing (where it's not very useful) and in the final render dialog. --On the command line, this is specified as --sbpctth for EmberRender and EmberAnimate. -Allow double clicking to toggle the supersample field in the flame tab between 1 and 2 for easily checking the effect of the field. -When showing affine values as polar coordinates, show angles normalized to 360 to match Chaotica. -Fuse Count spinner now toggles between 15 and 100 when double clicking for easily checking the effect of the field. -Added field for limiting the range in the x and y direction that the initial points are chosen from. -Added a field called K2 which is an alternative way to set brightness, ignored when zero. --This has no effect for many variations, but hs a noticeable effect for some. -Added new variations: arcsech arcsech2 arcsinh arctanh asteria block bwraps_rand circlecrop2 coth_spiral crackle2 depth_blur depth_blur2 depth_gaussian depth_gaussian2 depth_ngon depth_ngon2 depth_sine depth_sine2 dragonfire dspherical dust excinis exp2 flipx flowerdb foci_p gaussian glynnia2 glynnsim4 glynnsim5 henon henon hex_rand hex_truchet hypershift lazyjess lens lozi lozi modulusx modulusy oscilloscope2 point_symmetry pointsymmetry projective pulse rotate scry2 shift smartshape spher squares starblur2 swirl3 swirl3r tanh_spiral target0 target2 tile_hlp truchet_glyph truchet_inv truchet_knot unicorngaloshen vibration vibration2 --hex_truchet, hex_rand should always use double. They are extremely sensitive. --Bug fixes: -Bounds sign was flipped for x coordinate of world space when center was not zero. -Right clicking and dragging spinner showed menu on mouse up, even if it was very far away. -Text boxes for size in final render dialog were hard to type in. Same bug as xform weight used to be so fix the same way. -Fix spelling to be plural in toggle color speed box. -Stop using the blank user palette to generate flames. Either put colored palettes in it, or exclude it from randoms. -Clicking the random palette button for a palette file with only one palette in it would freeze the program. -Clicking none scale in final render did not re-render the preview. -Use less precision on random xaos. No need for 12 decimal places. -The term sub batch is overloaded in the options dialog. Change the naming and tooltip of those settings for cpu and opencl. --Also made clear in the tooltip for the default opencl quality setting that the value is per device. -The arrows spinner in palette editor appears like a read-only label. Made it look like a spinner. -Fix border colors for various spin boxes and table headers in the style sheet. Requires reload. -Fix a bug in the bwraps variation which would produce different results than Chaotica and Apophysis. -Synth was allowed to be selected for random flame generation when using an Nvidia card but it shouldn't have been because Nvidia has a hard time compiling synth. -A casting bug in the OpenCL kernels for log scaling and density filtering was preventing successful compilations on Intel iGPUs. Fixed even though we don't support anything other than AMD and Nvidia. -Palette rotation (click and drag) position was not being reset when loading a new flame. -When the xform circles were hidden, opening and closing the options dialog would improperly reshow them. -Double click toggle was broken on integer spin boxes. -Fixed tab order of some controls. -Creating a palette from a jpg in the palette editor only produced a single color. --Needed to package imageformats/qjpeg.dll with the Windows installer. -The basic memory benchmark test flame was not really testing memory. Make it more spread out. -Remove the temporal samples field from the flame tab, it was never used because it's only an animation parameter which is specified in the final render dialog or on the command line with EmberAnimate. --Code changes: -Add IsEmpty() to Palette to determine if a palette is all black. -Attempt to avoid selecting a blank palette in PaletteList::GetRandomPalette(). -Add function ScanForChaosNodes() and some associated helper functions in XmlToEmber. -Make variation param name correction be case insensitive in XmlToEmber. -Report error when assigning a variation param value in XmlToEmber. -Add SubBatchPercentPerThread() method to RendererCL. -Override enterEvent() and leaveEvent() in DoubleSpinBox and SpinBox to prevent the context menu from showing up on right mouse up after already leaving the spinner. -Filtering the mouse wheel event in TableWidget no longer appears to be needed. It was probably an old Qt bug that has been fixed. -Gui/ember syncing code in the final render dialog needed to be reworked to accommodate absolute sizes.
955 lines
39 KiB
C++
955 lines
39 KiB
C++
#include "FractoriumPch.h"
|
|
#include "FinalRenderDialog.h"
|
|
#include "Fractorium.h"
|
|
|
|
/// <summary>
|
|
/// Constructor which sets up the GUI for the final rendering dialog.
|
|
/// Settings used to populate widgets with initial values.
|
|
/// This function contains the render function as a lambda.
|
|
/// </summary>
|
|
/// <param name="settings">Pointer to the global settings object to use</param>
|
|
/// <param name="p">The parent widget</param>
|
|
/// <param name="f">The window flags. Default: 0.</param>
|
|
FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowFlags f)
|
|
: QDialog(p, f)
|
|
{
|
|
ui.setupUi(this);
|
|
int row = 0, spinHeight = 20;
|
|
double dmax = numeric_limits<double>::max();
|
|
QTableWidget* table = ui.FinalRenderParamsTable;
|
|
m_Info = OpenCLInfo::Instance();
|
|
m_Fractorium = qobject_cast<Fractorium*>(p);
|
|
m_Settings = FractoriumSettings::DefInstance();
|
|
ui.FinalRenderThreadCountSpin->setRange(1, Timing::ProcessorCount());
|
|
connect(ui.FinalRenderEarlyClipCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnEarlyClipCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderYAxisUpCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnYAxisUpCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderTransparencyCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnTransparencyCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderOpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderDoublePrecisionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoublePrecisionCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderDoAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderDoSequenceCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoSequenceCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderCurrentSpin, SIGNAL(valueChanged(int)), this, SLOT(OnCurrentSpinChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderApplyToAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnApplyAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderKeepAspectCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnKeepAspectCheckBoxStateChanged(int)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderScaleNoneRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderScaleWidthRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderScaleHeightRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderDeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
|
|
SetupSpinner<DoubleSpinBox, double>(ui.FinalRenderSizeTable, this, row, -1, m_WidthScaleSpin, spinHeight, 0.001, 99.99, 0.1, SIGNAL(valueChanged(double)), SLOT(OnWidthScaleChanged(double)), true, 1.0, 1.0, 1.0);
|
|
SetupSpinner<SpinBox, int>(ui.FinalRenderSizeTable, this, row, -1, m_WidthSpin, spinHeight, 10, std::numeric_limits<int>::max(), 10, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)), true, 1920, 1920, 1920);
|
|
SetupSpinner<DoubleSpinBox, double>(ui.FinalRenderSizeTable, this, row, -1, m_HeightScaleSpin, spinHeight, 0.001, 99.99, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHeightScaleChanged(double)), true, 1.0, 1.0, 1.0);
|
|
SetupSpinner<SpinBox, int>(ui.FinalRenderSizeTable, this, row, -1, m_HeightSpin, spinHeight, 10, std::numeric_limits<int>::max(), 10, SIGNAL(valueChanged(int)), SLOT(OnHeightChanged(int)), true, 1080, 1080, 1080);
|
|
int spinsize = 120;
|
|
m_WidthScaleSpin->setDecimals(4);
|
|
m_HeightScaleSpin->setDecimals(4);
|
|
m_WidthScaleSpin->setFixedWidth(spinsize);
|
|
m_HeightScaleSpin->setFixedWidth(spinsize);
|
|
m_WidthScaleSpin->SmallStep(0.0001);
|
|
m_HeightScaleSpin->SmallStep(0.0001);
|
|
m_WidthSpin->setFixedWidth(spinsize);
|
|
m_HeightSpin->setFixedWidth(spinsize);
|
|
m_WidthSpinnerWidget = new DoubleIntSpinnerWidget(m_WidthScaleSpin, m_WidthSpin, ui.FinalRenderSizeTable);
|
|
m_WidthSpinnerWidget->m_DoubleSpinBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
m_WidthSpinnerWidget->m_SpinBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
m_WidthSpinnerWidget->setMinimumWidth(spinsize);
|
|
m_WidthSpinnerWidget->setMaximumWidth(spinsize);
|
|
ui.FinalRenderSizeTable->setCellWidget(0, 1, m_WidthSpinnerWidget);
|
|
//
|
|
m_HeightSpinnerWidget = new DoubleIntSpinnerWidget(m_HeightScaleSpin, m_HeightSpin, ui.FinalRenderSizeTable);
|
|
m_HeightSpinnerWidget->m_DoubleSpinBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
m_HeightSpinnerWidget->m_SpinBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
m_HeightSpinnerWidget->setMinimumWidth(spinsize);
|
|
m_HeightSpinnerWidget->setMaximumWidth(spinsize);
|
|
ui.FinalRenderSizeTable->setCellWidget(1, 1, m_HeightSpinnerWidget);
|
|
row = 0;
|
|
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, 1000, 1000, 1000);
|
|
SetupSpinner<SpinBox, int> (table, this, row, 1, m_TemporalSamplesSpin, spinHeight, 1, 5000, 50, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000, 1000, 1000);
|
|
SetupSpinner<SpinBox, int> (table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 2, 1, 1);
|
|
SetupSpinner<SpinBox, int> (table, this, row, 1, m_StripsSpin, spinHeight, 1, 64, 1, SIGNAL(valueChanged(int)), SLOT(OnStripsChanged(int)), true, 1, 1, 1);
|
|
m_MemoryCellIndex = row++;//Memory usage.
|
|
m_ItersCellIndex = row++;//Iters.
|
|
m_PathCellIndex = row;
|
|
QStringList comboList;
|
|
#ifdef _WIN32
|
|
comboList.append("bmp");
|
|
#endif
|
|
comboList.append("jpg");
|
|
comboList.append("png");
|
|
comboList.append("exr");
|
|
m_Tbcw = new TwoButtonComboWidget("...", "Open", comboList, 22, 40, 22, table);
|
|
table->setCellWidget(row, 1, m_Tbcw);
|
|
table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
connect(m_Tbcw->m_Button1, SIGNAL(clicked(bool)), this, SLOT(OnFileButtonClicked(bool)), Qt::QueuedConnection);
|
|
connect(m_Tbcw->m_Button2, SIGNAL(clicked(bool)), this, SLOT(OnShowFolderButtonClicked(bool)), Qt::QueuedConnection);
|
|
connect(m_Tbcw->m_Combo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnExtIndexChanged(int)), Qt::QueuedConnection);
|
|
m_PrefixEdit = new QLineEdit(table);
|
|
table->setCellWidget(row++, 1, m_PrefixEdit);
|
|
m_SuffixEdit = new QLineEdit(table);
|
|
table->setCellWidget(row++, 1, m_SuffixEdit);
|
|
connect(m_PrefixEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPrefixChanged(const QString&)), Qt::QueuedConnection);
|
|
connect(m_SuffixEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnSuffixChanged(const QString&)), Qt::QueuedConnection);
|
|
ui.FinalRenderStartButton->disconnect(SIGNAL(clicked(bool)));
|
|
connect(ui.FinalRenderStartButton, SIGNAL(clicked(bool)), this, SLOT(OnRenderClicked(bool)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderPauseButton, SIGNAL(clicked(bool)), this, SLOT(OnPauseClicked(bool)), Qt::QueuedConnection);
|
|
connect(ui.FinalRenderStopButton, SIGNAL(clicked(bool)), this, SLOT(OnCancelRenderClicked(bool)), Qt::QueuedConnection);
|
|
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
|
ui.FinalRenderSizeTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
|
table = ui.FinalRenderDeviceTable;
|
|
table->clearContents();
|
|
table->setRowCount(0);
|
|
|
|
if (m_Info->Ok() && !m_Info->Devices().empty())
|
|
{
|
|
//This is an extra attempt to prevent crashes on unsupported hardware:
|
|
//Hardware which does not have an OpenCL 1.2 driver installed will hard crash the program upon initializing a renderer. There is no way around this.
|
|
//When the user selects the OpenCL settings in the main options dialog, they can select the desired hardware before an attempt is made to create the renderer.
|
|
//However, in the final render dialog, the renderer is created and its settings changed for every applicable UI change made by the user so that it can be
|
|
//applied to the preview thumbnail.
|
|
//When they click Use OpenCL, it will immediately try to create the renderer with whatever devices are present in the device table.
|
|
//Since the first entry is always selected by default, the renderer will be created using it.
|
|
//If that entry is a supported device, then all is fine.
|
|
//However, if it's not a supported device, it will immediately crash the program.
|
|
//In such a scenario, the user will never be able to use OpenCL for the final render, even though they were able to use it for the interactive render.
|
|
//This workaround is to detect if the user has successfully specified and used any device other than the default on the main window in the options dialog.
|
|
//If so, use that instead.
|
|
//Since this code only runs once upon startup, they must close the program and re-run it after properly configuring an OpenCL device on the main window.
|
|
//At that point, when they open the final render dialog, it will use the correct device.
|
|
if (m_Settings->FinalDevices().isEmpty() &&//Is it the first run?
|
|
!m_Settings->Devices().empty() &&//They've successfully used OpenCL in the main window?
|
|
!(m_Settings->Devices().size() == 1 && m_Settings->Devices()[0].toInt() == 0))//Extra check it wasn't just the default, in which case it'd offer no value here since the default here is also 0.
|
|
m_Settings->FinalDevices(m_Settings->Devices());//Assign what was used in the main window here.
|
|
|
|
SetupDeviceTable(table, m_Settings->FinalDevices());
|
|
|
|
for (int i = 0; i < table->rowCount(); i++)
|
|
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(i, 1)))
|
|
connect(radio, SIGNAL(toggled(bool)), this, SLOT(OnDeviceTableRadioToggled(bool)), Qt::QueuedConnection);
|
|
|
|
ui.FinalRenderOpenCLCheckBox->setChecked(m_Settings->FinalOpenCL());
|
|
}
|
|
else
|
|
{
|
|
table->setEnabled(false);
|
|
ui.FinalRenderOpenCLCheckBox->setChecked(false);
|
|
ui.FinalRenderOpenCLCheckBox->setEnabled(false);
|
|
}
|
|
|
|
ui.FinalRenderEarlyClipCheckBox->setChecked( m_Settings->FinalEarlyClip());
|
|
ui.FinalRenderYAxisUpCheckBox->setChecked( m_Settings->FinalYAxisUp());
|
|
ui.FinalRenderTransparencyCheckBox->setChecked( m_Settings->FinalTransparency());
|
|
ui.FinalRenderDoublePrecisionCheckBox->setChecked( m_Settings->FinalDouble());
|
|
ui.FinalRenderSaveXmlCheckBox->setChecked( m_Settings->FinalSaveXml());
|
|
ui.FinalRenderDoAllCheckBox->setChecked( m_Settings->FinalDoAll());
|
|
ui.FinalRenderDoSequenceCheckBox->setChecked( m_Settings->FinalDoSequence());
|
|
ui.FinalRenderPng16BitCheckBox->setChecked( m_Settings->FinalPng16Bit());
|
|
ui.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect());
|
|
ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount());
|
|
#ifdef _WIN32
|
|
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(m_Settings->FinalThreadPriority() + 2);
|
|
#else
|
|
auto tpc = ui.FinalRenderThreadPriorityComboBox->count() - 1;
|
|
|
|
if (m_Settings->FinalThreadPriority() == THREAD_PRIORITY_LOWEST)
|
|
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(0);
|
|
else if (m_Settings->FinalThreadPriority() == THREAD_PRIORITY_HIGHEST)
|
|
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(tpc);
|
|
else
|
|
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(Clamp<int>(m_Settings->FinalThreadPriority() / 25, 0, tpc));
|
|
|
|
#endif
|
|
ui.FinalRenderOpenCLSubBatchPctSpin->setValue(m_Settings->FinalOpenCLSubBatchPct());
|
|
m_QualitySpin->setValue(m_Settings->FinalQuality());
|
|
m_TemporalSamplesSpin->setValue(m_Settings->FinalTemporalSamples());
|
|
m_SupersampleSpin->setValue(m_Settings->FinalSupersample());
|
|
m_StripsSpin->setValue(int(m_Settings->FinalStrips()));
|
|
Scale(eScaleType(m_Settings->FinalScale()));
|
|
int index = 0;
|
|
#ifdef _WIN32
|
|
|
|
if (m_Settings->FinalExt().endsWith("bmp", Qt::CaseInsensitive))
|
|
m_Tbcw->m_Combo->setCurrentIndex(index);
|
|
|
|
index++;
|
|
#endif
|
|
|
|
if (m_Settings->FinalExt().endsWith("jpg", Qt::CaseInsensitive))
|
|
m_Tbcw->m_Combo->setCurrentIndex(index);
|
|
|
|
index++;
|
|
|
|
if (m_Settings->FinalExt().endsWith("png", Qt::CaseInsensitive))
|
|
m_Tbcw->m_Combo->setCurrentIndex(index);
|
|
|
|
index++;
|
|
|
|
if (m_Settings->FinalExt().endsWith("exr", Qt::CaseInsensitive))
|
|
m_Tbcw->m_Combo->setCurrentIndex(index);
|
|
|
|
//Explicitly call these to enable/disable the appropriate controls.
|
|
OnOpenCLCheckBoxStateChanged(ui.FinalRenderOpenCLCheckBox->isChecked());
|
|
OnDoAllCheckBoxStateChanged(ui.FinalRenderDoAllCheckBox->isChecked());
|
|
OnDoSequenceCheckBoxStateChanged(ui.FinalRenderDoSequenceCheckBox->isChecked());
|
|
QSize s = size();
|
|
int desktopHeight = qApp->desktop()->availableGeometry().height();
|
|
s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90))));
|
|
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry()));
|
|
//Update these with new controls.
|
|
QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderTransparencyCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderOpenCLCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderPng16BitCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderDoublePrecisionCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderSaveXmlCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderDoAllCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderDoSequenceCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderCurrentSpin);
|
|
w = SetTabOrder(this, w, ui.FinalRenderDeviceTable);
|
|
w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin);
|
|
w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox);
|
|
w = SetTabOrder(this, w, ui.FinalRenderOpenCLSubBatchPctSpin);
|
|
w = SetTabOrder(this, w, m_WidthScaleSpin);
|
|
w = SetTabOrder(this, w, m_HeightScaleSpin);
|
|
w = SetTabOrder(this, w, ui.FinalRenderScaleNoneRadioButton);
|
|
w = SetTabOrder(this, w, ui.FinalRenderScaleWidthRadioButton);
|
|
w = SetTabOrder(this, w, ui.FinalRenderScaleHeightRadioButton);
|
|
w = SetTabOrder(this, w, ui.FinalRenderKeepAspectCheckBox);
|
|
w = SetTabOrder(this, w, m_QualitySpin);
|
|
w = SetTabOrder(this, w, m_TemporalSamplesSpin);
|
|
w = SetTabOrder(this, w, m_SupersampleSpin);
|
|
w = SetTabOrder(this, w, m_StripsSpin);
|
|
w = SetTabOrder(this, w, m_Tbcw);
|
|
w = SetTabOrder(this, w, m_Tbcw->m_Combo);
|
|
w = SetTabOrder(this, w, m_Tbcw->m_Button1);
|
|
w = SetTabOrder(this, w, m_Tbcw->m_Button2);
|
|
w = SetTabOrder(this, w, m_PrefixEdit);
|
|
w = SetTabOrder(this, w, m_SuffixEdit);
|
|
w = SetTabOrder(this, w, ui.FinalRenderTextOutput);
|
|
w = SetTabOrder(this, w, ui.FinalRenderStartButton);
|
|
w = SetTabOrder(this, w, ui.FinalRenderPauseButton);
|
|
w = SetTabOrder(this, w, ui.FinalRenderStopButton);
|
|
w = SetTabOrder(this, w, ui.FinalRenderCloseButton);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the final render dialog and specify whether it was called from the toolbar or the sequence render button.
|
|
/// </summary>
|
|
/// <param name="fromSequence">True if this is called from the sequence render button, else false.</param>
|
|
void FractoriumFinalRenderDialog::Show(bool fromSequence)
|
|
{
|
|
m_FromSequence = fromSequence;
|
|
#ifdef __APPLE__
|
|
exec();
|
|
#else
|
|
show();
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// GUI settings wrapper functions, getters only.
|
|
/// </summary>
|
|
|
|
bool FractoriumFinalRenderDialog::EarlyClip() { return ui.FinalRenderEarlyClipCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::YAxisUp() { return ui.FinalRenderYAxisUpCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::Transparency() { return ui.FinalRenderTransparencyCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::OpenCL() { return ui.FinalRenderOpenCLCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::Double() { return ui.FinalRenderDoublePrecisionCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::SaveXml() { return ui.FinalRenderSaveXmlCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::DoAll() { return ui.FinalRenderDoAllCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::DoSequence() { return ui.FinalRenderDoSequenceCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::Png16Bit() { return ui.FinalRenderPng16BitCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::KeepAspect() { return ui.FinalRenderKeepAspectCheckBox->isChecked(); }
|
|
bool FractoriumFinalRenderDialog::ApplyToAll() { return ui.FinalRenderApplyToAllCheckBox->isChecked(); }
|
|
QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentText(); }
|
|
QString FractoriumFinalRenderDialog::Path() { return ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->text(); }
|
|
void FractoriumFinalRenderDialog::Path(const QString& s) { ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->setText(s); }
|
|
QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); }
|
|
QString FractoriumFinalRenderDialog::Suffix() { return m_SuffixEdit->text(); }
|
|
uint FractoriumFinalRenderDialog::Current() { return ui.FinalRenderCurrentSpin->value(); }
|
|
uint FractoriumFinalRenderDialog::ThreadCount() { return ui.FinalRenderThreadCountSpin->value(); }
|
|
#ifdef _WIN32
|
|
int FractoriumFinalRenderDialog::ThreadPriority() { return ui.FinalRenderThreadPriorityComboBox->currentIndex() - 2; }
|
|
#else
|
|
int FractoriumFinalRenderDialog::ThreadPriority()
|
|
{
|
|
if (ui.FinalRenderThreadPriorityComboBox->currentIndex() == 0)
|
|
return THREAD_PRIORITY_LOWEST;
|
|
else if (ui.FinalRenderThreadPriorityComboBox->currentIndex() == (ui.FinalRenderThreadPriorityComboBox->count() - 1))
|
|
return THREAD_PRIORITY_HIGHEST;
|
|
else
|
|
return ui.FinalRenderThreadPriorityComboBox->currentIndex() * 25;
|
|
}
|
|
#endif
|
|
double FractoriumFinalRenderDialog::OpenCLSubBatchPct() { return ui.FinalRenderOpenCLSubBatchPctSpin->value(); }
|
|
double FractoriumFinalRenderDialog::WidthScale() { return m_WidthScaleSpin->value(); }
|
|
double FractoriumFinalRenderDialog::HeightScale() { return m_HeightScaleSpin->value(); }
|
|
double FractoriumFinalRenderDialog::Quality() { return m_QualitySpin->value(); }
|
|
uint FractoriumFinalRenderDialog::TemporalSamples() { return m_TemporalSamplesSpin->value(); }
|
|
uint FractoriumFinalRenderDialog::Supersample() { return m_SupersampleSpin->value(); }
|
|
uint FractoriumFinalRenderDialog::Strips() { return m_StripsSpin->value(); }
|
|
QList<QVariant> FractoriumFinalRenderDialog::Devices() { return DeviceTableToSettings(ui.FinalRenderDeviceTable); }
|
|
|
|
/// <summary>
|
|
/// Capture the current state of the Gui.
|
|
/// Used to hold options for performing the final render.
|
|
/// </summary>
|
|
/// <returns>The state of the Gui as a struct</returns>
|
|
FinalRenderGuiState FractoriumFinalRenderDialog::State()
|
|
{
|
|
FinalRenderGuiState state;
|
|
state.m_EarlyClip = EarlyClip();
|
|
state.m_YAxisUp = YAxisUp();
|
|
state.m_Transparency = Transparency();
|
|
state.m_OpenCL = OpenCL();
|
|
state.m_Double = Double();
|
|
state.m_SaveXml = SaveXml();
|
|
state.m_DoAll = DoAll();
|
|
state.m_DoSequence = DoSequence();
|
|
state.m_Png16Bit = Png16Bit();
|
|
state.m_KeepAspect = KeepAspect();
|
|
state.m_Scale = Scale();
|
|
state.m_Path = Path();
|
|
state.m_Ext = Ext();
|
|
state.m_Prefix = Prefix();
|
|
state.m_Suffix = Suffix();
|
|
state.m_Devices = Devices();
|
|
state.m_ThreadCount = ThreadCount();
|
|
state.m_ThreadPriority = ThreadPriority();
|
|
state.m_SubBatchPct = OpenCLSubBatchPct();
|
|
state.m_WidthScale = WidthScale();
|
|
state.m_HeightScale = HeightScale();
|
|
state.m_Quality = Quality();
|
|
state.m_TemporalSamples = TemporalSamples();
|
|
state.m_Supersample = Supersample();
|
|
state.m_Strips = Strips();
|
|
return state;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the type of scaling desired based on what radio button has been selected.
|
|
/// </summary>
|
|
/// <returns>The type of scaling as an eScaleType enum</returns>
|
|
eScaleType FractoriumFinalRenderDialog::Scale()
|
|
{
|
|
if (ui.FinalRenderScaleNoneRadioButton->isChecked())
|
|
return eScaleType::SCALE_NONE;
|
|
else if (ui.FinalRenderScaleWidthRadioButton->isChecked())
|
|
return eScaleType::SCALE_WIDTH;
|
|
else if (ui.FinalRenderScaleHeightRadioButton->isChecked())
|
|
return eScaleType::SCALE_HEIGHT;
|
|
else
|
|
return eScaleType::SCALE_NONE;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the type of scaling desired which will select the corresponding radio button.
|
|
/// </summary>
|
|
/// <param name="scale">The type of scaling to use</param>
|
|
void FractoriumFinalRenderDialog::Scale(eScaleType scale)
|
|
{
|
|
ui.FinalRenderScaleNoneRadioButton->blockSignals(true);
|
|
|
|
if (scale == eScaleType::SCALE_NONE)
|
|
ui.FinalRenderScaleNoneRadioButton->setChecked(true);
|
|
else if (scale == eScaleType::SCALE_WIDTH)
|
|
ui.FinalRenderScaleWidthRadioButton->setChecked(true);
|
|
else if (scale == eScaleType::SCALE_HEIGHT)
|
|
ui.FinalRenderScaleHeightRadioButton->setChecked(true);
|
|
else
|
|
ui.FinalRenderScaleNoneRadioButton->setChecked(true);
|
|
|
|
ui.FinalRenderScaleNoneRadioButton->blockSignals(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Simple wrapper to put moving the cursor to the end in a signal
|
|
/// so it can be called from a thread.
|
|
/// </summary>
|
|
void FractoriumFinalRenderDialog::MoveCursorToEnd()
|
|
{
|
|
ui.FinalRenderTextOutput->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether to use early clipping before spatial filtering.
|
|
/// </summary>
|
|
/// <param name="index">True to early clip, else don't.</param>
|
|
void FractoriumFinalRenderDialog::OnEarlyClipCheckBoxStateChanged(int state)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the positive Y axis of the final output image is up.
|
|
/// </summary>
|
|
/// <param name="yup">True if the positive y axis is up, else false.</param>
|
|
void FractoriumFinalRenderDialog::OnYAxisUpCheckBoxStateChanged(int state)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether to use transparency in png images.
|
|
/// </summary>
|
|
/// <param name="index">True to use transparency, else don't.</param>
|
|
void FractoriumFinalRenderDialog::OnTransparencyCheckBoxStateChanged(int state)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set whether to use OpenCL in the rendering process or not.
|
|
/// Also disable or enable the CPU and OpenCL related controls based on the state passed in.
|
|
/// </summary>
|
|
/// <param name="state">Use OpenCL if state == Qt::Checked, else don't.</param>
|
|
void FractoriumFinalRenderDialog::OnOpenCLCheckBoxStateChanged(int state)
|
|
{
|
|
bool checked = state == Qt::Checked;
|
|
ui.FinalRenderDeviceTable->setEnabled(checked);
|
|
ui.FinalRenderOpenCLSubBatchPctSpin->setEnabled(checked);
|
|
ui.FinalRenderThreadCountSpin->setEnabled(!checked);
|
|
ui.FinalRenderThreadPriorityLabel->setEnabled(!checked);
|
|
ui.FinalRenderThreadPriorityComboBox->setEnabled(!checked);
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set whether to use double or single precision in the rendering process or not.
|
|
/// This will recreate the entire controller.
|
|
/// </summary>
|
|
/// <param name="state">Use double if state == Qt::Checked, else float.</param>
|
|
void FractoriumFinalRenderDialog::OnDoublePrecisionCheckBoxStateChanged(int state)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The do all checkbox was changed.
|
|
/// If checked, render all embers available in the currently opened file, else
|
|
/// only render the current ember.
|
|
/// </summary>
|
|
/// <param name="state">The state of the checkbox</param>
|
|
void FractoriumFinalRenderDialog::OnDoAllCheckBoxStateChanged(int state)
|
|
{
|
|
if (!state)
|
|
ui.FinalRenderDoSequenceCheckBox->setChecked(false);
|
|
|
|
ui.FinalRenderDoSequenceCheckBox->setEnabled(ui.FinalRenderDoAllCheckBox->isChecked());
|
|
}
|
|
|
|
/// <summary>
|
|
/// The do sequence checkbox was changed.
|
|
/// If checked, render all embers available in the currently opened file as an animation sequence, else
|
|
/// render them individually.
|
|
/// </summary>
|
|
/// <param name="state">The state of the checkbox</param>
|
|
void FractoriumFinalRenderDialog::OnDoSequenceCheckBoxStateChanged(int state)
|
|
{
|
|
bool checked = ui.FinalRenderDoSequenceCheckBox->isChecked();
|
|
m_TemporalSamplesSpin->setEnabled(checked);
|
|
|
|
if (checked)
|
|
m_StripsSpin->setValue(1);
|
|
|
|
m_StripsSpin->setEnabled(!checked);
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current ember spinner was changed, update fields.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnCurrentSpinChanged(int d)
|
|
{
|
|
m_Controller->SetEmber(d - 1, false);
|
|
m_Controller->SyncCurrentToGui();
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The apply all checkbox was changed.
|
|
/// If checked, set values for all embers in the file to the values specified in the GUI.
|
|
/// </summary>
|
|
/// <param name="state">The state of the checkbox</param>
|
|
void FractoriumFinalRenderDialog::OnApplyAllCheckBoxStateChanged(int state)
|
|
{
|
|
if (state && m_Controller.get())
|
|
m_Controller->SyncGuiToEmbers();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The width spinner was changed, recompute required memory.
|
|
/// If the aspect ratio checkbox is checked, set the value of
|
|
/// the height spinner as well to be in proportion.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnWidthScaleChanged(double d)
|
|
{
|
|
if (m_Controller.get())
|
|
{
|
|
if (ui.FinalRenderKeepAspectCheckBox->isChecked())
|
|
m_HeightScaleSpin->SetValueStealth(m_WidthScaleSpin->value());
|
|
|
|
if (SetMemory())
|
|
m_Controller->SyncCurrentToSizeSpinners(false, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The width spinner was changed, recompute required memory.
|
|
/// If the aspect ratio checkbox is checked, set the value of
|
|
/// the height spinner as well to be in proportion.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnWidthChanged(int d)
|
|
{
|
|
if (m_Controller.get())
|
|
{
|
|
m_Controller->SyncGuiToEmbers(m_WidthSpin->value(), m_HeightSpin->value());//Copy changed width value from gui to ember (height will be copied even though it hasn't changed).
|
|
m_Controller->SyncCurrentToSizeSpinners(true, false, true, false);//Compute how much the original width had to be scaled by to equal the new width and put the value in the width scale spinner.
|
|
|
|
if (ui.FinalRenderKeepAspectCheckBox->isChecked())
|
|
{
|
|
m_HeightScaleSpin->SetValueStealth(m_WidthScaleSpin->value());//Make the height scale spinner match the newly computed width scale spinner.
|
|
m_Controller->SyncGuiToEmbers(0, 0, false, true);//Scale height by the amount in the height scale spinner and assign the value to the ember height.
|
|
m_Controller->SyncCurrentToSizeSpinners(false, true, false, true);//Copy new height value from the ember to the height spinner.
|
|
}
|
|
}
|
|
|
|
if (SetMemory()) {}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The height spinner was changed, recompute required memory.
|
|
/// If the aspect ratio checkbox is checked, set the value of
|
|
/// the width spinner as well to be in proportion.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnHeightScaleChanged(double d)
|
|
{
|
|
if (m_Controller.get())
|
|
{
|
|
if (ui.FinalRenderKeepAspectCheckBox->isChecked())
|
|
m_WidthScaleSpin->SetValueStealth(m_HeightScaleSpin->value());
|
|
|
|
if (SetMemory())
|
|
m_Controller->SyncCurrentToSizeSpinners(false, true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The height spinner was changed, recompute required memory.
|
|
/// If the aspect ratio checkbox is checked, set the value of
|
|
/// the width spinner as well to be in proportion.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnHeightChanged(int d)
|
|
{
|
|
if (m_Controller.get())
|
|
{
|
|
m_Controller->SyncGuiToEmbers(m_WidthSpin->value(), m_HeightSpin->value());//Copy changed height value from gui to ember (width will be copied even though it hasn't changed).
|
|
m_Controller->SyncCurrentToSizeSpinners(true, false, false, true);//Compute how much the original height had to be scaled by to equal the new height and put the value in the height scale spinner.
|
|
|
|
if (ui.FinalRenderKeepAspectCheckBox->isChecked())
|
|
{
|
|
m_WidthScaleSpin->SetValueStealth(m_HeightScaleSpin->value());//Make the width scale spinner match the newly computed height scale spinner.
|
|
m_Controller->SyncGuiToEmbers(0, 0, true, false);//Scale width by the amount in the width scale spinner and assign the value to the ember width.
|
|
m_Controller->SyncCurrentToSizeSpinners(false, true, true, false);//Copy new width value from the ember to the width spinner.
|
|
}
|
|
}
|
|
|
|
if (SetMemory()) {}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether to keep the aspect ratio of the desired width and height the same
|
|
/// as that of the original width and height.
|
|
/// </summary>
|
|
/// <param name="checked">The state of the checkbox</param>
|
|
void FractoriumFinalRenderDialog::OnKeepAspectCheckBoxStateChanged(int state)
|
|
{
|
|
if (state && m_Controller.get())
|
|
m_HeightScaleSpin->setValue(m_WidthScaleSpin->value());
|
|
|
|
//m_HeightScaleSpin->SetValueStealth(m_WidthScaleSpin->value());
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The scaling method radio button selection was changed.
|
|
/// </summary>
|
|
/// <param name="checked">The state of the radio button</param>
|
|
void FractoriumFinalRenderDialog::OnScaleRadioButtonChanged(bool checked)//This is wrong. Does not react when scale is switched back to zero. Fix!//TODO
|
|
{
|
|
if (checked)
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The check state of one of the OpenCL devices was changed.
|
|
/// This does a special check to always ensure at least one device,
|
|
/// as well as one primary is checked.
|
|
/// </summary>
|
|
/// <param name="row">The row of the cell</param>
|
|
/// <param name="col">The column of the cell</param>
|
|
void FractoriumFinalRenderDialog::OnDeviceTableCellChanged(int row, int col)
|
|
{
|
|
if (auto item = ui.FinalRenderDeviceTable->item(row, col))
|
|
{
|
|
HandleDeviceTableCheckChanged(ui.FinalRenderDeviceTable, row, col);
|
|
SetMemory();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The primary device radio button selection was changed.
|
|
/// If the device was specified as primary, but was not selected
|
|
/// for inclusion, it will automatically be selected for inclusion.
|
|
/// </summary>
|
|
/// <param name="checked">The state of the radio button</param>
|
|
void FractoriumFinalRenderDialog::OnDeviceTableRadioToggled(bool checked)
|
|
{
|
|
int row;
|
|
auto s = sender();
|
|
auto table = ui.FinalRenderDeviceTable;
|
|
QRadioButton* radio = nullptr;
|
|
|
|
if (s)
|
|
{
|
|
for (row = 0; row < table->rowCount(); row++)
|
|
if (radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1)))
|
|
if (s == radio)
|
|
{
|
|
HandleDeviceTableCheckChanged(ui.FinalRenderDeviceTable, row, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (checked)
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The quality spinner was changed, recompute required memory.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnQualityChanged(double d)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The temporal samples spinner was changed, recompute required memory.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnTemporalSamplesChanged(int d)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The supersample spinner was changed, recompute required memory.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnSupersampleChanged(int d)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The supersample spinner was changed, recompute required memory.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnStripsChanged(int d)
|
|
{
|
|
SetMemory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the save folder dialog.
|
|
/// Called when the ... file button is clicked.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnFileButtonClicked(bool checked)
|
|
{
|
|
QString s = m_Fractorium->SetupSaveFolderDialog();
|
|
|
|
if (Exists(s))
|
|
{
|
|
m_Settings->SaveFolder(s);//Any time they exit the box with a valid value, preserve it in the settings.
|
|
Path(m_Controller->ComposePath(m_Controller->Name()));//And update the GUI.
|
|
SetMemory();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the folder where the last rendered image was saved to.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnShowFolderButtonClicked(bool checked)
|
|
{
|
|
QString s = m_Settings->SaveFolder();
|
|
|
|
if (Exists(s))
|
|
QDesktopServices::openUrl(QUrl::fromLocalFile(s));
|
|
else
|
|
QDesktopServices::openUrl(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation)[0]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change the extension of the output image, which also may change the
|
|
/// number of channels used in the final output buffer.
|
|
/// </summary>
|
|
/// <param name="d">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnExtIndexChanged(int d)
|
|
{
|
|
if (SetMemory())
|
|
Path(m_Controller->ComposePath(m_Controller->Name()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change the prefix prepended to the output file name.
|
|
/// </summary>
|
|
/// <param name="s">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnPrefixChanged(const QString& s)
|
|
{
|
|
Path(m_Controller->ComposePath(m_Controller->Name()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change the suffix appended to the output file name.
|
|
/// </summary>
|
|
/// <param name="s">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnSuffixChanged(const QString& s)
|
|
{
|
|
Path(m_Controller->ComposePath(m_Controller->Name()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start the render process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnRenderClicked(bool checked)
|
|
{
|
|
if (CreateControllerFromGUI(true))
|
|
{
|
|
Pause(false);
|
|
m_Controller->Render();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pause or resume the render process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnPauseClicked(bool checked)
|
|
{
|
|
if (m_Controller.get())
|
|
if (m_Controller->Running())
|
|
Pause(!m_Controller->Paused());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancel the render.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void FractoriumFinalRenderDialog::OnCancelRenderClicked(bool checked)
|
|
{
|
|
if (m_Controller.get())
|
|
{
|
|
Pause(false);
|
|
m_Controller->CancelRender();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pause or resume the render process.
|
|
/// </summary>
|
|
/// <param name="checked">True to pause, else resume.</param>
|
|
void FractoriumFinalRenderDialog::Pause(bool paused)
|
|
{
|
|
if (paused)
|
|
{
|
|
m_Controller->Pause(true);
|
|
ui.FinalRenderPauseButton->setText("Resume");
|
|
}
|
|
else
|
|
{
|
|
m_Controller->Pause(false);
|
|
ui.FinalRenderPauseButton->setText("Pause");
|
|
}
|
|
|
|
ui.FinalRenderStartButton->setEnabled(!paused);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uses the options and the ember to populate widgets.
|
|
/// Called when the dialog is initially shown.
|
|
/// </summary>
|
|
/// <param name="e">The event</param>
|
|
void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
|
|
{
|
|
if (m_Controller.get() && m_Controller->m_Run)//On Linux, this event will be called when the main window minimized/maximized while rendering, so filter it out.
|
|
return;
|
|
|
|
QString firstfile;
|
|
|
|
if (CreateControllerFromGUI(true))//Create controller if it does not exist, or if it does and the renderer is not running.
|
|
{
|
|
int index = int(m_Fractorium->m_Controller->Index()) + 1;
|
|
#ifdef DO_DOUBLE
|
|
Ember<double> ed;
|
|
EmberFile<double> efi;
|
|
m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember<double>& ember)
|
|
{
|
|
ember.SyncSize();
|
|
ember.m_Quality = m_Settings->FinalQuality();
|
|
ember.m_Supersample = m_Settings->FinalSupersample();
|
|
ember.m_TemporalSamples = m_Settings->FinalTemporalSamples();
|
|
});//Copy the whole file, will take about 0.2ms per ember in the file.
|
|
firstfile = efi.m_Filename;
|
|
#else
|
|
Ember<float> ed;
|
|
EmberFile<float> efi;
|
|
m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember<float>& ember)
|
|
{
|
|
ember.SyncSize();
|
|
ember.m_Quality = m_Settings->FinalQuality();
|
|
ember.m_Supersample = m_Settings->FinalSupersample();
|
|
ember.m_TemporalSamples = m_Settings->FinalTemporalSamples();
|
|
});//Copy the whole file, will take about 0.2ms per ember in the file.
|
|
firstfile = efi.m_Filename;
|
|
#endif
|
|
m_Controller->SetEmberFile(efi, true);//Move the temp file into the final render controller.
|
|
ui.FinalRenderCurrentSpin->setMaximum(int(efi.Size()));
|
|
ui.FinalRenderCurrentSpin->blockSignals(true);
|
|
ui.FinalRenderCurrentSpin->setValue(index);//Set the currently selected ember to the one that was being edited.
|
|
ui.FinalRenderCurrentSpin->blockSignals(false);
|
|
OnCurrentSpinChanged(index);//Force update in case the ember was new, but at the same index as the previous one.
|
|
m_Controller->m_ImageCount = 0;
|
|
SetMemory();
|
|
m_Controller->ResetProgress();
|
|
QString s = m_Settings->SaveFolder();
|
|
|
|
if (Exists(s))
|
|
Path(m_Controller->ComposePath(m_Controller->Name()));//Update the GUI.
|
|
}
|
|
|
|
if (m_FromSequence)
|
|
{
|
|
ui.FinalRenderDoAllCheckBox->setChecked(true);
|
|
ui.FinalRenderDoSequenceCheckBox->setChecked(true);
|
|
ui.FinalRenderApplyToAllCheckBox->setChecked(true);
|
|
m_PrefixEdit->setText(firstfile + "_");
|
|
}
|
|
|
|
ui.FinalRenderTextOutput->clear();
|
|
QDialog::showEvent(e);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Close the dialog without running, or if running, cancel and exit.
|
|
/// Settings will not be saved.
|
|
/// Control will be returned to Fractorium::OnActiOn().
|
|
/// </summary>
|
|
void FractoriumFinalRenderDialog::reject()
|
|
{
|
|
if (m_Controller.get())
|
|
{
|
|
m_Controller->CancelRender();
|
|
m_Controller->DeleteRenderer();
|
|
}
|
|
|
|
QDialog::reject();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create the controller from the options and optionally its underlying renderer.
|
|
/// </summary>
|
|
/// <returns>True if successful, else false.</returns>
|
|
bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
|
|
{
|
|
int index = Current() - 1;
|
|
#ifdef DO_DOUBLE
|
|
size_t elementSize = Double() ? sizeof(double) : sizeof(float);
|
|
#else
|
|
size_t elementSize = sizeof(float);
|
|
#endif
|
|
|
|
if (!m_Controller.get() || (m_Controller->SizeOfT() != elementSize))
|
|
{
|
|
#ifdef DO_DOUBLE
|
|
Ember<double> ed;
|
|
Ember<double> orig;
|
|
EmberFile<double> efd;
|
|
#else
|
|
Ember<float> ed;
|
|
Ember<float> orig;
|
|
EmberFile<float> efd;
|
|
#endif
|
|
|
|
//First check if a controller has already been created, and if so, save its embers and gracefully shut it down.
|
|
if (m_Controller.get())
|
|
{
|
|
#ifdef DO_DOUBLE
|
|
m_Controller->CopyEmberFile(efd, false, [&](Ember<double>& ember) { });//Convert float to double or save double verbatim;
|
|
#else
|
|
m_Controller->CopyEmberFile(efd, false, [&](Ember<float>& ember) { });//Convert float to double or save double verbatim;
|
|
#endif
|
|
m_Controller->Shutdown();
|
|
}
|
|
|
|
//Create a float or double controller based on the GUI.
|
|
#ifdef DO_DOUBLE
|
|
|
|
if (Double())
|
|
m_Controller = unique_ptr<FinalRenderEmberControllerBase>(new FinalRenderEmberController<double>(this));
|
|
else
|
|
#endif
|
|
m_Controller = unique_ptr<FinalRenderEmberControllerBase>(new FinalRenderEmberController<float>(this));
|
|
|
|
//Restore the ember and ember file.
|
|
if (m_Controller.get())
|
|
{
|
|
m_Controller->SetEmberFile(efd, true);//Convert float to double and move or move double verbatim.
|
|
m_Controller->SetEmber(index, false);
|
|
}
|
|
}
|
|
|
|
if (m_Controller.get())
|
|
{
|
|
if (createRenderer)
|
|
return m_Controller->CreateRendererFromGUI();
|
|
else
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compute the amount of memory needed via call to SyncAndComputeMemory(), then
|
|
/// assign the result to the table cell as text.
|
|
/// Report errors if not enough memory is available for any of the selected devices.
|
|
/// </summary>
|
|
/// <returns>True if devices and a controller is present, else false.</returns>
|
|
bool FractoriumFinalRenderDialog::SetMemory()
|
|
{
|
|
if (isVisible() && CreateControllerFromGUI(true))
|
|
{
|
|
QString s;
|
|
auto p = m_Controller->SyncAndComputeMemory();
|
|
ui.FinalRenderParamsTable->item(m_MemoryCellIndex, 1)->setText(ToString<qulonglong>(get<1>(p)));
|
|
ui.FinalRenderParamsTable->item(m_ItersCellIndex, 1)->setText(ToString<qulonglong>(get<2>(p)));
|
|
|
|
if (OpenCL())
|
|
ui.FinalRenderTextOutput->setText(m_Controller->CheckMemory(p));
|
|
else
|
|
ui.FinalRenderTextOutput->clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|