#include "FractoriumPch.h" #include "FinalRenderDialog.h" #include "Fractorium.h" QString FractoriumFinalRenderDialog::m_Prefix = ""; QString FractoriumFinalRenderDialog::m_Suffix = ""; /// /// 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. /// /// Pointer to the global settings object to use /// The parent widget /// The window flags. Default: 0. FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowFlags f) : QDialog(p, f) { ui.setupUi(this); int row = 0; const auto spinHeight = 20; const auto dmax = numeric_limits::max(); QTableWidget* table = ui.FinalRenderParamsTable; m_Info = OpenCLInfo::Instance(); m_Fractorium = qobject_cast(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(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(ui.FinalRenderSizeTable, this, row, -1, m_WidthSpin, spinHeight, 10, std::numeric_limits::max(), 10, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)), true, 1920, 1920, 1920); SetupSpinner(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(ui.FinalRenderSizeTable, this, row, -1, m_HeightSpin, spinHeight, 10, std::numeric_limits::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(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, 1000, 1000, 1000); SetupSpinner (table, this, row, 1, m_TemporalSamplesSpin, spinHeight, 1, 5000, 50, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000, 1000, 1000); SetupSpinner (table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 2, 1, 1); SetupSpinner (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); m_PrefixEdit->setText(m_Prefix); table->setCellWidget(row++, 1, m_PrefixEdit); m_SuffixEdit = new QLineEdit(table); m_SuffixEdit->setText(m_Suffix); 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(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(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())); const auto bumpmenu = new QMenu(this); const auto add10 = new QAction("Add 10% Quality", this); add10->setProperty("tag", QVariant(0.10)); const auto add25 = new QAction("Add 25% Quality", this); add25->setProperty("tag", QVariant(0.25)); const auto add50 = new QAction("Add 50% Quality", this); add50->setProperty("tag", QVariant(0.50)); const auto add100 = new QAction("Add 100% Quality", this); add100->setProperty("tag", QVariant(1.0)); const auto add200 = new QAction("Add 200% Quality", this); add200->setProperty("tag", QVariant(2.0)); bumpmenu->addAction(add10); bumpmenu->addAction(add25); bumpmenu->addAction(add50); bumpmenu->addAction(add100); bumpmenu->addAction(add200); ui.FinalRenderBumpQualityStartButton->setMenu(bumpmenu); ui.FinalRenderBumpQualityStartButton->setProperty("tag", add25->property("tag")); ui.FinalRenderBumpQualityStartButton->setText(add25->text()); ui.FinalRenderBumpQualityStartButton->setEnabled(false); const auto saamenu = new QMenu(this); #ifdef _WIN32 const auto saabmp = new QAction("bmp", this); saamenu->addAction(saabmp); connect(saabmp, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked())); #endif const auto saajpg = new QAction("jpg", this); const auto saapng = new QAction("png", this); const auto saaexr = new QAction("exr", this); saamenu->addAction(saajpg); saamenu->addAction(saapng); saamenu->addAction(saaexr); ui.FinalRenderSaveAgainAsButton->setMenu(saamenu); ui.FinalRenderSaveAgainAsButton->setProperty("tag", "jpg"); ui.FinalRenderSaveAgainAsButton->setText("Save Again as jpg"); ui.FinalRenderSaveAgainAsButton->setEnabled(false); connect(ui.FinalRenderSaveAgainAsButton, SIGNAL(clicked()), this, SLOT(OnSaveAgainAsClicked())); connect(ui.FinalRenderBumpQualityStartButton, SIGNAL(clicked()), this, SLOT(OnQualityBumpClicked())); connect(saajpg, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked())); connect(saapng, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked())); connect(saaexr, SIGNAL(triggered()), this, SLOT(OnSaveAgainAsClicked())); connect(add10, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked())); connect(add25, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked())); connect(add50, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked())); connect(add100, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked())); connect(add200, SIGNAL(triggered()), this, SLOT(OnQualityBumpClicked())); 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(); const auto 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. auto 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.FinalRenderSaveAgainAsButton); w = SetTabOrder(this, w, ui.FinalRenderBumpQualityStartButton); w = SetTabOrder(this, w, ui.FinalRenderStartButton); w = SetTabOrder(this, w, ui.FinalRenderPauseButton); w = SetTabOrder(this, w, ui.FinalRenderStopButton); w = SetTabOrder(this, w, ui.FinalRenderCloseButton); } FractoriumFinalRenderDialog::~FractoriumFinalRenderDialog() { m_Prefix = m_PrefixEdit->text(); m_Suffix = m_SuffixEdit->text(); } /// /// Show the final render dialog and specify whether it was called from the toolbar or the sequence render button. /// /// True if this is called from the sequence render button, else false. void FractoriumFinalRenderDialog::Show(bool fromSequence) { m_FromSequence = fromSequence; #ifdef __APPLE__ exec(); #else show(); #endif } /// /// GUI settings wrapper functions, getters only. /// 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 FractoriumFinalRenderDialog::Devices() { return DeviceTableToSettings(ui.FinalRenderDeviceTable); } /// /// Capture the current state of the Gui. /// Used to hold options for performing the final render. /// /// The state of the Gui as a struct 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; } /// /// Return the type of scaling desired based on what radio button has been selected. /// /// The type of scaling as an eScaleType enum 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; } /// /// Set the type of scaling desired which will select the corresponding radio button. /// /// The type of scaling to use 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); } /// /// Simple wrapper to put moving the cursor to the end in a signal /// so it can be called from a thread. /// void FractoriumFinalRenderDialog::MoveCursorToEnd() { ui.FinalRenderTextOutput->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); } /// /// Whether to use early clipping before spatial filtering. /// /// True to early clip, else don't. void FractoriumFinalRenderDialog::OnEarlyClipCheckBoxStateChanged(int state) { SetMemory(); } /// /// Whether the positive Y axis of the final output image is up. /// /// True if the positive y axis is up, else false. void FractoriumFinalRenderDialog::OnYAxisUpCheckBoxStateChanged(int state) { SetMemory(); } /// /// Whether to use transparency in png images. /// /// True to use transparency, else don't. void FractoriumFinalRenderDialog::OnTransparencyCheckBoxStateChanged(int state) { SetMemory(); } /// /// 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. /// /// Use OpenCL if state == Qt::Checked, else don't. void FractoriumFinalRenderDialog::OnOpenCLCheckBoxStateChanged(int state) { const auto 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(); } /// /// Set whether to use double or single precision in the rendering process or not. /// This will recreate the entire controller. /// /// Use double if state == Qt::Checked, else float. void FractoriumFinalRenderDialog::OnDoublePrecisionCheckBoxStateChanged(int state) { SetMemory(); } /// /// The do all checkbox was changed. /// If checked, render all embers available in the currently opened file, else /// only render the current ember. /// /// The state of the checkbox void FractoriumFinalRenderDialog::OnDoAllCheckBoxStateChanged(int state) { if (!state) ui.FinalRenderDoSequenceCheckBox->setChecked(false); ui.FinalRenderDoSequenceCheckBox->setEnabled(ui.FinalRenderDoAllCheckBox->isChecked()); ui.FinalRenderSaveAgainAsButton->setEnabled(false); ui.FinalRenderBumpQualityStartButton->setEnabled(false); } /// /// 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. /// /// The state of the checkbox void FractoriumFinalRenderDialog::OnDoSequenceCheckBoxStateChanged(int state) { const auto checked = ui.FinalRenderDoSequenceCheckBox->isChecked(); m_TemporalSamplesSpin->setEnabled(checked); if (checked) m_StripsSpin->setValue(1); m_StripsSpin->setEnabled(!checked); SetMemory(); } /// /// The current ember spinner was changed, update fields. /// /// Ignored void FractoriumFinalRenderDialog::OnCurrentSpinChanged(int d) { m_Controller->SetEmber(d - 1, false); m_Controller->SyncCurrentToGui(); SetMemory(); } /// /// The apply all checkbox was changed. /// If checked, set values for all embers in the file to the values specified in the GUI. /// /// The state of the checkbox void FractoriumFinalRenderDialog::OnApplyAllCheckBoxStateChanged(int state) { if (state && m_Controller.get()) m_Controller->SyncGuiToEmbers(); ui.FinalRenderSaveAgainAsButton->setEnabled(false); ui.FinalRenderBumpQualityStartButton->setEnabled(false); } /// /// 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. /// /// Ignored 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); } } /// /// 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. /// /// Ignored 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()) {} } /// /// 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. /// /// Ignored 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); } } /// /// 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. /// /// Ignored 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()) {} } /// /// Whether to keep the aspect ratio of the desired width and height the same /// as that of the original width and height. /// /// The state of the checkbox void FractoriumFinalRenderDialog::OnKeepAspectCheckBoxStateChanged(int state) { if (state && m_Controller.get()) m_HeightScaleSpin->setValue(m_WidthScaleSpin->value()); //m_HeightScaleSpin->SetValueStealth(m_WidthScaleSpin->value()); SetMemory(); } /// /// The scaling method radio button selection was changed. /// /// The state of the radio button void FractoriumFinalRenderDialog::OnScaleRadioButtonChanged(bool checked)//This is wrong. Does not react when scale is switched back to zero. Fix!//TODO { if (checked) SetMemory(); } /// /// 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. /// /// The row of the cell /// The column of the cell void FractoriumFinalRenderDialog::OnDeviceTableCellChanged(int row, int col) { if (const auto item = ui.FinalRenderDeviceTable->item(row, col)) { HandleDeviceTableCheckChanged(ui.FinalRenderDeviceTable, row, col); SetMemory(); } } /// /// 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. /// /// The state of the radio button void FractoriumFinalRenderDialog::OnDeviceTableRadioToggled(bool checked) { int row; const auto s = sender(); const auto table = ui.FinalRenderDeviceTable; QRadioButton* radio = nullptr; if (s) { for (row = 0; row < table->rowCount(); row++) if (radio = qobject_cast(table->cellWidget(row, 1))) if (s == radio) { HandleDeviceTableCheckChanged(ui.FinalRenderDeviceTable, row, 1); break; } } if (checked) SetMemory(); } /// /// The quality spinner was changed, recompute required memory. /// /// Ignored void FractoriumFinalRenderDialog::OnQualityChanged(double d) { SetMemory(); } /// /// The temporal samples spinner was changed, recompute required memory. /// /// Ignored void FractoriumFinalRenderDialog::OnTemporalSamplesChanged(int d) { SetMemory(); } /// /// The supersample spinner was changed, recompute required memory. /// /// Ignored void FractoriumFinalRenderDialog::OnSupersampleChanged(int d) { SetMemory(); } /// /// The supersample spinner was changed, recompute required memory. /// /// Ignored void FractoriumFinalRenderDialog::OnStripsChanged(int d) { SetMemory(); } /// /// Show the save folder dialog. /// Called when the ... file button is clicked. /// /// Ignored void FractoriumFinalRenderDialog::OnFileButtonClicked(bool checked) { const auto 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(); } } /// /// Show the folder where the last rendered image was saved to. /// /// Ignored void FractoriumFinalRenderDialog::OnShowFolderButtonClicked(bool checked) { const auto s = m_Settings->SaveFolder(); if (Exists(s)) QDesktopServices::openUrl(QUrl::fromLocalFile(s)); else QDesktopServices::openUrl(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation)[0]); } /// /// Change the extension of the output image, which also may change the /// number of channels used in the final output buffer. /// /// Ignored void FractoriumFinalRenderDialog::OnExtIndexChanged(int d) { if (SetMemory()) Path(m_Controller->ComposePath(m_Controller->Name())); } /// /// Change the prefix prepended to the output file name. /// /// Ignored void FractoriumFinalRenderDialog::OnPrefixChanged(const QString& s) { Path(m_Controller->ComposePath(m_Controller->Name())); } /// /// Change the suffix appended to the output file name. /// /// Ignored void FractoriumFinalRenderDialog::OnSuffixChanged(const QString& s) { Path(m_Controller->ComposePath(m_Controller->Name())); } /// /// Increase the quality of the last render and start rendering again. /// Note this is only when rendering a single image with no strips. /// void FractoriumFinalRenderDialog::OnQualityBumpClicked() { const auto act = qobject_cast(sender()); const auto tbtn = qobject_cast(sender()); if (tbtn) { if (m_Controller.get()) { double d = tbtn->property("tag").toDouble(); m_QualitySpin->SetValueStealth(std::ceil(Quality() + (Quality() * d))); tbtn->setEnabled(false); ui.FinalRenderSaveAgainAsButton->setEnabled(false); m_Controller->BumpQualityRender(d); } } else if (act) { ui.FinalRenderBumpQualityStartButton->setText(act->text()); ui.FinalRenderBumpQualityStartButton->setProperty("tag", act->property("tag")); } } /// /// Save the last rendered image output again to a different file format. /// Note this is only when rendering a single image with no strips. /// void FractoriumFinalRenderDialog::OnSaveAgainAsClicked() { const auto act = qobject_cast(sender()); const auto tbtn = qobject_cast(sender()); if (tbtn) { if (m_Controller.get()) { const auto s = tbtn->property("tag").toString(); m_Tbcw->m_Combo->blockSignals(true); m_Tbcw->m_Combo->setCurrentText(s); m_Tbcw->m_Combo->blockSignals(false); const auto filename = m_Controller->SaveCurrentAgain(); ui.FinalRenderTextOutput->append("\nSaved again as " + filename); } } else if (act) { ui.FinalRenderSaveAgainAsButton->setText("Save Again as " + act->text()); ui.FinalRenderSaveAgainAsButton->setProperty("tag", act->text()); } } /// /// Start the render process. /// /// Ignored void FractoriumFinalRenderDialog::OnRenderClicked(bool checked) { if (CreateControllerFromGUI(true)) { Pause(false); ui.FinalRenderSaveAgainAsButton->setEnabled(false); ui.FinalRenderBumpQualityStartButton->setEnabled(false); m_Controller->Render(); } } /// /// Pause or resume the render process. /// /// Ignored void FractoriumFinalRenderDialog::OnPauseClicked(bool checked) { if (m_Controller.get()) if (m_Controller->Running()) Pause(!m_Controller->Paused()); } /// /// Cancel the render. /// /// Ignored void FractoriumFinalRenderDialog::OnCancelRenderClicked(bool checked) { if (m_Controller.get()) { Pause(false); m_Controller->CancelRender(); } } /// /// Pause or resume the render process. /// /// True to pause, else resume. 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); } /// /// Uses the options and the ember to populate widgets. /// Called when the dialog is initially shown. /// /// The event void FractoriumFinalRenderDialog::showEvent(QShowEvent* e) { if (m_Controller.get())//On Linux, this event will be called when the main window minimized/maximized, so filter it out if the window and controller have already been created. return; QString firstfile; if (CreateControllerFromGUI(true))//Create controller if it does not exist, or if it does and the renderer is not running. { const auto index = static_cast(m_Fractorium->m_Controller->Index()) + 1; #ifdef DO_DOUBLE Ember ed; EmberFile efi; m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember& 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 ed; EmberFile efi; m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember& 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(static_cast(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); } /// /// Close the dialog without running, or if running, cancel and exit. /// Settings will not be saved. /// Control will be returned to Fractorium::OnActiOn(). /// void FractoriumFinalRenderDialog::reject() { if (m_Controller.get()) { m_Controller->CancelRender(); m_Controller->DeleteRenderer(); } QDialog::reject(); } /// /// Create the controller from the options and optionally its underlying renderer. /// /// True if successful, else false. bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer) { const auto 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 ed; Ember orig; EmberFile efd; #else Ember ed; Ember orig; EmberFile 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& ember) { });//Convert float to double or save double verbatim; #else m_Controller->CopyEmberFile(efd, false, [&](Ember& 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(new FinalRenderEmberController(this)); else #endif m_Controller = unique_ptr(new FinalRenderEmberController(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; } /// /// 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. /// /// True if devices and a controller is present, else false. bool FractoriumFinalRenderDialog::SetMemory() { if (isVisible() && CreateControllerFromGUI(true)) { const auto p = m_Controller->SyncAndComputeMemory(); ui.FinalRenderParamsTable->item(m_MemoryCellIndex, 1)->setText(ToString(get<1>(p))); ui.FinalRenderParamsTable->item(m_ItersCellIndex, 1)->setText(ToString(get<2>(p))); if (OpenCL()) ui.FinalRenderTextOutput->setText(m_Controller->CheckMemory(p)); else ui.FinalRenderTextOutput->clear(); ui.FinalRenderSaveAgainAsButton->setEnabled(false); ui.FinalRenderBumpQualityStartButton->setEnabled(false); return true; } return false; }