diff --git a/Source/EmberCL/OpenCLWrapper.cpp b/Source/EmberCL/OpenCLWrapper.cpp index c16701c..e976f0a 100644 --- a/Source/EmberCL/OpenCLWrapper.cpp +++ b/Source/EmberCL/OpenCLWrapper.cpp @@ -933,6 +933,7 @@ bool OpenCLWrapper::Shared() const { return m_Shared; } const cl::Context& OpenCLWrapper::Context() const { return m_Context; } size_t OpenCLWrapper::PlatformIndex() const { return m_PlatformIndex; } size_t OpenCLWrapper::DeviceIndex() const { return m_DeviceIndex; } +size_t OpenCLWrapper::TotalDeviceIndex() const { return m_Info->TotalDeviceIndex(m_PlatformIndex, m_DeviceIndex); } const string& OpenCLWrapper::DeviceName() const { return m_Info->DeviceName(m_PlatformIndex, m_DeviceIndex); } size_t OpenCLWrapper::LocalMemSize() const { return m_LocalMemSize; } size_t OpenCLWrapper::GlobalMemSize() const { return m_GlobalMemSize; } diff --git a/Source/EmberCL/OpenCLWrapper.h b/Source/EmberCL/OpenCLWrapper.h index 6360e53..e7ead10 100644 --- a/Source/EmberCL/OpenCLWrapper.h +++ b/Source/EmberCL/OpenCLWrapper.h @@ -172,8 +172,8 @@ public: const cl::Context& Context() const; size_t PlatformIndex() const; size_t DeviceIndex() const; - const string& DeviceName() const; size_t TotalDeviceIndex() const; + const string& DeviceName() const; size_t LocalMemSize() const; size_t GlobalMemSize() const; size_t MaxAllocSize() const; diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index ffb4b89..0abad2f 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -394,6 +394,14 @@ const string& RendererCL::DEKernel() const { return m_DEOpenCLKernel template const string& RendererCL::FinalAccumKernel() const { return m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip(), Renderer::NumChannels(), Transparency()); } +/// +/// Get the a const referece to the devices this renderer will use. +/// Use this cautiously and do not use const_cast to manipulate the vector. +/// +/// A const reference to a vector of unique_ptr of devices +template +const vector>& RendererCL::Devices() const { return m_Devices; } + /// /// Virtual functions overridden from RendererCLBase. /// diff --git a/Source/EmberCL/RendererCL.h b/Source/EmberCL/RendererCL.h index 6f674a7..ce52292 100644 --- a/Source/EmberCL/RendererCL.h +++ b/Source/EmberCL/RendererCL.h @@ -135,6 +135,9 @@ public: const string& DEKernel() const; const string& FinalAccumKernel() const; + //Access to underlying OpenCL structures. Use cautiously. + const vector>& Devices() const; + //Virtual functions overridden from RendererCLBase. virtual bool ReadFinal(byte* pixels); virtual bool ClearFinal(); diff --git a/Source/EmberCL/RendererClDevice.cpp b/Source/EmberCL/RendererClDevice.cpp index 547e719..e7e019d 100644 --- a/Source/EmberCL/RendererClDevice.cpp +++ b/Source/EmberCL/RendererClDevice.cpp @@ -55,6 +55,4 @@ bool RendererClDevice::Ok() const { return m_Init; } bool RendererClDevice::Shared() const { return m_Shared; } bool RendererClDevice::Nvidia() const { return m_NVidia; } size_t RendererClDevice::WarpSize() const { return m_WarpSize; } -size_t RendererClDevice::PlatformIndex() const { return m_PlatformIndex; } -size_t RendererClDevice::DeviceIndex() const { return m_DeviceIndex; } } diff --git a/Source/EmberCL/RendererClDevice.h b/Source/EmberCL/RendererClDevice.h index 55a4eb5..6e02018 100644 --- a/Source/EmberCL/RendererClDevice.h +++ b/Source/EmberCL/RendererClDevice.h @@ -24,8 +24,6 @@ public: bool Shared() const; bool Nvidia() const; size_t WarpSize() const; - size_t PlatformIndex() const; - size_t DeviceIndex() const; size_t m_Calls; OpenCLWrapper m_Wrapper; diff --git a/Source/Fractorium/FinalRenderDialog.cpp b/Source/Fractorium/FinalRenderDialog.cpp index edbabaf..f388668 100644 --- a/Source/Fractorium/FinalRenderDialog.cpp +++ b/Source/Fractorium/FinalRenderDialog.cpp @@ -748,62 +748,23 @@ bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer) /// assign the result to the table cell as text. /// Report errors if not enough memory is available for any of the selected devices. /// -/// True devices and a controller is present, else false. +/// True if devices and a controller is present, else false. bool FractoriumFinalRenderDialog::SetMemory() { if (isVisible() && CreateControllerFromGUI(true)) { - bool error = false; - tuple p = m_Controller->SyncAndComputeMemory(); QString s; + 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() && !m_Wrappers.empty()) - { - auto devices = Devices(); - - for (size_t i = 0; i < m_Wrappers.size(); i++) - { - if (devices.contains(int(i))) - { - size_t histSize = get<0>(p); - size_t totalSize = get<1>(p); - size_t maxAlloc = m_Wrappers[i].MaxAllocSize(); - size_t totalAvail = m_Wrappers[i].GlobalMemSize(); - QString temp; - - if (histSize > maxAlloc) - { - temp = "Histogram/Accumulator memory size of " + ToString(histSize) + - " is greater than the max OpenCL allocation size of " + ToString(maxAlloc); - } - - if (totalSize > totalAvail) - { - temp += "\n\nTotal required memory size of " + ToString(totalSize) + - " is greater than the max OpenCL available memory of " + ToString(totalAvail); - } - - if (!temp.isEmpty()) - { - error = true; - s += QString::fromStdString(m_Wrappers[i].DeviceName()) + ":\n" + temp + "\n\n"; - } - } - } - - if (!s.isEmpty()) - s += "Rendering will most likely fail."; - - ui.FinalRenderTextOutput->setText(s); - } - - if (!error) + if (OpenCL()) + ui.FinalRenderTextOutput->setText(m_Controller->CheckMemory(p)); + else ui.FinalRenderTextOutput->clear(); return true; } return false; -} +} \ No newline at end of file diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp index a861079..b25ceff 100644 --- a/Source/Fractorium/FinalRenderEmberController.cpp +++ b/Source/Fractorium/FinalRenderEmberController.cpp @@ -931,6 +931,85 @@ void FinalRenderEmberController::SetProgressComplete(int val) QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, val)); } +/// +/// Check if the amount of required memory is greater than that available on +/// all required OpenCL devices. Also check if enough space is available for the max allocation. +/// No check is done for CPU renders. +/// Report errors if not enough memory is available for any of the selected devices. +/// +/// A string with an error report if required memory exceeds available memory on any device, else empty string. +template +QString FinalRenderEmberController::CheckMemory(const tuple& p) +{ + bool error = false; + QString s; + size_t histSize = get<0>(p); + size_t totalSize = get<1>(p); + auto& selectedDevices = m_FinalRenderDialog->Devices(); + static vector*> clRenderers; + clRenderers.clear(); + + //Find all OpenCL renderers currently being used and place them in a vector of pointers. + if (m_FinalRenderDialog->DoSequence()) + { + for (auto& r : m_Renderers) + if (auto clr = dynamic_cast*>(r.get())) + clRenderers.push_back(clr); + } + else + { + if (auto clr = dynamic_cast*>(m_Renderer.get())) + clRenderers.push_back(clr); + } + + //Iterate through each renderer and examine each device it's using. + for (auto r : clRenderers) + { + auto& devices = r->Devices(); + + for (auto& d : devices) + { + auto& wrapper = d->m_Wrapper; + auto index = wrapper.TotalDeviceIndex(); + + if (selectedDevices.contains(int(index))) + { + bool err = false; + QString temp; + size_t maxAlloc = wrapper.MaxAllocSize(); + size_t totalAvail = wrapper.GlobalMemSize(); + + if (histSize > maxAlloc) + { + err = true; + temp = "Histogram/Accumulator memory size of " + ToString(histSize) + + " is greater than the max OpenCL allocation size of " + ToString(maxAlloc); + } + + if (totalSize > totalAvail) + { + if (err) + temp += "\n\n"; + + temp += "Total required memory size of " + ToString(totalSize) + + " is greater than the max OpenCL available memory of " + ToString(totalAvail); + } + + if (!temp.isEmpty()) + { + error = true; + s += QString::fromStdString(wrapper.DeviceName()) + ":\n" + temp + "\n\n"; + } + } + } + } + + if (!s.isEmpty()) + s += "Rendering will most likely fail."; + + return s; +} + template class FinalRenderEmberController; #ifdef DO_DOUBLE diff --git a/Source/Fractorium/FinalRenderEmberController.h b/Source/Fractorium/FinalRenderEmberController.h index eea2755..9ea2c07 100644 --- a/Source/Fractorium/FinalRenderEmberController.h +++ b/Source/Fractorium/FinalRenderEmberController.h @@ -13,7 +13,6 @@ /// class Fractorium; class FractoriumFinalRenderDialog; -//class FractoriumEmberControllerBase; /// /// Used to hold the options specified in the current state of the Gui for performing the final render. @@ -68,6 +67,7 @@ public: virtual double OriginalAspect() { return 1; } virtual QString ComposePath(const QString& name) { return ""; } virtual void CancelRender() { } + virtual QString CheckMemory(const tuple& p) { return ""; } bool CreateRendererFromGUI(); void Output(const QString& s); @@ -126,6 +126,7 @@ public: virtual QString Name() const override { return QString::fromStdString(m_Ember->m_Name); } virtual QString ComposePath(const QString& name) override; virtual void CancelRender() override; + virtual QString CheckMemory(const tuple& p) override; //Non Virtual functions. EmberNs::Renderer* FirstOrDefaultRenderer();