mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-06-30 21:36:33 -04:00
--Code changes
-Remove all dependencies on Intel Threading Building Blocks. -Write our own version of parallel_for().
This commit is contained in:
@ -12,9 +12,29 @@
|
||||
/// <summary>
|
||||
/// Ember class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
static void parallel_for(size_t start, size_t end, size_t parlevel, std::function<void(size_t)> func)
|
||||
{
|
||||
const auto ct = parlevel == 0 ? EmberNs::Timing::ProcessorCount() : parlevel;
|
||||
std::vector<std::thread> threads(ct);
|
||||
const auto chunkSize = (end - start) / ct;
|
||||
|
||||
for (size_t i = 0; i < ct; i++)
|
||||
{
|
||||
threads.push_back(std::thread([&, i]
|
||||
{
|
||||
const auto chunkStart = chunkSize* i;
|
||||
const auto chunkEnd = std::min(chunkStart + chunkSize, end);
|
||||
|
||||
for (size_t j = chunkStart; j < chunkEnd; j++)
|
||||
func(j);
|
||||
}));
|
||||
}
|
||||
|
||||
EmberNs::Join(threads);
|
||||
}
|
||||
|
||||
template <typename T> class Interpolater;
|
||||
|
||||
/// <summary>
|
||||
|
@ -66,12 +66,6 @@
|
||||
#include "libxml2/libxml/parser.h"
|
||||
#endif
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
//Intel's Threading Building Blocks is what's used for all threading.
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
#endif
|
||||
|
||||
#define GLM_FORCE_RADIANS 1
|
||||
#define GLM_ENABLE_EXPERIMENTAL 1
|
||||
|
||||
@ -88,7 +82,6 @@
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
using namespace tbb;
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace glm;
|
||||
|
@ -406,9 +406,9 @@ public:
|
||||
{
|
||||
for (size_t j = 0; j < width; j++)
|
||||
{
|
||||
v[(width * 3 * i) + (j * 3)] = static_cast<byte>(m_Entries[j][0] * T(255));//Palettes are as [0..1], so convert to [0..255] here since it's for GUI display.
|
||||
v[(width * 3 * i) + (j * 3) + 1] = static_cast<byte>(m_Entries[j][1] * T(255));
|
||||
v[(width * 3 * i) + (j * 3) + 2] = static_cast<byte>(m_Entries[j][2] * T(255));
|
||||
v[(width * 3 * i) + (j * 3)] = static_cast<byte>(m_Entries[j][0] * static_cast<T>(255));//Palettes are as [0..1], so convert to [0..255] here since it's for GUI display.
|
||||
v[(width * 3 * i) + (j * 3) + 1] = static_cast<byte>(m_Entries[j][1] * static_cast<T>(255));
|
||||
v[(width * 3 * i) + (j * 3) + 2] = static_cast<byte>(m_Entries[j][2] * static_cast<T>(255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -871,19 +871,12 @@ bool Renderer<T, bucketT>::Alloc(bool histOnly)
|
||||
template <typename T, typename bucketT>
|
||||
bool Renderer<T, bucketT>::ResetBuckets(bool resetHist, bool resetAccum)
|
||||
{
|
||||
//parallel_invoke(
|
||||
//[&]
|
||||
//{
|
||||
if (resetHist && !m_HistBuckets.empty())
|
||||
Memset(m_HistBuckets);
|
||||
|
||||
//},
|
||||
//[&]
|
||||
//{
|
||||
if (resetAccum && !m_AccumulatorBuckets.empty())
|
||||
Memset(m_AccumulatorBuckets);
|
||||
|
||||
//});
|
||||
return resetHist || resetAccum;
|
||||
}
|
||||
|
||||
@ -930,7 +923,7 @@ eRenderStatus Renderer<T, bucketT>::LogScaleDensityFilter(bool forceOutput)
|
||||
//Timing t(4);
|
||||
//Original didn't parallelize this, doing so gives a 50-75% speedup.
|
||||
//The value can be directly assigned, which is quicker than summing.
|
||||
parallel_for(startRow, endRow, static_cast<size_t>(1), [&](size_t j)
|
||||
parallel_for(startRow, endRow, m_ThreadsToUse, [&](size_t j)
|
||||
{
|
||||
size_t row = j * m_SuperRasW;
|
||||
size_t rowEnd = row + endCol;
|
||||
@ -954,11 +947,7 @@ eRenderStatus Renderer<T, bucketT>::LogScaleDensityFilter(bool forceOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
, tbb::static_partitioner()
|
||||
#endif
|
||||
);
|
||||
});
|
||||
|
||||
if (m_Callback && !m_Abort)
|
||||
if (!m_Callback->ProgressFunc(m_Ember, m_ProgressParameter, 100.0, 1, 0))
|
||||
@ -982,14 +971,13 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
|
||||
bool scf = !(Supersample() & 1);
|
||||
intmax_t ss = Floor<T>(Supersample() / static_cast<T>(2));
|
||||
T scfact = std::pow(Supersample() / (Supersample() + static_cast<T>(1)), static_cast<T>(2));
|
||||
size_t threads = m_ThreadsToUse;
|
||||
size_t startRow = Supersample() - 1;
|
||||
size_t endRow = m_SuperRasH - (Supersample() - 1);//Original did + which is most likely wrong.
|
||||
intmax_t startCol = Supersample() - 1;
|
||||
intmax_t endCol = m_SuperRasW - (Supersample() - 1);
|
||||
size_t chunkSize = static_cast<size_t>(std::ceil(static_cast<double>(endRow - startRow) / static_cast<double>(threads)));
|
||||
size_t chunkSize = static_cast<size_t>(std::ceil(static_cast<double>(endRow - startRow) / static_cast<double>(m_ThreadsToUse)));
|
||||
//parallel_for scales very well, dividing the work almost perfectly among all processors.
|
||||
parallel_for(static_cast<size_t>(0), threads, static_cast<size_t>(1), [&] (size_t threadIndex)
|
||||
parallel_for(static_cast<size_t>(0), m_ThreadsToUse, m_ThreadsToUse, [&] (size_t threadIndex)
|
||||
{
|
||||
size_t pixelNumber = 0;
|
||||
const auto localStartRow = static_cast<intmax_t>(std::min<size_t>(startRow + (threadIndex * chunkSize), endRow - 1));
|
||||
@ -1123,11 +1111,7 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
, tbb::static_partitioner()
|
||||
#endif
|
||||
);
|
||||
});
|
||||
|
||||
if (m_Callback && !m_Abort)
|
||||
m_Callback->ProgressFunc(m_Ember, m_ProgressParameter, 100.0, 1, 0);
|
||||
@ -1166,7 +1150,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<v4F>& pixels,
|
||||
//The original does it this way as well and it's roughly 11 times faster to do it this way than inline below with each pixel.
|
||||
if (EarlyClip())
|
||||
{
|
||||
parallel_for(static_cast<size_t>(0), m_SuperRasH, static_cast<size_t>(1), [&](size_t j)
|
||||
parallel_for(static_cast<size_t>(0), m_SuperRasH, m_ThreadsToUse, [&](size_t j)
|
||||
{
|
||||
auto rowStart = m_AccumulatorBuckets.data() + (j * m_SuperRasW);//Pull out of inner loop for optimization.
|
||||
const auto rowEnd = rowStart + m_SuperRasW;
|
||||
@ -1176,11 +1160,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<v4F>& pixels,
|
||||
GammaCorrection(*rowStart, background, g, linRange, vibrancy, false, glm::value_ptr(*rowStart));//Write back in place.
|
||||
rowStart++;
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
, tbb::static_partitioner()
|
||||
#endif
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (m_Abort)
|
||||
@ -1193,7 +1173,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<v4F>& pixels,
|
||||
//otherwise artifacts that resemble page tearing will occur in an interactive run. It's
|
||||
//critical to never exit this loop prematurely.
|
||||
//for (size_t j = 0; j < FinalRasH(); j++)//Keep around for debugging.
|
||||
parallel_for(static_cast<size_t>(0), FinalRasH(), static_cast<size_t>(1), [&](size_t j)
|
||||
parallel_for(static_cast<size_t>(0), FinalRasH(), m_ThreadsToUse, [&](size_t j)
|
||||
{
|
||||
Color<bucketT> newBucket;
|
||||
size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRasW();//Pull out of inner loop for optimization.
|
||||
@ -1226,11 +1206,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<v4F>& pixels,
|
||||
auto pf = reinterpret_cast<float*>(pv4T);
|
||||
GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, true, pf);
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
, tbb::static_partitioner()
|
||||
#endif
|
||||
);
|
||||
});
|
||||
|
||||
//Insert the palette into the image for debugging purposes. Not implemented on the GPU.
|
||||
if (m_InsertPalette)
|
||||
@ -1288,7 +1264,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
m_ThreadEmbers.insert(m_ThreadEmbers.begin(), m_ThreadsToUse, m_Ember);
|
||||
}
|
||||
|
||||
parallel_for(static_cast<size_t>(0), m_ThreadsToUse, static_cast<size_t>(1), [&] (size_t threadIndex)
|
||||
parallel_for(static_cast<size_t>(0), m_ThreadsToUse, m_ThreadsToUse, [&] (size_t threadIndex)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
SetThreadPriority(GetCurrentThread(), static_cast<int>(m_Priority));
|
||||
@ -1375,11 +1351,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
, tbb::static_partitioner()
|
||||
#endif
|
||||
);
|
||||
});
|
||||
stats.m_Iters = std::accumulate(m_SubBatch.begin(), m_SubBatch.end(), 0ULL);//Sum of iter count of all threads.
|
||||
stats.m_Badvals = std::accumulate(m_BadVals.begin(), m_BadVals.end(), 0ULL);
|
||||
stats.m_IterMs = m_IterTimer.Toc();
|
||||
|
@ -163,6 +163,7 @@ private:
|
||||
m_Option.nId = 0;
|
||||
m_Option.pszArg = _T("--fillmein");
|
||||
m_Option.nArgType = SO_NONE;
|
||||
m_Val = T();
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -22,7 +22,7 @@ static std::recursive_mutex fileCs;
|
||||
static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t height, int quality, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
|
||||
{
|
||||
bool b = false;
|
||||
FILE* file;
|
||||
FILE* file = nullptr;
|
||||
errno_t fileResult;
|
||||
|
||||
//Just to be extra safe.
|
||||
@ -114,7 +114,10 @@ static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t he
|
||||
|
||||
jpeg_finish_compress(&info);
|
||||
jpeg_destroy_compress(&info);
|
||||
fclose(file);
|
||||
|
||||
if (file != nullptr)
|
||||
fclose(file);
|
||||
|
||||
b = true;
|
||||
}
|
||||
|
||||
@ -138,7 +141,7 @@ static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t he
|
||||
static bool WritePng(const char* filename, byte* image, size_t width, size_t height, size_t bytesPerChannel, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
|
||||
{
|
||||
bool b = false;
|
||||
FILE* file;
|
||||
FILE* file = nullptr;
|
||||
errno_t fileResult;
|
||||
|
||||
//Just to be extra safe.
|
||||
@ -220,7 +223,10 @@ static bool WritePng(const char* filename, byte* image, size_t width, size_t hei
|
||||
png_write_image(png_ptr, rows.data());
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
fclose(file);
|
||||
|
||||
if (file != nullptr)
|
||||
fclose(file);
|
||||
|
||||
b = true;
|
||||
}
|
||||
|
||||
@ -313,7 +319,9 @@ static bool SaveBmp(const char* filename, const byte* image, size_t width, size_
|
||||
|
||||
if ((file = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == NULL)
|
||||
{
|
||||
CloseHandle(file);
|
||||
if (file != 0)
|
||||
CloseHandle(file);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -389,7 +397,7 @@ static bool WriteExr16(const char* filename, Rgba* image, size_t width, size_t h
|
||||
try
|
||||
{
|
||||
rlg l(fileCs);
|
||||
file = std::make_unique<RgbaOutputFile>(filename, iw, ih, WRITE_RGBA);
|
||||
file = std::make_unique<RgbaOutputFile>(filename, iw, ih, RgbaChannels::WRITE_RGBA);
|
||||
}
|
||||
catch (std::exception)
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
/// <param name="height">The height of the image in pixels</param>
|
||||
void SetImage(vector<byte>& v, uint width, uint height)
|
||||
{
|
||||
const auto size = 64;
|
||||
constexpr auto size = 64;
|
||||
m_Image = QImage(width, height, QImage::Format_RGBA8888);
|
||||
memcpy(m_Image.scanLine(0), v.data(), SizeOf(v));//Memcpy the data in.
|
||||
m_Pixmap = QPixmap::fromImage(m_Image).scaled(QSize(size, size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);//Create a QPixmap out of the QImage, scaled to size.
|
||||
|
@ -66,10 +66,10 @@ public:
|
||||
virtual void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0, bool dowidth = true, bool doheight = true) { }
|
||||
virtual void SyncCurrentToSizeSpinners(bool scale, bool size, bool doWidth = true, bool doHeight = true) { }
|
||||
virtual void ResetProgress(bool total = true) { }
|
||||
virtual tuple<size_t, size_t, size_t> SyncAndComputeMemory() { return tuple<size_t, size_t, size_t>(0, 0, 0); }
|
||||
virtual double OriginalAspect() { return 1; }
|
||||
virtual tuple<size_t, size_t, size_t> SyncAndComputeMemory() { return tuple<size_t, size_t, size_t>(0, 0, 0); }
|
||||
virtual double OriginalAspect() { return 1; }
|
||||
virtual QString ComposePath(const QString& name, bool unique = true) { return ""; }
|
||||
virtual bool BumpQualityRender(double d) { return false; }
|
||||
virtual bool BumpQualityRender(double d) { return false; }
|
||||
virtual QString SaveCurrentAgain() { return ""; }
|
||||
virtual void CancelRender() { }
|
||||
virtual QString CheckMemory(const tuple<size_t, size_t, size_t>& p) { return ""; }
|
||||
@ -118,7 +118,7 @@ public:
|
||||
void SetEmber(size_t index, bool verbatim) override;
|
||||
void SaveCurrentAsXml(QString filename = "") override;
|
||||
bool Render() override;
|
||||
bool BumpQualityRender(double d) override;
|
||||
bool BumpQualityRender(double d) override;
|
||||
bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) override;
|
||||
int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
|
||||
size_t Index() const override { return m_Ember->m_Index; }
|
||||
|
@ -136,7 +136,7 @@ static bool Exists(const QString& s)
|
||||
/// <returns>The converted color</returns>
|
||||
static QColor VisibleColor(const QColor& color)
|
||||
{
|
||||
const auto threshold = 105;
|
||||
constexpr int threshold = 105;
|
||||
const auto delta = (color.red() * 0.299) + //Magic numbers gotten from a Stack Overflow post.
|
||||
(color.green() * 0.587) +
|
||||
(color.blue() * 0.114);
|
||||
|
@ -397,7 +397,7 @@ void TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
|
||||
{
|
||||
if (const auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(int(i))))
|
||||
{
|
||||
//It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI.
|
||||
//It is critical that Qt::DirectConnection is passed because this is running on a different thread than the UI.
|
||||
//This ensures the events are processed in order as each preview is updated, and that control does not return here
|
||||
//until the update is complete.
|
||||
if (m_PreviewRun)
|
||||
|
@ -22,7 +22,7 @@ enum class eXformUpdate : et { UPDATE_SPECIFIC, UPDATE_CURRENT, UPDATE_SELECTED,
|
||||
/// An enum representing the type of synchronizing to do between the list of Embers kept in memory
|
||||
/// and the widgets in the library tree.
|
||||
/// </summary>
|
||||
enum eLibraryUpdate { INDEX = 1, NAME = 2, POINTER = 4 };
|
||||
enum class eLibraryUpdate { INDEX = 1, NAME = 2, POINTER = 4 };
|
||||
|
||||
/// <summary>
|
||||
/// FractoriumEmberController and Fractorium need each other, but each can't include the other.
|
||||
@ -628,6 +628,10 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~PreviewRenderer()
|
||||
{
|
||||
}
|
||||
|
||||
void Render(uint start, uint end)
|
||||
{
|
||||
Stop();
|
||||
@ -700,7 +704,7 @@ public:
|
||||
m_Tree(tree),
|
||||
m_EmberFile(emberFile)
|
||||
{
|
||||
auto f = m_Controller->m_Fractorium;
|
||||
const auto f = m_Controller->m_Fractorium;
|
||||
m_PreviewRenderer.Callback(nullptr);
|
||||
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
|
||||
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
|
||||
|
@ -151,13 +151,13 @@ void FractoriumEmberController<T>::SyncLibrary(eLibraryUpdate update)
|
||||
{
|
||||
if (auto item = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i)))//Cast the child widget to the EmberTreeWidgetItem type.
|
||||
{
|
||||
if (update & eLibraryUpdate::INDEX)
|
||||
if (static_cast<uint>(update) & static_cast<uint>(eLibraryUpdate::INDEX))
|
||||
it->m_Index = i;
|
||||
|
||||
if (update & eLibraryUpdate::NAME)
|
||||
if (static_cast<uint>(update) & static_cast<uint>(eLibraryUpdate::NAME))
|
||||
item->setText(0, QString::fromStdString(it->m_Name));
|
||||
|
||||
if (update & eLibraryUpdate::POINTER)
|
||||
if (static_cast<uint>(update) & static_cast<uint>(eLibraryUpdate::POINTER))
|
||||
item->SetEmberPointer(&(*it));
|
||||
|
||||
if (item->checkState(0) == Qt::Checked)
|
||||
@ -367,7 +367,7 @@ void FractoriumEmberController<T>::MoveLibraryItems(const QModelIndexList& items
|
||||
return false;
|
||||
});
|
||||
tree->update();
|
||||
SyncLibrary(eLibraryUpdate(eLibraryUpdate::INDEX | eLibraryUpdate::POINTER));
|
||||
SyncLibrary(eLibraryUpdate(static_cast<uint>(eLibraryUpdate::INDEX) | static_cast<uint>(eLibraryUpdate::POINTER)));
|
||||
//SyncLibrary(eLibraryUpdate(eLibraryUpdate::INDEX | eLibraryUpdate::POINTER | eLibraryUpdate::NAME));
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, c
|
||||
if (filename != "")
|
||||
{
|
||||
bool ret = false;
|
||||
auto size = width * height;
|
||||
const auto size = width * height;
|
||||
auto settings = m_Fractorium->m_Settings;
|
||||
QFileInfo fileInfo(filename);
|
||||
QString suffix = fileInfo.suffix();
|
||||
@ -245,7 +245,7 @@ template <typename T>
|
||||
int FractoriumEmberController<T>::ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs)
|
||||
{
|
||||
QString status;
|
||||
m_Fractorium->m_ProgressBar->setValue(int(fraction));//Only really applies to iter and filter, because final accum only gives progress 0 and 100.
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_ProgressBar, "setValue", Qt::QueuedConnection, Q_ARG(const int, int(fraction)));//Only really applies to iter and filter, because final accum only gives progress 0 and 100.
|
||||
|
||||
if (stage == 0)
|
||||
status = "Iterating";
|
||||
@ -254,7 +254,7 @@ int FractoriumEmberController<T>::ProgressFunc(Ember<T>& ember, void* foo, doubl
|
||||
else if (stage == 2)
|
||||
status = "Spatial Filtering + Final Accumulation";
|
||||
|
||||
m_Fractorium->m_RenderStatusLabel->setText(status);
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status));
|
||||
return m_ProcessActions.empty() ? 1 : 0;//If they've done anything, abort.
|
||||
}
|
||||
|
||||
@ -289,10 +289,10 @@ bool FractoriumEmberController<T>::SyncSizes()
|
||||
auto gl = m_Fractorium->ui.GLDisplay;
|
||||
RendererCL<T, float>* rendererCL = nullptr;
|
||||
|
||||
if (!m_GLController->SizesMatch())
|
||||
if (gl && !m_GLController->SizesMatch())
|
||||
{
|
||||
m_GLController->ClearWindow();
|
||||
gl->SetDimensions(int(m_Ember.m_FinalRasW), int(m_Ember.m_FinalRasH));
|
||||
gl->SetDimensions(static_cast<int>(m_Ember.m_FinalRasW), static_cast<int>(m_Ember.m_FinalRasH));
|
||||
gl->Allocate();
|
||||
gl->SetViewport();
|
||||
|
||||
@ -321,12 +321,12 @@ bool FractoriumEmberController<T>::Render()
|
||||
bool success = true;
|
||||
auto gl = m_Fractorium->ui.GLDisplay;
|
||||
RendererCL<T, float>* rendererCL = nullptr;
|
||||
eProcessAction qualityAction, action;
|
||||
eProcessAction qualityAction = eProcessAction::NOTHING, action = eProcessAction::NOTHING;
|
||||
//Quality is the only parameter we update inside the timer.
|
||||
//This is to allow the user to rapidly increase the quality spinner
|
||||
//without fully resetting the render. Instead, it will just keep iterating
|
||||
//where it last left off in response to an increase.
|
||||
T d = T(m_Fractorium->m_QualitySpin->value());
|
||||
T d = static_cast<T>(m_Fractorium->m_QualitySpin->value());
|
||||
|
||||
if (d < m_Ember.m_Quality)//Full restart if quality decreased.
|
||||
{
|
||||
@ -358,8 +358,8 @@ bool FractoriumEmberController<T>::Render()
|
||||
if (action != eProcessAction::NOTHING)
|
||||
{
|
||||
size_t i = 0;
|
||||
int solo = m_Ember.m_Solo;
|
||||
bool forceFinal = m_Fractorium->HaveFinal();
|
||||
const auto solo = m_Ember.m_Solo;
|
||||
const bool forceFinal = m_Fractorium->HaveFinal();
|
||||
|
||||
if (solo != -1)
|
||||
{
|
||||
@ -388,7 +388,7 @@ bool FractoriumEmberController<T>::Render()
|
||||
}
|
||||
|
||||
//Determining if a completely new rendering process is being started.
|
||||
bool iterBegin = ProcessState() == eProcessState::NONE;
|
||||
const auto iterBegin = ProcessState() == eProcessState::NONE;
|
||||
|
||||
if (iterBegin)
|
||||
{
|
||||
@ -398,14 +398,15 @@ bool FractoriumEmberController<T>::Render()
|
||||
m_SubBatchCount = m_Fractorium->m_Settings->OpenCLSubBatch();
|
||||
|
||||
m_Fractorium->m_ProgressBar->setValue(0);
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Starting");
|
||||
const QString status = "Starting";
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status));
|
||||
}
|
||||
|
||||
//If the rendering process hasn't finished, render with the current specified action.
|
||||
if (ProcessState() != eProcessState::ACCUM_DONE)
|
||||
{
|
||||
//if (m_Renderer->Run(m_FinalImage, 0) == RENDER_OK)//Full, non-incremental render for debugging.
|
||||
bool update = iterBegin || m_Fractorium->m_Settings->ContinuousUpdate();
|
||||
const bool update = iterBegin || m_Fractorium->m_Settings->ContinuousUpdate();
|
||||
eRenderStatus result;
|
||||
|
||||
if ((result = m_Renderer->Run(m_FinalImage, 0, m_SubBatchCount, update)) == eRenderStatus::RENDER_OK)//Force output on iterBegin or if the settings specify to always do it.
|
||||
@ -428,23 +429,25 @@ bool FractoriumEmberController<T>::Render()
|
||||
//Rendering has finished, update final stats.
|
||||
if (ProcessState() == eProcessState::ACCUM_DONE)
|
||||
{
|
||||
auto stats = m_Renderer->Stats();
|
||||
const auto stats = m_Renderer->Stats();
|
||||
auto iters = ToString<qulonglong>(stats.m_Iters);
|
||||
auto scaledQuality = ToString(uint(m_Renderer->ScaledQuality()));
|
||||
auto scaledQuality = ToString(static_cast<intmax_t>(m_Renderer->ScaledQuality()));
|
||||
auto renderTime = m_RenderElapsedTimer.Format(m_RenderElapsedTimer.Toc());
|
||||
m_Fractorium->m_ProgressBar->setValue(100);
|
||||
|
||||
//Only certain stats can be reported with OpenCL.
|
||||
if (m_Renderer->RendererType() == eRendererType::OPENCL_RENDERER)
|
||||
{
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Total time: " + QString::fromStdString(renderTime) + ".");
|
||||
const QString status = "Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Total time: " + QString::fromStdString(renderTime) + ".";
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status));
|
||||
}
|
||||
else
|
||||
{
|
||||
double percent = double(stats.m_Badvals) / double(stats.m_Iters);
|
||||
const auto percent = static_cast<double>(stats.m_Badvals) / static_cast<double>(stats.m_Iters);
|
||||
auto badVals = ToString<qulonglong>(stats.m_Badvals);
|
||||
auto badPercent = QLocale::system().toString(percent * 100, 'f', 2);
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Bad values: " + badVals + " (" + badPercent + "%). Total time: " + QString::fromStdString(renderTime) + ".");
|
||||
const QString status = "Iters: " + iters + ". Scaled quality: " + scaledQuality + ". Bad values: " + badVals + " (" + badPercent + "%). Total time: " + QString::fromStdString(renderTime) + ".";
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status));
|
||||
}
|
||||
|
||||
if (m_LastEditWasUndoRedo && (m_UndoIndex == m_UndoList.size() - 1))//Traversing through undo list, reached the end, so put back in regular edit mode.
|
||||
@ -453,7 +456,7 @@ bool FractoriumEmberController<T>::Render()
|
||||
}
|
||||
else if (m_EditState == eEditUndoState::REGULAR_EDIT)//Regular edit, just add to the end of the undo list.
|
||||
{
|
||||
auto btn = QApplication::mouseButtons();
|
||||
const auto btn = QApplication::mouseButtons();
|
||||
|
||||
if ((action == eProcessAction::ACCUM_ONLY || action == eProcessAction::FILTER_AND_ACCUM) ||
|
||||
(!btn.testFlag(Qt::LeftButton) && !btn.testFlag(Qt::RightButton) && !btn.testFlag(Qt::MiddleButton)))
|
||||
@ -512,7 +515,8 @@ bool FractoriumEmberController<T>::Render()
|
||||
auto errors = m_Renderer->ErrorReport();
|
||||
success = false;
|
||||
m_FailedRenders++;
|
||||
m_Fractorium->m_RenderStatusLabel->setText("Rendering failed, see info tab. Try changing parameters.");
|
||||
const QString status = "Rendering failed, see info tab. Try changing parameters.";
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status));
|
||||
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoRenderingTextEdit);
|
||||
m_Renderer->ClearErrorReport();
|
||||
|
||||
@ -520,7 +524,8 @@ 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.");
|
||||
const QString status2 = "Rendering failed 3 or more times, stopping all rendering, see info tab. Try changing renderer types.";
|
||||
QMetaObject::invokeMethod(m_Fractorium->m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status2));
|
||||
ClearFinalImages();
|
||||
m_GLController->ClearWindow();
|
||||
|
||||
@ -604,11 +609,11 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
|
||||
m_Fractorium->m_QualitySpin->setValue(val);
|
||||
|
||||
if (auto rendererCL = dynamic_cast<RendererCL<T, float>*>(m_Renderer.get()))
|
||||
rendererCL->SubBatchPercentPerThread(float(s->OpenCLSubBatchPct()));
|
||||
rendererCL->SubBatchPercentPerThread(static_cast<float>(s->OpenCLSubBatchPct()));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto quality = m_Fractorium->m_Settings->CpuQuality();
|
||||
const auto quality = m_Fractorium->m_Settings->CpuQuality();
|
||||
m_Fractorium->m_QualitySpin->DoubleClickZero(quality);
|
||||
m_Fractorium->m_QualitySpin->DoubleClickNonZero(quality);
|
||||
|
||||
@ -684,9 +689,9 @@ void Fractorium::ShutdownAndRecreateFromOptions(bool updatePreviews)
|
||||
bool Fractorium::CreateRendererFromOptions(bool updatePreviews)
|
||||
{
|
||||
bool ok = true;
|
||||
bool useOpenCL = m_Info->Ok() && m_Settings->OpenCL();
|
||||
const auto useOpenCL = m_Info->Ok() && m_Settings->OpenCL();
|
||||
auto v = Devices(m_Settings->Devices());
|
||||
bool doOpenCL = useOpenCL && !v.empty();
|
||||
const auto doOpenCL = useOpenCL && !v.empty();
|
||||
ui.ActionCopyKernel->setEnabled(doOpenCL);
|
||||
|
||||
//The most important option to process is what kind of renderer is desired, so do it first.
|
||||
@ -712,8 +717,9 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews)
|
||||
{
|
||||
rendererCL->m_CompileBegun = [&]()
|
||||
{
|
||||
m_RenderStatusLabel->setText("Compiling OpenCL kernel...");
|
||||
m_RenderStatusLabel->repaint();
|
||||
const QString status = "Compiling OpenCL kernel...";
|
||||
QMetaObject::invokeMethod(m_RenderStatusLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, status));
|
||||
//m_RenderStatusLabel->repaint();
|
||||
QApplication::processEvents();
|
||||
};
|
||||
}
|
||||
@ -728,7 +734,7 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews)
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
bool Fractorium::CreateControllerFromOptions()
|
||||
{
|
||||
size_t elementSize =
|
||||
const size_t elementSize =
|
||||
#ifdef DO_DOUBLE
|
||||
m_Settings->Double() ? sizeof(double) :
|
||||
#endif
|
||||
@ -736,14 +742,14 @@ bool Fractorium::CreateControllerFromOptions()
|
||||
|
||||
if (!m_Controller.get() || (m_Controller->SizeOfT() != elementSize))
|
||||
{
|
||||
auto hue = m_PaletteHueSpin->value();
|
||||
auto sat = m_PaletteSaturationSpin->value();
|
||||
auto bright = m_PaletteBrightnessSpin->value();
|
||||
auto con = m_PaletteContrastSpin->value();
|
||||
auto blur = m_PaletteBlurSpin->value();
|
||||
auto freq = m_PaletteFrequencySpin->value();
|
||||
auto rot = m_PreviewPaletteRotation;
|
||||
double scale;
|
||||
const auto hue = m_PaletteHueSpin->value();
|
||||
const auto sat = m_PaletteSaturationSpin->value();
|
||||
const auto bright = m_PaletteBrightnessSpin->value();
|
||||
const auto con = m_PaletteContrastSpin->value();
|
||||
const auto blur = m_PaletteBlurSpin->value();
|
||||
const auto freq = m_PaletteFrequencySpin->value();
|
||||
const auto rot = m_PreviewPaletteRotation;
|
||||
double scale = 0;
|
||||
uint current = 0;
|
||||
#ifdef DO_DOUBLE
|
||||
EmberFile<double> efd;
|
||||
@ -752,7 +758,7 @@ bool Fractorium::CreateControllerFromOptions()
|
||||
EmberFile<float> efd;
|
||||
Palette<float> tempPalette;
|
||||
#endif
|
||||
QModelIndex index = ui.LibraryTree->currentIndex();
|
||||
const QModelIndex index = ui.LibraryTree->currentIndex();
|
||||
ui.LibraryTree->clear();//This must be here before FillLibraryTree() is called below, else a spurious EmberTreeItemChanged event will be called on a deleted object.
|
||||
|
||||
//First check if a controller has already been created, and if so, save its embers and gracefully shut it down.
|
||||
@ -822,7 +828,7 @@ void Fractorium::StartRenderTimer(bool updatePreviews)
|
||||
{
|
||||
//Starting the render timer, either for the first time
|
||||
//or from a paused state, such as resizing or applying new options.
|
||||
bool newController = CreateControllerFromOptions();
|
||||
const auto newController = CreateControllerFromOptions();
|
||||
|
||||
if (m_Controller.get())
|
||||
{
|
||||
|
@ -102,7 +102,7 @@ public:
|
||||
TopArrow(int width, size_t index)
|
||||
{
|
||||
QPolygon area;
|
||||
const auto center = 10;
|
||||
constexpr int center = 10;
|
||||
const auto mid = width / 2;
|
||||
const auto left = center - mid;
|
||||
const auto right = center + mid;
|
||||
|
@ -44,14 +44,10 @@ protected:
|
||||
if (e->type() == QEvent::MouseMove)
|
||||
{
|
||||
if (const auto me = dynamic_cast<QMouseEvent*>(e))
|
||||
{
|
||||
emit MouseDragged(me->localPos(), me->globalPos());
|
||||
}
|
||||
}
|
||||
else if (e->type() == QEvent::MouseButtonRelease)
|
||||
{
|
||||
emit MouseReleased();
|
||||
}
|
||||
|
||||
return QTableWidget::eventFilter(obj, e);
|
||||
}
|
||||
|
Reference in New Issue
Block a user