diff --git a/Builds/MSVC/Installer/FractoriumInstaller.wixproj b/Builds/MSVC/Installer/FractoriumInstaller.wixproj index 2eef9c6..a91a64a 100644 --- a/Builds/MSVC/Installer/FractoriumInstaller.wixproj +++ b/Builds/MSVC/Installer/FractoriumInstaller.wixproj @@ -6,7 +6,7 @@ 3.7 {c8096c47-e358-438c-a520-146d46b0637d} 2.0 - Fractorium_1.0.0.7 + Fractorium_1.0.0.8 Package $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets diff --git a/Builds/MSVC/Installer/Product.wxs b/Builds/MSVC/Installer/Product.wxs index 321f4dd..deef6ff 100644 --- a/Builds/MSVC/Installer/Product.wxs +++ b/Builds/MSVC/Installer/Product.wxs @@ -1,6 +1,6 @@ - + @@ -13,7 +13,7 @@ - + ; /*Only implemented as post.*/ \ EXPORTPREPOSTREGVAR(DCBubble, T) \ EXPORTPREPOSTREGVAR(DCCarpet, T) \ diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 85a2043..fe2d422 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -998,17 +998,19 @@ public: /// The angle to rotate by void RotateAffines(T angle) { - for (size_t i = 0; i < XformCount(); i++)//Only look at normal xforms, exclude final. + size_t i = 0; + + while (auto xform = GetTotalXform(i++))//Flam3 only allowed animation with normal xforms. This has been changed to allow animations of final xforms. { //Don't rotate xforms with animate set to 0. - if (m_Xforms[i].m_Animate == 0) + if (xform->m_Animate == 0) continue; //Assume that if there are no variations, then it's a padding xform. - if (m_Xforms[i].Empty() && m_AffineInterp != eAffineInterp::AFFINE_INTERP_LOG) + if (xform->Empty() && m_AffineInterp != eAffineInterp::AFFINE_INTERP_LOG) continue; - m_Xforms[i].m_Affine.Rotate(angle * DEG_2_RAD_T); + xform->m_Affine.Rotate(angle * DEG_2_RAD_T); //Don't rotate post. } } diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index f4fc906..ebb0a06 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c) namespace EmberNs { -#define EMBER_VERSION "1.0.0.7" +#define EMBER_VERSION "1.0.0.8" #define EPS6 T(1e-6) #define EPS std::numeric_limits::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way. #define ISAAC_SIZE 4 diff --git a/Source/Ember/Renderer.cpp b/Source/Ember/Renderer.cpp index a6ce63c..3241e73 100644 --- a/Source/Ember/Renderer.cpp +++ b/Source/Ember/Renderer.cpp @@ -1093,7 +1093,8 @@ eRenderStatus Renderer::GaussianDensityFilter() } /// -/// Thin wrapper around AccumulatorToFinalImage(). +/// Produce a final, visible image by clipping, gamma correcting and spatial filtering the color values +/// in the density filtering buffer and save to the passed in buffer. /// /// The pixel vector to allocate and store the final image in /// Offset in the buffer to store the pixels to @@ -1101,31 +1102,20 @@ eRenderStatus Renderer::GaussianDensityFilter() template eRenderStatus Renderer::AccumulatorToFinalImage(vector& pixels, size_t finalOffset) { - if (PrepFinalAccumVector(pixels)) - return AccumulatorToFinalImage(pixels.data(), finalOffset); - - return eRenderStatus::RENDER_ERROR; -} - -/// -/// Produce a final, visible image by clipping, gamma correcting and spatial filtering the color values -/// in the density filtering buffer and save to the passed in buffer. -/// -/// The pre-allocated pixel buffer to store the final image in -/// Offset in the buffer to store the pixels to. Default: 0. -/// True if not prematurely aborted, else false. -template -eRenderStatus Renderer::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset) -{ - if (!pixels) - return eRenderStatus::RENDER_ERROR; - EnterFinalAccum(); + + if (!PrepFinalAccumVector(pixels)) + { + LeaveFinalAccum(); + return eRenderStatus::RENDER_ERROR; + } + //Timing t(4); size_t filterWidth = m_SpatialFilter->FinalFilterWidth(); bucketT g, linRange, vibrancy; Color background; - pixels += finalOffset; + auto p = pixels.data(); + p += finalOffset; PrepFinalAccumVals(background, g, linRange, vibrancy);//After this, background has been scaled from 0-1 to 0-255. //If early clip, go through the entire accumulator and perform gamma correction first. @@ -1165,7 +1155,7 @@ eRenderStatus Renderer::AccumulatorToFinalImage(v4F* pixels, size_t size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRasW();//Pull out of inner loop for optimization. size_t y = m_DensityFilterOffset + (j * Supersample());//Start at the beginning row of each super sample block. size_t clampedFilterH = std::min(filterWidth, m_SuperRasH - y);//Make sure the filter doesn't go past the bottom of the gutter. - auto pv4T = pixels + pixelsRowStart; + auto pv4T = p + pixelsRowStart; for (size_t i = 0; i < FinalRasW(); i++, pv4T++) { @@ -1210,11 +1200,11 @@ eRenderStatus Renderer::AccumulatorToFinalImage(v4F* pixels, size_t { for (i = 0; i < FinalRasW(); i++) { - auto p = pixels + (i + j * FinalRasW()); - p->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0]; - p->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1]; - p->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2]; - p->a = 1; + auto pp = p + (i + j * FinalRasW()); + pp->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0]; + pp->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1]; + pp->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2]; + pp->a = 1; } } } diff --git a/Source/Ember/Renderer.h b/Source/Ember/Renderer.h index bc213f4..b8e0b60 100644 --- a/Source/Ember/Renderer.h +++ b/Source/Ember/Renderer.h @@ -77,7 +77,6 @@ protected: virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false); virtual eRenderStatus GaussianDensityFilter(); virtual eRenderStatus AccumulatorToFinalImage(vector& pixels, size_t finalOffset); - virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset); virtual EmberStats Iterate(size_t iterCount, size_t temporalSample); virtual void ComputeCurves(); diff --git a/Source/Ember/RendererBase.cpp b/Source/Ember/RendererBase.cpp index 40d8d12..07eda77 100644 --- a/Source/Ember/RendererBase.cpp +++ b/Source/Ember/RendererBase.cpp @@ -514,6 +514,13 @@ size_t RendererBase::ThreadCount() const { return m_ThreadsToUse; } /// eRendererType::CPU_RENDERER eRendererType RendererBase::RendererType() const { return eRendererType::CPU_RENDERER; } +/// +/// Get whether the renderer uses a shared texture with OpenGL. +/// This only applies to the OpenCL renderer (which can be shared or unshared), so it's always false in the base. +/// +/// True if shared, else false. Always false in the base. +bool RendererBase::Shared() const { return false; } + /// /// //Non-virtual threading control. /// diff --git a/Source/Ember/RendererBase.h b/Source/Ember/RendererBase.h index fbb2604..df91b7c 100644 --- a/Source/Ember/RendererBase.h +++ b/Source/Ember/RendererBase.h @@ -164,6 +164,7 @@ public: //Virtual render properties, getters and setters. virtual size_t ThreadCount() const; virtual eRendererType RendererType() const; + virtual bool Shared() const; //Abstract render properties, getters only. virtual size_t TemporalSamples() const = 0; diff --git a/Source/Ember/Variation.h b/Source/Ember/Variation.h index 0ce7e2a..e7d09e2 100644 --- a/Source/Ember/Variation.h +++ b/Source/Ember/Variation.h @@ -197,6 +197,8 @@ enum class eVariationId : et VAR_HOLE , VAR_HORSESHOE , VAR_HYPERBOLIC , + VAR_HYPERCROP , + VAR_HYPERSHIFT2 , VAR_HYPERTILE , VAR_HYPERTILE1 , VAR_HYPERTILE2 , @@ -540,6 +542,8 @@ enum class eVariationId : et VAR_PRE_HOLE, VAR_PRE_HORSESHOE, VAR_PRE_HYPERBOLIC, + VAR_PRE_HYPERCROP, + VAR_PRE_HYPERSHIFT2, VAR_PRE_HYPERTILE, VAR_PRE_HYPERTILE1, VAR_PRE_HYPERTILE2, @@ -883,6 +887,8 @@ enum class eVariationId : et VAR_POST_HOLE, VAR_POST_HORSESHOE, VAR_POST_HYPERBOLIC, + VAR_POST_HYPERCROP, + VAR_POST_HYPERSHIFT2, VAR_POST_HYPERTILE, VAR_POST_HYPERTILE1, VAR_POST_HYPERTILE2, diff --git a/Source/Ember/VariationList.cpp b/Source/Ember/VariationList.cpp index c01b75c..fdae759 100644 --- a/Source/Ember/VariationList.cpp +++ b/Source/Ember/VariationList.cpp @@ -359,6 +359,8 @@ VariationList::VariationList() ADDPREPOSTREGVAR(Sphereblur) ADDPREPOSTREGVAR(Cpow3) ADDPREPOSTREGVAR(Concentric) + ADDPREPOSTREGVAR(Hypercrop) + ADDPREPOSTREGVAR(Hypershift2) //ADDPREPOSTREGVAR(LinearXZ) //ADDPREPOSTREGVAR(LinearYZ) //DC are special. diff --git a/Source/Ember/Variations06.h b/Source/Ember/Variations06.h index 9146472..fc8c2b6 100644 --- a/Source/Ember/Variations06.h +++ b/Source/Ember/Variations06.h @@ -1487,12 +1487,6 @@ public: PARVARCOPY(CrobVariation) - /// - /// Functions the specified helper. - /// - /// The helper. - /// The out point. - /// The rand. virtual void Func(IteratorHelper& helper, Point& outPoint, QTIsaac& rand) override { T gradTmp, secTmp, xTmp = 0, yTmp = 0; diff --git a/Source/Ember/Variations07.h b/Source/Ember/Variations07.h index c08ff5a..5b02e19 100644 --- a/Source/Ember/Variations07.h +++ b/Source/Ember/Variations07.h @@ -2013,6 +2013,256 @@ private: T m_Zblur; }; +/// +/// hypercrop. +/// +template +class HypercropVariation : public ParametricVariation +{ +public: + HypercropVariation(T weight = 1.0) : ParametricVariation("hypercrop", eVariationId::VAR_HYPERCROP, weight, false, false, false, false, true) + { + Init(); + } + + PARVARCOPY(HypercropVariation) + + virtual void Func(IteratorHelper& helper, Point& outPoint, QTIsaac& rand) override + { + T fx = helper.In.x; + T fy = helper.In.y; + T fz = helper.In.z; + T a0 = T(M_PI) / m_N; + T len = 1 / Zeps(std::cos(a0)); + T d = m_Rad * std::sin(a0) * len; + T angle = Floor(helper.m_PrecalcAtanyx * m_Coeff) / m_Coeff + T(M_PI) / m_N; + T x0 = std::cos(angle) * len; + T y0 = std::sin(angle) * len; + + if (std::sqrt(Sqr(helper.In.x - x0) + Sqr(helper.In.y - y0)) < d) + { + if (m_Zero > 1.5) + { + fx = x0; + fy = y0; + fz = 0; + } + else + { + if (m_Zero > 0.5) + { + fx = 0; + fy = 0; + fz = 0; + } + else + { + T rangle = std::atan2(helper.In.y - y0, helper.In.x - x0); + fx = x0 + std::cos(rangle) * d; + fy = y0 + std::sin(rangle) * d; + fz = 0; + } + } + } + + helper.Out.x = fx * m_Weight; + helper.Out.y = fy * m_Weight; + helper.Out.z = fz * m_Weight; + } + + virtual string OpenCLString() const override + { + ostringstream ss, ss2; + intmax_t i = 0, varIndex = IndexInXform(); + ss2 << "_" << XformIndexInEmber() << "]"; + string index = ss2.str(); + string weight = WeightDefineString(); + string n = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string rad = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string zero = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string coeff = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + ss << "\t{\n" + << "\t\treal_t fx = vIn.x;\n" + << "\t\treal_t fy = vIn.y;\n" + << "\t\treal_t fz = vIn.z;\n" + << "\t\treal_t a0 = M_PI / " << n << ";\n" + << "\t\treal_t len = 1 / Zeps(cos(a0));\n" + << "\t\treal_t d = " << rad << " * sin(a0) * len;\n" + << "\t\treal_t angle = floor(precalcAtanyx * " << coeff << ") / " << coeff << " + M_PI / " << n << ";\n" + << "\t\treal_t x0 = cos(angle) * len;\n" + << "\t\treal_t y0 = sin(angle) * len;\n" + << "\n" + << "\t\tif (sqrt(Sqr(vIn.x - x0) + Sqr(vIn.y - y0)) < d)\n" + << "\t\t{\n" + << "\t\t if (" << zero << " > 1.5)\n" + << "\t\t {\n" + << "\t\t fx = x0;\n" + << "\t\t fy = y0;\n" + << "\t\t fz = 0;\n" + << "\t\t }\n" + << "\t\t else\n" + << "\t\t {\n" + << "\t\t if (" << zero << " > 0.5)\n" + << "\t\t {\n" + << "\t\t fx = 0;\n" + << "\t\t fy = 0;\n" + << "\t\t fz = 0;\n" + << "\t\t }\n" + << "\t\t else\n" + << "\t\t {\n" + << "\t\t real_t rangle = atan2(vIn.y - y0, vIn.x - x0);\n" + << "\t\t fx = x0 + cos(rangle) * d;\n" + << "\t\t fy = y0 + sin(rangle) * d;\n" + << "\t\t fz = 0;\n" + << "\t\t }\n" + << "\t\t }\n" + << "\t\t}\n" + << "\n" + << "\t\tvOut.x = fx * " << weight << ";\n" + << "\t\tvOut.y = fy * " << weight << ";\n" + << "\t\tvOut.z = fz * " << weight << ";\n" + << "\t}\n"; + return ss.str(); + } + + virtual void Precalc() override + { + m_N = Zeps(m_N); + m_Coeff = Zeps(m_N * T(0.5) / T(M_PI)); + } + + virtual vector OpenCLGlobalFuncNames() const override + { + return vector { "Zeps", "Sqr" }; + } + +protected: + void Init() + { + string prefix = Prefix(); + m_Params.clear(); + m_Params.push_back(ParamWithName(&m_N, prefix + "hypercrop_n", 4)); + m_Params.push_back(ParamWithName(&m_Rad, prefix + "hypercrop_rad", 1)); + m_Params.push_back(ParamWithName(&m_Zero, prefix + "hypercrop_zero")); + m_Params.push_back(ParamWithName(true, &m_Coeff, prefix + "hypercrop_coeff"));//Precalc. + } + +private: + T m_N; + T m_Rad; + T m_Zero; + T m_Coeff;//Precalc. +}; + +/// +/// hypershift2. +/// +template +class Hypershift2Variation : public ParametricVariation +{ +public: + Hypershift2Variation(T weight = 1.0) : ParametricVariation("hypershift2", eVariationId::VAR_HYPERSHIFT2, weight) + { + Init(); + } + + PARVARCOPY(Hypershift2Variation) + + virtual void Func(IteratorHelper& helper, Point& outPoint, QTIsaac& rand) override + { + T fx = helper.In.x * m_Scale2; + T fy = helper.In.y * m_Scale2; + T rad = 1 / Zeps(fx * fx + fy * fy); + T x = rad * fx + m_Shift; + T y = rad * fy; + rad = m_Weight * m_Scale / Zeps(x * x + y * y); + T angle = ((rand.Rand() % int(m_P)) * 2 + 1) * T(M_PI) / m_P; + T X = rad * x + m_Shift; + T Y = rad * y; + T cosa = std::cos(angle); + T sina = std::sin(angle); + + if (m_VarType == eVariationType::VARTYPE_REG) + outPoint.m_X = outPoint.m_Y = outPoint.m_Z = 0;//This variation assigns, instead of summing, so order will matter. + + helper.Out.x = cosa * X - sina * Y; + helper.Out.y = sina * X + cosa * Y; + helper.Out.z = helper.In.z * rad; + } + + virtual string OpenCLString() const override + { + ostringstream ss, ss2; + intmax_t i = 0, varIndex = IndexInXform(); + ss2 << "_" << XformIndexInEmber() << "]"; + string index = ss2.str(); + string weight = WeightDefineString(); + string p = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string q = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string shift = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string scale = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + string scale2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index; + ss << "\t{\n" + << "\t\treal_t fx = vIn.x * " << scale2 << ";\n" + << "\t\treal_t fy = vIn.y * " << scale2 << ";\n" + << "\t\treal_t rad = 1 / Zeps(fx * fx + fy * fy);\n" + << "\t\treal_t x = rad * fx + " << shift << ";\n" + << "\t\treal_t y = rad * fy;\n" + << "\t\trad = " << weight << " * " << shift << " / Zeps(x * x + y * y);\n" + << "\t\treal_t angle = ((MwcNext(mwc) % (int)" << p << ") * 2 + 1) * M_PI / " << p << ";\n" + << "\t\treal_t X = rad * x + " << shift << ";\n" + << "\t\treal_t Y = rad * y;\n" + << "\t\treal_t cosa = cos(angle);\n" + << "\t\treal_t sina = sin(angle);\n"; + + if (m_VarType == eVariationType::VARTYPE_REG) + ss << "\t\toutPoint->m_X = outPoint->m_Y = outPoint->m_Z = 0;\n"; + + ss << "\t\tvOut.x = cosa * X - sina * Y;\n" + << "\t\tvOut.y = sina * X + cosa * Y;\n" + << "\t\tvOut.z = vIn.z * rad;\n" + << "\t}\n"; + return ss.str(); + } + + virtual void Precalc() override + { + T pq = T(M_PI) / m_Q; + T pp = T(M_PI) / m_P; + T spq = std::sin(pq); + T spp = std::sin(pp); + m_Shift = std::sin(T(M_PI) * T(0.5) - pq - pp); + m_Shift = m_Shift / std::sqrt(1 - Sqr(spq) - Sqr(spp)); + m_Scale2 = 1 / std::sqrt(Sqr(sin(T(M_PI) / 2 + pp)) / Sqr(spq) - 1); + m_Scale2 = m_Scale2 * (std::sin(T(M_PI) / 2 + pp) / spq - 1); + m_Scale = 1 - m_Shift * m_Shift; + } + + virtual vector OpenCLGlobalFuncNames() const override + { + return vector { "Zeps" }; + } + +protected: + void Init() + { + string prefix = Prefix(); + m_Params.clear(); + m_Params.push_back(ParamWithName(&m_P, prefix + "hypershift2_p", 3, eParamType::INTEGER_NONZERO)); + m_Params.push_back(ParamWithName(&m_Q, prefix + "hypershift2_q", 7, eParamType::INTEGER_NONZERO)); + m_Params.push_back(ParamWithName(true, &m_Shift, prefix + "hypershift2_shift"));//Precalc. + m_Params.push_back(ParamWithName(true, &m_Scale, prefix + "hypershift2_scale")); + m_Params.push_back(ParamWithName(true, &m_Scale2, prefix + "hypershift2_scale2")); + } + +private: + T m_P; + T m_Q; + T m_Shift;//Precalc. + T m_Scale; + T m_Scale2; +}; + MAKEPREPOSTPARVAR(Splits3D, splits3D, SPLITS3D) MAKEPREPOSTPARVAR(Waves2B, waves2b, WAVES2B) MAKEPREPOSTPARVAR(JacCn, jac_cn, JAC_CN) @@ -2035,4 +2285,6 @@ MAKEPREPOSTPARVAR(Helix, helix, HELIX) MAKEPREPOSTPARVAR(Sphereblur, sphereblur, SPHEREBLUR) MAKEPREPOSTPARVAR(Cpow3, cpow3, CPOW3) MAKEPREPOSTPARVAR(Concentric, concentric, CONCENTRIC) +MAKEPREPOSTPARVAR(Hypercrop, hypercrop, HYPERCROP) +MAKEPREPOSTPARVAR(Hypershift2, hypershift2, HYPERSHIFT2) } diff --git a/Source/EmberCL/OpenCLInfo.cpp b/Source/EmberCL/OpenCLInfo.cpp index 73ceff0..75e1a33 100644 --- a/Source/EmberCL/OpenCLInfo.cpp +++ b/Source/EmberCL/OpenCLInfo.cpp @@ -43,7 +43,7 @@ OpenCLInfo::OpenCLInfo() if (!platformOk) { m_Platforms.push_back(platforms[platform]); - m_PlatformNames.push_back(platforms[platform].getInfo(nullptr) + " " + platforms[platform].getInfo(nullptr) + " " + platforms[platform].getInfo(nullptr)); + m_PlatformNames.push_back(platforms[platform].getInfo(nullptr).c_str() + " "s + platforms[platform].getInfo(nullptr).c_str() + " "s + platforms[platform].getInfo(nullptr).c_str()); workingPlatformIndex++; platformOk = true; } @@ -58,7 +58,7 @@ OpenCLInfo::OpenCLInfo() } m_Devices.back().push_back(devices[platform][device]); - m_DeviceNames.back().push_back(devices[platform][device].getInfo(nullptr) + " " + devices[platform][device].getInfo(nullptr));// + " " + devices[platform][device].getInfo()); + m_DeviceNames.back().push_back(devices[platform][device].getInfo(nullptr).c_str() + " "s + devices[platform][device].getInfo(nullptr).c_str());// + " " + devices[platform][device].getInfo().c_str()); m_AllDeviceNames.push_back(m_DeviceNames.back().back()); m_DeviceIndices.push_back(pair(workingPlatformIndex, workingDeviceIndex++)); m_Init = true;//If at least one platform and device succeeded, OpenCL is ok. It's now ok to begin building and running programs. @@ -183,6 +183,29 @@ size_t OpenCLInfo::TotalDeviceIndex(size_t platform, size_t device) const return index; } +/// +/// Get a pointer to a device based on its ID. +/// +/// The device ID +/// Stores the platform index of the device if found. +/// Stores the device index of the device if found. +/// A pointer to the device if found, else nullptr. +const cl::Device* OpenCLInfo::DeviceFromId(cl_device_id id, size_t& platform, size_t& device) const +{ + for (auto& p : m_DeviceIndices) + { + if (m_Devices[p.first][p.second]() == id) + { + platform = p.first; + device = p.second; + return &(m_Devices[p.first][p.second]); + } + } + + platform = device = 0; + return nullptr; +} + /// /// Create a context that is optionally shared with OpenGL and place it in the /// passed in context ref parameter. @@ -209,6 +232,7 @@ bool OpenCLInfo::CreateContext(const cl::Platform& platform, cl::Context& contex context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);//May need to tinker with this on Mac. #else #if defined WIN32 + //::wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext()); cl_context_properties props[] = { CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), @@ -269,7 +293,7 @@ string OpenCLInfo::DumpInfo() const for (size_t device = 0; device < m_Devices[platform].size(); device++) { os << "Device " << device << ": " << DeviceName(platform, device); - os << "\nCL_DEVICE_OPENCL_C_VERSION: " << GetInfo(platform, device, CL_DEVICE_OPENCL_C_VERSION); + os << "\nCL_DEVICE_OPENCL_C_VERSION: " << GetInfo(platform, device, CL_DEVICE_OPENCL_C_VERSION).c_str(); os << "\nCL_DEVICE_LOCAL_MEM_SIZE: " << GetInfo(platform, device, CL_DEVICE_LOCAL_MEM_SIZE); os << "\nCL_DEVICE_LOCAL_MEM_TYPE: " << GetInfo(platform, device, CL_DEVICE_LOCAL_MEM_TYPE); os << "\nCL_DEVICE_MAX_COMPUTE_UNITS: " << GetInfo(platform, device, CL_DEVICE_MAX_COMPUTE_UNITS); diff --git a/Source/EmberCL/OpenCLInfo.h b/Source/EmberCL/OpenCLInfo.h index cc466f2..75dfd9b 100644 --- a/Source/EmberCL/OpenCLInfo.h +++ b/Source/EmberCL/OpenCLInfo.h @@ -29,6 +29,7 @@ public: const vector& AllDeviceNames() const; const vector& DeviceNames(size_t platform) const; size_t TotalDeviceIndex(size_t platform, size_t device) const; + const cl::Device* DeviceFromId(cl_device_id id, size_t& platform, size_t& device) const; string DumpInfo() const; bool Ok() const; bool CreateContext(const cl::Platform& platform, cl::Context& context, bool shared); diff --git a/Source/EmberCL/OpenCLWrapper.cpp b/Source/EmberCL/OpenCLWrapper.cpp index c5c4ca5..cf26ec3 100644 --- a/Source/EmberCL/OpenCLWrapper.cpp +++ b/Source/EmberCL/OpenCLWrapper.cpp @@ -387,7 +387,6 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con { if (shared) { - //::wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext()); cl::ImageGL imageGL(m_Context, flags, GL_TEXTURE_2D, 0, texName, &err); NamedImage2DGL namedImageGL(imageGL, name); diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index 242ac93..fd43163 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -29,50 +29,11 @@ RendererCL::RendererCL(const vector>& devices, m_DEOpenCLKernelCreator(typeid(T) == typeid(double), false), m_FinalAccumOpenCLKernelCreator(typeid(T) == typeid(double)) { - Init(); - Init(devices, shared, outputTexID); -} - -/// -/// Initialization of fields, no OpenCL initialization is done here. -template -void RendererCL::Init() -{ - m_Init = false; - m_DoublePrecision = typeid(T) == typeid(double); - //Buffer names. - m_EmberBufferName = "Ember"; - m_XformsBufferName = "Xforms"; - m_ParVarsBufferName = "ParVars"; - m_GlobalSharedBufferName = "GlobalShared"; - m_SeedsBufferName = "Seeds"; - m_DistBufferName = "Dist"; - m_CarToRasBufferName = "CarToRas"; - m_DEFilterParamsBufferName = "DEFilterParams"; - m_SpatialFilterParamsBufferName = "SpatialFilterParams"; - m_DECoefsBufferName = "DECoefs"; - m_DEWidthsBufferName = "DEWidths"; - m_DECoefIndicesBufferName = "DECoefIndices"; - m_SpatialFilterCoefsBufferName = "SpatialFilterCoefs"; - m_CurvesCsaName = "CurvesCsa"; - m_HostBufferName = "Host"; - m_HistBufferName = "Hist"; - m_AccumBufferName = "Accum"; - m_FinalImageName = "Final"; - m_PointsBufferName = "Points"; - //It's critical that these numbers never change. They are - //based on the cuburn model of each kernel launch containing - //256 threads. 32 wide by 8 high. Everything done in the OpenCL - //iteraion kernel depends on these dimensions. - m_IterCountPerKernel = 256; - m_IterBlockWidth = 32; - m_IterBlockHeight = 8; - m_IterBlocksWide = 64; - m_IterBlocksHigh = 2; m_PaletteFormat.image_channel_order = CL_RGBA; m_PaletteFormat.image_channel_data_type = CL_FLOAT; m_FinalFormat.image_channel_order = CL_RGBA; m_FinalFormat.image_channel_data_type = CL_FLOAT; + Init(devices, shared, outputTexID); } /// @@ -97,11 +58,12 @@ bool RendererCL::Init(const vector>& devices, b return false; bool b = false; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; auto& zeroizeProgram = m_IterOpenCLKernelCreator.ZeroizeKernel(); auto& sumHistProgram = m_IterOpenCLKernelCreator.SumHistKernel(); ostringstream os; m_Init = false; + m_Shared = false; m_Devices.clear(); m_Devices.reserve(devices.size()); m_OutputTexID = outputTexID; @@ -115,11 +77,11 @@ bool RendererCL::Init(const vector>& devices, b if ((b = cld->Init()))//Build a simple program to ensure OpenCL is working right. { - if (b && !(b = cld->m_Wrapper.AddProgram(m_IterOpenCLKernelCreator.ZeroizeEntryPoint(), zeroizeProgram, m_IterOpenCLKernelCreator.ZeroizeEntryPoint(), m_DoublePrecision))) { AddToReport(loc); } + if (b && !(b = cld->m_Wrapper.AddProgram(m_IterOpenCLKernelCreator.ZeroizeEntryPoint(), zeroizeProgram, m_IterOpenCLKernelCreator.ZeroizeEntryPoint(), m_DoublePrecision))) { ErrorStr(loc, "Failed to init zeroize program: "s + cld->ErrorReportString(), cld.get()); } - if (b && !(b = cld->m_Wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, 256, 1, 0, nullptr))) { AddToReport(loc); } + if (b && !(b = cld->m_Wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, 256, 1, 0, nullptr))) { ErrorStr(loc, "Failed to init palette buffer: "s + cld->ErrorReportString(), cld.get()); } - if (b && !(b = cld->m_Wrapper.AddAndWriteBuffer(m_GlobalSharedBufferName, m_GlobalShared.second.data(), m_GlobalShared.second.size() * sizeof(m_GlobalShared.second[0])))) { AddToReport(loc); }//Empty at start, will be filled in later if needed. + if (b && !(b = cld->m_Wrapper.AddAndWriteBuffer(m_GlobalSharedBufferName, m_GlobalShared.second.data(), m_GlobalShared.second.size() * sizeof(m_GlobalShared.second[0])))) { ErrorStr(loc, "Failed to init global shared buffer: "s + cld->ErrorReportString(), cld.get()); }//Empty at start, will be filled in later if needed. if (b) { @@ -127,33 +89,35 @@ bool RendererCL::Init(const vector>& devices, b } else { - os << loc << ": failed to init platform " << devices[i].first << ", device " << devices[i].second; - AddToReport(loc); + ErrorStr(loc, "Failed to init programs for platform", cld.get()); break; } } + else + { + ErrorStr(loc, "Failed to init device, "s + cld->ErrorReportString(), cld.get()); + break; + } } catch (const std::exception& e) { - os << loc << ": failed to init platform " << devices[i].first << ", device " << devices[i].second << ": " << e.what(); - AddToReport(os.str()); + ErrorStr(loc, "Failed to init platform: "s + e.what(), nullptr); } catch (...) { - os << loc << ": failed to init platform " << devices[i].first << ", device " << devices[i].second; - AddToReport(os.str()); + ErrorStr(loc, "Failed to init platform with unknown exception", nullptr); } } - if (b && m_Devices.size() == devices.size()) + if (b && (m_Devices.size() == devices.size())) { auto& firstWrapper = m_Devices[0]->m_Wrapper; m_DEOpenCLKernelCreator = DEOpenCLKernelCreator(m_DoublePrecision, m_Devices[0]->Nvidia()); //Build a simple program to ensure OpenCL is working right. - if (b && !(b = firstWrapper.AddProgram(m_DEOpenCLKernelCreator.LogScaleAssignDEEntryPoint(), m_DEOpenCLKernelCreator.LogScaleAssignDEKernel(), m_DEOpenCLKernelCreator.LogScaleAssignDEEntryPoint(), m_DoublePrecision))) { AddToReport(loc); } + if (b && !(b = firstWrapper.AddProgram(m_DEOpenCLKernelCreator.LogScaleAssignDEEntryPoint(), m_DEOpenCLKernelCreator.LogScaleAssignDEKernel(), m_DEOpenCLKernelCreator.LogScaleAssignDEEntryPoint(), m_DoublePrecision))) { ErrorStr(loc, "failed to init log scale program", m_Devices[0].get()); } - if (b && !(b = firstWrapper.AddProgram(m_IterOpenCLKernelCreator.SumHistEntryPoint(), sumHistProgram, m_IterOpenCLKernelCreator.SumHistEntryPoint(), m_DoublePrecision))) { AddToReport(loc); } + if (b && !(b = firstWrapper.AddProgram(m_IterOpenCLKernelCreator.SumHistEntryPoint(), sumHistProgram, m_IterOpenCLKernelCreator.SumHistEntryPoint(), m_DoublePrecision))) { ErrorStr(loc, "Failed to init sum histogram program", m_Devices[0].get()); } if (b) { @@ -168,16 +132,15 @@ bool RendererCL::Init(const vector>& devices, b FillSeeds(); for (size_t device = 0; device < m_Devices.size(); device++) - if (b && !(b = m_Devices[device]->m_Wrapper.AddAndWriteBuffer(m_SeedsBufferName, reinterpret_cast(m_Seeds[device].data()), SizeOf(m_Seeds[device])))) { AddToReport(loc); break; } + if (b && !(b = m_Devices[device]->m_Wrapper.AddAndWriteBuffer(m_SeedsBufferName, reinterpret_cast(m_Seeds[device].data()), SizeOf(m_Seeds[device])))) { ErrorStr(loc, "Failed to init seeds buffer", m_Devices[device].get()); break; } } + m_Shared = shared; m_Init = b; } else { - m_Devices.clear(); - os << loc << ": failed to init all devices and platforms."; - AddToReport(os.str()); + ErrorStr(loc, "Failed to init all devices and platforms", nullptr); } return m_Init; @@ -192,7 +155,7 @@ template bool RendererCL::SetOutputTexture(GLuint outputTexID) { bool success = true; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (!m_Devices.empty()) { @@ -202,7 +165,7 @@ bool RendererCL::SetOutputTexture(GLuint outputTexID) if (!firstWrapper.AddAndWriteImage(m_FinalImageName, CL_MEM_WRITE_ONLY, m_FinalFormat, FinalRasW(), FinalRasH(), 0, nullptr, firstWrapper.Shared(), m_OutputTexID)) { - AddToReport(loc); + ErrorStr(loc, "Failed to init set output texture", m_Devices[0].get()); success = false; } @@ -293,10 +256,10 @@ template bool RendererCL::ClearHist() { bool b = !m_Devices.empty(); - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; for (size_t i = 0; i < m_Devices.size(); i++) - if (b && !(b = ClearBuffer(i, m_HistBufferName, uint(SuperRasW()), uint(SuperRasH()), sizeof(v4bT)))) { AddToReport(loc); break; } + if (b && !(b = ClearBuffer(i, m_HistBufferName, uint(SuperRasW()), uint(SuperRasH()), sizeof(v4bT)))) { ErrorStr(loc, "Failed to clear histogram", m_Devices[i].get()); break; } return b; } @@ -310,9 +273,9 @@ template bool RendererCL::ClearHist(size_t device) { bool b = device < m_Devices.size(); - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; - if (b && !(b = ClearBuffer(device, m_HistBufferName, uint(SuperRasW()), uint(SuperRasH()), sizeof(v4bT)))) { AddToReport(loc); } + if (b && !(b = ClearBuffer(device, m_HistBufferName, uint(SuperRasW()), uint(SuperRasH()), sizeof(v4bT)))) { ErrorStr(loc, "Failed to clear histogram", m_Devices[device].get()); } return b; } @@ -338,10 +301,10 @@ template bool RendererCL::WritePoints(size_t device, vector>& vec) { bool b = false; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (device < m_Devices.size()) - if (!(b = m_Devices[device]->m_Wrapper.WriteBuffer(m_PointsBufferName, reinterpret_cast(vec.data()), SizeOf(vec)))) { AddToReport(loc); } + if (!(b = m_Devices[device]->m_Wrapper.WriteBuffer(m_PointsBufferName, reinterpret_cast(vec.data()), SizeOf(vec)))) { ErrorStr(loc, "Failed to write points buffer", m_Devices[device].get()); } return b; } @@ -423,6 +386,7 @@ template bool RendererCL::ClearFinal() { vector v; + static std::string loc = __FUNCTION__; if (!m_Devices.empty()) { @@ -431,18 +395,16 @@ bool RendererCL::ClearFinal() if (this->PrepFinalAccumVector(v)) { - bool b = wrapper.WriteImage2D(index, wrapper.Shared(), FinalRasW(), FinalRasH(), 0, v.data()); - - if (!b) - AddToReport(__FUNCTION__); - - return b; + if (!wrapper.WriteImage2D(index, wrapper.Shared(), FinalRasW(), FinalRasH(), 0, v.data())) + ErrorStr(loc, "Failed to clear final buffer", m_Devices[0].get()); + else + return false; } else return false; } - else - return false; + + return false; } /// @@ -469,18 +431,6 @@ bool RendererCL::Ok() const return !m_Devices.empty() && m_Init; } -/// -/// Clear the error report for this class as well as the OpenCLWrapper members of each device. -/// -template -void RendererCL::ClearErrorReport() -{ - EmberReport::ClearErrorReport(); - - for (auto& device : m_Devices) - device->m_Wrapper.ClearErrorReport(); -} - /// /// The sub batch size for OpenCL will always be how many /// iterations are ran per kernel call. The caller can't @@ -514,20 +464,23 @@ template bool RendererCL::CreateDEFilter(bool& newAlloc) { bool b = true; + static std::string loc = __FUNCTION__; if (!m_Devices.empty() && Renderer::CreateDEFilter(newAlloc)) { //Copy coefs and widths here. Convert and copy the other filter params right before calling the filtering kernel. if (newAlloc) { - const char* loc = __FUNCTION__; auto& wrapper = m_Devices[0]->m_Wrapper; - if (b && !(b = wrapper.AddAndWriteBuffer(m_DECoefsBufferName, reinterpret_cast(const_cast(m_DensityFilter->Coefs())), m_DensityFilter->CoefsSizeBytes()))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_DECoefsBufferName, reinterpret_cast(const_cast(m_DensityFilter->Coefs())), m_DensityFilter->CoefsSizeBytes()))) + ErrorStr(loc, "Failed to set DE coefficients buffer", m_Devices[0].get()); - if (b && !(b = wrapper.AddAndWriteBuffer(m_DEWidthsBufferName, reinterpret_cast(const_cast(m_DensityFilter->Widths())), m_DensityFilter->WidthsSizeBytes()))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_DEWidthsBufferName, reinterpret_cast(const_cast(m_DensityFilter->Widths())), m_DensityFilter->WidthsSizeBytes()))) + ErrorStr(loc, "Failed to set DE widths buffer", m_Devices[0].get()); - if (b && !(b = wrapper.AddAndWriteBuffer(m_DECoefIndicesBufferName, reinterpret_cast(const_cast(m_DensityFilter->CoefIndices())), m_DensityFilter->CoefsIndicesSizeBytes()))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_DECoefIndicesBufferName, reinterpret_cast(const_cast(m_DensityFilter->CoefIndices())), m_DensityFilter->CoefsIndicesSizeBytes()))) + ErrorStr(loc, "Failed to set DE coefficient indices buffer", m_Devices[0].get()); } } else @@ -546,11 +499,13 @@ template bool RendererCL::CreateSpatialFilter(bool& newAlloc) { bool b = true; + static std::string loc = __FUNCTION__; if (!m_Devices.empty() && Renderer::CreateSpatialFilter(newAlloc)) { if (newAlloc) - if (!(b = m_Devices[0]->m_Wrapper.AddAndWriteBuffer(m_SpatialFilterCoefsBufferName, reinterpret_cast(m_SpatialFilter->Filter()), m_SpatialFilter->BufferSizeBytes()))) { AddToReport(__FUNCTION__); } + if (!(b = m_Devices[0]->m_Wrapper.AddAndWriteBuffer(m_SpatialFilterCoefsBufferName, reinterpret_cast(m_SpatialFilter->Filter()), m_SpatialFilter->BufferSizeBytes()))) + ErrorStr(loc, "Failed to set patial filter coefficients buffer", m_Devices[0].get()); } else b = false; @@ -568,6 +523,25 @@ eRendererType RendererCL::RendererType() const return eRendererType::OPENCL_RENDERER; } +/// +/// Get whether the renderer uses a shared texture with OpenGL. +/// +/// True if shared, else false. +template +bool RendererCL::Shared() const { return m_Shared; } + +/// +/// Clear the error report for this class as well as the OpenCLWrapper members of each device. +/// +template +void RendererCL::ClearErrorReport() +{ + EmberReport::ClearErrorReport(); + + for (auto& device : m_Devices) + device->m_Wrapper.ClearErrorReport(); +} + /// /// Concatenate and return the error report for this class and the /// OpenCLWrapper member of each device as a single string. @@ -579,7 +553,7 @@ string RendererCL::ErrorReportString() auto s = EmberReport::ErrorReportString(); for (auto& device : m_Devices) - s += device->m_Wrapper.ErrorReportString(); + s += device->ErrorReportString(); return s; } @@ -596,7 +570,7 @@ vector RendererCL::ErrorReport() for (auto& device : m_Devices) { - auto s = device->m_Wrapper.ErrorReport(); + auto s = device->ErrorReport(); ours.insert(ours.end(), s.begin(), s.end()); } @@ -615,14 +589,18 @@ template bool RendererCL::RandVec(vector>& randVec) { bool b = Renderer::RandVec(randVec); - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (!m_Devices.empty()) { FillSeeds(); for (size_t device = 0; device < m_Devices.size(); device++) - if (b && !(b = m_Devices[device]->m_Wrapper.AddAndWriteBuffer(m_SeedsBufferName, reinterpret_cast(m_Seeds[device].data()), SizeOf(m_Seeds[device])))) { AddToReport(loc); break; } + if (b && !(b = m_Devices[device]->m_Wrapper.AddAndWriteBuffer(m_SeedsBufferName, reinterpret_cast(m_Seeds[device].data()), SizeOf(m_Seeds[device])))) + { + ErrorStr(loc, "Failed to set randoms buffer", m_Devices[device].get()); + break; + } } else b = false; @@ -664,32 +642,32 @@ bool RendererCL::Alloc(bool histOnly) m_XformsCL.resize(m_Ember.TotalXformCount()); bool b = true; size_t size = SuperSize() * sizeof(v4bT);//Size of histogram and density filter buffer. - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; auto& wrapper = m_Devices[0]->m_Wrapper; - if (b && !(b = wrapper.AddBuffer(m_DEFilterParamsBufferName, sizeof(m_DensityFilterCL)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddBuffer(m_DEFilterParamsBufferName, sizeof(m_DensityFilterCL)))) { ErrorStr(loc, "Failed to set DE filter parameters buffer", m_Devices[0].get()); } - if (b && !(b = wrapper.AddBuffer(m_SpatialFilterParamsBufferName, sizeof(m_SpatialFilterCL)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddBuffer(m_SpatialFilterParamsBufferName, sizeof(m_SpatialFilterCL)))) { ErrorStr(loc, "Failed to set spatial filter parameters buffer", m_Devices[0].get()); } - if (b && !(b = wrapper.AddBuffer(m_CurvesCsaName, SizeOf(m_Csa)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddBuffer(m_CurvesCsaName, SizeOf(m_Csa)))) { ErrorStr(loc, "Failed to set curves buffer", m_Devices[0].get()); } - if (b && !(b = wrapper.AddBuffer(m_AccumBufferName, size))) { AddToReport(loc); }//Accum buffer. + if (b && !(b = wrapper.AddBuffer(m_AccumBufferName, size))) { ErrorStr(loc, "Failed to set accum buffer", m_Devices[0].get()); } for (auto& device : m_Devices) { - if (b && !(b = device->m_Wrapper.AddBuffer(m_EmberBufferName, sizeof(m_EmberCL)))) { AddToReport(loc); break; } + if (b && !(b = device->m_Wrapper.AddBuffer(m_EmberBufferName, sizeof(m_EmberCL)))) { ErrorStr(loc, "Failed to set ember buffer", device.get()); break; } - if (b && !(b = device->m_Wrapper.AddBuffer(m_XformsBufferName, SizeOf(m_XformsCL)))) { AddToReport(loc); break; } + if (b && !(b = device->m_Wrapper.AddBuffer(m_XformsBufferName, SizeOf(m_XformsCL)))) { ErrorStr(loc, "Failed to set xforms buffer", device.get()); break; } - if (b && !(b = device->m_Wrapper.AddBuffer(m_ParVarsBufferName, 128 * sizeof(T)))) { AddToReport(loc); break; } + if (b && !(b = device->m_Wrapper.AddBuffer(m_ParVarsBufferName, 128 * sizeof(T)))) { ErrorStr(loc, "Failed to set parametric variations buffer", device.get()); break; } - if (b && !(b = device->m_Wrapper.AddBuffer(m_DistBufferName, CHOOSE_XFORM_GRAIN))) { AddToReport(loc); break; }//Will be resized for xaos. + if (b && !(b = device->m_Wrapper.AddBuffer(m_DistBufferName, CHOOSE_XFORM_GRAIN))) { ErrorStr(loc, "Failed to set xforms distribution buffer", device.get()); break; }//Will be resized for xaos. - if (b && !(b = device->m_Wrapper.AddBuffer(m_CarToRasBufferName, sizeof(m_CarToRasCL)))) { AddToReport(loc); break; } + if (b && !(b = device->m_Wrapper.AddBuffer(m_CarToRasBufferName, sizeof(m_CarToRasCL)))) { ErrorStr(loc, "Failed to set cartesian to raster buffer", device.get()); break; } - if (b && !(b = device->m_Wrapper.AddBuffer(m_HistBufferName, size))) { AddToReport(loc); break; }//Histogram. Will memset to zero later. + if (b && !(b = device->m_Wrapper.AddBuffer(m_HistBufferName, size))) { ErrorStr(loc, "Failed to set histogram buffer", device.get()); break; }//Histogram. Will memset to zero later. - if (b && !(b = device->m_Wrapper.AddBuffer(m_PointsBufferName, IterGridKernelCount() * sizeof(PointCL)))) { AddToReport(loc); break; }//Points between iter calls. + if (b && !(b = device->m_Wrapper.AddBuffer(m_PointsBufferName, IterGridKernelCount() * sizeof(PointCL)))) { ErrorStr(loc, "Failed to set points buffer", device.get()); break; }//Points between iter calls. //Global shared is allocated once and written when building the kernel. } @@ -699,7 +677,7 @@ bool RendererCL::Alloc(bool histOnly) LeaveResize(); - if (b && !(b = SetOutputTexture(m_OutputTexID))) { AddToReport(loc); } + if (b && !(b = SetOutputTexture(m_OutputTexID))) { ErrorStr(loc, "Failed to set output texture", m_Devices[0].get()); } return b; } @@ -765,23 +743,28 @@ eRenderStatus RendererCL::GaussianDensityFilter() /// /// Run final accumulation on the primary device. -/// If pixels is nullptr, the output will remain in the OpenCL 2D image. -/// However, if pixels is not nullptr, the output will be copied. This is -/// useful when rendering in OpenCL, but saving the output to a file. +/// If the first device is not shared, the output will remain in the OpenCL 2D image and no copying will take place. +/// If it is shared, then the image will be copied into the pixels vector. /// -/// The pixels to copy the final image to if not nullptr +/// The pixel vector to allocate and store the final image in /// Offset in the buffer to store the pixels to -/// True if success and not aborted, else false. +/// True if not prematurely aborted, else false. template -eRenderStatus RendererCL::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset) +eRenderStatus RendererCL::AccumulatorToFinalImage(vector& pixels, size_t finalOffset) { auto status = RunFinalAccum(); - if (status == eRenderStatus::RENDER_OK && pixels && !m_Devices.empty() && !m_Devices[0]->m_Wrapper.Shared()) + if (status == eRenderStatus::RENDER_OK && !m_Devices.empty() && !m_Devices[0]->m_Wrapper.Shared()) { - pixels += finalOffset; + if (PrepFinalAccumVector(pixels)) + { + auto p = pixels.data(); + p += finalOffset; - if (!ReadFinal(pixels)) + if (!ReadFinal(p)) + status = eRenderStatus::RENDER_ERROR; + } + else status = eRenderStatus::RENDER_ERROR; } @@ -805,7 +788,7 @@ EmberStats RendererCL::Iterate(size_t iterCount, size_t temporalSamp { bool b = true; EmberStats stats;//Do not record bad vals with with GPU. If the user needs to investigate bad vals, use the CPU. - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; //Only need to do this once on the beginning of a new render. Last iter will always be 0 at the beginning of a full render or temporal sample. if (m_LastIter == 0) @@ -825,19 +808,34 @@ EmberStats RendererCL::Iterate(size_t iterCount, size_t temporalSamp auto& wrapper = device->m_Wrapper; if (b && !(b = wrapper.WriteBuffer(m_EmberBufferName, reinterpret_cast(&m_EmberCL), sizeof(m_EmberCL)))) + { + ErrorStr(loc, "Write ember buffer failed", device.get()); break; + } if (b && !(b = wrapper.WriteBuffer(m_XformsBufferName, reinterpret_cast(m_XformsCL.data()), sizeof(m_XformsCL[0]) * m_XformsCL.size()))) + { + ErrorStr(loc, "Write xforms buffer failed", device.get()); break; + } if (b && !(b = wrapper.AddAndWriteBuffer(m_DistBufferName, reinterpret_cast(const_cast(XformDistributions())), XformDistributionsSize())))//Will be resized for xaos. + { + ErrorStr(loc, "Write xforms distribution buffer failed", device.get()); break; + } if (b && !(b = wrapper.WriteBuffer(m_CarToRasBufferName, reinterpret_cast(&m_CarToRasCL), sizeof(m_CarToRasCL)))) + { + ErrorStr(loc, "Write cartesian to raster buffer failed", device.get()); break; + } if (b && !(b = wrapper.AddAndWriteImage("Palette", CL_MEM_READ_ONLY, m_PaletteFormat, m_Dmap.m_Entries.size(), 1, 0, m_Dmap.m_Entries.data()))) + { + ErrorStr(loc, "Write palette buffer failed", device.get()); break; + } if (b) { @@ -846,8 +844,13 @@ EmberStats RendererCL::Iterate(size_t iterCount, size_t temporalSamp //Don't know the size of the parametric varations parameters buffer until the ember is examined. //So set it up right before the run. if (!m_Params.second.empty()) + { if (!wrapper.AddAndWriteBuffer(m_ParVarsBufferName, m_Params.second.data(), m_Params.second.size() * sizeof(m_Params.second[0]))) + { + ErrorStr(loc, "Write parametric variations buffer failed", device.get()); break; + } + } } else break; @@ -873,7 +876,7 @@ EmberStats RendererCL::Iterate(size_t iterCount, size_t temporalSamp else { m_Abort = true; - AddToReport(loc); + ErrorStr(loc, "Iiteration failed", nullptr); } return stats; @@ -894,7 +897,7 @@ bool RendererCL::BuildIterProgramForEmber(bool doAccum) { //Timing t; bool b = !m_Devices.empty(); - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; IterOpenCLKernelCreator::ParVarIndexDefines(m_Ember, m_Params, false, true);//Do with string and no vals. IterOpenCLKernelCreator::SharedDataIndexDefines(m_Ember, m_GlobalShared, true, true);//Do with string and vals only once on build since it won't change until another build occurs. @@ -909,7 +912,7 @@ bool RendererCL::BuildIterProgramForEmber(bool doAccum) { rlg l(m_ResizeCs);//Just use the resize CS for lack of a better one. b = false; - AddToReport(string(loc) + "()\n" + dev->m_Wrapper.DeviceName() + ":\nBuilding the following program failed: \n" + m_IterKernel + "\n"); + ErrorStr(loc, "Building the following program failed\n"s + m_IterKernel, dev); } else if (!m_GlobalShared.second.empty()) { @@ -917,7 +920,7 @@ bool RendererCL::BuildIterProgramForEmber(bool doAccum) { rlg l(m_ResizeCs);//Just use the resize CS for lack of a better one. b = false; - AddToReport(string(loc) + "()\n" + dev->m_Wrapper.DeviceName() + ":\nAdding global shared buffer failed.\n"); + ErrorStr(loc, "Adding global shared buffer failed", dev); } } }; @@ -960,7 +963,7 @@ bool RendererCL::RunIter(size_t iterCount, size_t temporalSample, si bool success = !m_Devices.empty(); uint histSuperSize = uint(SuperSize()); size_t launches = size_t(ceil(double(iterCount) / IterCountPerGrid())); - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; vector threadVec; std::atomic atomLaunchesRan; std::atomic atomItersRan, atomItersRemaining; @@ -1006,31 +1009,31 @@ bool RendererCL::RunIter(size_t iterCount, size_t temporalSample, si size_t iterCountThisLaunch = iterCountPerKernel * IterGridKernelWidth() * IterGridKernelHeight(); //cout << "itersRemaining " << itersRemaining << ", iterCountPerKernel " << iterCountPerKernel << ", iterCountThisLaunch " << iterCountThisLaunch << "\n"; - if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, iterCountPerKernel))) { AddToReport(loc); }//Number of iters for each thread to run. + if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, iterCountPerKernel))) { ErrorStr(loc, "Setting iter count argument failed", m_Devices[dev].get()); }//Number of iters for each thread to run. - if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, fuse))) { AddToReport(loc); }//Number of iters to fuse. + if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, fuse))) { ErrorStr(loc, "Setting fuse count argument failed", m_Devices[dev].get()); }//Number of iters to fuse. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_SeedsBufferName))) { AddToReport(loc); }//Seeds. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_SeedsBufferName))) { ErrorStr(loc, "Setting seeds buffer argument failed", m_Devices[dev].get()); }//Seeds. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_EmberBufferName))) { AddToReport(loc); }//Ember. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_EmberBufferName))) { ErrorStr(loc, "Setting ember buffer argument failed", m_Devices[dev].get()); }//Ember. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_XformsBufferName))) { AddToReport(loc); }//Xforms. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_XformsBufferName))) { ErrorStr(loc, "Setting xforms buffer argument failed", m_Devices[dev].get()); }//Xforms. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_ParVarsBufferName))) { AddToReport(loc); }//Parametric variation parameters. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_ParVarsBufferName))) { ErrorStr(loc, "Setting parametric variations buffer argument failed", m_Devices[dev].get()); }//Parametric variation parameters. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_GlobalSharedBufferName))) { AddToReport(loc); }//Global shared data. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_GlobalSharedBufferName))) { ErrorStr(loc, "Setting global shared buffer argument failed", m_Devices[dev].get()); }//Global shared data. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_DistBufferName))) { AddToReport(loc); }//Xform distributions. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_DistBufferName))) { ErrorStr(loc, "Setting xforms distribution buffer argument failed", m_Devices[dev].get()); }//Xform distributions. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_CarToRasBufferName))) { AddToReport(loc); }//Coordinate converter. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_CarToRasBufferName))) { ErrorStr(loc, "Setting cartesian to raster buffer argument failed", m_Devices[dev].get()); }//Coordinate converter. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HistBufferName))) { AddToReport(loc); }//Histogram. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HistBufferName))) { ErrorStr(loc, "Setting histogram buffer argument failed", m_Devices[dev].get()); }//Histogram. - if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, histSuperSize))) { AddToReport(loc); }//Histogram size. + if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, histSuperSize))) { ErrorStr(loc, "Setting histogram size argument failed", m_Devices[dev].get()); }//Histogram size. - if (b && !(b = wrapper.SetImageArg (kernelIndex, argIndex++, false, "Palette"))) { AddToReport(loc); }//Palette. + if (b && !(b = wrapper.SetImageArg (kernelIndex, argIndex++, false, "Palette"))) { ErrorStr(loc, "Setting palette argument failed", m_Devices[dev].get()); }//Palette. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_PointsBufferName))) { AddToReport(loc); }//Random start points. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_PointsBufferName))) { ErrorStr(loc, "Setting points buffer argument failed", m_Devices[dev].get()); }//Random start points. if (b && !(b = wrapper.RunKernel(kernelIndex, IterGridKernelWidth(),//Total grid dims. @@ -1042,7 +1045,7 @@ bool RendererCL::RunIter(size_t iterCount, size_t temporalSample, si { success = false; m_Abort = true; - AddToReport(loc); + ErrorStr(loc, "Error running iteration program", m_Devices[dev].get()); atomLaunchesRan.fetch_sub(1); break; } @@ -1107,9 +1110,9 @@ bool RendererCL::RunIter(size_t iterCount, size_t temporalSample, si if (m_Devices.size() > 1)//Determine whether/when to sum histograms of secondary devices with the primary. { - if (((TemporalSamples() == 1) || (temporalSample == TemporalSamples() - 1)) &&//If there are no temporal samples (not animating), or the current one is the last... + if (((TemporalSamples() == 1) || (temporalSample == TemporalSamples() - 1)) &&//If there are no temporal samples (not animating), or the current one is the last... (probably doesn't matter anymore since we never use multiple renders for a single frame when animating, instead each frame gets its own renderer). ((m_LastIter + itersRan) >= ItersPerTemporalSample()))//...and the required number of iters for that sample have completed... - if (success && !(success = SumDeviceHist())) { AddToReport(loc); }//...read the histogram from the secondary devices and sum them to the primary. + if (success && !(success = SumDeviceHist())) { ErrorStr(loc, "Summing histograms failed", nullptr); }//...read the histogram from the secondary devices and sum them to the primary. } //t2.Toc(__FUNCTION__); @@ -1125,12 +1128,12 @@ eRenderStatus RendererCL::RunLogScaleFilter() { //Timing t(4); bool b = !m_Devices.empty(); + static std::string loc = __FUNCTION__; if (b) { auto& wrapper = m_Devices[0]->m_Wrapper; int kernelIndex = wrapper.FindKernelIndex(m_DEOpenCLKernelCreator.LogScaleAssignDEEntryPoint()); - const char* loc = __FUNCTION__; if (kernelIndex != -1) { @@ -1142,16 +1145,16 @@ eRenderStatus RendererCL::RunLogScaleFilter() size_t gridH = m_DensityFilterCL.m_SuperRasH; OpenCLWrapper::MakeEvenGridDims(blockW, blockH, gridW, gridH); - if (b && !(b = wrapper.AddAndWriteBuffer(m_DEFilterParamsBufferName, reinterpret_cast(&m_DensityFilterCL), sizeof(m_DensityFilterCL)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_DEFilterParamsBufferName, reinterpret_cast(&m_DensityFilterCL), sizeof(m_DensityFilterCL)))) { ErrorStr(loc, "Adding DE filter parameters buffer failed", m_Devices[0].get()); } - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HistBufferName))) { AddToReport(loc); }//Histogram. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HistBufferName))) { ErrorStr(loc, "Setting histogram buffer argument failed", m_Devices[0].get()); }//Histogram. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_AccumBufferName))) { AddToReport(loc); }//Accumulator. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_AccumBufferName))) { ErrorStr(loc, "Setting accumulator buffer argument failed", m_Devices[0].get()); }//Accumulator. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_DEFilterParamsBufferName))) { AddToReport(loc); }//DensityFilterCL. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_DEFilterParamsBufferName))) { ErrorStr(loc, "Setting DE filter parameters buffer argument failed", m_Devices[0].get()); }//DensityFilterCL. //t.Tic(); - if (b && !(b = wrapper.RunKernel(kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { AddToReport(loc); } + if (b && !(b = wrapper.RunKernel(kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { ErrorStr(loc, "Running log scale program failed", m_Devices[0].get()); } //t.Toc(loc); @@ -1162,7 +1165,7 @@ eRenderStatus RendererCL::RunLogScaleFilter() else { b = false; - AddToReport(loc); + ErrorStr(loc, "Invalid kernel index for log scale program", m_Devices[0].get()); } } @@ -1212,7 +1215,7 @@ eRenderStatus RendererCL::RunDensityFilter() uint chunkSizeH = gapH + 1;//Chunk size is also in terms of blocks and is one block (the one running) plus the gap below it. double totalChunks = chunkSizeW * chunkSizeH; - if (b && !(b = wrapper.AddAndWriteBuffer(m_DEFilterParamsBufferName, reinterpret_cast(&m_DensityFilterCL), sizeof(m_DensityFilterCL)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_DEFilterParamsBufferName, reinterpret_cast(&m_DensityFilterCL), sizeof(m_DensityFilterCL)))) { ErrorStr(loc, "Writing DE filter parameters buffer failed", m_Devices[0].get()); } #ifdef ROW_ONLY_DE blockSizeW = 64;//These *must* both be divisible by 16 or else pixels will go missing. @@ -1256,7 +1259,11 @@ eRenderStatus RendererCL::RunDensityFilter() for (uint colChunkPass = 0; b && !m_Abort && colChunkPass < chunkSizeW; colChunkPass++)//Number of horizontal passes. { //t2.Tic(); - if (b && !(b = RunDensityFilterPrivate(kernelIndex, gridW, gridH, blockSizeW, blockSizeH, chunkSizeW, chunkSizeH, colChunkPass, rowChunkPass))) { m_Abort = true; AddToReport(loc); } + if (b && !(b = RunDensityFilterPrivate(kernelIndex, gridW, gridH, blockSizeW, blockSizeH, chunkSizeW, chunkSizeH, colChunkPass, rowChunkPass))) + { + m_Abort = true; + ErrorStr(loc, "Running DE filter program for row chunk "s + std::to_string(rowChunkPass) + ", col chunk "s + std::to_string(colChunkPass) + " failed", m_Devices[0].get()); + } //t2.Toc(loc); @@ -1282,7 +1289,7 @@ eRenderStatus RendererCL::RunDensityFilter() else { b = false; - AddToReport(loc); + ErrorStr(loc, "Invalid kernel index for DE filter program", m_Devices[0].get()); } return m_Abort ? eRenderStatus::RENDER_ABORT : (b ? eRenderStatus::RENDER_OK : eRenderStatus::RENDER_ERROR); @@ -1304,7 +1311,7 @@ eRenderStatus RendererCL::RunFinalAccum() size_t blockW; size_t blockH; uint curvesSet = m_CurvesSet ? 1 : 0; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (!m_Abort && accumKernelIndex != -1) { @@ -1312,9 +1319,9 @@ eRenderStatus RendererCL::RunFinalAccum() //This is needed with or without early clip. ConvertSpatialFilter(); - if (b && !(b = wrapper.AddAndWriteBuffer(m_SpatialFilterParamsBufferName, reinterpret_cast(&m_SpatialFilterCL), sizeof(m_SpatialFilterCL)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_SpatialFilterParamsBufferName, reinterpret_cast(&m_SpatialFilterCL), sizeof(m_SpatialFilterCL)))) { ErrorStr(loc, "Adding spatial filter parameters buffer", m_Devices[0].get()); } - if (b && !(b = wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.data(), SizeOf(m_Csa)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.data(), SizeOf(m_Csa)))) { ErrorStr(loc, "Adding curves buffer", m_Devices[0].get()); } //Since early clip requires gamma correcting the entire accumulator first, //it can't be done inside of the normal final accumulation kernel, so @@ -1332,16 +1339,16 @@ eRenderStatus RendererCL::RunFinalAccum() gridH = m_SpatialFilterCL.m_SuperRasH; OpenCLWrapper::MakeEvenGridDims(blockW, blockH, gridW, gridH); - if (b && !(b = wrapper.SetBufferArg(gammaCorrectKernelIndex, argIndex++, m_AccumBufferName))) { AddToReport(loc); }//Accumulator. + if (b && !(b = wrapper.SetBufferArg(gammaCorrectKernelIndex, argIndex++, m_AccumBufferName))) { ErrorStr(loc, "Setting early clip accumulator buffer argument failed", m_Devices[0].get()); }//Accumulator. - if (b && !(b = wrapper.SetBufferArg(gammaCorrectKernelIndex, argIndex++, m_SpatialFilterParamsBufferName))) { AddToReport(loc); }//SpatialFilterCL. + if (b && !(b = wrapper.SetBufferArg(gammaCorrectKernelIndex, argIndex++, m_SpatialFilterParamsBufferName))) { ErrorStr(loc, "Setting early clip spatial filter parameters buffer argument failed", m_Devices[0].get()); }//SpatialFilterCL. - if (b && !(b = wrapper.RunKernel(gammaCorrectKernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { AddToReport(loc); } + if (b && !(b = wrapper.RunKernel(gammaCorrectKernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { ErrorStr(loc, "Running early clip gamma correction program failed", m_Devices[0].get()); } } else { b = false; - AddToReport(loc); + ErrorStr(loc, "Invalid kernel index for early clip gamma correction program", m_Devices[0].get()); } } @@ -1352,32 +1359,32 @@ eRenderStatus RendererCL::RunFinalAccum() gridH = m_SpatialFilterCL.m_FinalRasH; OpenCLWrapper::MakeEvenGridDims(blockW, blockH, gridW, gridH); - if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_AccumBufferName))) { AddToReport(loc); }//Accumulator. + if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_AccumBufferName))) { ErrorStr(loc, "Setting accumulator buffer argument failed", m_Devices[0].get()); }//Accumulator. - if (b && !(b = wrapper.SetImageArg(accumKernelIndex, argIndex++, wrapper.Shared(), m_FinalImageName))) { AddToReport(loc); }//Final image. + if (b && !(b = wrapper.SetImageArg(accumKernelIndex, argIndex++, wrapper.Shared(), m_FinalImageName))) { ErrorStr(loc, "Setting accumulator final image buffer argument failed", m_Devices[0].get()); }//Final image. - if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_SpatialFilterParamsBufferName))) { AddToReport(loc); }//SpatialFilterCL. + if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_SpatialFilterParamsBufferName))) { ErrorStr(loc, "Setting spatial filter parameters buffer argument failed", m_Devices[0].get()); }//SpatialFilterCL. - if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_SpatialFilterCoefsBufferName))) { AddToReport(loc); }//Filter coefs. + if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_SpatialFilterCoefsBufferName))) { ErrorStr(loc, "Setting spatial filter coefficients buffer argument failed", m_Devices[0].get()); }//Filter coefs. - if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_CurvesCsaName))) { AddToReport(loc); }//Curve points. + if (b && !(b = wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_CurvesCsaName))) { ErrorStr(loc, "Setting curves buffer argument failed", m_Devices[0].get()); }//Curve points. - if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, curvesSet))) { AddToReport(loc); }//Do curves. + if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, curvesSet))) { ErrorStr(loc, "Setting curves boolean argument failed", m_Devices[0].get()); }//Do curves. if (b && wrapper.Shared()) - if (b && !(b = wrapper.EnqueueAcquireGLObjects(m_FinalImageName))) { AddToReport(loc); } + if (b && !(b = wrapper.EnqueueAcquireGLObjects(m_FinalImageName))) { ErrorStr(loc, "Acquiring OpenGL texture failed", m_Devices[0].get()); } - if (b && !(b = wrapper.RunKernel(accumKernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { AddToReport(loc); } + if (b && !(b = wrapper.RunKernel(accumKernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { ErrorStr(loc, "Running final accumulation program failed", m_Devices[0].get()); } if (b && wrapper.Shared()) - if (b && !(b = wrapper.EnqueueReleaseGLObjects(m_FinalImageName))) { AddToReport(loc); } + if (b && !(b = wrapper.EnqueueReleaseGLObjects(m_FinalImageName))) { ErrorStr(loc, "Releasing OpenGL texture failed", m_Devices[0].get()); } //t.Toc((char*)loc); } else { b = false; - AddToReport(loc); + ErrorStr(loc, "Invalid kernel index for final accumulation program", m_Devices[0].get()); } return b ? eRenderStatus::RENDER_OK : eRenderStatus::RENDER_ERROR; @@ -1402,7 +1409,7 @@ bool RendererCL::ClearBuffer(size_t device, const string& bufferName auto& wrapper = m_Devices[device]->m_Wrapper; int kernelIndex = wrapper.FindKernelIndex(m_IterOpenCLKernelCreator.ZeroizeEntryPoint()); cl_uint argIndex = 0; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (kernelIndex != -1) { @@ -1413,17 +1420,17 @@ bool RendererCL::ClearBuffer(size_t device, const string& bufferName b = true; OpenCLWrapper::MakeEvenGridDims(blockW, blockH, gridW, gridH); - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, bufferName))) { AddToReport(loc); }//Buffer of byte. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, bufferName))) { ErrorStr(loc, "Setting clear buffer argument failed", m_Devices[device].get()); }//Buffer of byte. - if (b && !(b = wrapper.SetArg(kernelIndex, argIndex++, width * elementSize))) { AddToReport(loc); }//Width. + if (b && !(b = wrapper.SetArg(kernelIndex, argIndex++, width * elementSize))) { ErrorStr(loc, "Setting clear buffer width argument failed", m_Devices[device].get()); }//Width. - if (b && !(b = wrapper.SetArg(kernelIndex, argIndex++, height))) { AddToReport(loc); }//Height. + if (b && !(b = wrapper.SetArg(kernelIndex, argIndex++, height))) { ErrorStr(loc, "Setting clear buffer height argument failed", m_Devices[device].get()); }//Height. - if (b && !(b = wrapper.RunKernel(kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { AddToReport(loc); } + if (b && !(b = wrapper.RunKernel(kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { ErrorStr(loc, "Running clear buffer program failed", m_Devices[device].get()); } } else { - AddToReport(loc); + ErrorStr(loc, "Invalid kernel index for clear buffer program", m_Devices[device].get()); } } @@ -1450,36 +1457,36 @@ bool RendererCL::RunDensityFilterPrivate(size_t kernelIndex, size_t //Timing t(4); bool b = true; cl_uint argIndex = 0; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (!m_Devices.empty()) { auto& wrapper = m_Devices[0]->m_Wrapper; - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_HistBufferName))) { AddToReport(loc); } argIndex++;//Histogram. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_HistBufferName))) { ErrorStr(loc, "Setting histogram buffer argument failed", m_Devices[0].get()); } argIndex++;//Histogram. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_AccumBufferName))) { AddToReport(loc); } argIndex++;//Accumulator. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_AccumBufferName))) { ErrorStr(loc, "Setting accumulator buffer argument failed", m_Devices[0].get()); } argIndex++;//Accumulator. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DEFilterParamsBufferName))) { AddToReport(loc); } argIndex++;//FlameDensityFilterCL. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DEFilterParamsBufferName))) { ErrorStr(loc, "Setting DE filter parameters buffer argument failed", m_Devices[0].get()); } argIndex++;//FlameDensityFilterCL. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DECoefsBufferName))) { AddToReport(loc); } argIndex++;//Coefs. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DECoefsBufferName))) { ErrorStr(loc, "Setting DE coefficients buffer argument failed", m_Devices[0].get()); } argIndex++;//Coefs. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DEWidthsBufferName))) { AddToReport(loc); } argIndex++;//Widths. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DEWidthsBufferName))) { ErrorStr(loc, "Setting DE widths buffer argument failed", m_Devices[0].get()); } argIndex++;//Widths. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DECoefIndicesBufferName))) { AddToReport(loc); } argIndex++;//Coef indices. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex, m_DECoefIndicesBufferName))) { ErrorStr(loc, "Setting DE coefficient indices buffer argument failed", m_Devices[0].get()); } argIndex++;//Coef indices. - if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, chunkSizeW))) { AddToReport(loc); } argIndex++;//Chunk size width (gapW + 1). + if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, chunkSizeW))) { ErrorStr(loc, "Setting chunk size width argument failed", m_Devices[0].get()); } argIndex++;//Chunk size width (gapW + 1). - if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, chunkSizeH))) { AddToReport(loc); } argIndex++;//Chunk size height (gapH + 1). + if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, chunkSizeH))) { ErrorStr(loc, "Setting chunk size height argument failed", m_Devices[0].get()); } argIndex++;//Chunk size height (gapH + 1). - if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, colChunkPass))) { AddToReport(loc); } argIndex++;//Column chunk, horizontal pass. + if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, colChunkPass))) { ErrorStr(loc, "Setting col chunk pass argument failed", m_Devices[0].get()); } argIndex++;//Column chunk, horizontal pass. - if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, rowChunkPass))) { AddToReport(loc); } argIndex++;//Row chunk, vertical pass. + if (b && !(b = wrapper.SetArg(kernelIndex, argIndex, rowChunkPass))) { ErrorStr(loc, "Setting row chunk pass argument failed", m_Devices[0].get()); } argIndex++;//Row chunk, vertical pass. //t.Toc(__FUNCTION__ " set args"); //t.Tic(); - if (b && !(b = wrapper.RunKernel(kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { AddToReport(loc); }//Method 7, accumulating to temp box area. + if (b && !(b = wrapper.RunKernel(kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { ErrorStr(loc, "Running DE filter program failed", m_Devices[0].get()); }//Method 7, accumulating to temp box area. //t.Toc(__FUNCTION__ " RunKernel()"); return b; @@ -1512,7 +1519,7 @@ int RendererCL::MakeAndGetDensityFilterProgram(size_t ss, uint filte if (wrapper.AddProgram(deEntryPoint, kernel, deEntryPoint, m_DoublePrecision)) kernelIndex = wrapper.FindKernelIndex(deEntryPoint);//Try to find it again, it will be present if successfully built. else - AddToReport(string(loc) + "():\nBuilding the following program failed: \n" + kernel + "\n"); + ErrorStr(loc, "Adding the DE filter program at "s + deEntryPoint + " failed to build:\n"s + kernel, m_Devices[0].get()); } } @@ -1541,7 +1548,7 @@ int RendererCL::MakeAndGetFinalAccumProgram() if (wrapper.AddProgram(finalAccumEntryPoint, kernel, finalAccumEntryPoint, m_DoublePrecision)) kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint);//Try to find it again, it will be present if successfully built. else - AddToReport(loc); + ErrorStr(loc, "Adding final accumulation program "s + finalAccumEntryPoint + " failed"s, m_Devices[0].get()); } } @@ -1560,7 +1567,7 @@ int RendererCL::MakeAndGetGammaCorrectionProgram() auto& wrapper = m_Devices[0]->m_Wrapper; auto& gammaEntryPoint = m_FinalAccumOpenCLKernelCreator.GammaCorrectionEntryPoint(); int kernelIndex = wrapper.FindKernelIndex(gammaEntryPoint); - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (kernelIndex == -1)//Has not been built yet. { @@ -1570,7 +1577,7 @@ int RendererCL::MakeAndGetGammaCorrectionProgram() if (b) kernelIndex = wrapper.FindKernelIndex(gammaEntryPoint);//Try to find it again, it will be present if successfully built. else - AddToReport(loc); + ErrorStr(loc, "Adding gamma correction program "s + gammaEntryPoint + " failed"s, m_Devices[0].get()); } return kernelIndex; @@ -1589,15 +1596,15 @@ bool RendererCL::CreateHostBuffer() { bool b = true; size_t size = SuperSize() * sizeof(v4bT);//Size of histogram and density filter buffer. - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; if (b = Renderer::Alloc(true))//Allocate the histogram memory to point this HOST_PTR buffer to, other buffers not needed. { - if (b && !(b = m_Devices[0]->m_Wrapper.AddHostBuffer(m_HostBufferName, size, reinterpret_cast(HistBuckets())))) - AddToReport(string(loc) + ": creating OpenCL HOST_PTR buffer to point to host side histogram failed.");//Host side histogram for temporary use with multiple devices. + if (b && !(b = m_Devices[0]->m_Wrapper.AddHostBuffer(m_HostBufferName, size, reinterpret_cast(HistBuckets()))))//Host side histogram for temporary use with multiple devices. + ErrorStr(loc, "Creating OpenCL HOST_PTR buffer to point to host side histogram failed", m_Devices[0].get()); } else - AddToReport(string(loc) + ": allocating host side histogram failed.");//Allocating histogram failed, something is seriously wrong. + ErrorStr(loc, "Allocating host side histogram failed", m_Devices[0].get());//Allocating histogram failed, something is seriously wrong. return b; } @@ -1617,7 +1624,7 @@ bool RendererCL::SumDeviceHist() //Timing t; bool b = true; auto& wrapper = m_Devices[0]->m_Wrapper; - const char* loc = __FUNCTION__; + static std::string loc = __FUNCTION__; size_t blockW = m_Devices[0]->Nvidia() ? 32 : 16;//Max work group size is 256 on AMD, which means 16x16. size_t blockH = m_Devices[0]->Nvidia() ? 32 : 16; size_t gridW = SuperRasW(); @@ -1635,20 +1642,21 @@ bool RendererCL::SumDeviceHist() { cl_uint argIndex = 0; - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HostBufferName))) { break; }//Source buffer of v4bT. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HostBufferName))) { ErrorStr(loc, "Setting host buffer argument failed", m_Devices[device].get()); break; }//Source buffer of v4bT. - if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HistBufferName))) { break; }//Dest buffer of v4bT. + if (b && !(b = wrapper.SetBufferArg(kernelIndex, argIndex++, m_HistBufferName))) { ErrorStr(loc, "Setting histogram buffer argument failed", m_Devices[device].get()); break; }//Dest buffer of v4bT. - if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, uint(SuperRasW())))) { break; }//Width in pixels. + if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, uint(SuperRasW())))) { ErrorStr(loc, "Setting width argument failed", m_Devices[device].get()); break; }//Width in pixels. - if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, uint(SuperRasH())))) { break; }//Height in pixels. + if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, uint(SuperRasH())))) { ErrorStr(loc, "Setting height argument failed", m_Devices[device].get()); break; }//Height in pixels. - if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, (device == m_Devices.size() - 1) ? 1 : 0))) { break; }//Clear the source buffer on the last device. + if (b && !(b = wrapper.SetArg (kernelIndex, argIndex++, (device == m_Devices.size() - 1) ? 1 : 0))) { ErrorStr(loc, "Setting clear argument failed", m_Devices[device].get()); break; }//Clear the source buffer on the last device. - if (b && !(b = wrapper.RunKernel (kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { break; } + if (b && !(b = wrapper.RunKernel (kernelIndex, gridW, gridH, 1, blockW, blockH, 1))) { ErrorStr(loc, "Running histogram sum program failed", m_Devices[device].get()); break; } } else { + ErrorStr(loc, "Running histogram reading and clearing programs failed", m_Devices[device].get()); break; } } @@ -1656,9 +1664,7 @@ bool RendererCL::SumDeviceHist() if (!b) { - ostringstream os; - os << loc << ": failed to sum histograms from the secondary device(s) to the primary device."; - AddToReport(os.str()); + ErrorStr(loc, "Summing histograms from the secondary device(s) to the primary device failed", nullptr); } //t.Toc(loc); @@ -1825,6 +1831,25 @@ void RendererCL::FillSeeds() } } +/// +/// Compose an error string based on the strings and device passed in, add it to the error report and return the string. +/// +/// The location where the error occurred +/// The text of the error +/// The device the error occurred on +/// The new error string +template +std::string RendererCL::ErrorStr(const std::string& loc, const std::string& error, RendererClDevice* dev) +{ + std::string str = loc + "()"s + (dev ? + "\n"s + + dev->m_Wrapper.DeviceName() + "\nPlatform: " + + std::to_string(dev->PlatformIndex()) + ", device: " + std::to_string(dev->DeviceIndex()) : "") + ", error:\n" + + error + "\n"; + AddToReport(str); + return str; +} + template EMBERCL_API class RendererCL; #ifdef DO_DOUBLE diff --git a/Source/EmberCL/RendererCL.h b/Source/EmberCL/RendererCL.h index 8ac17c4..19bd05a 100644 --- a/Source/EmberCL/RendererCL.h +++ b/Source/EmberCL/RendererCL.h @@ -146,12 +146,13 @@ public: //Public virtual functions overridden from Renderer or RendererBase. virtual size_t MemoryAvailable() override; virtual bool Ok() const override; - virtual void ClearErrorReport() override; virtual size_t SubBatchSize() const override; virtual size_t ThreadCount() const override; virtual bool CreateDEFilter(bool& newAlloc) override; virtual bool CreateSpatialFilter(bool& newAlloc) override; virtual eRendererType RendererType() const override; + virtual bool Shared() const override; + virtual void ClearErrorReport() override; virtual string ErrorReportString() override; virtual vector ErrorReport() override; virtual bool RandVec(vector>& randVec) override; @@ -165,13 +166,12 @@ protected: virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true) override; virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false) override; virtual eRenderStatus GaussianDensityFilter() override; - virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset) override; + virtual eRenderStatus AccumulatorToFinalImage(vector& pixels, size_t finalOffset) override; virtual EmberStats Iterate(size_t iterCount, size_t temporalSample) override; #ifndef TEST_CL private: #endif - void Init(); //Private functions for making and running OpenCL programs. bool BuildIterProgramForEmber(bool doAccum = true); bool RunIter(size_t iterCount, size_t temporalSample, size_t& itersRan); @@ -192,35 +192,40 @@ private: void ConvertSpatialFilter(); void ConvertEmber(Ember& ember, EmberCL& emberCL, vector>& xformsCL); void ConvertCarToRas(const CarToRas& carToRas); - - bool m_Init; - bool m_DoublePrecision; - size_t m_IterCountPerKernel; - size_t m_IterBlocksWide, m_IterBlockWidth; - size_t m_IterBlocksHigh, m_IterBlockHeight; + std::string ErrorStr(const std::string& loc, const std::string& error, RendererClDevice* dev); + bool m_Init = false; + bool m_Shared = false; + bool m_DoublePrecision = typeid(T) == typeid(double); + //It's critical that these numbers never change. They are + //based on the cuburn model of each kernel launch containing + //256 threads. 32 wide by 8 high. Everything done in the OpenCL + //iteraion kernel depends on these dimensions. + size_t m_IterCountPerKernel = 256; + size_t m_IterBlocksWide = 64, m_IterBlockWidth = 32; + size_t m_IterBlocksHigh = 2, m_IterBlockHeight = 8; size_t m_MaxDEBlockSizeW; size_t m_MaxDEBlockSizeH; //Buffer names. - string m_EmberBufferName; - string m_XformsBufferName; - string m_ParVarsBufferName; - string m_GlobalSharedBufferName; - string m_SeedsBufferName; - string m_DistBufferName; - string m_CarToRasBufferName; - string m_DEFilterParamsBufferName; - string m_SpatialFilterParamsBufferName; - string m_CurvesCsaName; - string m_DECoefsBufferName; - string m_DEWidthsBufferName; - string m_DECoefIndicesBufferName; - string m_SpatialFilterCoefsBufferName; - string m_HostBufferName; - string m_HistBufferName; - string m_AccumBufferName; - string m_FinalImageName; - string m_PointsBufferName; + string m_EmberBufferName = "Ember"; + string m_XformsBufferName = "Xforms"; + string m_ParVarsBufferName = "ParVars"; + string m_GlobalSharedBufferName = "GlobalShared"; + string m_SeedsBufferName = "Seeds"; + string m_DistBufferName = "Dist"; + string m_CarToRasBufferName = "CarToRas"; + string m_DEFilterParamsBufferName = "DEFilterParams"; + string m_SpatialFilterParamsBufferName = "SpatialFilterParams"; + string m_DECoefsBufferName = "DECoefs"; + string m_DEWidthsBufferName = "DEWidths"; + string m_DECoefIndicesBufferName = "DECoefIndices"; + string m_SpatialFilterCoefsBufferName = "SpatialFilterCoefs"; + string m_CurvesCsaName = "CurvesCsa"; + string m_HostBufferName = "Host"; + string m_HistBufferName = "Hist"; + string m_AccumBufferName = "Accum"; + string m_FinalImageName = "Final"; + string m_PointsBufferName = "Points"; //Kernels. string m_IterKernel; diff --git a/Source/EmberCL/RendererClDevice.cpp b/Source/EmberCL/RendererClDevice.cpp index fb3e27b..2674ac3 100644 --- a/Source/EmberCL/RendererClDevice.cpp +++ b/Source/EmberCL/RendererClDevice.cpp @@ -55,4 +55,39 @@ 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; } + +/// +/// Clear the error report for this class as well as the wrapper. +/// +void RendererClDevice::ClearErrorReport() +{ + EmberReport::ClearErrorReport(); + m_Wrapper.ClearErrorReport(); +} + +/// +/// Concatenate and return the error report for this class and the +/// wrapper as a single string. +/// +/// The concatenated error report string +string RendererClDevice::ErrorReportString() +{ + auto s = EmberReport::ErrorReportString(); + return s + m_Wrapper.ErrorReportString(); +} + +/// +/// Concatenate and return the error report for this class and the +/// wrapper as a vector of strings. +/// +/// The concatenated error report vector of strings +vector RendererClDevice::ErrorReport() +{ + auto ours = EmberReport::ErrorReport(); + auto s = m_Wrapper.ErrorReport(); + ours.insert(ours.end(), s.begin(), s.end()); + return ours; +} } diff --git a/Source/EmberCL/RendererClDevice.h b/Source/EmberCL/RendererClDevice.h index e6a7487..b1f3a34 100644 --- a/Source/EmberCL/RendererClDevice.h +++ b/Source/EmberCL/RendererClDevice.h @@ -24,6 +24,13 @@ public: bool Shared() const; bool Nvidia() const; size_t WarpSize() const; + size_t PlatformIndex() const; + size_t DeviceIndex() const; + + //Public virtual functions overridden from base classes. + virtual void ClearErrorReport() override; + virtual string ErrorReportString() override; + virtual vector ErrorReport() override; size_t m_Calls; OpenCLWrapper m_Wrapper; diff --git a/Source/Fractorium/AboutDialog.ui b/Source/Fractorium/AboutDialog.ui index 2c4c828..c7cecdb 100644 --- a/Source/Fractorium/AboutDialog.ui +++ b/Source/Fractorium/AboutDialog.ui @@ -58,7 +58,7 @@ QFrame::NoFrame - <html><head/><body><p align="center">Fractorium 1.0.0.7</p><p align="center"><span style=" font-size:10pt;">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"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html> + <html><head/><body><p align="center">Fractorium 1.0.0.8</p><p align="center"><span style=" font-size:10pt;">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"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html> Qt::RichText diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp index 2ba761c..284955f 100644 --- a/Source/Fractorium/FinalRenderEmberController.cpp +++ b/Source/Fractorium/FinalRenderEmberController.cpp @@ -439,7 +439,6 @@ bool FinalRenderEmberController::CreateRenderer(eRendererType renderType, con vector errorReport; m_Devices = devices;//Store values for re-creation later on. m_OutputTexID = 0;//Don't care about tex ID when doing final render. - m_Shared = shared;//So shared is of course false. if (m_FinalRenderDialog->DoSequence()) { diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index 2f81ffd..e32d3e5 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -213,6 +213,11 @@ Fractorium::Fractorium(QWidget* p) //this constructor exits, GLWidget::InitGL() will create the initial flock and start the rendering timer //which executes whenever the program is idle. Upon starting the timer, the renderer //will be initialized. + //auto cdc = wglGetCurrentDC(); + //auto cc = wglGetCurrentContext(); + //qDebug() << "Fractorium::Fractorium():"; + //qDebug() << "Current DC: " << cdc; + //qDebug() << "Current Context: " << cc; QTimer::singleShot(1000, [&]() { ui.GLDisplay->InitGL(); }); } diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index ff27812..8db68d0 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -268,7 +268,6 @@ protected: //Non-templated members. bool m_Rendering = false; - bool m_Shared = true; bool m_LastEditWasUndoRedo; vector> m_Devices; size_t m_SubBatchCount = 1;//Will be ovewritten by the options on first render. diff --git a/Source/Fractorium/FractoriumRender.cpp b/Source/Fractorium/FractoriumRender.cpp index e46dd75..e7bfeb2 100644 --- a/Source/Fractorium/FractoriumRender.cpp +++ b/Source/Fractorium/FractoriumRender.cpp @@ -476,8 +476,7 @@ bool FractoriumEmberController::Render() //Update it on finish because the rendering process is completely done. if (update || ProcessState() == eProcessState::ACCUM_DONE) { - if (m_FinalImage.size() == m_Renderer->FinalDimensions())//Make absolutely sure the correct amount of data is passed. - gl->update(); + gl->update();//Queue update. if (ProcessState() == eProcessState::ACCUM_DONE) SaveCurrentToOpenedFile();//Will not save if the previews are still rendering. @@ -543,26 +542,34 @@ bool FractoriumEmberController::CreateRenderer(eRendererType renderType, cons auto s = m_Fractorium->m_Settings; auto gl = m_Fractorium->ui.GLDisplay; - if (!m_Renderer.get() || (m_Renderer->RendererType() != renderType) || !Equal(m_Devices, devices)) + if (!m_Renderer.get() || (m_Renderer->RendererType() != renderType) || !Equal(m_Devices, devices) || m_Renderer->Shared() != shared) { EmberReport emberReport; vector errorReport; DeleteRenderer();//Delete the renderer and refresh the textures. - //Before starting, must take care of allocations. - gl->Allocate(true);//Forcing a realloc of the texture is necessary on AMD, but not on nVidia. - m_Renderer = unique_ptr(::CreateRenderer(renderType, devices, shared, gl->OutputTexID(), emberReport));//Always make bucket type float. - errorReport = emberReport.ErrorReport(); - if (errorReport.empty()) + //Before starting, must take care of allocations. + if (gl->Allocate(true))//Forcing a realloc of the texture is necessary on AMD, but not on nVidia. { - m_Devices = devices; - m_OutputTexID = gl->OutputTexID(); - m_Shared = shared; + m_Renderer = unique_ptr(::CreateRenderer(renderType, devices, shared, gl->OutputTexID(), emberReport));//Always make bucket type float. + errorReport = emberReport.ErrorReport(); + + if (errorReport.empty()) + { + m_Devices = devices; + m_OutputTexID = gl->OutputTexID(); + } + else + { + ok = false; + m_Fractorium->ShowCritical("Renderer Creation Error", "Could not create requested renderer, fallback CPU renderer created. See info tab for details."); + m_Fractorium->ErrorReportToQTextEdit(errorReport, m_Fractorium->ui.InfoRenderingTextEdit); + } } else { ok = false; - m_Fractorium->ShowCritical("Renderer Creation Error", "Could not create requested renderer, fallback CPU renderer created. See info tab for details."); + m_Fractorium->ShowCritical("Renderer Creation Error", "Could not create OpenGL texture, interactive rendering will be disabled."); m_Fractorium->ErrorReportToQTextEdit(errorReport, m_Fractorium->ui.InfoRenderingTextEdit); } } @@ -662,14 +669,16 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews) auto v = Devices(m_Settings->Devices()); //The most important option to process is what kind of renderer is desired, so do it first. - if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews)) + if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews, useOpenCL && m_Settings->SharedTexture())) { //If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded. ShowCritical("Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead."); m_Settings->OpenCL(false); + m_Settings->SharedTexture(false); ui.ActionCpu->setChecked(true); ui.ActionCL->setChecked(false); m_OptionsDialog->ui.OpenCLCheckBox->setChecked(false); + m_OptionsDialog->ui.SharedTextureCheckBox->setChecked(false); m_FinalRenderDialog->ui.FinalRenderOpenCLCheckBox->setChecked(false); ok = false; } diff --git a/Source/Fractorium/FractoriumSettings.cpp b/Source/Fractorium/FractoriumSettings.cpp index 1e2d871..faa24d9 100644 --- a/Source/Fractorium/FractoriumSettings.cpp +++ b/Source/Fractorium/FractoriumSettings.cpp @@ -98,6 +98,9 @@ void FractoriumSettings::EnsureDefaults() if (OpenPaletteImageFolder() == "") OpenPaletteImageFolder(QCoreApplication::applicationDirPath()); + + if (value(SHAREDTEXTURE).toString() == "")//Set this to true if the setting is missing because it only needs to be false for the rare system that has problems with shared textures. + SharedTexture(true); } /// @@ -116,6 +119,9 @@ void FractoriumSettings::Transparency(bool b) { setValue(TRANSPARENCY, b); bool FractoriumSettings::OpenCL() { return value(OPENCL).toBool(); } void FractoriumSettings::OpenCL(bool b) { setValue(OPENCL, b); } +bool FractoriumSettings::SharedTexture() { return value(SHAREDTEXTURE).toBool(); } +void FractoriumSettings::SharedTexture(bool b) { setValue(SHAREDTEXTURE, b); } + bool FractoriumSettings::Double() { return value(DOUBLEPRECISION).toBool(); } void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b); } @@ -158,14 +164,14 @@ void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH, uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); } void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); } -uint FractoriumSettings::CpuQuality() { return value(CPU_QUALITY).toUInt(); } -void FractoriumSettings::CpuQuality(uint i) { setValue(CPU_QUALITY, i); } +uint FractoriumSettings::CpuQuality() { return value(CPUQUALITY).toUInt(); } +void FractoriumSettings::CpuQuality(uint i) { setValue(CPUQUALITY, i); } -uint FractoriumSettings::OpenClQuality() { return value(OPENCL_QUALITY).toUInt(); } -void FractoriumSettings::OpenClQuality(uint i) { setValue(OPENCL_QUALITY, i); } +uint FractoriumSettings::OpenClQuality() { return value(OPENCLQUALITY).toUInt(); } +void FractoriumSettings::OpenClQuality(uint i) { setValue(OPENCLQUALITY, i); } -bool FractoriumSettings::LoadLast() { return value(LOAD_LAST).toBool(); } -void FractoriumSettings::LoadLast(bool b) { setValue(LOAD_LAST, b); } +bool FractoriumSettings::LoadLast() { return value(LOADLAST).toBool(); } +void FractoriumSettings::LoadLast(bool b) { setValue(LOADLAST, b); } /// /// Sequence generation settings. diff --git a/Source/Fractorium/FractoriumSettings.h b/Source/Fractorium/FractoriumSettings.h index 4b238df..f9a825f 100644 --- a/Source/Fractorium/FractoriumSettings.h +++ b/Source/Fractorium/FractoriumSettings.h @@ -10,6 +10,7 @@ #define YAXISUP "render/yaxisup" #define TRANSPARENCY "render/transparency" #define OPENCL "render/opencl" +#define SHAREDTEXTURE "render/sharedtexture" #define DOUBLEPRECISION "render/dp64" #define CONTUPDATE "render/continuousupdate" #define SHOWALLXFORMS "render/dragshowallxforms" @@ -24,9 +25,9 @@ #define CPUSUBBATCH "render/cpusubbatch" #define OPENCLSUBBATCH "render/openclsubbatch" #define RANDOMCOUNT "render/randomcount" -#define CPU_QUALITY "render/cpuquality" -#define OPENCL_QUALITY "render/openclquality" -#define LOAD_LAST "render/loadlastonstart" +#define CPUQUALITY "render/cpuquality" +#define OPENCLQUALITY "render/openclquality" +#define LOADLAST "render/loadlastonstart" #define STAGGER "sequence/stagger" #define STAGGERMAX "sequence/staggermax" @@ -114,6 +115,9 @@ public: bool OpenCL(); void OpenCL(bool b); + bool SharedTexture(); + void SharedTexture(bool b); + bool Double(); void Double(bool b); diff --git a/Source/Fractorium/FractoriumXforms.cpp b/Source/Fractorium/FractoriumXforms.cpp index 2569b89..0ef60f0 100644 --- a/Source/Fractorium/FractoriumXforms.cpp +++ b/Source/Fractorium/FractoriumXforms.cpp @@ -332,6 +332,7 @@ void FractoriumEmberController::AddFinalXform() Update([&]() { Xform final; + final.m_Animate = 0; final.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));//Just a placeholder so other parts of the code don't see it as being empty. m_Ember.SetFinalXform(final); int index = int(m_Ember.TotalXformCount() - 1);//Set index to the last item. diff --git a/Source/Fractorium/GLWidget.cpp b/Source/Fractorium/GLWidget.cpp index e861d30..968b5e3 100644 --- a/Source/Fractorium/GLWidget.cpp +++ b/Source/Fractorium/GLWidget.cpp @@ -121,12 +121,12 @@ GLWidget::GLWidget(QWidget* p) setFormat(fmt); */ - auto qsf = this->format(); - qDebug() << "Constructor*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion(); - qDebug() << "Profile: " << qsf.profile(); - qDebug() << "Depth buffer size: " << qsf.depthBufferSize(); - qDebug() << "Swap behavior: " << qsf.swapBehavior(); - qDebug() << "Swap interval: " << qsf.swapInterval(); + //auto qsf = this->format(); + //qDebug() << "Constructor*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion(); + //qDebug() << "Profile: " << qsf.profile(); + //qDebug() << "Depth buffer size: " << qsf.depthBufferSize(); + //qDebug() << "Swap behavior: " << qsf.swapBehavior(); + //qDebug() << "Swap interval: " << qsf.swapInterval(); } /// @@ -144,12 +144,12 @@ void GLWidget::InitGL() { if (!m_Init) { - auto qsf = this->format(); - qDebug() << "InitGL*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion(); - qDebug() << "Profile: " << qsf.profile(); - qDebug() << "Depth buffer size: " << qsf.depthBufferSize(); - qDebug() << "Swap behavior: " << qsf.swapBehavior(); - qDebug() << "Swap interval: " << qsf.swapInterval(); + //auto qsf = this->format(); + //qDebug() << "InitGL*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion(); + //qDebug() << "Profile: " << qsf.profile(); + //qDebug() << "Depth buffer size: " << qsf.depthBufferSize(); + //qDebug() << "Swap behavior: " << qsf.swapBehavior(); + //qDebug() << "Swap interval: " << qsf.swapInterval(); int w = std::ceil(m_Fractorium->ui.GLParentScrollArea->width() * devicePixelRatioF()); int h = std::ceil(m_Fractorium->ui.GLParentScrollArea->height() * devicePixelRatioF()); SetDimensions(w, h); @@ -182,6 +182,65 @@ void GLWidget::InitGL() m_Fractorium->m_Controller->DelayedStartRenderTimer(); m_Init = true; + /* + auto clinfo = OpenCLInfo::DefInstance(); + auto& platforms = clinfo->Platforms(); + auto& alldevices = clinfo->Devices(); + std::vector strs; + auto cdc = wglGetCurrentDC(); + auto cc = wglGetCurrentContext(); + ostringstream os; + strs.push_back(os.str()); os.str(""); os << "GLWidget::InitGL():"; + strs.push_back(os.str()); os.str(""); os << "\nCurrent DC: " << cdc; + strs.push_back(os.str()); os.str(""); os << "\nCurrent Context: " << cc; + + for (int platform = 0; platform < platforms.size(); platform++) + { + cl_context_properties props[] = + { + CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), + CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), + CL_CONTEXT_PLATFORM, reinterpret_cast((platforms[platform])()), + 0 + }; + // Find CL capable devices in the current GL context + //wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext()); + ::wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext()); + size_t sizedev; + cl_device_id devices[32]; + clGetGLContextInfoKHR_fn clGetGLContextInfo = (clGetGLContextInfoKHR_fn)clGetExtensionFunctionAddressForPlatform(platforms[platform](), "clGetGLContextInfoKHR"); + clGetGLContextInfo(props, CL_DEVICES_FOR_GL_CONTEXT_KHR, 32 * sizeof(cl_device_id), devices, &sizedev); + sizedev = (cl_uint)(sizedev / sizeof(cl_device_id)); + + for (int i = 0; i < sizedev; i++) + { + std::string s; + size_t pi, di; + auto dd = clinfo->DeviceFromId(devices[i], pi, di); + + if (dd) + { + auto& dev = *dd; + auto& plat = platforms[pi]; + strs.push_back(os.str()); os.str(""); os << "\nPlatform[" << pi << "], device[" << di << "] is GL capable."; + strs.push_back(os.str()); os.str(""); os << "\nPlatform profile: " << plat.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nPlatform version: " << plat.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nPlatform name: " << plat.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nPlatform vendor: " << plat.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nPlatform extensions: " << plat.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nVendor: " << dev.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nDevice: " << dev.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nDriver version: " << dev.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nDevice profile: " << dev.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nDevice version: " << dev.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nDevice extensions: " << dev.getInfo(nullptr).c_str() << endl; + strs.push_back(os.str()); os.str(""); os << "\nDevice OpenCL C version: " << dev.getInfo(nullptr).c_str() << endl; + } + } + } + + m_Fractorium->ErrorReportToQTextEdit(strs, m_Fractorium->ui.InfoRenderingTextEdit); + */ } } @@ -243,23 +302,28 @@ void GLWidget::DrawQuad() this->glEnable(GL_TEXTURE_2D); this->glActiveTexture(GL_TEXTURE0); auto renderer = m_Fractorium->m_Controller->Renderer(); - auto finalImage = m_Fractorium->m_Controller->FinalImage(); //Ensure all allocation has taken place first. - if (m_OutputTexID != 0 && finalImage && !finalImage->empty()) + if (m_OutputTexID != 0) { glBindTexture(GL_TEXTURE_2D, m_OutputTexID);//The texture to draw to. auto scaledW = std::ceil(width() * devicePixelRatioF()); auto scaledH = std::ceil(height() * devicePixelRatioF()); //Only draw if the dimensions match exactly. - if (m_TexWidth == m_Fractorium->m_Controller->FinalRasW() && - m_TexHeight == m_Fractorium->m_Controller->FinalRasH() && - ((m_TexWidth * m_TexHeight) == GLint(finalImage->size()))) + if (m_TexWidth == m_Fractorium->m_Controller->FinalRasW() && m_TexHeight == m_Fractorium->m_Controller->FinalRasH()) { //Copy data from CPU to OpenGL if using a CPU renderer. This is not needed when using OpenCL. - if (renderer->RendererType() == eRendererType::CPU_RENDERER) - this->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_TexWidth, m_TexHeight, GL_RGBA, GL_FLOAT, finalImage->data()); + if (renderer->RendererType() == eRendererType::CPU_RENDERER || !renderer->Shared()) + { + auto finalImage = m_Fractorium->m_Controller->FinalImage(); + + if (finalImage &&//Make absolutely sure all image dimensions match when copying host side buffer to GL texture. + !finalImage->empty() && + ((m_TexWidth * m_TexHeight) == GLint(finalImage->size())) && + (finalImage->size() == renderer->FinalDimensions())) + this->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_TexWidth, m_TexHeight, GL_RGBA, GL_FLOAT, finalImage->data()); + } m_QuadProgram->bind(); this->glVertexAttribPointer(m_TexturePosAttr, 2, GL_FLOAT, GL_FALSE, 0, m_TexVerts.data()); @@ -363,12 +427,12 @@ GLuint GLWidget::OutputTexID() { return m_OutputTexID; } void GLWidget::initializeGL() { #ifdef USE_GLSL - auto qsf = this->format(); - qDebug() << "initializeGL*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion(); - qDebug() << "Profile: " << qsf.profile(); - qDebug() << "Depth buffer size: " << qsf.depthBufferSize(); - qDebug() << "Swap behavior: " << qsf.swapBehavior(); - qDebug() << "Swap interval: " << qsf.swapInterval(); + //auto qsf = this->format(); + //qDebug() << "initializeGL*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion(); + //qDebug() << "Profile: " << qsf.profile(); + //qDebug() << "Depth buffer size: " << qsf.depthBufferSize(); + //qDebug() << "Swap behavior: " << qsf.swapBehavior(); + //qDebug() << "Swap interval: " << qsf.swapInterval(); if (!m_Init && m_Fractorium) { @@ -541,11 +605,7 @@ void GLEmberController::DrawImage() if (SizesMatch())//Ensure all sizes are correct. If not, do nothing. { - auto finalImage = m_FractoriumEmberController->FinalImage(); - - if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage)//Final image only matters for CPU renderer. - if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage->size() == renderer->FinalDimensions()) - m_GL->DrawQuad();//Output image is drawn here. + m_GL->DrawQuad();//Output image is drawn here. } renderer->LeaveResize();//Unlock, may not be necessary. @@ -1149,6 +1209,7 @@ bool GLWidget::Allocate(bool force) } #endif + this->glFinish(); return m_OutputTexID != 0; } diff --git a/Source/Fractorium/OptionsDialog.cpp b/Source/Fractorium/OptionsDialog.cpp index d6e473e..fd4c0c1 100644 --- a/Source/Fractorium/OptionsDialog.cpp +++ b/Source/Fractorium/OptionsDialog.cpp @@ -47,6 +47,8 @@ FractoriumOptionsDialog::FractoriumOptionsDialog(QWidget* p, Qt::WindowFlags f) ui.DeviceTable->setEnabled(false); ui.OpenCLCheckBox->setChecked(false); ui.OpenCLCheckBox->setEnabled(false); + ui.SharedTextureCheckBox->setChecked(false); + ui.SharedTextureCheckBox->setEnabled(false); ui.OpenCLSubBatchSpin->setEnabled(false); ui.OpenCLQualitySpin->setEnabled(false); ui.OpenCLFilteringDERadioButton->setEnabled(false); @@ -67,6 +69,7 @@ bool FractoriumOptionsDialog::YAxisUp() { return ui.YAxisUpCheckBox->isChecked() bool FractoriumOptionsDialog::Transparency() { return ui.TransparencyCheckBox->isChecked(); } bool FractoriumOptionsDialog::ContinuousUpdate() { return ui.ContinuousUpdateCheckBox->isChecked(); } bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); } +bool FractoriumOptionsDialog::SharedTexture() { return ui.SharedTextureCheckBox->isChecked(); } bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); } bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); } bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); } @@ -127,6 +130,7 @@ void FractoriumOptionsDialog::OnOpenCLCheckBoxStateChanged(int state) ui.DeviceTable->setEnabled(checked); ui.ThreadCountSpin->setEnabled(!checked); ui.CpuSubBatchSpin->setEnabled(!checked); + ui.SharedTextureCheckBox->setEnabled(checked); ui.OpenCLSubBatchSpin->setEnabled(checked); ui.OpenCLQualitySpin->setEnabled(checked); ui.CpuQualitySpin->setEnabled(!checked); @@ -180,6 +184,7 @@ void FractoriumOptionsDialog::GuiToData() m_Settings->Transparency(Transparency()); m_Settings->ContinuousUpdate(ContinuousUpdate()); m_Settings->OpenCL(OpenCL()); + m_Settings->SharedTexture(SharedTexture()); m_Settings->Double(Double()); m_Settings->ShowAllXforms(ShowAllXforms()); m_Settings->ToggleType(ToggleType()); @@ -217,6 +222,7 @@ void FractoriumOptionsDialog::DataToGui() ui.TransparencyCheckBox->setChecked(m_Settings->Transparency()); ui.ContinuousUpdateCheckBox->setChecked(m_Settings->ContinuousUpdate()); ui.OpenCLCheckBox->setChecked(m_Settings->OpenCL()); + ui.SharedTextureCheckBox->setChecked(m_Settings->SharedTexture()); ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double()); ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms()); ui.ToggleTypeCheckBox->setChecked(m_Settings->ToggleType()); diff --git a/Source/Fractorium/OptionsDialog.h b/Source/Fractorium/OptionsDialog.h index 0dd153f..ca5fc3d 100644 --- a/Source/Fractorium/OptionsDialog.h +++ b/Source/Fractorium/OptionsDialog.h @@ -25,23 +25,12 @@ class FractoriumOptionsDialog : public QDialog public: FractoriumOptionsDialog(QWidget* p = nullptr, Qt::WindowFlags f = 0); - -public slots: - void OnOpenCLCheckBoxStateChanged(int state); - void OnDeviceTableCellChanged(int row, int col); - void OnDeviceTableRadioToggled(bool checked); - virtual void accept() override; - virtual void reject() override; - -protected: - virtual void showEvent(QShowEvent* e) override; - -private: bool EarlyClip(); bool YAxisUp(); bool Transparency(); bool ContinuousUpdate(); bool OpenCL(); + bool SharedTexture(); bool Double(); bool ShowAllXforms(); bool ToggleType(); @@ -55,6 +44,17 @@ private: void DataToGui(); void GuiToData(); +public slots: + void OnOpenCLCheckBoxStateChanged(int state); + void OnDeviceTableCellChanged(int row, int col); + void OnDeviceTableRadioToggled(bool checked); + virtual void accept() override; + virtual void reject() override; + +protected: + virtual void showEvent(QShowEvent* e) override; + +private: Ui::OptionsDialog ui; shared_ptr m_Info; SpinBox* m_XmlTemporalSamplesSpin; diff --git a/Source/Fractorium/OptionsDialog.ui b/Source/Fractorium/OptionsDialog.ui index 5e63adc..573addf 100644 --- a/Source/Fractorium/OptionsDialog.ui +++ b/Source/Fractorium/OptionsDialog.ui @@ -119,17 +119,7 @@ - - - - <html><head/><body><p>Checked: Positive Y direction is up.</p><p>Unchecked: Positive Y direction is down.</p></body></html> - - - Positive Y Up - - - - + <html><head/><body><p>Checked: use 64-bit double precision numbers (slower, but better image quality).</p><p>Unchecked: use 32-bit single precision numbers (faster, but worse image quality).</p></body></html> @@ -139,17 +129,7 @@ - - - - <html><head/><body><p>Use transparency in the final image.</p><p>This will not make a difference in the editor, but will when saving as .png and opening in other programs.</p></body></html> - - - Transparency - - - - + <html><head/><body><p>Checked: show all xforms while dragging.</p><p>Unchecked: only show current xform while dragging.</p></body></html> @@ -159,17 +139,7 @@ - - - - <html><head/><body><p>Continually update output image during interactive rendering.</p><p>This will slow down performance, but will give continuous updates on how the final render will look. Note that only log scale filtering is applied on each update. Full DE is not applied until iteration is complete.</p></body></html> - - - Continuous Update - - - - + <html><head/><body><p>Checked: right clicking toggles spin boxes, right button dragging disabled.</p><p>Unchecked: double clicking toggles spin boxes, right button dragging enabled.</p></body></html> @@ -179,17 +149,7 @@ - - - - <html><head/><body><p>Save each RGBA component as 16-bits when saving Png files.</p><p>This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.</p></body></html> - - - Save 16-bit Png - - - - + @@ -277,7 +237,7 @@ - + @@ -476,7 +436,7 @@ in interactive mode for each mouse movement - + <html><head/><body><p>Checked: load the flame from the previous run on startup.</p><p>Unchecked: create randoms on startup.</p></body></html> @@ -486,6 +446,56 @@ in interactive mode for each mouse movement + + + + <html><head/><body><p>Share the memory between the final image output and the OpenGL texture used to display the image result on the interactive renderer.</p><p>This is a highly recommended performance optimization for interactive editing when using OpenCL if your card supports it.</p><p>If creating the OpenCL renderer fails, uncheck this option. You will see a slight performance decrease in interactive rendering.</p></body></html> + + + Shared Texture + + + + + + + <html><head/><body><p>Checked: Positive Y direction is up.</p><p>Unchecked: Positive Y direction is down.</p></body></html> + + + Positive Y Up + + + + + + + <html><head/><body><p>Use transparency in the final image.</p><p>This will not make a difference in the editor, but will when saving as .png and opening in other programs.</p></body></html> + + + Transparency + + + + + + + <html><head/><body><p>Continually update output image during interactive rendering.</p><p>This will slow down performance, but will give continuous updates on how the final render will look. Note that only log scale filtering is applied on each update. Full DE is not applied until iteration is complete.</p></body></html> + + + Continuous Update + + + + + + + <html><head/><body><p>Save each RGBA component as 16-bits when saving Png files.</p><p>This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.</p></body></html> + + + Save 16-bit Png + + + @@ -903,13 +913,9 @@ in interactive mode for each mouse movement EarlyClipCheckBox OpenCLCheckBox - YAxisUpCheckBox DoublePrecisionCheckBox - TransparencyCheckBox ShowAllXformsCheckBox - ContinuousUpdateCheckBox ToggleTypeCheckBox - Png16BitCheckBox LoadLastOnStartCheckBox ThreadCountSpin CpuSubBatchSpin