diff --git a/Builds/MSVC/VS2013/Fractorium.vcxproj b/Builds/MSVC/VS2013/Fractorium.vcxproj index 5a78d0e..d23e04d 100644 --- a/Builds/MSVC/VS2013/Fractorium.vcxproj +++ b/Builds/MSVC/VS2013/Fractorium.vcxproj @@ -824,6 +824,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" + diff --git a/Builds/MSVC/VS2013/Fractorium.vcxproj.filters b/Builds/MSVC/VS2013/Fractorium.vcxproj.filters index bb3dac8..7729f24 100644 --- a/Builds/MSVC/VS2013/Fractorium.vcxproj.filters +++ b/Builds/MSVC/VS2013/Fractorium.vcxproj.filters @@ -297,6 +297,9 @@ Header Files + + Widgets + diff --git a/Builds/QtCreator/Fractorium/Fractorium.pro b/Builds/QtCreator/Fractorium/Fractorium.pro index 3e0a3be..486cae4 100644 --- a/Builds/QtCreator/Fractorium/Fractorium.pro +++ b/Builds/QtCreator/Fractorium/Fractorium.pro @@ -74,7 +74,8 @@ HEADERS += \ ../../../Source/EmberCommon/JpegUtils.h \ ../../../Source/EmberCommon/EmberCommonPch.h \ ../../../Source/Fractorium/FractoriumCommon.h \ - ../../../Source/Fractorium/DoubleSpinBoxTableItemDelegate.h + ../../../Source/Fractorium/DoubleSpinBoxTableItemDelegate.h \ + ../../../Source/Fractorium/PaletteTableWidgetItem.h FORMS += \ ../../../Source/Fractorium/AboutDialog.ui \ diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 7b2bf71..c93bd77 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -976,8 +976,8 @@ public: //were directly written to, must manually call them here. CacheXforms(); - //Need to merge chaos. Original does chaos all the time, but really only need to do it if at least one xform in at least one ember uses it, else skip. - //Omit final xform from chaos processing. + //Need to merge xaos. Original does xaos all the time, but really only need to do it if at least one xform in at least one ember uses it, else skip. + //Omit final xform from xaos processing. if (Interpolater::AnyXaosPresent(embers, size)) { for (size_t i = 0; i < XformCount(); i++) diff --git a/Source/Ember/Renderer.cpp b/Source/Ember/Renderer.cpp index 938202b..cb6c723 100644 --- a/Source/Ember/Renderer.cpp +++ b/Source/Ember/Renderer.cpp @@ -1216,7 +1216,7 @@ EmberStats Renderer::Iterate(size_t iterCount, size_t temporalSample { #endif #ifdef WIN32 - //SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + SetThreadPriority(GetCurrentThread(), m_Priority); #endif //Timing t; IterParams params; diff --git a/Source/Ember/RendererBase.cpp b/Source/Ember/RendererBase.cpp index b84ab74..e13da47 100644 --- a/Source/Ember/RendererBase.cpp +++ b/Source/Ember/RendererBase.cpp @@ -28,6 +28,7 @@ RendererBase::RendererBase() m_LastIter = 0; m_LastIterPercent = 0; m_InteractiveFilter = FILTER_LOG; + m_Priority = eThreadPriority::NORMAL; m_ProcessState = NONE; m_ProcessAction = FULL_RENDER; m_InRender = false; @@ -546,6 +547,15 @@ void RendererBase::BytesPerChannel(size_t bytesPerChannel) /// The number of channels per pixel in the output image size_t RendererBase::NumChannels() const { return m_NumChannels; } + +/// +/// Get/set the priority used for the CPU rendering threads. +/// This does not affect OpenCL rendering. +/// +/// The priority to use for the CPU rendering threads +eThreadPriority RendererBase::Priority() const { return m_Priority; } +void RendererBase::Priority(eThreadPriority priority) { m_Priority = priority; } + /// /// Get the type of filter to use for preview renders during interactive rendering. /// Using basic log scaling is quicker, but doesn't provide any bluring. diff --git a/Source/Ember/RendererBase.h b/Source/Ember/RendererBase.h index b62e555..670cb32 100644 --- a/Source/Ember/RendererBase.h +++ b/Source/Ember/RendererBase.h @@ -157,6 +157,8 @@ public: size_t BytesPerChannel() const; void BytesPerChannel(size_t bytesPerChannel); size_t NumChannels() const; + eThreadPriority Priority() const; + void Priority(eThreadPriority priority); eInteractiveFilter InteractiveFilter() const; void InteractiveFilter(eInteractiveFilter filter); @@ -216,6 +218,7 @@ protected: size_t m_LastTemporalSample; size_t m_LastIter; double m_LastIterPercent; + eThreadPriority m_Priority; eProcessAction m_ProcessAction; eProcessState m_ProcessState; eInteractiveFilter m_InteractiveFilter; diff --git a/Source/Ember/Utils.h b/Source/Ember/Utils.h index a57104b..5af5f82 100644 --- a/Source/Ember/Utils.h +++ b/Source/Ember/Utils.h @@ -8,6 +8,26 @@ /// namespace EmberNs { +#ifndef _WIN32 + #define THREAD_PRIORITY_LOWEST 1 + #define THREAD_PRIORITY_BELOW_NORMAL 25 + #define THREAD_PRIORITY_NORMAL 50 + #define THREAD_PRIORITY_ABOVE_NORMAL 75 + #define THREAD_PRIORITY_HIGHEST 99 +#endif + +/// +/// Enum to encapsulate and add type safety to the thread priority defines. +/// +enum eThreadPriority +{ + LOWEST = THREAD_PRIORITY_LOWEST,//-2 + BELOW_NORMAL = THREAD_PRIORITY_BELOW_NORMAL,//-1 + NORMAL = THREAD_PRIORITY_NORMAL,//0 + ABOVE_NORMAL = THREAD_PRIORITY_ABOVE_NORMAL,//1 + HIGHEST = THREAD_PRIORITY_HIGHEST//2 +}; + /// /// Thin wrapper around std::find_if() to relieve the caller of having to /// pass the implicitly obvious .begin() and .end(), and then compare the results to .end(). diff --git a/Source/EmberAnimate/EmberAnimate.cpp b/Source/EmberAnimate/EmberAnimate.cpp index 83d49a1..74ac525 100644 --- a/Source/EmberAnimate/EmberAnimate.cpp +++ b/Source/EmberAnimate/EmberAnimate.cpp @@ -258,6 +258,7 @@ bool EmberAnimate(EmberOptions& opt) renderer->Transparency(opt.Transparency()); renderer->NumChannels(channels); renderer->BytesPerChannel(opt.BitsPerChannel() / 8); + renderer->Priority((eThreadPriority)Clamp((uint)eThreadPriority::LOWEST, (uint)eThreadPriority::HIGHEST, opt.Priority())); renderer->Callback(opt.DoProgress() ? progress.get() : nullptr); std::function saveFunc = [&](uint threadVecIndex) diff --git a/Source/EmberCommon/EmberCommon.h b/Source/EmberCommon/EmberCommon.h index 3670e2f..69926eb 100644 --- a/Source/EmberCommon/EmberCommon.h +++ b/Source/EmberCommon/EmberCommon.h @@ -43,7 +43,7 @@ public: cout << "\r" << string(m_S.length() * 2, ' ');//Clear what was previously here, * 2 just to be safe because the end parts of previous strings might be longer. m_SS.str("");//Begin new output. - m_SS << "\rStage = " << (stage ? "filtering" : "chaos"); + m_SS << "\rStage = " << (stage ? "filtering" : "iterating"); m_SS << ", progress = " << int(fraction) << "%"; m_SS << ", eta = " << t.Format(etaMs); m_S = m_SS.str(); diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index 39e2b49..0b83f1d 100644 --- a/Source/EmberCommon/EmberOptions.h +++ b/Source/EmberCommon/EmberOptions.h @@ -80,6 +80,7 @@ enum eOptionIDs OPT_REPEAT, OPT_TRIES, OPT_MAX_XFORMS, + OPT_PRIORITY, OPT_SS,//Float value args. OPT_QS, @@ -341,18 +342,19 @@ public: "\t\t\t\t\t33: Histogram: float, Accumulator: float.\n"//This differs from the original which used an int hist for bits 33. "\t\t\t\t\t64: Histogram: double, Accumulator: double.\n")); - INITUINTOPTION(PrintEditDepth, Eou(OPT_USE_ALL, OPT_PRINT_EDIT_DEPTH, _T("--print_edit_depth"), 0, SO_REQ_SEP, "\t--print_edit_depth= Depth to truncate tag structure when converting a flame to xml. 0 prints all tags [default: 0].\n")); - INITUINTOPTION(JpegQuality, Eou(OPT_RENDER_ANIM, OPT_JPEG, _T("--jpeg"), 95, SO_REQ_SEP, "\t--jpeg= Jpeg quality 0-100 for compression [default: 95].\n")); - INITUINTOPTION(FirstFrame, Eou(OPT_USE_ANIMATE, OPT_BEGIN, _T("--begin"), UINT_MAX, SO_REQ_SEP, "\t--begin= Time of first frame to render [default: first time specified in file].\n")); - INITUINTOPTION(LastFrame, Eou(OPT_USE_ANIMATE, OPT_END, _T("--end"), UINT_MAX, SO_REQ_SEP, "\t--end= Time of last frame to render [default: last time specified in the input file].\n")); - INITUINTOPTION(Time, Eou(OPT_ANIM_GENOME, OPT_TIME, _T("--time"), 0, SO_REQ_SEP, "\t--time= Time of first and last frame (ie do one frame).\n")); - INITUINTOPTION(Frame, Eou(OPT_ANIM_GENOME, OPT_FRAME, _T("--frame"), 0, SO_REQ_SEP, "\t--frame= Synonym for \"time\".\n")); - INITUINTOPTION(Dtime, Eou(OPT_USE_ANIMATE, OPT_DTIME, _T("--dtime"), 1, SO_REQ_SEP, "\t--dtime= Time between frames [default: 1].\n")); - INITUINTOPTION(Frames, Eou(OPT_USE_GENOME, OPT_NFRAMES, _T("--nframes"), 20, SO_REQ_SEP, "\t--nframes= Number of frames for each stage of the animation [default: 20].\n")); - INITUINTOPTION(Loops, Eou(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops= Number of times to rotate each control point in sequence [default: 1].\n")); - INITUINTOPTION(Repeat, Eou(OPT_USE_GENOME, OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat= Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n")); - INITUINTOPTION(Tries, Eou(OPT_USE_GENOME, OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries= Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n")); - INITUINTOPTION(MaxXforms, Eou(OPT_USE_GENOME, OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms= The maximum number of xforms allowed in the final output.\n")); + INITUINTOPTION(PrintEditDepth, Eou(OPT_USE_ALL, OPT_PRINT_EDIT_DEPTH, _T("--print_edit_depth"), 0, SO_REQ_SEP, "\t--print_edit_depth= Depth to truncate tag structure when converting a flame to xml. 0 prints all tags [default: 0].\n")); + INITUINTOPTION(JpegQuality, Eou(OPT_RENDER_ANIM, OPT_JPEG, _T("--jpeg"), 95, SO_REQ_SEP, "\t--jpeg= Jpeg quality 0-100 for compression [default: 95].\n")); + INITUINTOPTION(FirstFrame, Eou(OPT_USE_ANIMATE, OPT_BEGIN, _T("--begin"), UINT_MAX, SO_REQ_SEP, "\t--begin= Time of first frame to render [default: first time specified in file].\n")); + INITUINTOPTION(LastFrame, Eou(OPT_USE_ANIMATE, OPT_END, _T("--end"), UINT_MAX, SO_REQ_SEP, "\t--end= Time of last frame to render [default: last time specified in the input file].\n")); + INITUINTOPTION(Time, Eou(OPT_ANIM_GENOME, OPT_TIME, _T("--time"), 0, SO_REQ_SEP, "\t--time= Time of first and last frame (ie do one frame).\n")); + INITUINTOPTION(Frame, Eou(OPT_ANIM_GENOME, OPT_FRAME, _T("--frame"), 0, SO_REQ_SEP, "\t--frame= Synonym for \"time\".\n")); + INITUINTOPTION(Dtime, Eou(OPT_USE_ANIMATE, OPT_DTIME, _T("--dtime"), 1, SO_REQ_SEP, "\t--dtime= Time between frames [default: 1].\n")); + INITUINTOPTION(Frames, Eou(OPT_USE_GENOME, OPT_NFRAMES, _T("--nframes"), 20, SO_REQ_SEP, "\t--nframes= Number of frames for each stage of the animation [default: 20].\n")); + INITUINTOPTION(Loops, Eou(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops= Number of times to rotate each control point in sequence [default: 1].\n")); + INITUINTOPTION(Repeat, Eou(OPT_USE_GENOME, OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat= Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n")); + INITUINTOPTION(Tries, Eou(OPT_USE_GENOME, OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries= Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n")); + INITUINTOPTION(MaxXforms, Eou(OPT_USE_GENOME, OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms= The maximum number of xforms allowed in the final output.\n")); + INITUINTOPTION(Priority, Eou(OPT_RENDER_ANIM, OPT_PRIORITY, _T("--priority"), eThreadPriority::NORMAL, SO_REQ_SEP, "\t--priority= The priority of the CPU rendering threads from -2 - 2. This does not apply to OpenCL rendering.\n")); //Double. INITDOUBLEOPTION(SizeScale, Eod(OPT_RENDER_ANIM, OPT_SS, _T("--ss"), 1, SO_REQ_SEP, "\t--ss= Size scale. All dimensions are scaled by this amount [default: 1.0].\n")); @@ -482,6 +484,7 @@ public: PARSEUINTOPTION(OPT_REPEAT, Repeat); PARSEUINTOPTION(OPT_TRIES, Tries); PARSEUINTOPTION(OPT_MAX_XFORMS, MaxXforms); + PARSEUINTOPTION(OPT_PRIORITY, Priority); PARSEDOUBLEOPTION(OPT_SS, SizeScale);//Float args. PARSEDOUBLEOPTION(OPT_QS, QualityScale); @@ -697,6 +700,7 @@ public: EmberOptionEntry Repeat; EmberOptionEntry Tries; EmberOptionEntry MaxXforms; + EmberOptionEntry Priority; EmberOptionEntry SizeScale;//Value double. EmberOptionEntry QualityScale; diff --git a/Source/EmberRender/EmberRender.cpp b/Source/EmberRender/EmberRender.cpp index 9e90fb9..1a686da 100644 --- a/Source/EmberRender/EmberRender.cpp +++ b/Source/EmberRender/EmberRender.cpp @@ -154,6 +154,7 @@ bool EmberRender(EmberOptions& opt) renderer->Transparency(opt.Transparency()); renderer->NumChannels(channels); renderer->BytesPerChannel(opt.BitsPerChannel() / 8); + renderer->Priority((eThreadPriority)Clamp((uint)eThreadPriority::LOWEST, (uint)eThreadPriority::HIGHEST, opt.Priority())); renderer->Callback(opt.DoProgress() ? progress.get() : nullptr); for (i = 0; i < embers.size(); i++) diff --git a/Source/Fractorium/FinalRenderDialog.cpp b/Source/Fractorium/FinalRenderDialog.cpp index 7c20b60..fe4eaaa 100644 --- a/Source/Fractorium/FinalRenderDialog.cpp +++ b/Source/Fractorium/FinalRenderDialog.cpp @@ -109,15 +109,16 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set 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.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect()); - ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount()); + 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.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect()); + ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount()); + ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(m_Settings->FinalThreadPriority() + 2); m_QualitySpin->setValue(m_Settings->FinalQuality()); m_TemporalSamplesSpin->setValue(m_Settings->FinalTemporalSamples()); @@ -141,7 +142,10 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90)))); setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry())); - + ui.FinalRenderThreadHorizontalLayout->setAlignment(Qt::AlignLeft); + ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadCountSpin, Qt::AlignLeft); + ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityLabel, Qt::AlignLeft); + ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityComboBox, Qt::AlignLeft); QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox); //Update these with new controls. @@ -155,6 +159,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set w = SetTabOrder(this, w, ui.FinalRenderPlatformCombo); w = SetTabOrder(this, w, ui.FinalRenderDeviceCombo); w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin); + w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox); w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox); w = SetTabOrder(this, w, m_WidthScaleSpin); w = SetTabOrder(this, w, m_HeightScaleSpin); @@ -201,6 +206,7 @@ uint FractoriumFinalRenderDialog::Current() { return ui.FinalRenderCurrentSpin-> uint FractoriumFinalRenderDialog::PlatformIndex() { return ui.FinalRenderPlatformCombo->currentIndex(); } uint FractoriumFinalRenderDialog::DeviceIndex() { return ui.FinalRenderDeviceCombo->currentIndex(); } uint FractoriumFinalRenderDialog::ThreadCount() { return ui.FinalRenderThreadCountSpin->value(); } +uint FractoriumFinalRenderDialog::ThreadPriority() { return ui.FinalRenderThreadPriorityComboBox->currentIndex() - 2; } double FractoriumFinalRenderDialog::WidthScale() { return m_WidthScaleSpin->value(); } double FractoriumFinalRenderDialog::HeightScale() { return m_HeightScaleSpin->value(); } double FractoriumFinalRenderDialog::Quality() { return m_QualitySpin->value(); } @@ -234,6 +240,7 @@ FinalRenderGuiState FractoriumFinalRenderDialog::State() state.m_PlatformIndex = PlatformIndex(); state.m_DeviceIndex = DeviceIndex(); state.m_ThreadCount = ThreadCount(); + state.m_ThreadPriority = ThreadPriority(); state.m_WidthScale = WidthScale(); state.m_HeightScale = HeightScale(); state.m_Quality = Quality(); @@ -327,6 +334,7 @@ void FractoriumFinalRenderDialog::OnOpenCLCheckBoxStateChanged(int state) ui.FinalRenderPlatformCombo->setEnabled(checked); ui.FinalRenderDeviceCombo->setEnabled(checked); ui.FinalRenderThreadCountSpin->setEnabled(!checked); + ui.FinalRenderThreadPriorityComboBox->setEnabled(!checked); SetMemory(); } diff --git a/Source/Fractorium/FinalRenderDialog.h b/Source/Fractorium/FinalRenderDialog.h index 794d911..7eaab89 100644 --- a/Source/Fractorium/FinalRenderDialog.h +++ b/Source/Fractorium/FinalRenderDialog.h @@ -68,6 +68,7 @@ public: uint PlatformIndex(); uint DeviceIndex(); uint ThreadCount(); + uint ThreadPriority(); double WidthScale(); double HeightScale(); double Quality(); diff --git a/Source/Fractorium/FinalRenderDialog.ui b/Source/Fractorium/FinalRenderDialog.ui index 0f21b09..058657e 100644 --- a/Source/Fractorium/FinalRenderDialog.ui +++ b/Source/Fractorium/FinalRenderDialog.ui @@ -284,26 +284,84 @@ - - - - 0 - 0 - - - - <html><head/><body><p>The number of threads to use with CPU rendering.</p><p>Decrease for a more responsive system during rendering, increase for better performance.</p></body></html> - - - Threads - - - 1 - - - 64 - - + + + + + + 0 + 0 + + + + <html><head/><body><p>The number of threads to use with CPU rendering.</p><p>Decrease for a more responsive system during rendering, increase for better performance.</p></body></html> + + + Threads + + + 1 + + + 64 + + + + + + + + 0 + 0 + + + + Priority: + + + + + + + + 0 + 0 + + + + 2 + + + 5 + + + + Lowest + + + + + Below Normal + + + + + Normal + + + + + Above Normal + + + + + Highest + + + + + @@ -1063,7 +1121,6 @@ FinalRenderOpenCLCheckBox FinalRenderPlatformCombo FinalRenderDeviceCombo - FinalRenderThreadCountSpin FinalRenderParamsTable FinalRenderTextOutput StartRenderButton diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp index f94fcc3..163d619 100644 --- a/Source/Fractorium/FinalRenderEmberController.cpp +++ b/Source/Fractorium/FinalRenderEmberController.cpp @@ -170,6 +170,7 @@ FinalRenderEmberController::FinalRenderEmberController(FractoriumFinalRenderD m_Renderer->EarlyClip(m_GuiState.m_EarlyClip); m_Renderer->YAxisUp(m_GuiState.m_YAxisUp); m_Renderer->ThreadCount(m_GuiState.m_ThreadCount); + m_Renderer->Priority((eThreadPriority)m_GuiState.m_ThreadPriority); m_Renderer->Transparency(m_GuiState.m_Transparency); m_Renderer->m_ProgressParameter = reinterpret_cast(¤tStripForProgress); @@ -488,8 +489,9 @@ int FinalRenderEmberController::ProgressFunc(Ember& ember, void* foo, doub else if (stage == 2) QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, intFract)); + QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString(m_FinishedImageCount) + " / " + ToString(m_ImageCount) + " Eta: " + QString::fromStdString(m_RenderTimer.Format(etaMs)))); QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTextOutput, "update", Qt::QueuedConnection); - //QApplication::processEvents(); + return m_Run ? 1 : 0; } @@ -670,6 +672,7 @@ void FinalRenderEmberController::RenderComplete(Ember& ember) m_Settings->FinalScale(m_GuiState.m_Scale); m_Settings->FinalExt(m_GuiState.m_Ext); m_Settings->FinalThreadCount(m_GuiState.m_ThreadCount); + m_Settings->FinalThreadPriority(m_GuiState.m_ThreadPriority); m_Settings->FinalQuality(m_GuiState.m_Quality); m_Settings->FinalTemporalSamples(m_GuiState.m_TemporalSamples); m_Settings->FinalSupersample(m_GuiState.m_Supersample); diff --git a/Source/Fractorium/FinalRenderEmberController.h b/Source/Fractorium/FinalRenderEmberController.h index 6d989b4..440236a 100644 --- a/Source/Fractorium/FinalRenderEmberController.h +++ b/Source/Fractorium/FinalRenderEmberController.h @@ -38,6 +38,7 @@ struct FinalRenderGuiState uint m_PlatformIndex; uint m_DeviceIndex; uint m_ThreadCount; + uint m_ThreadPriority; double m_WidthScale; double m_HeightScale; double m_Quality; diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index 9cf142a..ad10e7f 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -26,6 +26,7 @@ Fractorium::Fractorium(QWidget* p) m_FontSize = 9; m_VarSortMode = 1;//Sort by weight by default. + m_PaletteSortMode = 0;//Sort by palette ascending by default. m_ColorDialog = new QColorDialog(this); m_Settings = new FractoriumSettings(this); diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index 93465d9..12dc284 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -268,6 +268,9 @@ public slots: void OnPaletteCellDoubleClicked(int row, int col); void OnPaletteRandomSelectButtonClicked(bool checked); void OnPaletteRandomAdjustButtonClicked(bool checked); + void OnPaletteFilterLineEditTextChanged(const QString& text); + void OnPaletteFilterClearButtonClicked(bool checked); + void OnPaletteHeaderSectionClicked(int col); //Rendering/progress. void StartRenderTimer(); @@ -458,6 +461,7 @@ private: int m_FontSize; int m_VarSortMode; + int m_PaletteSortMode; int m_PreviousPaletteRow; OpenCLWrapper m_Wrapper; unique_ptr m_Controller; diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui index 84d12aa..3a06e6a 100644 --- a/Source/Fractorium/Fractorium.ui +++ b/Source/Fractorium/Fractorium.ui @@ -4866,7 +4866,7 @@ SpinBox List of available palette files and their palettes - + QLayout::SetDefaultConstraint @@ -5100,7 +5100,7 @@ SpinBox - + Qt::StrongFocus @@ -5237,6 +5237,50 @@ SpinBox + + + + 5 + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + + + + + + + 30 + 0 + + + + + 30 + 16777215 + + + + X + + + false + + + + + diff --git a/Source/Fractorium/FractoriumPalette.cpp b/Source/Fractorium/FractoriumPalette.cpp index 5e9f43a..00502c8 100644 --- a/Source/Fractorium/FractoriumPalette.cpp +++ b/Source/Fractorium/FractoriumPalette.cpp @@ -1,5 +1,6 @@ #include "FractoriumPch.h" #include "Fractorium.h" +#include "PaletteTableWidgetItem.h" #define PALETTE_CELL_HEIGHT 16 @@ -20,7 +21,7 @@ void Fractorium::InitPaletteUI() //Palette adjustment table. QTableWidget* table = ui.PaletteAdjustTable; table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//Split width over all columns evenly. - + SetupSpinner(table, this, row, 1, m_PaletteHueSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); SetupSpinner(table, this, row, 1, m_PaletteSaturationSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); SetupSpinner(table, this, row, 1, m_PaletteBrightnessSpin, spinHeight, -255, 255, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); @@ -41,8 +42,11 @@ void Fractorium::InitPaletteUI() QTableWidgetItem* previewPaletteItem = new QTableWidgetItem(); palettePreviewTable->setItem(0, 1, previewPaletteItem); + connect(ui.PaletteFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPaletteFilterLineEditTextChanged(const QString&))); + connect(ui.PaletteFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteFilterClearButtonClicked(bool))); paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side. - paletteTable->horizontalHeader()->setSectionsClickable(false); + paletteTable->horizontalHeader()->setSectionsClickable(true); + connect(paletteTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(OnPaletteHeaderSectionClicked(int)), Qt::QueuedConnection); } /// @@ -109,18 +113,20 @@ bool FractoriumEmberController::FillPaletteTable(const string& s) //Palette list table. for (size_t i = 0; i < paletteSize; i++) { - Palette* p = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, i); - vector v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT); - QTableWidgetItem* nameCol = new QTableWidgetItem(p->m_Name.c_str()); + if (auto p = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, i)) + { + auto v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT); + auto nameCol = new QTableWidgetItem(p->m_Name.c_str()); - nameCol->setToolTip(p->m_Name.c_str()); - paletteTable->setItem(i, 0, nameCol); + nameCol->setToolTip(p->m_Name.c_str()); + paletteTable->setItem(i, 0, nameCol); - QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888); - QTableWidgetItem* paletteItem = new QTableWidgetItem(); + QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888); + auto paletteItem = new PaletteTableWidgetItem(p); - paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image)); - paletteTable->setItem(i, 1, paletteItem); + paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image)); + paletteTable->setItem(i, 1, paletteItem); + } } paletteTable->blockSignals(false); @@ -138,7 +144,11 @@ bool FractoriumEmberController::FillPaletteTable(const string& s) return false; } -void Fractorium::OnPaletteFilenameComboChanged(const QString& text) { m_Controller->FillPaletteTable(text.toStdString()); } +void Fractorium::OnPaletteFilenameComboChanged(const QString& text) +{ + m_Controller->FillPaletteTable(text.toStdString()); + ui.PaletteListTable->sortItems(0, m_PaletteSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder); +} /// /// Apply adjustments to the current ember's palette. @@ -222,13 +232,11 @@ void Fractorium::OnPaletteAdjust(int d) { m_Controller->PaletteAdjust(); } /// Resets the rendering process. /// /// The table row clicked -/// The table col clicked +/// The table column clicked template void FractoriumEmberController::PaletteCellClicked(int row, int col) { - Palette* palette = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, row); - - if (palette) + if (auto palette = m_PaletteList.GetPalette(m_CurrentPaletteFilePath, row)) { m_TempPalette = *palette;//Deep copy. ApplyPaletteToEmber();//Copy temp palette to ember palette and apply adjustments. @@ -236,12 +244,25 @@ void FractoriumEmberController::PaletteCellClicked(int row, int col) } } +/// +/// Map the palette in the clicked row index to the index +/// in the palette list, then pass that index to PaletteCellClicked(). +/// This resolves the case where the sort order of the palette table +/// is different than the internal order of the palette list. +/// +/// The table row clicked +/// The table column clicked, ignored void Fractorium::OnPaletteCellClicked(int row, int col) { - if (m_PreviousPaletteRow != row) + if (auto item = dynamic_cast(ui.PaletteListTable->item(row, 1))) { - m_Controller->PaletteCellClicked(row, col); - m_PreviousPaletteRow = row;//Save for comparison on next click. + auto index = item->Index(); + + if (m_PreviousPaletteRow != index) + { + m_Controller->PaletteCellClicked(index, col); + m_PreviousPaletteRow = index;//Save for comparison on next click. + } } } @@ -252,7 +273,7 @@ void Fractorium::OnPaletteCellClicked(int row, int col) /// Resets the rendering process. /// /// The table row clicked -/// The table col clicked +/// The table column clicked void Fractorium::OnPaletteCellDoubleClicked(int row, int col) { ResetPaletteControls(); @@ -309,6 +330,54 @@ void Fractorium::OnPaletteRandomAdjustButtonClicked(bool checked) OnPaletteAdjust(0); } +/// +/// Apply the text in the palette filter text box to only show palettes whose names +/// contain the substring. +/// Called when the user types in the palette filter text box. +/// +/// The text to filter on +void Fractorium::OnPaletteFilterLineEditTextChanged(const QString& text) +{ + auto table = ui.PaletteListTable; + + table->setUpdatesEnabled(false); + + for (uint i = 0; i < uint(table->rowCount()); i++) + { + if (auto item = table->item(i, 0)) + { + if (!item->text().contains(text, Qt::CaseInsensitive)) + table->hideRow(i); + else + table->showRow(i); + } + } + + ui.PaletteListTable->sortItems(0, m_PaletteSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);//Must re-sort every time the filter changes. + table->setUpdatesEnabled(true); +} + +/// +/// Clear the palette name filter, which will display all palettes. +/// Called when clear palette filter button is clicked. +/// +/// Ignored +void Fractorium::OnPaletteFilterClearButtonClicked(bool checked) +{ + ui.PaletteFilterLineEdit->clear(); +} + +/// +/// Change the sorting to be either ascending or descending. +/// Called when user clicks the table headers. +/// +/// Column index of the header clicked, ignored. +void Fractorium::OnPaletteHeaderSectionClicked(int col) +{ + m_PaletteSortMode = !m_PaletteSortMode; + ui.PaletteListTable->sortItems(0, m_PaletteSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder); +} + /// /// Reset the palette controls. /// Usually in response to a palette cell double click. diff --git a/Source/Fractorium/FractoriumSettings.cpp b/Source/Fractorium/FractoriumSettings.cpp index c752b8e..a0a1845 100644 --- a/Source/Fractorium/FractoriumSettings.cpp +++ b/Source/Fractorium/FractoriumSettings.cpp @@ -44,6 +44,8 @@ void FractoriumSettings::EnsureDefaults() if (FinalThreadCount() == 0 || FinalThreadCount() > Timing::ProcessorCount()) FinalThreadCount(Timing::ProcessorCount()); + FinalThreadPriority(Clamp((uint)eThreadPriority::LOWEST, (uint)eThreadPriority::HIGHEST, FinalThreadPriority())); + if (CpuSubBatch() < 1) CpuSubBatch(1); @@ -184,6 +186,9 @@ void FractoriumSettings::FinalDeviceIndex(uint i) { setValue(FINALDEVICEINDE uint FractoriumSettings::FinalThreadCount() { return value(FINALTHREADCOUNT).toUInt(); } void FractoriumSettings::FinalThreadCount(uint i) { setValue(FINALTHREADCOUNT, i); } +uint FractoriumSettings::FinalThreadPriority() { return value(FINALTHREADPRIORITY).toUInt(); } +void FractoriumSettings::FinalThreadPriority(uint i) { setValue(FINALTHREADPRIORITY, i); } + uint FractoriumSettings::FinalQuality() { return value(FINALQUALITY).toUInt(); } void FractoriumSettings::FinalQuality(uint i) { setValue(FINALQUALITY, i); } diff --git a/Source/Fractorium/FractoriumSettings.h b/Source/Fractorium/FractoriumSettings.h index 05f57e9..1475029 100644 --- a/Source/Fractorium/FractoriumSettings.h +++ b/Source/Fractorium/FractoriumSettings.h @@ -34,6 +34,7 @@ #define FINALPLATFORMINDEX "finalrender/platformindex" #define FINALDEVICEINDEX "finalrender/deviceindex" #define FINALTHREADCOUNT "finalrender/threadcount" +#define FINALTHREADPRIORITY "finalrender/threadpriority" #define FINALQUALITY "finalrender/quality" #define FINALTEMPORALSAMPLES "finalrender/temporalsamples" #define FINALSUPERSAMPLE "finalrender/supersample" @@ -151,6 +152,9 @@ public: uint FinalThreadCount(); void FinalThreadCount(uint b); + uint FinalThreadPriority(); + void FinalThreadPriority(uint b); + uint FinalQuality(); void FinalQuality(uint i); diff --git a/Source/Fractorium/FractoriumXformsVariations.cpp b/Source/Fractorium/FractoriumXformsVariations.cpp index cae22a7..c36c270 100644 --- a/Source/Fractorium/FractoriumXformsVariations.cpp +++ b/Source/Fractorium/FractoriumXformsVariations.cpp @@ -12,7 +12,6 @@ void Fractorium::InitXformsVariationsUI() tree->header()->setSectionsClickable(true); connect(tree->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnTreeHeaderSectionClicked(int))); connect(ui.VariationsFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnVariationsFilterLineEditTextChanged(const QString&))); - connect(ui.VariationsFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnVariationsFilterLineEditTextChanged(const QString&))); connect(ui.VariationsFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnVariationsFilterClearButtonClicked(bool))); //Setting dimensions in the designer with a layout is futile, so must hard code here. @@ -268,7 +267,7 @@ void FractoriumEmberController::FillVariationTreeWithXform(Xform* xform) /// /// Change the sorting to be either by variation ID, or by weight. -/// If sorting by variation ID, repeated clicks will altername ascending or descending. +/// If sorting by variation ID, repeated clicks will alternate ascending or descending. /// Called when user clicks the tree headers. /// /// Column index of the header clicked. Sort by name if 0, sort by weight if 1. @@ -277,7 +276,7 @@ void Fractorium::OnTreeHeaderSectionClicked(int logicalIndex) m_VarSortMode = logicalIndex; ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder); - if (logicalIndex == 1) + if (m_VarSortMode == 1) ui.VariationsTree->scrollToTop(); } diff --git a/Source/Fractorium/PaletteTableWidgetItem.h b/Source/Fractorium/PaletteTableWidgetItem.h new file mode 100644 index 0000000..42a9af0 --- /dev/null +++ b/Source/Fractorium/PaletteTableWidgetItem.h @@ -0,0 +1,38 @@ +#pragma once + +#include "FractoriumPch.h" + +/// +/// PaletteTableWidgetItem class. +/// + +/// +/// A thin derivation of QTableWidgetItem which keeps a pointer to a palette object. +/// The lifetime of the palette object must be greater than or equal to +/// the lifetime of this object. +/// +class PaletteTableWidgetItemBase : public QTableWidgetItem +{ +public: + PaletteTableWidgetItemBase() + { + } + + virtual size_t Index() const { return 0; } +}; + +template +class PaletteTableWidgetItem : public PaletteTableWidgetItemBase +{ +public: + PaletteTableWidgetItem(Palette* palette) + : m_Palette(palette) + { + } + + virtual size_t Index() const override { return m_Palette->m_Index; } + Palette* GetPalette() const { return m_Palette; } + +private: + Palette* m_Palette; +}; \ No newline at end of file