From 3e70b8eec6a3b6c7017e21f89f5f14dd904bbd8b Mon Sep 17 00:00:00 2001 From: mfeemster Date: Tue, 9 Dec 2014 00:24:28 -0800 Subject: [PATCH] Report memory required in final render dialog. Fix broken state of ember during failed render with strips. --- Data/Version History.txt | 12 +++++ Source/Ember/RendererBase.cpp | 27 +++++++++--- Source/Ember/RendererBase.h | 3 +- Source/EmberCL/OpenCLWrapper.cpp | 9 ++-- Source/EmberCL/OpenCLWrapper.h | 4 +- Source/EmberCommon/EmberCommon.h | 37 +++++++--------- Source/EmberRender/EmberRender.cpp | 1 + Source/Fractorium/FinalRenderDialog.cpp | 44 +++++++++++++++++-- .../Fractorium/FinalRenderEmberController.cpp | 13 +++--- .../Fractorium/FinalRenderEmberController.h | 4 +- 10 files changed, 112 insertions(+), 42 deletions(-) diff --git a/Data/Version History.txt b/Data/Version History.txt index 53f0f33..9c6e1c7 100644 --- a/Data/Version History.txt +++ b/Data/Version History.txt @@ -1,3 +1,15 @@ +--User changes + Use actual strips count when computing memory requirements in the final render dialog. + Output CL_DEVICE_ADDRESS_BITS when passing --openclinfo to command line programs. + Warn if single/total allocations are greater than the max allowed for OpenCL in the final render dialog. + +--Bug Fixes + Fix bad values left in an ember when a render with strips > 1 fails in the final render dialog. + +--Code Changes + Break RendererBase::MemoryRequired() into two functions, it and HistMemRequired() and return a tuple. + Add GlobalMemSize() and MaxAllocSize() functions in OpenCLWrapper. + 0.4.1.6 Beta 11/29/2014 --User Changes None. diff --git a/Source/Ember/RendererBase.cpp b/Source/Ember/RendererBase.cpp index 5f7a9c8..9293b5b 100644 --- a/Source/Ember/RendererBase.cpp +++ b/Source/Ember/RendererBase.cpp @@ -116,12 +116,10 @@ void RendererBase::ChangeVal(std::function func, eProcessAction acti } /// -/// Return the amount of memory needed to render the current ember. -/// Optionally include the memory needed for the final output image. +/// Return the amount of memory needed for the histogram. /// -/// If true include the memory needed for the final output image, else don't. -/// The memory required to render the current ember -size_t RendererBase::MemoryRequired(size_t strips, bool includeFinal) +/// The memory required for the histogram to render the current ember +size_t RendererBase::HistMemoryRequired(size_t strips) { bool newFilterAlloc = false; @@ -130,9 +128,24 @@ size_t RendererBase::MemoryRequired(size_t strips, bool includeFinal) ComputeBounds(); //Because ComputeBounds() was called, this includes gutter. - size_t histSize = (SuperSize() * HistBucketSize()) / strips; + return (SuperSize() * HistBucketSize()) / strips; +} - return (histSize * 2) + (includeFinal ? FinalBufferSize() : 0);//Multiply hist by 2 to account for the density filtering buffer which is the same size as the histogram. +/// +/// Return a pair whose first member contains the amount of memory needed for the histogram, +/// and whose second member contains the total the amount of memory needed to render the current ember. +/// Optionally include the memory needed for the final output image in pair.second. +/// +/// If true include the memory needed for the final output image, else don't. +/// The histogram memory required in first, and the total memory required in second +pair RendererBase::MemoryRequired(size_t strips, bool includeFinal) +{ + pair p; + + p.first = HistMemoryRequired(strips); + p.second = (p.first * 2) + (includeFinal ? FinalBufferSize() : 0);//Multiply hist by 2 to account for the density filtering buffer which is the same size as the histogram. + + return p; } /// diff --git a/Source/Ember/RendererBase.h b/Source/Ember/RendererBase.h index eb81bf7..47b4e92 100644 --- a/Source/Ember/RendererBase.h +++ b/Source/Ember/RendererBase.h @@ -98,7 +98,8 @@ public: //Non-virtual processing functions. void ChangeVal(std::function func, eProcessAction action); - size_t MemoryRequired(size_t strips, bool includeFinal); + size_t HistMemoryRequired(size_t strips); + pair MemoryRequired(size_t strips, bool includeFinal); vector> RandVec(); bool PrepFinalAccumVector(vector& pixels); diff --git a/Source/EmberCL/OpenCLWrapper.cpp b/Source/EmberCL/OpenCLWrapper.cpp index ffc23be..1c24553 100644 --- a/Source/EmberCL/OpenCLWrapper.cpp +++ b/Source/EmberCL/OpenCLWrapper.cpp @@ -554,13 +554,13 @@ int OpenCLWrapper::FindImageIndex(const string& name, bool shared) { for (size_t i = 0; i < m_GLImages.size(); i++) if (m_GLImages[i].m_Name == name) - return i; + return int(i); } else { for (size_t i = 0; i < m_Images.size(); i++) if (m_Images[i].m_Name == name) - return i; + return int(i); } return -1; @@ -966,7 +966,7 @@ bool OpenCLWrapper::RunKernel(uint kernelIndex, uint totalGridWidth, uint totalG /// The device field/feature to query /// The value of the field template -T OpenCLWrapper::GetInfo(size_t platform, size_t device, cl_device_info name) +T OpenCLWrapper::GetInfo(size_t platform, size_t device, cl_device_info name) const { T val; @@ -1094,6 +1094,7 @@ string OpenCLWrapper::DumpInfo() os << "CL_DEVICE_MAX_READ_IMAGE_ARGS: " << GetInfo (platform, device, CL_DEVICE_MAX_READ_IMAGE_ARGS) << endl; os << "CL_DEVICE_MAX_WRITE_IMAGE_ARGS: " << GetInfo (platform, device, CL_DEVICE_MAX_WRITE_IMAGE_ARGS) << endl; os << "CL_DEVICE_MAX_MEM_ALLOC_SIZE: " << GetInfo(platform, device, CL_DEVICE_MAX_MEM_ALLOC_SIZE) << endl; + os << "CL_DEVICE_ADDRESS_BITS: " << GetInfo (platform, device, CL_DEVICE_ADDRESS_BITS) << endl; os << "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE: " << GetInfo (platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE) << endl; os << "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE: " << GetInfo (platform, device, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE) << endl; @@ -1126,7 +1127,9 @@ bool OpenCLWrapper::Shared() const { return m_Shared; } cl::Context OpenCLWrapper::Context() const { return m_Context; } uint OpenCLWrapper::PlatformIndex() const { return m_PlatformIndex; } uint OpenCLWrapper::DeviceIndex() const { return m_DeviceIndex; } +size_t OpenCLWrapper::GlobalMemSize() const { return GetInfo(PlatformIndex(), DeviceIndex(), CL_DEVICE_GLOBAL_MEM_SIZE); } uint OpenCLWrapper::LocalMemSize() const { return m_LocalMemSize; } +size_t OpenCLWrapper::MaxAllocSize() const { return GetInfo(PlatformIndex(), DeviceIndex(), CL_DEVICE_MAX_MEM_ALLOC_SIZE); } /// /// Makes the even grid dims. diff --git a/Source/EmberCL/OpenCLWrapper.h b/Source/EmberCL/OpenCLWrapper.h index a58eb12..551416b 100644 --- a/Source/EmberCL/OpenCLWrapper.h +++ b/Source/EmberCL/OpenCLWrapper.h @@ -175,7 +175,7 @@ public: //Info. template - T GetInfo(size_t platform, size_t device, cl_device_info name); + T GetInfo(size_t platform, size_t device, cl_device_info name) const; string PlatformName(size_t platform); vector PlatformNames(); string DeviceName(size_t platform, size_t device); @@ -190,6 +190,8 @@ public: uint PlatformIndex() const; uint DeviceIndex() const; uint LocalMemSize() const; + size_t GlobalMemSize() const; + size_t MaxAllocSize() const; static void MakeEvenGridDims(uint blockW, uint blockH, uint& gridW, uint& gridH); diff --git a/Source/EmberCommon/EmberCommon.h b/Source/EmberCommon/EmberCommon.h index 0a9016e..a21f029 100644 --- a/Source/EmberCommon/EmberCommon.h +++ b/Source/EmberCommon/EmberCommon.h @@ -281,7 +281,7 @@ static bool StripsRender(RendererBase* renderer, Ember& ember, vector& std::function perStripError, std::function& finalEmber)> allStripsFinished) { - bool success = true; + bool success = false; size_t origHeight, realHeight = ember.m_FinalRasH; T centerY = ember.m_CenterY; T floatStripH = T(ember.m_FinalRasH) / T(strips); @@ -321,32 +321,29 @@ static bool StripsRender(RendererBase* renderer, Ember& ember, vector& renderer->SetEmber(ember);//Set one final time after modifications for strips. } - if ((renderer->Run(finalImage, time, 0, false, stripOffset) != RENDER_OK) || renderer->Aborted() || finalImage.empty()) - { - perStripError(strip); - success = false; - break; - } - else + if ((renderer->Run(finalImage, time, 0, false, stripOffset) == RENDER_OK) && !renderer->Aborted() && !finalImage.empty()) { perStripFinish(strip); } + else + { + perStripError(strip); + break; + } if (strip == strips - 1) - { - //Restore the ember values to their original values. - if (strips > 1) - { - ember.m_Quality /= strips; - ember.m_FinalRasH = realHeight; - ember.m_CenterY = centerY; - renderer->SetEmber(ember);//Further processing will require the dimensions to match the original ember, so re-assign. - } - - allStripsFinished(ember); - } + success = true; } + //Restore the ember values to their original values. + ember.m_Quality /= strips; + ember.m_FinalRasH = realHeight; + ember.m_CenterY = centerY; + renderer->SetEmber(ember);//Further processing will require the dimensions to match the original ember, so re-assign. + + if (success) + allStripsFinished(ember); + Memset(finalImage); return success; diff --git a/Source/EmberRender/EmberRender.cpp b/Source/EmberRender/EmberRender.cpp index 064c099..5d12f9b 100644 --- a/Source/EmberRender/EmberRender.cpp +++ b/Source/EmberRender/EmberRender.cpp @@ -330,6 +330,7 @@ int _tmain(int argc, _TCHAR* argv[]) //This must be done in the application and not in the EmberCL DLL. #ifdef WIN32 _putenv_s("GPU_MAX_ALLOC_PERCENT", "100"); + //_putenv_s("GPU_FORCE_64BIT_PTR", "1"); #else putenv(const_cast("GPU_MAX_ALLOC_PERCENT=100")); #endif diff --git a/Source/Fractorium/FinalRenderDialog.cpp b/Source/Fractorium/FinalRenderDialog.cpp index 959731d..cb9150a 100644 --- a/Source/Fractorium/FinalRenderDialog.cpp +++ b/Source/Fractorium/FinalRenderDialog.cpp @@ -708,10 +708,48 @@ bool FractoriumFinalRenderDialog::SetMemory() { if (isVisible() && CreateControllerFromGUI(true)) { - pair p = m_Controller->SyncAndComputeMemory(); + bool error = false; + tuple 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()) + { + if (!m_Wrapper.Ok() || PlatformIndex() != m_Wrapper.PlatformIndex() || DeviceIndex() != m_Wrapper.DeviceIndex()) + m_Wrapper.Init(PlatformIndex(), DeviceIndex()); + + if (m_Wrapper.Ok()) + { + size_t histSize = get<0>(p); + size_t totalSize = get<1>(p); + size_t maxAlloc = m_Wrapper.MaxAllocSize(); + size_t totalAvail = m_Wrapper.GlobalMemSize(); + QString s; + + if (histSize > maxAlloc) + { + s = "Histogram/Accumulator memory size of " + ToString(histSize) + + " is greater than the max OpenCL allocation size of " + ToString(maxAlloc); + } + + if (totalSize > totalAvail) + { + s += "\n\nTotal required memory size of " + ToString(totalSize) + + " is greater than the max OpenCL available memory of " + ToString(totalAvail); + } + + if (!s.isEmpty()) + { + error = true; + ui.FinalRenderTextOutput->setText(s + ".\n\nRendering will most likely fail."); + } + } + } + + if (!error) + ui.FinalRenderTextOutput->clear(); - ui.FinalRenderParamsTable->item(m_MemoryCellIndex, 1)->setText(ToString(p.first)); - ui.FinalRenderParamsTable->item(m_ItersCellIndex, 1)->setText(ToString(p.second)); return true; } diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp index 54aea94..bf6f824 100644 --- a/Source/Fractorium/FinalRenderEmberController.cpp +++ b/Source/Fractorium/FinalRenderEmberController.cpp @@ -151,8 +151,9 @@ FinalRenderEmberController::FinalRenderEmberController(FractoriumFinalRenderD { m_Run = true; m_TotalTimer.Tic();//Begin timing for progress of all operations. - size_t i; m_GuiState = m_FinalRenderDialog->State();//Cache render settings from the GUI before running. + + 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. QString path = doAll ? ComposePath(QString::fromStdString(m_EmberFile.m_Embers[0].m_Name)) : ComposePath(Name()); @@ -566,8 +567,9 @@ void FinalRenderEmberController::ResetProgress(bool total) /// /// If successful, memory required in bytes, else zero. template -pair FinalRenderEmberController::SyncAndComputeMemory() +tuple FinalRenderEmberController::SyncAndComputeMemory() { + size_t iterCount; pair p(0, 0); if (m_Renderer.get()) @@ -587,11 +589,12 @@ pair FinalRenderEmberController::SyncAndComputeMemory() m_Renderer->ComputeCamera(); CancelPreviewRender(); m_FinalPreviewRenderFunc(); - p.first = m_Renderer->MemoryRequired(m_FinalRenderDialog->Strips(), true); - p.second = m_Renderer->TotalIterCount(strips); + + p = m_Renderer->MemoryRequired(strips, true); + iterCount = m_Renderer->TotalIterCount(strips); } - return p; + return tuple(p.first, p.second, iterCount); } /// diff --git a/Source/Fractorium/FinalRenderEmberController.h b/Source/Fractorium/FinalRenderEmberController.h index 35efdd0..4ce9ad6 100644 --- a/Source/Fractorium/FinalRenderEmberController.h +++ b/Source/Fractorium/FinalRenderEmberController.h @@ -63,7 +63,7 @@ public: virtual void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0) { } virtual void SyncCurrentToSizeSpinners(bool scale, bool size) { } virtual void ResetProgress(bool total = true) { } - virtual pair SyncAndComputeMemory() { return pair(0, 0); } + virtual tuple SyncAndComputeMemory() { return tuple(0, 0, 0); } virtual double OriginalAspect() { return 1; } virtual QString ComposePath(const QString& name) { return ""; } @@ -121,7 +121,7 @@ public: virtual void SyncGuiToEmbers(size_t widthOverride = 0, size_t heightOverride = 0) override; virtual void SyncCurrentToSizeSpinners(bool scale, bool size) override; virtual void ResetProgress(bool total = true) override; - virtual pair SyncAndComputeMemory() override; + virtual tuple SyncAndComputeMemory() override; virtual double OriginalAspect() override { return double(m_Ember->m_OrigFinalRasW) / m_Ember->m_OrigFinalRasH; } virtual QString Name() const override { return QString::fromStdString(m_Ember->m_Name); } virtual QString ComposePath(const QString& name) override;