mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-07-01 22:06:10 -04:00
0.4.1.8 Beta - Date pending testing.
--User changes Thread image writing in EmberAnimate and when doing animation sequence in final render dialog. Add total time output for verbose mode in EmberAnimate to match EmberRender. --Bug Fixes Fix incorrect iters ran/requested percentage in EmberAnimate to match EmberRender. Fix motion blur being disabled when doing animations in final render dialog. Allow for boolean command line options which default to true to be set to false. --Code Changes Minor changes to enable a Mac build. Double the memory required for the final output buffer in RendererBase::MemoryRequired() when threading image writing. Reuse same buffer for RgbaToRgb() in EmberRender and EmberAnimate. Only resize in RgbaToRgb() if the two vectors are not the same. Add a final output buffer ping-ponging mechanism to facilitate threaded writes in controllers.
This commit is contained in:
@ -58,7 +58,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><br/><span style=" font-size:12pt;">Fractorium 0.4.1.7 Beta</span></p><p align="center"><span style=" font-size:10pt;"><br/>A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><span style=" font-size:10pt;">Matt Feemster</span></p></body></html></string>
|
||||
<string><html><head/><body><p align="center"><br/><span style=" font-size:12pt;">Fractorium 0.4.1.8 Beta</span></p><p align="center"><span style=" font-size:10pt;"><br/>A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><span style=" font-size:10pt;">Matt Feemster</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
|
@ -152,7 +152,8 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
m_Run = true;
|
||||
m_TotalTimer.Tic();//Begin timing for progress of all operations.
|
||||
m_GuiState = m_FinalRenderDialog->State();//Cache render settings from the GUI before running.
|
||||
|
||||
m_FinalImageIndex = 0;
|
||||
|
||||
size_t i;
|
||||
bool doAll = m_GuiState.m_DoAll && m_EmberFile.Size() > 1;
|
||||
uint currentStripForProgress = 0;//Sort of a hack to get the strip value to the progress function.
|
||||
@ -216,6 +217,7 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
//even when using double precision, which most cards at the time of this writing already exceed.
|
||||
m_GuiState.m_Strips = 1;
|
||||
m_Renderer->SetEmber(m_EmberFile.m_Embers);//Copy all embers to the local storage inside the renderer.
|
||||
uint finalImageIndex = m_FinalImageIndex;
|
||||
|
||||
//Render each image, cancelling if m_Run ever gets set to false.
|
||||
for (i = 0; i < m_EmberFile.Size() && m_Run; i++)
|
||||
@ -224,35 +226,44 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
m_Renderer->Reset();//Have to manually set this since the ember is not set each time through.
|
||||
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
|
||||
|
||||
StripsRender<T>(m_Renderer.get(), m_EmberFile.m_Embers[i], m_FinalImage, i, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
[&](size_t strip) { currentStripForProgress = strip; },//Pre strip.
|
||||
[&](size_t strip) { m_Stats = m_Renderer->Stats(); },//Post strip.
|
||||
[&](size_t strip)//Error.
|
||||
//Can't use strips render here. Run() must be called directly for animation.
|
||||
if (m_Renderer->Run(m_FinalImage[finalImageIndex], i) != RENDER_OK)
|
||||
{
|
||||
Output("Renderering failed.\n");
|
||||
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRenderDialog->ui.FinalRenderTextOutput, false);//Internally calls invoke.
|
||||
},
|
||||
[&](Ember<T>& finalEmber) { RenderComplete(finalEmber); });//Final strip.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_WriteThread.joinable())
|
||||
m_WriteThread.join();
|
||||
|
||||
SetProgressComplete(100);
|
||||
m_Stats = m_Renderer->Stats();
|
||||
m_FinalImageIndex = finalImageIndex;//Will be used inside of RenderComplete(). Set here when no threads are running.
|
||||
//RenderComplete(m_EmberFile.m_Embers[i]);//Non-threaded version for testing.
|
||||
m_WriteThread = std::thread([&] { RenderComplete(m_EmberFile.m_Embers[i]); });
|
||||
}
|
||||
|
||||
finalImageIndex ^= 1;//Toggle the index.
|
||||
}
|
||||
|
||||
if (m_WriteThread.joinable())
|
||||
m_WriteThread.join();
|
||||
}
|
||||
else//Render all images, but not as an animation sequence (without temporal samples motion blur).
|
||||
{
|
||||
for (i = 0; i < m_EmberFile.Size() && m_Run; i++)
|
||||
{
|
||||
m_EmberFile.m_Embers[i].m_TemporalSamples = 1;//No temporal sampling.
|
||||
}
|
||||
|
||||
//Render each image, cancelling if m_Run ever gets set to false.
|
||||
for (i = 0; i < m_EmberFile.Size() && m_Run; i++)
|
||||
{
|
||||
Output("Image " + ToString(m_FinishedImageCount) + ":\n" + ComposePath(QString::fromStdString(m_EmberFile.m_Embers[i].m_Name)));
|
||||
m_EmberFile.m_Embers[i].m_TemporalSamples = 1;//No temporal sampling.
|
||||
m_Renderer->SetEmber(m_EmberFile.m_Embers[i]);
|
||||
m_Renderer->PrepFinalAccumVector(m_FinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
|
||||
m_Renderer->PrepFinalAccumVector(m_FinalImage[m_FinalImageIndex]);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
|
||||
m_Stats.Clear();
|
||||
Memset(m_FinalImage);
|
||||
Memset(m_FinalImage[m_FinalImageIndex]);
|
||||
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
|
||||
|
||||
StripsRender<T>(m_Renderer.get(), m_EmberFile.m_Embers[i], m_FinalImage, 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
StripsRender<T>(m_Renderer.get(), m_EmberFile.m_Embers[i], m_FinalImage[m_FinalImageIndex], 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
[&](size_t strip) { currentStripForProgress = strip; },//Pre strip.
|
||||
[&](size_t strip) { m_Stats += m_Renderer->Stats(); },//Post strip.
|
||||
[&](size_t strip)//Error.
|
||||
@ -270,13 +281,13 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
ResetProgress();
|
||||
m_Ember->m_TemporalSamples = 1;
|
||||
m_Renderer->SetEmber(*m_Ember);
|
||||
m_Renderer->PrepFinalAccumVector(m_FinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
|
||||
m_Renderer->PrepFinalAccumVector(m_FinalImage[m_FinalImageIndex]);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
|
||||
m_Stats.Clear();
|
||||
Memset(m_FinalImage);
|
||||
Memset(m_FinalImage[m_FinalImageIndex]);
|
||||
Output(ComposePath(QString::fromStdString(m_Ember->m_Name)));
|
||||
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
|
||||
|
||||
StripsRender<T>(m_Renderer.get(), *m_Ember, m_FinalImage, 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
StripsRender<T>(m_Renderer.get(), *m_Ember, m_FinalImage[m_FinalImageIndex], 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
|
||||
[&](size_t strip) { currentStripForProgress = strip; },//Pre strip.
|
||||
[&](size_t strip) { m_Stats += m_Renderer->Stats(); },//Post strip.
|
||||
[&](size_t strip)//Error.
|
||||
@ -287,6 +298,7 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
|
||||
[&](Ember<T>& finalEmber) { RenderComplete(finalEmber); });//Final strip.
|
||||
}
|
||||
|
||||
m_FinalImageIndex = 0;
|
||||
QString totalTimeString = "All renders completed in: " + QString::fromStdString(m_TotalTimer.Format(m_TotalTimer.Toc())) + ".";
|
||||
Output(totalTimeString);
|
||||
|
||||
@ -589,7 +601,7 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
|
||||
CancelPreviewRender();
|
||||
m_FinalPreviewRenderFunc();
|
||||
|
||||
p = m_Renderer->MemoryRequired(strips, true);
|
||||
p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence());
|
||||
iterCount = m_Renderer->TotalIterCount(strips);
|
||||
}
|
||||
|
||||
@ -677,11 +689,15 @@ void FinalRenderEmberController<T>::RenderComplete(Ember<T>& ember)
|
||||
}
|
||||
|
||||
m_FinishedImageCount++;
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderIterationProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 100));//Just to be safe.
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderFilteringProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 100));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 100));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int((float(m_FinishedImageCount) / float(m_ImageCount)) * 100)));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString(m_FinishedImageCount) + " / " + ToString(m_ImageCount)));
|
||||
|
||||
//In a thread if animating, so don't set to complete because it'll be out of sync with the rest of the progress bars.
|
||||
if (!m_GuiState.m_DoSequence)
|
||||
{
|
||||
SetProgressComplete(100);//Just to be safe.
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int((float(m_FinishedImageCount) / float(m_ImageCount)) * 100)));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString(m_FinishedImageCount) + " / " + ToString(m_ImageCount)));
|
||||
|
||||
status = "Pure render time: " + QString::fromStdString(renderTimeString);
|
||||
Output(status);
|
||||
@ -733,6 +749,19 @@ void FinalRenderEmberController<T>::SyncGuiToEmber(Ember<T>& ember, size_t width
|
||||
ember.m_Supersample = m_FinalRenderDialog->m_SupersampleSpin->value();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the iteration, density filter, and final accumulation progress bars to the same value.
|
||||
/// Usually 0 or 100.
|
||||
/// </summary>
|
||||
/// <param name="val">The value to set them to</param>
|
||||
template <typename T>
|
||||
void FinalRenderEmberController<T>::SetProgressComplete(int val)
|
||||
{
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderIterationProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, val));//Just to be safe.
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderFilteringProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, val));
|
||||
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, val));
|
||||
}
|
||||
|
||||
template class FinalRenderEmberController<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
|
@ -131,6 +131,7 @@ protected:
|
||||
void CancelPreviewRender();
|
||||
void RenderComplete(Ember<T>& ember);
|
||||
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0);
|
||||
void SetProgressComplete(int val);
|
||||
|
||||
Ember<T>* m_Ember;
|
||||
Ember<T> m_PreviewEmber;
|
||||
|
Binary file not shown.
@ -23,6 +23,7 @@ FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractor
|
||||
m_RenderType = CPU_RENDERER;
|
||||
m_OutputTexID = 0;
|
||||
m_SubBatchCount = 1;//Will be ovewritten by the options on first render.
|
||||
m_FinalImageIndex = 0;
|
||||
m_Fractorium = fractorium;
|
||||
m_RenderTimer = nullptr;
|
||||
m_RenderRestartTimer = nullptr;
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
|
||||
//Embers.
|
||||
virtual void SetEmber(const Ember<float>& ember, bool verbatim = false) { }
|
||||
virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }
|
||||
virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }//Uncomment default lambdas once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
|
||||
virtual void SetEmberFile(const EmberFile<float>& emberFile) { }
|
||||
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }
|
||||
virtual void SetTempPalette(const Palette<float>& palette) { }
|
||||
@ -202,7 +202,7 @@ public:
|
||||
void DeleteRenderer();
|
||||
void SaveCurrentRender(const QString& filename, bool forcePull);
|
||||
RendererBase* Renderer() { return m_Renderer.get(); }
|
||||
vector<byte>* FinalImage() { return &m_FinalImage; }
|
||||
vector<byte>* FinalImage() { return &(m_FinalImage[m_FinalImageIndex]); }
|
||||
vector<byte>* PreviewFinalImage() { return &m_PreviewFinalImage; }
|
||||
|
||||
protected:
|
||||
@ -215,6 +215,7 @@ protected:
|
||||
bool m_Rendering;
|
||||
bool m_Shared;
|
||||
bool m_LastEditWasUndoRedo;
|
||||
uint m_FinalImageIndex;
|
||||
uint m_Platform;
|
||||
uint m_Device;
|
||||
uint m_SubBatchCount;
|
||||
@ -229,7 +230,8 @@ protected:
|
||||
QString m_LastSaveAll;
|
||||
QString m_LastSaveCurrent;
|
||||
CriticalSection m_Cs;
|
||||
vector<byte> m_FinalImage;
|
||||
std::thread m_WriteThread;
|
||||
vector<byte> m_FinalImage[2];
|
||||
vector<byte> m_PreviewFinalImage;
|
||||
vector<eProcessAction> m_ProcessActions;
|
||||
unique_ptr<EmberNs::RendererBase> m_Renderer;
|
||||
|
@ -124,9 +124,9 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, b
|
||||
FractoriumSettings* settings = m_Fractorium->m_Settings;
|
||||
RendererCLBase* rendererCL = dynamic_cast<RendererCLBase*>(m_Renderer.get());
|
||||
|
||||
if (forcePull && rendererCL && m_Renderer->PrepFinalAccumVector(m_FinalImage))
|
||||
if (forcePull && rendererCL && m_Renderer->PrepFinalAccumVector(m_FinalImage[m_FinalImageIndex]))
|
||||
{
|
||||
if (!rendererCL->ReadFinal(m_FinalImage.data()))
|
||||
if (!rendererCL->ReadFinal(m_FinalImage[m_FinalImageIndex].data()))
|
||||
{
|
||||
m_Fractorium->ShowCritical("GPU Read Error", "Could not read image from the GPU, aborting image save.", true);
|
||||
return;
|
||||
@ -134,17 +134,17 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, b
|
||||
}
|
||||
|
||||
//Ensure dimensions are valid.
|
||||
if (m_FinalImage.size() < (width * height * m_Renderer->NumChannels() * m_Renderer->BytesPerChannel()))
|
||||
if (m_FinalImage[m_FinalImageIndex].size() < (width * height * m_Renderer->NumChannels() * m_Renderer->BytesPerChannel()))
|
||||
{
|
||||
m_Fractorium->ShowCritical("Save Failed", "Dimensions didn't match, not saving.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
data = m_FinalImage.data();//Png and channels == 4.
|
||||
data = m_FinalImage[m_FinalImageIndex].data();//Png and channels == 4.
|
||||
|
||||
if ((suffix == "jpg" || suffix == "bmp") && m_Renderer->NumChannels() == 4)
|
||||
{
|
||||
RgbaToRgb(m_FinalImage, vecRgb, width, height);
|
||||
RgbaToRgb(m_FinalImage[m_FinalImageIndex], vecRgb, width, height);
|
||||
|
||||
data = vecRgb.data();
|
||||
}
|
||||
@ -358,7 +358,7 @@ bool FractoriumEmberController<T>::Render()
|
||||
if (ProcessState() != ACCUM_DONE)
|
||||
{
|
||||
//if (m_Renderer->Run(m_FinalImage, 0) == RENDER_OK)//Full, non-incremental render for debugging.
|
||||
if (m_Renderer->Run(m_FinalImage, 0, m_SubBatchCount, iterBegin) == RENDER_OK)//Force output on iterBegin.
|
||||
if (m_Renderer->Run(m_FinalImage[m_FinalImageIndex], 0, m_SubBatchCount, iterBegin) == RENDER_OK)//Force output on iterBegin.
|
||||
{
|
||||
//The amount to increment sub batch while rendering proceeds is purely empirical.
|
||||
//Change later if better values can be derived/observed.
|
||||
@ -433,7 +433,7 @@ bool FractoriumEmberController<T>::Render()
|
||||
//Update it on finish because the rendering process is completely done.
|
||||
if (iterBegin || ProcessState() == ACCUM_DONE)
|
||||
{
|
||||
if (m_FinalImage.size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed.
|
||||
if (m_FinalImage[m_FinalImageIndex].size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed.
|
||||
//gl->repaint();
|
||||
gl->update();
|
||||
|
||||
@ -460,7 +460,7 @@ bool FractoriumEmberController<T>::Render()
|
||||
m_Rendering = false;
|
||||
StopRenderTimer(true);
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Rendering failed 3 or more times, stopping all rendering, see info tab. Try changing renderer types.");
|
||||
Memset(m_FinalImage);
|
||||
Memset(m_FinalImage[m_FinalImageIndex]);
|
||||
|
||||
if (rendererCL)
|
||||
rendererCL->ClearFinal();
|
||||
@ -642,6 +642,11 @@ bool Fractorium::CreateControllerFromOptions()
|
||||
if (m_Controller.get())
|
||||
{
|
||||
m_Controller->CopyTempPalette(tempPalette);//Convert float to double or save double verbatim;
|
||||
|
||||
//Replace below with this once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
|
||||
//m_Controller->CopyEmber(ed);
|
||||
//m_Controller->CopyEmberFile(efd);
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
m_Controller->CopyEmber(ed, [&](Ember<double>& ember) { });
|
||||
m_Controller->CopyEmberFile(efd, [&](Ember<double>& ember) { });
|
||||
|
Reference in New Issue
Block a user