--User changes

-Add support for multiple GPU devices.
  --These options are present in the command line and in Fractorium.
 -Change scheme of specifying devices from platform,device to just total device index.
  --Single number on the command line.
  --Change from combo boxes for device selection to a table of all devices in Fractorium.
 -Temporal samples defaults to 100 instead of 1000 which was needless overkill.

--Bug fixes
 -EmberAnimate, EmberRender, FractoriumSettings, FinalRenderDialog: Fix wrong order of arguments to Clamp() when assigning thread priority.
 -VariationsDC.h: Fix NVidia OpenCL compilation error in DCTriangleVariation.
 -FractoriumXformsColor.cpp: Checking for null pixmap pointer is not enough, must also check if the underlying buffer is null via call to QPixmap::isNull().

--Code changes
 -Ember.h: Add case for FLAME_MOTION_NONE and default in ApplyFlameMotion().
 -EmberMotion.h: Call base constructor.
 -EmberPch.h: #pragma once only on Windows.
 -EmberToXml.h:
  --Handle different types of exceptions.
  --Add default cases to ToString().
 -Isaac.h: Remove unused variable in constructor.
 -Point.h: Call base constructor in Color().
 -Renderer.h/cpp:
  --Add bool to Alloc() to only allocate memory for the histogram. Needed for multi-GPU.
  --Make CoordMap() return a const ref, not a pointer.
 -SheepTools.h:
  --Use 64-bit types like the rest of the code already does.
  --Fix some comment misspellings.
 -Timing.h: Make BeginTime(), EndTime(), ElapsedTime() and Format() be const functions.
 -Utils.h:
  --Add new functions Equal() and Split().
  --Handle more exception types in ReadFile().
  --Get rid of most legacy blending of C and C++ argument parsing.
 -XmlToEmber.h:
  --Get rid of most legacy blending of C and C++ code from flam3.
  --Remove some unused variables.
 -EmberAnimate:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
  --If a render fails, exit since there is no point in continuing an animation with a missing frame.
  --Pass variables to threaded save better, which most likely fixes a very subtle bug that existed before.
  --Remove some unused variables.
 -EmberGenome, EmberRender:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
 -EmberRender:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
  --Only print values when not rendering with OpenCL, since they're always 0 in that case.
 -EmberCLPch.h:
  --#pragma once only on Windows.
  --#include <atomic>.
 -IterOpenCLKernelCreator.h: Add new kernel for summing two histograms. This is needed for multi-GPU.
 -OpenCLWrapper.h:
  --Move all OpenCL info related code into its own class OpenCLInfo.
  --Add members to cache the values of global memory size and max allocation size.
 -RendererCL.h/cpp:
  --Redesign to accomodate multi-GPU.
  --Constructor now takes a vector of devices.
  --Remove DumpErrorReport() function, it's handled in the base.
  --ClearBuffer(), ReadPoints(), WritePoints(), ReadHist() and WriteHist() now optionally take a device index as a parameter.
  --MakeDmap() override and m_DmapCL member removed because it no longer applies since the histogram is always float since the last commit.
  --Add new function SumDeviceHist() to sum histograms from two devices by first copying to a temporary on the host, then a temporary on the device, then summing.
  --m_Calls member removed, as it's now per-device.
  --OpenCLWrapper removed.
  --m_Seeds member is now a vector of vector of seeds, to accomodate a separate and different array of seeds for each device.
  --Added member m_Devices, a vector of unique_ptr of RendererCLDevice.
 -EmberCommon.h
  --Added Devices() function to convert from a vector of device indices to a vector of platform,device indices.
  --Changed CreateRenderer() to accept a vector of devices to create a single RendererCL which will split work across multiple devices.
  --Added CreateRenderers() function to accept a vector of devices to create multiple RendererCL, each which will render on a single device.
  --Add more comments to some existing functions.
 -EmberCommonPch.h: #pragma once only on Windows.
 -EmberOptions.h:
  --Remove --platform option, it's just sequential device number now with the --device option.
  --Make --out be OPT_USE_RENDER instead of OPT_RENDER_ANIM since it's an error condition when animating. It makes no sense to write all frames to a single image.
  --Add Devices() function to parse comma separated --device option string and return a vector of device indices.
  --Make int and uint types be 64-bit, so intmax_t and size_t.
  --Make better use of macros.
 -JpegUtils.h: Make string parameters to WriteJpeg() and WritePng() be const ref.
 -All project files: Turn off buffer security check option in Visual Studio (/Gs-)
 -deployment.pri: Remove the line OTHER_FILES +=, it's pointless and was causing problems.
 -Ember.pro, EmberCL.pro: Add CONFIG += plugin, otherwise it wouldn't link.
 -EmberCL.pro: Add new files for multi-GPU support.
 -build_all.sh: use -j4 and QMAKE=${QMAKE:/usr/bin/qmake}
 -shared_settings.pri:
  -Add version string.
  -Remove old DESTDIR definitions.
  -Add the following lines or else nothing would build:
   CONFIG(release, debug|release) {
    CONFIG += warn_off
    DESTDIR = ../../../Bin/release
   }

   CONFIG(debug, debug|release) {
    DESTDIR = ../../../Bin/debug
   }

   QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t))
   LIBS += -L/usr/lib -lpthread
 -AboutDialog.ui: Another futile attempt to make it look correct on Linux.
 -FinalRenderDialog.h/cpp:
  --Add support for multi-GPU.
  --Change from combo boxes for device selection to a table of all devices.
  --Ensure device selection makes sense.
  --Remove "FinalRender" prefix of various function names, it's implied given the context.
 -FinalRenderEmberController.h/cpp:
  --Add support for multi-GPU.
  --Change m_FinishedImageCount to be atomic.
  --Move CancelRender() from the base to FinalRenderEmberController<T>.
  --Refactor RenderComplete() to omit any progress related functionality or image saving since it can be potentially ran in a thread.
  --Consolidate setting various renderer fields into SyncGuiToRenderer().
 -Fractorium.cpp: Allow for resizing of the options dialog to show the entire device table.
 -FractoriumCommon.h: Add various functions to handle a table showing the available OpenCL devices on the system.
 -FractoriumEmberController.h/cpp: Remove m_FinalImageIndex, it's no longer needed.
 -FractoriumRender.cpp: Scale the interactive sub batch count and quality by the number of devices used.
 -FractoriumSettings.h/cpp:
  --Temporal samples defaults to 100 instead of 1000 which was needless overkill.
  --Add multi-GPU support, remove old device,platform pair.
 -FractoriumToolbar.cpp: Disable OpenCL toolbar button if there are no devices present on the system.
 -FractoriumOptionsDialog.h/cpp:
  --Add support for multi-GPU.
  --Consolidate more assignments in DataToGui().
  --Enable/disable CPU/OpenCL items in response to OpenCL checkbox event.
 -Misc: Convert almost everything to size_t for unsigned, intmax_t for signed.
This commit is contained in:
mfeemster 2015-09-12 18:33:45 -07:00
parent a4aae06b02
commit 018ba26b5f
85 changed files with 3869 additions and 2517 deletions

View File

@ -232,6 +232,7 @@
<FloatingPointModel>Precise</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<FloatingPointExceptions>false</FloatingPointExceptions>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@ -250,6 +250,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"</
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<StringPooling>true</StringPooling>
<FloatingPointExceptions>false</FloatingPointExceptions>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View File

@ -235,6 +235,7 @@
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<FloatingPointExceptions>false</FloatingPointExceptions>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -298,8 +299,10 @@
<ClCompile Include="..\..\..\Source\EmberCL\FinalAccumOpenCLKernelCreator.cpp" />
<ClCompile Include="..\..\..\Source\EmberCL\DEOpenCLKernelCreator.cpp" />
<ClCompile Include="..\..\..\Source\EmberCL\IterOpenCLKernelCreator.cpp" />
<ClCompile Include="..\..\..\Source\EmberCL\OpenCLInfo.cpp" />
<ClCompile Include="..\..\..\Source\EmberCL\OpenCLWrapper.cpp" />
<ClCompile Include="..\..\..\Source\EmberCL\RendererCL.cpp" />
<ClCompile Include="..\..\..\Source\EmberCL\RendererClDevice.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\Source\EmberCL\EmberCLFunctions.h" />
@ -307,9 +310,11 @@
<ClInclude Include="..\..\..\Source\EmberCL\DEOpenCLKernelCreator.h" />
<ClInclude Include="..\..\..\Source\EmberCL\FinalAccumOpenCLKernelCreator.h" />
<ClInclude Include="..\..\..\Source\EmberCL\IterOpenCLKernelCreator.h" />
<ClInclude Include="..\..\..\Source\EmberCL\OpenCLInfo.h" />
<ClInclude Include="..\..\..\Source\EmberCL\OpenCLWrapper.h" />
<ClInclude Include="..\..\..\Source\EmberCL\RendererCL.h" />
<ClInclude Include="..\..\..\Source\EmberCL\EmberCLPch.h" />
<ClInclude Include="..\..\..\Source\EmberCL\RendererClDevice.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -36,6 +36,12 @@
<ClCompile Include="..\..\..\Source\EmberCL\IterOpenCLKernelCreator.cpp">
<Filter>Kernel Creators</Filter>
</ClCompile>
<ClCompile Include="..\..\..\Source\EmberCL\RendererClDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\Source\EmberCL\OpenCLInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\Source\EmberCL\OpenCLWrapper.h">
@ -62,5 +68,11 @@
<ClInclude Include="..\..\..\Source\EmberCL\IterOpenCLKernelCreator.h">
<Filter>Kernel Creators</Filter>
</ClInclude>
<ClInclude Include="..\..\..\Source\EmberCL\RendererClDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\Source\EmberCL\OpenCLInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -250,6 +250,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"</
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<StringPooling>true</StringPooling>
<FloatingPointExceptions>false</FloatingPointExceptions>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View File

@ -251,6 +251,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"</
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<FloatingPointExceptions>false</FloatingPointExceptions>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View File

@ -250,6 +250,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"</
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<StringPooling>true</StringPooling>
<FloatingPointExceptions>false</FloatingPointExceptions>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View File

@ -246,6 +246,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"</
<StringPooling>true</StringPooling>
<FloatingPointExceptions>false</FloatingPointExceptions>
<AdditionalOptions>/bigobj -Zm150 %(AdditionalOptions)</AdditionalOptions>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@ -1,4 +1,5 @@
TEMPLATE = lib
CONFIG += plugin
CONFIG += shared
CONFIG -= app_bundle
CONFIG -= qt

View File

@ -188,6 +188,3 @@ export (DEPLOYMENT)
export (LIBS)
export (QMAKE_EXTRA_TARGETS)
}
OTHER_FILES +=

View File

@ -1,4 +1,5 @@
TEMPLATE = lib
CONFIG += plugin
CONFIG += shared
CONFIG -= app_bundle
CONFIG -= qt
@ -14,23 +15,27 @@ QMAKE_CXXFLAGS += -D_CONSOLE
QMAKE_CXXFLAGS += -BUILDING_EMBERCL
SOURCES += \
../../../Source/EmberCL/DllMain.cpp \
../../../Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp \
../../../Source/EmberCL/IterOpenCLKernelCreator.cpp \
../../../Source/EmberCL/OpenCLWrapper.cpp \
../../../Source/EmberCL/RendererCL.cpp \
../../../Source/EmberCL/DEOpenCLKernelCreator.cpp
../../../Source/EmberCL/DllMain.cpp \
../../../Source/EmberCL/DEOpenCLKernelCreator.cpp \
../../../Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp \
../../../Source/EmberCL/IterOpenCLKernelCreator.cpp \
../../../Source/EmberCL/OpenCLInfo.cpp \
../../../Source/EmberCL/OpenCLWrapper.cpp \
../../../Source/EmberCL/RendererCL.cpp \
../../../Source/EmberCL/RendererCLDevice.cpp
include(deployment.pri)
qtcAddDeployment()
HEADERS += \
../../../Source/EmberCL/DEOpenCLKernelCreator.h \
../../../Source/EmberCL/EmberCLFunctions.h \
../../../Source/EmberCL/EmberCLPch.h \
../../../Source/EmberCL/EmberCLStructs.h \
../../../Source/EmberCL/FinalAccumOpenCLKernelCreator.h \
../../../Source/EmberCL/IterOpenCLKernelCreator.h \
../../../Source/EmberCL/OpenCLWrapper.h \
../../../Source/EmberCL/RendererCL.h
../../../Source/EmberCL/DEOpenCLKernelCreator.h \
../../../Source/EmberCL/EmberCLFunctions.h \
../../../Source/EmberCL/EmberCLPch.h \
../../../Source/EmberCL/EmberCLStructs.h \
../../../Source/EmberCL/FinalAccumOpenCLKernelCreator.h \
../../../Source/EmberCL/IterOpenCLKernelCreator.h \
../../../Source/EmberCL/OpenCLInfo.h \
../../../Source/EmberCL/OpenCLWrapper.h \
../../../Source/EmberCL/RendererCL.h \
../../../Source/EmberCL/RendererCLDevice.h

View File

@ -3,8 +3,8 @@
REBUILD=''
NVIDIA=''
NATIVE=''
CONCURRENCY='-j9'
QMAKE=${QMAKE:-qmake}
CONCURRENCY='-j4'
QMAKE=${QMAKE:/usr/bin/qmake}
RELEASE='CONFIG+=release CONFIG-=debug'
while test $# -gt 0

View File

@ -1,5 +1,15 @@
CONFIG += warn_off
VERSION = 0.1.4.7
VERSION = 0.1.4.9
CONFIG(release, debug|release) {
CONFIG += warn_off
DESTDIR = ../../../Bin/release
}
CONFIG(debug, debug|release) {
DESTDIR = ../../../Bin/debug
}
QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t))
macx {
LIBS += -framework OpenGL
@ -33,9 +43,6 @@ native {
QMAKE_CXXFLAGS += -march=k8
}
release:DESTDIR = ../../../release
debug:DESTDIR = ../../../debug
OBJECTS_DIR = $$DESTDIR/.obj
MOC_DIR = $$DESTDIR/.moc
RCC_DIR = $$DESTDIR/.qrc
@ -44,6 +51,7 @@ UI_DIR = $$DESTDIR/.ui
LIBS += -L/usr/lib -ljpeg
LIBS += -L/usr/lib -lpng
LIBS += -L/usr/lib -ltbb
LIBS += -L/usr/lib -lpthread
LIBS += -L/usr/lib/x86_64-linux-gnu -lxml2
CMAKE_CXXFLAGS += -DCL_USE_DEPRECATED_OPENCL_1_1_APIS

View File

@ -293,7 +293,7 @@ template <typename T>
typename m4T Affine2D<T>::ToMat4ColMajor(bool center) const
{
m4T mat(A(), B(), 0, center ? 0 : C(), //Col0...
D(), E(), 0, center ? 0 : F(), //1
D(), E(), 0, center ? 0 : F(), //1
0, 0, 1, 0, //2
0, 0, 0, 1);//3
@ -309,7 +309,7 @@ template <typename T>
typename m4T Affine2D<T>::ToMat4RowMajor(bool center) const
{
m4T mat(A(), D(), 0, 0,
B(), E(), 0, 0,
B(), E(), 0, 0,
0, 0, 1, 0,
center ? 0 : C(), center ? 0 : F(), 0, 1);

View File

@ -69,7 +69,7 @@ public:
template <typename U>
Curves<T>& operator = (const Curves<U>& curves)
{
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
m_Points[i][0].x = T(curves.m_Points[i][0].x); m_Points[i][0].y = T(curves.m_Points[i][0].y); m_Weights[i].x = T(curves.m_Weights[i].x);
m_Points[i][1].x = T(curves.m_Points[i][1].x); m_Points[i][1].y = T(curves.m_Points[i][1].y); m_Weights[i].y = T(curves.m_Weights[i].y);
@ -87,7 +87,7 @@ public:
/// <returns>Reference to updated self</returns>
Curves<T>& operator += (const Curves<T>& curves)
{
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
m_Points[i][0] += curves.m_Points[i][0];
m_Points[i][1] += curves.m_Points[i][1];
@ -107,7 +107,7 @@ public:
/// <returns>Reference to updated self</returns>
Curves<T>& operator *= (const Curves<T>& curves)
{
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
m_Points[i][0] *= curves.m_Points[i][0];
m_Points[i][1] *= curves.m_Points[i][1];
@ -127,7 +127,7 @@ public:
/// <returns>Reference to updated self</returns>
Curves<T>& operator *= (const T& t)
{
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
m_Points[i][0] *= t;
m_Points[i][1] *= t;
@ -145,7 +145,7 @@ public:
/// </summary>
void Init()
{
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
m_Points[i][0] = v2T(0);//0,0 -> 0,0 -> 1,1 -> 1,1.
m_Points[i][1] = v2T(0);
@ -173,7 +173,7 @@ public:
{
bool set = false;
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
if ((m_Points[i][0] != v2T(0)) ||
(m_Points[i][1] != v2T(0)) ||
@ -257,7 +257,7 @@ Curves<T> operator * (const Curves<T>& curves, const T& t)
{
Curves<T> c(curves);
for (uint i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
c.m_Points[i][0] *= t;
c.m_Points[i][1] *= t;

View File

@ -1075,14 +1075,15 @@ public:
/// </summary>
/// <param name="sym">The type of symmetry to add</param>
/// <param name="rand">The random context to use for generating random symmetry</param>
void AddSymmetry(int sym, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
void AddSymmetry(intmax_t sym, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
{
size_t i, k, result = 0;
intmax_t k;
size_t i, result = 0;
T a;
if (sym == 0)
{
static int symDistrib[] = {
static intmax_t symDistrib[] = {
-4, -3,
-2, -2, -2,
-1, -1, -1,
@ -1356,6 +1357,9 @@ public:
case FLAME_MOTION_VIBRANCY:
APP_FMP(m_Vibrancy);
break;
case FLAME_MOTION_NONE:
default:
break;
}
}
}
@ -1581,7 +1585,7 @@ public:
//Whether or not any symmetry was added. This field is in a bit of a state of conflict right now as flam3 has a severe bug.
//Xml field: "symmetry".
int m_Symmetry;
intmax_t m_Symmetry;
//The number of iterations per pixel of the final output image. Note this is not affected by the increase in pixels in the
//histogram and DE filtering buffer due to supersampling. It can be affected by a non-zero zoom value though.

View File

@ -36,6 +36,7 @@ public:
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
MotionParam(const MotionParam<T>& other)
: pair <eEmberMotionParam, T>()
{
operator=<T>(other);
}
@ -70,8 +71,8 @@ public:
template <typename U>
MotionParam &operator = (const MotionParam<U>& other)
{
this->first = other.first;
this->second = T(other.second);
this->first = other.first;
this->second = T(other.second);
return *this;
}

View File

@ -1,4 +1,6 @@
#pragma once
#ifdef WIN32
#pragma once
#endif
/// <summary>
/// Precompiled header file. Place all system includes here with appropriate #defines for different operating systems and compilers.

View File

@ -104,13 +104,19 @@ public:
b = false;
}
}
catch (...)
catch (const std::exception& e)
{
if (f.is_open())
f.close();
cout << "Error: Writing flame " << filename << " failed: " << e.what() << endl;
b = false;
}
catch (...)
{
cout << "Error: Writing flame " << filename << " failed." << endl;
b = false;
}
if (f.is_open())
f.close();
return b;
}
@ -125,7 +131,7 @@ public:
/// <param name="intPalette">If true use integers instead of floating point numbers when embedding a non-hex formatted palette, else use floating point numbers.</param>
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
/// <returns>The Xml string representation of the passed in ember</returns>
string ToString(Ember<T>& ember, string extraAttributes, size_t printEditDepth, bool doEdits, bool intPalette, bool hexPalette = true)
string ToString(Ember<T>& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool intPalette, bool hexPalette = true)
{
size_t i, j;
string s;
@ -313,7 +319,7 @@ public:
/// <param name="sheepGen">The sheep generation used if > 0. Default: 0.</param>
/// <param name="sheepId">The sheep id used if > 0. Default: 0.</param>
/// <returns></returns>
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, string action, string nick, string url, string id, string comment, int sheepGen = 0, int sheepId = 0)
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, const string& action, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen = 0, intmax_t sheepId = 0)
{
char timeString[128];
time_t myTime;
@ -750,6 +756,7 @@ private:
case MOTION_TRIANGLE:
os << "\"triangle\"";
break;
default:
case MOTION_SAW:
os << "\"saw\"";
break;
@ -761,7 +768,7 @@ private:
T cx = 0.0;
T cy = 0.0;
for (int i = 0; i < motion.m_MotionParams.size(); ++i)
for (size_t i = 0; i < motion.m_MotionParams.size(); ++i)
{
switch(motion.m_MotionParams[i].first)
{
@ -816,6 +823,9 @@ private:
case FLAME_MOTION_VIBRANCY:
os << " vibrancy=\"" << motion.m_MotionParams[i].second << "\"";
break;
case FLAME_MOTION_NONE:
default:
break;
}
}

View File

@ -57,7 +57,7 @@ class EMBER_API QTIsaac
public:
enum { N = (1 << ALPHA) };
UintBytes m_Cache;
int m_LastIndex;
size_t m_LastIndex;
/// <summary>
/// Global ISAAC RNG to be used from anywhere. This is not thread safe, so take caution to only
@ -94,7 +94,7 @@ public:
Srand(a, b, c, s);
m_LastIndex = 0;
m_Cache.Uint = Rand();
T temp = RandByte();//Need to call at least once so other libraries can link.
RandByte();//Need to call at least once so other libraries can link.
}
/// <summary>
@ -291,12 +291,12 @@ public:
{
if (s == nullptr)//Default to using time plus index as the seed if s was nullptr.
{
for (int i = 0; i < N; i++)
m_Rc.randrsl[i] = static_cast<T>(NowMs()) + i;
for (size_t i = 0; i < N; i++)
m_Rc.randrsl[i] = static_cast<T>(NowMs() + i);
}
else
{
for (int i = 0; i < N; i++)
for (size_t i = 0; i < N; i++)
m_Rc.randrsl[i] = s[i];
}

View File

@ -39,7 +39,7 @@ public:
/// <param name="index">The index in the palette file</param>
/// <param name="size">The size of the palette which should be 256</param>
/// <param name="xmlPaletteEntries">A pointer to 256 color entries</param>
Palette(const string& name, int index, uint size, v4T* xmlPaletteEntries)
Palette(const string& name, int index, size_t size, v4T* xmlPaletteEntries)
{
m_Name = name;
m_Index = index;
@ -86,7 +86,7 @@ public:
0x00, 0x81, 0x96, 0x8d, 0x00, 0x81, 0x9a, 0x8d, 0x00, 0x85, 0x9a, 0x8d, 0x00, 0x89, 0x9e, 0x8d, 0x00, 0x89, 0x9e, 0x8d, 0x00, 0x8d, 0xa2, 0x97, 0x00, 0x95, 0xa2, 0x97, 0x00, 0x8d, 0xa2, 0x97,
0x00, 0x96, 0xa6, 0x8d, 0x00, 0x9a, 0xa1, 0x8d, 0x00, 0x9e, 0xa9, 0x84, 0x00, 0x9e, 0xa6, 0x7a, 0x00, 0xa2, 0xa5, 0x71, 0x00, 0x9e, 0xa6, 0x71, 0x00, 0x9a, 0xa6, 0x71, 0x00, 0x95, 0x9d, 0x71 };
for (uint i = 0; i < size; i++)
for (size_t i = 0; i < size; i++)
{
m_Entries[i].a = T(palette15[i * 4 + 0]);
m_Entries[i].r = T(palette15[i * 4 + 1]);
@ -208,7 +208,7 @@ public:
palette.m_Filename = m_Filename;
palette.m_Entries.resize(Size());
for (uint i = 0; i < Size(); i++)
for (size_t i = 0; i < Size(); i++)
{
size_t ii = (i * 256) / COLORMAP_LENGTH;
T rgb[3], hsv[3];
@ -348,7 +348,7 @@ public:
if (palette.Size() != Size())
palette.m_Entries.resize(Size());
for (uint j = 0; j < palette.Size(); j++)
for (size_t j = 0; j < palette.Size(); j++)
{
palette.m_Entries[j] = m_Entries[j] * colorScalar;
palette.m_Entries[j].a = 1;
@ -362,16 +362,16 @@ public:
/// </summary>
/// <param name="height">The height of the output block</param>
/// <returns>A vector holding the color values</returns>
vector<byte> MakeRgbPaletteBlock(uint height)
vector<byte> MakeRgbPaletteBlock(size_t height)
{
size_t width = Size();
vector<byte> v(height * width * 3);
if (v.size() == (height * Size() * 3))
{
for (uint i = 0; i < height; i++)
for (size_t i = 0; i < height; i++)
{
for (uint j = 0; j < width; j++)
for (size_t j = 0; j < width; j++)
{
v[(width * 3 * i) + (j * 3)] = byte(m_Entries[j][0] * T(255));//Palettes are as [0..1], so convert to [0..255] here since it's for GUI display.
v[(width * 3 * i) + (j * 3) + 1] = byte(m_Entries[j][1] * T(255));
@ -443,7 +443,7 @@ public:
/// <param name="b">Blue 0 - 1</param>
static void HsvToRgb(T h, T s, T v, T& r, T& g, T& b)
{
int j;
intmax_t j;
T f, p, q, t;
while (h >= 6)
@ -522,7 +522,7 @@ public:
template<typename bucketT>
static void CalcNewRgb(bucketT* cBuf, T ls, T highPow, bucketT* newRgb)
{
int rgbi;
size_t rgbi;
T newls, lsratio;
bucketT newhsv[3];
T maxa, maxc;

View File

@ -80,7 +80,7 @@ public:
Palette<T>* GetRandomPalette()
{
auto p = m_Palettes.begin();
int i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % Size();
size_t i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % Size();
//Move p forward i elements.
while (i < paletteFileIndex && p != m_Palettes.end())
@ -91,7 +91,7 @@ public:
if (i < Size())
{
int paletteIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % p->second.size();
size_t paletteIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % p->second.size();
if (paletteIndex < p->second.size())
return &p->second[paletteIndex];
@ -106,11 +106,11 @@ public:
/// <param name="filename">The filename of the palette to retrieve</param>
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
/// <returns>A pointer to the requested palette if the index was in range, else nullptr.</returns>
Palette<T>* GetPalette(const string& filename, int i)
Palette<T>* GetPalette(const string& filename, size_t i)
{
auto& palettes = m_Palettes[filename];
if (!palettes.empty() && i < int(palettes.size()))
if (!palettes.empty() && i < palettes.size())
return &palettes[i];
return nullptr;
@ -142,7 +142,7 @@ public:
/// <param name="hue">The hue adjustment to apply</param>
/// <param name="palette">The palette to store the output</param>
/// <returns>True if successful, else false.</returns>
bool GetHueAdjustedPalette(const string& filename, int i, T hue, Palette<T>& palette)
bool GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette)
{
bool b = false;

View File

@ -151,6 +151,7 @@ public:
/// </summary>
/// <param name="color">The Color object to copy</param>
Color(const Color<T>& color)
: v4T()
{
Color<T>::operator=<T>(color);
}

View File

@ -671,7 +671,7 @@ Finish:
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
/// <returns>The EmberImageComments object with image comments filled out</returns>
template <typename T, typename bucketT>
EmberImageComments Renderer<T, bucketT>::ImageComments(EmberStats& stats, size_t printEditDepth, bool intPalette, bool hexPalette)
EmberImageComments Renderer<T, bucketT>::ImageComments(const EmberStats& stats, size_t printEditDepth, bool intPalette, bool hexPalette)
{
ostringstream ss;
EmberImageComments comments;
@ -708,7 +708,7 @@ void Renderer<T, bucketT>::MakeDmap(T colorScalar)
/// </summary>
/// <returns>True if success, else false</returns>
template <typename T, typename bucketT>
bool Renderer<T, bucketT>::Alloc()
bool Renderer<T, bucketT>::Alloc(bool histOnly)
{
bool b = true;
bool lock =
@ -730,6 +730,14 @@ bool Renderer<T, bucketT>::Alloc()
b &= (m_HistBuckets.size() == m_SuperSize);
}
if (histOnly)
{
if (lock)
LeaveResize();
return b;
}
if (m_SuperSize != m_AccumulatorBuckets.size())
{
m_AccumulatorBuckets.resize(m_SuperSize);
@ -1224,7 +1232,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
sp.sched_priority = m_Priority;
pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
#else
pthread_setschedprio(pthread_self(), (int)m_Priority);
pthread_setschedprio(pthread_self(), int(m_Priority));
#endif
//Timing t;
IterParams<T> params;
@ -1268,7 +1276,6 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
if (m_Callback && threadIndex == 0)
{
percent = 100.0 *
double
(
double
@ -1342,7 +1349,7 @@ template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsP
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnitY() const { return m_PixelsPerUnitY; }
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::K1() const { return m_K1; }
template <typename T, typename bucketT> bucketT Renderer<T, bucketT>::K2() const { return m_K2; }
template <typename T, typename bucketT> const CarToRas<T>* Renderer<T, bucketT>::CoordMap() const { return &m_CarToRas; }
template <typename T, typename bucketT> const CarToRas<T>& Renderer<T, bucketT>::CoordMap() const { return m_CarToRas; }
template <typename T, typename bucketT> tvec4<bucketT, glm::defaultp>* Renderer<T, bucketT>::HistBuckets() { return m_HistBuckets.data(); }
template <typename T, typename bucketT> tvec4<bucketT, glm::defaultp>* Renderer<T, bucketT>::AccumulatorBuckets() { return m_AccumulatorBuckets.data(); }
template <typename T, typename bucketT> SpatialFilter<bucketT>* Renderer<T, bucketT>::GetSpatialFilter() { return m_SpatialFilter.get(); }

View File

@ -38,9 +38,7 @@ namespace EmberNs
/// for every single function in this class, saying it can't find the implementation. This warning
/// can be safely ignored.
/// Template argument T expected to be float or double.
/// Template argument bucketT was originally used to experiment with different types for the histogram, however
/// the only types that work are float and double, so it's useless and should always match what T is.
/// Mismatched types between T and bucketT are undefined.
/// Template argument bucketT must always be float.
/// </summary>
template <typename T, typename bucketT>
class EMBER_API Renderer : public RendererBase
@ -65,12 +63,12 @@ public:
virtual bool CreateTemporalFilter(bool& newAlloc) override;
virtual size_t HistBucketSize() const override { return sizeof(tvec4<bucketT, glm::defaultp>); }
virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override;
virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) override;
virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) override;
protected:
//New virtual functions to be overridden in derived renderers that use the GPU, but not accessed outside.
virtual void MakeDmap(T colorScalar);
virtual bool Alloc();
virtual bool Alloc(bool histOnly = false);
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
virtual eRenderStatus LogScaleDensityFilter();
virtual eRenderStatus GaussianDensityFilter();
@ -89,7 +87,7 @@ public:
inline T PixelsPerUnitY() const;
inline bucketT K1() const;
inline bucketT K2() const;
inline const CarToRas<T>* CoordMap() const;
inline const CarToRas<T>& CoordMap() const;
inline tvec4<bucketT, glm::defaultp>* HistBuckets();
inline tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
inline SpatialFilter<bucketT>* GetSpatialFilter();

View File

@ -120,7 +120,7 @@ public:
virtual void ComputeQuality() = 0;
virtual void ComputeCamera() = 0;
virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0;
virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) = 0;
virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) = 0;
virtual DensityFilterBase* GetDensityFilter() = 0;
//Non-virtual renderer properties, getters only.

View File

@ -44,7 +44,7 @@ enum eCrossMode
/// Most functions in this class perform a particular action and return
/// a string describing what it did so it can be recorded in an Xml edit doc
/// to be saved with the ember when converting to Xml.
/// Since it's members can occupy significant memory space and also have
/// Since its members can occupy significant memory space and also have
/// hefty initialization sequences, it's important to declare one instance
/// and reuse it for the duration of the program instead of creating and deleting
/// them as local variables.
@ -186,11 +186,10 @@ public:
/// <param name="sym">The type of symmetry to add if random specified. If 0, it will be added randomly.</param>
/// <param name="speed">The speed to multiply the pre affine transforms by if the mutate mode is MUTATE_ALL_COEFS, else ignored.</param>
/// <returns>A string describing what was done</returns>
string Mutate(Ember<T>& ember, eMutateMode mode, vector<eVariationId>& useVars, int sym, T speed)
string Mutate(Ember<T>& ember, eMutateMode mode, vector<eVariationId>& useVars, intmax_t sym, T speed)
{
bool done = false;
size_t modXform;
char ministr[32];
T randSelect;
ostringstream os;
Ember<T> mutation;
@ -285,14 +284,13 @@ public:
else if (mode == MUTATE_POST_XFORMS)
{
bool same = (m_Rand.Rand() & 3) > 0;//25% chance of using the same post for all of them.
uint b = 1 + m_Rand.Rand() % 6;
size_t b = 1 + m_Rand.Rand() % 6;
sprintf_s(ministr, 32, "(%d%s)", b, same ? " same" : "");
os << "mutate post xforms " << ministr;
os << "mutate post xforms " << b << (same ? " same" : "");
for (size_t i = 0; i < ember.TotalXformCount(); i++)
{
int copy = (i > 0) && same;
bool copy = (i > 0) && same;
Xform<T>* xform = ember.GetTotalXform(i);
if (copy)//Copy the post from the first xform to the rest of them.
@ -605,7 +603,7 @@ public:
{
vector<eVariationId> useVars;
Random(ember, useVars, static_cast<int>(m_Rand.Frand<T>(-2, 2)), 0);
Random(ember, useVars, static_cast<intmax_t>(m_Rand.Frand<T>(-2, 2)), 0);
}
/// <summary>
@ -615,7 +613,7 @@ public:
/// <param name="useVars">A list of variations to use. If empty, any variation can be used.</param>
/// <param name="sym">The symmetry type to use from -2 to 2</param>
/// <param name="specXforms">The number of xforms to use. If 0, a quasi random count is used.</param>
void Random(Ember<T>& ember, vector<eVariationId>& useVars, int sym, size_t specXforms)
void Random(Ember<T>& ember, vector<eVariationId>& useVars, intmax_t sym, size_t specXforms)
{
bool postid, addfinal = false;
int var, samed, multid, samepost;
@ -806,9 +804,9 @@ public:
/// <param name="tries">The number of test renders to try before giving up</param>
/// <param name="changePalette">Change palette if true, else keep trying with the same palette.</param>
/// <param name="colorResolution">The resolution of the test histogram. This value ^ 3 will be used for the total size. Common value is 10.</param>
void ImproveColors(Ember<T>& ember, int tries, bool changePalette, int colorResolution)
void ImproveColors(Ember<T>& ember, size_t tries, bool changePalette, size_t colorResolution)
{
int i;
size_t i;
T best, b;
Ember<T> bestEmber = ember;
@ -847,7 +845,7 @@ public:
/// <param name="ember">The ember to render</param>
/// <param name="colorResolution">The resolution of the test histogram. This value ^ 3 will be used for the total size. Common value is 10.</param>
/// <returns>The number of histogram cells that weren't black</returns>
T TryColors(Ember<T>& ember, int colorResolution)
T TryColors(Ember<T>& ember, size_t colorResolution)
{
byte* p;
size_t i, hits = 0, res = colorResolution;
@ -903,7 +901,7 @@ public:
}
/// <summary>
/// Change around color coordinates. Optionall change out the entire palette.
/// Change around color coordinates. Optionally change out the entire palette.
/// </summary>
/// <param name="ember">The ember whose xform's color coordinates will be changed</param>
/// <param name="changePalette">Change palette if true, else don't</param>
@ -1071,7 +1069,7 @@ public:
/// <param name="result">The result of the spin</param>
/// <param name="frame">The frame in the sequence to be stored in the m_Time member of result</param>
/// <param name="blend">The interpolation time</param>
void Spin(Ember<T>& parent, Ember<T>* templ, Ember<T>& result, int frame, T blend)
void Spin(Ember<T>& parent, Ember<T>* templ, Ember<T>& result, size_t frame, T blend)
{
char temp[50];
@ -1112,7 +1110,7 @@ public:
/// <param name="frame">The frame in the sequence to be stored in the m_Time member of result</param>
/// <param name="seqFlag">True if embers points to the first or last ember in the entire sequence, else false.</param>
/// <param name="blend">The interpolation time</param>
void SpinInter(Ember<T>* parents, Ember<T>* templ, Ember<T>& result, int frame, bool seqFlag, T blend)
void SpinInter(Ember<T>* parents, Ember<T>* templ, Ember<T>& result, size_t frame, bool seqFlag, T blend)
{
char temp[50];
@ -1286,8 +1284,8 @@ public:
m_Samples.resize(samples);
params.m_Count = samples;
params.m_Skip = 20;
//params.m_OneColDiv2 = m_Renderer->CoordMap()->OneCol() / 2;
//params.m_OneRowDiv2 = m_Renderer->CoordMap()->OneRow() / 2;
//params.m_OneColDiv2 = m_Renderer->CoordMap().OneCol() / 2;
//params.m_OneRowDiv2 = m_Renderer->CoordMap().OneRow() / 2;
size_t bv = m_Iterator->Iterate(ember, params, m_Samples.data(), m_Rand);//Use a special fuse of 20, all other calls to this will use 15, or 100.
@ -1349,7 +1347,7 @@ public:
/// <param name="comment">The comment to include</param>
/// <param name="sheepGen">The sheep generation used if > 0. Default: 0.</param>
/// <param name="sheepId">The sheep id used if > 0. Default: 0.</param>
void SetSpinParams(bool smooth, T stagger, T offsetX, T offsetY, string nick, string url, string id, string comment, int sheepGen, int sheepId)
void SetSpinParams(bool smooth, T stagger, T offsetX, T offsetY, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen, intmax_t sheepId)
{
m_Smooth = smooth;
m_SheepGen = sheepGen;
@ -1365,8 +1363,8 @@ public:
private:
bool m_Smooth;
int m_SheepGen;
int m_SheepId;
intmax_t m_SheepGen;
intmax_t m_SheepId;
T m_Stagger;
T m_OffsetX;
T m_OffsetY;

View File

@ -62,19 +62,19 @@ public:
/// Return the begin time as a double.
/// </summary>
/// <returns></returns>
double BeginTime() { return static_cast<double>(m_BeginTime.time_since_epoch().count()); }
double BeginTime() const { return static_cast<double>(m_BeginTime.time_since_epoch().count()); }
/// <summary>
/// Return the end time as a double.
/// </summary>
/// <returns></returns>
double EndTime() { return static_cast<double>(m_EndTime.time_since_epoch().count()); }
double EndTime() const { return static_cast<double>(m_EndTime.time_since_epoch().count()); }
/// <summary>
/// Return the elapsed time in milliseconds.
/// </summary>
/// <returns>The elapsed time in milliseconds as a double</returns>
double ElapsedTime()
double ElapsedTime() const
{
duration<double> elapsed = duration_cast<milliseconds, Clock::rep, Clock::period>(m_EndTime - m_BeginTime);
@ -89,7 +89,7 @@ public:
/// </summary>
/// <param name="ms">The ms</param>
/// <returns>The formatted string</returns>
string Format(double ms)
string Format(double ms) const
{
stringstream ss;

View File

@ -213,13 +213,19 @@ static bool ReadFile(const char* filename, string& buf, bool nullTerminate = tru
fclose(f);
}
}
catch (...)
catch (const std::exception& e)
{
if (f != nullptr)
fclose(f);
cout << "Error: Reading file " << filename << " failed: " << e.what() << endl;
b = false;
}
catch (...)
{
cout << "Error: Reading file " << filename << " failed." << endl;
b = false;
}
if (f != nullptr)
fclose(f);
return b;
}
@ -270,7 +276,7 @@ static void CopyVec(vector<T>& dest, const vector<U>& source, std::function<void
template <typename T>
static void ClearVec(vector<T*>& vec, bool arrayDelete = false)
{
for (uint i = 0; i < vec.size(); i++)
for (size_t i = 0; i < vec.size(); i++)
{
if (vec[i] != nullptr)
{
@ -286,6 +292,32 @@ static void ClearVec(vector<T*>& vec, bool arrayDelete = false)
vec.clear();
}
/// <summary>
/// Determine whether all elements in two containers are equal.
/// </summary>
/// <param name="c1">The first collection to compare</param>
/// <param name="c2">The second collection to compare</param>
/// <returns>True if the sizes and all elements in both collections are equal, else false.</returns>
template <typename T>
static bool Equal(const T& c1, const T& c2)
{
bool equal = c1.size() == c2.size();
if (equal)
{
for (auto it1 = c1.begin(), it2 = c2.begin(); it1 != c1.end(); ++it1, ++it2)
{
if (*it1 != *it2)
{
equal = false;
break;
}
}
}
return equal;
}
/// <summary>
/// Thin wrapper around passing a vector to memset() to relieve
/// the caller of having to pass the size.
@ -305,15 +337,15 @@ static inline void Memset(vector<T>& vec, int val = 0)
/// <param name="x">The value to return the floor of</param>
/// <returns>The floored value</returns>
template <typename T>
static inline int Floor(T val)
static inline intmax_t Floor(T val)
{
if (val >= 0)
{
return static_cast<int>(val);
return static_cast<intmax_t>(val);
}
else
{
int i = static_cast<int>(val);//Truncate.
intmax_t i = static_cast<intmax_t>(val);//Truncate.
return i - (i > val);//Convert trunc to floor.
}
}
@ -862,25 +894,9 @@ static string GetPath(const string& filename)
/// <returns>The value of the specified environment variable if found, else default</returns>
template <typename T>
static inline T Arg(char* name, T def)
{
T t;
return t;
}
/// <summary>
/// Template specialization for Arg<>() with a type of int.
/// </summary>
/// <param name="name">The name of the environment variable to query</param>
/// <param name="def">The default value to return if the environment variable was not present</param>
/// <returns>The value of the specified environment variable if found, else default</returns>
template <>
#ifdef _WIN32
static
#endif
int Arg<int>(char* name, int def)
{
char* ch;
int returnVal;
T returnVal;
#ifdef WIN32
size_t len;
errno_t err = _dupenv_s(&ch, &len, name);
@ -892,7 +908,17 @@ int Arg<int>(char* name, int def)
if (err || !ch)
returnVal = def;
else
returnVal = atoi(ch);
{
T tempVal;
istringstream istr(ch);
istr >> tempVal;
if (!istr.bad() && !istr.fail())
returnVal = tempVal;
else
returnVal = def;
}
#ifdef WIN32
free(ch);
@ -900,21 +926,6 @@ int Arg<int>(char* name, int def)
return returnVal;
}
/// <summary>
/// Template specialization for Arg<>() with a type of uint.
/// </summary>
/// <param name="name">The name of the environment variable to query</param>
/// <param name="def">The default value to return if the environment variable was not present</param>
/// <returns>The value of the specified environment variable if found, else default</returns>
template <>
#ifdef _WIN32
static
#endif
uint Arg<uint>(char* name, uint def)
{
return Arg<int>(name, static_cast<int>(def));
}
/// <summary>
/// Template specialization for Arg<>() with a type of bool.
/// </summary>
@ -930,39 +941,6 @@ bool Arg<bool>(char* name, bool def)
return (Arg<int>(name, -999) != -999) ? true : def;
}
/// <summary>
/// Template specialization for Arg<>() with a type of double.
/// </summary>
/// <param name="name">The name of the environment variable to query</param>
/// <param name="def">The default value to return if the environment variable was not present</param>
/// <returns>The value of the specified environment variable if found, else default</returns>
template <>
#ifdef _WIN32
static
#endif
double Arg<double>(char* name, double def)
{
char* ch;
double returnVal;
#ifdef WIN32
size_t len;
errno_t err = _dupenv_s(&ch, &len, name);
#else
int err = 1;
ch = getenv(name);
#endif
if (err || !ch)
returnVal = def;
else
returnVal = atof(ch);
#ifdef WIN32
free(ch);
#endif
return returnVal;
}
/// <summary>
/// Template specialization for Arg<>() with a type of string.
/// </summary>
@ -1031,6 +1009,24 @@ static uint FindAndReplace(T& source, const T& find, const T& replace)
return replaceCount;
}
/// <summary>
/// Split a string into tokens and place them in a vector.
/// </summary>
/// <param name="str">The string to split</param>
/// <param name="del">The delimiter to split the string on</param>
/// <returns>The split strings, each as an element in a vector.</returns>
static vector<string> Split(const string& str, char del)
{
string tok;
vector<string> vec;
stringstream ss(str);
while (getline(ss, tok, del))
vec.push_back(tok);
return vec;
}
/// <summary>
/// Return a character pointer to a version string composed of the EMBER_OS and EMBER_VERSION values.
/// </summary>

View File

@ -1428,7 +1428,7 @@ public:
/// <param name="type">The type of the parameter</param>
/// <param name="min">The minimum value the parameter can be</param>
/// <param name="max">The maximum value the parameter can be</param>
ParamWithName(T* param, string name, T def = 0, eParamType type = REAL, T min = TLOW, T max = TMAX)
ParamWithName(T* param, const string& name, T def = 0, eParamType type = REAL, T min = TLOW, T max = TMAX)
{
Init(param, name, def, type, min, max);
}
@ -1481,7 +1481,7 @@ public:
/// <param name="min">The minimum value the parameter can be</param>
/// <param name="max">The maximum value the parameter can be</param>
/// <param name="isPrecalc">Whether the parameter is actually a precalculated value. Default: false.</param>
void Init(T* param, string name, T def = 0, eParamType type = REAL, T min = TLOW, T max = TMAX, bool isPrecalc = false)
void Init(T* param, const string& name, T def = 0, eParamType type = REAL, T min = TLOW, T max = TMAX, bool isPrecalc = false)
{
m_Param = param;
m_Def = def;

View File

@ -355,7 +355,7 @@ public:
//Keep a list of which variations derive from ParametricVariation.
//Note that these are not new copies, rather just pointers to the original instances in m_Variations.
for (uint i = 0; i < m_Variations.size(); i++)
for (size_t i = 0; i < m_Variations.size(); i++)
{
if (ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(m_Variations[i]))
m_ParametricVariations.push_back(parVar);
@ -412,7 +412,7 @@ public:
/// <returns>A pointer to the variation if found, else nullptr.</returns>
const Variation<T>* GetVariation(eVariationId id) const
{
for (uint i = 0; i < m_Variations.size() && m_Variations[i] != nullptr; i++)
for (size_t i = 0; i < m_Variations.size() && m_Variations[i] != nullptr; i++)
if (id == m_Variations[i]->VariationId())
return m_Variations[i];
@ -435,7 +435,7 @@ public:
/// <returns>A pointer to the variation if found, else nullptr.</returns>
const Variation<T>* GetVariation(const string& name) const
{
for (uint i = 0; i < m_Variations.size() && m_Variations[i] != nullptr; i++)
for (size_t i = 0; i < m_Variations.size() && m_Variations[i] != nullptr; i++)
if (!_stricmp(name.c_str(), m_Variations[i]->Name().c_str()))
return m_Variations[i];
@ -466,7 +466,7 @@ public:
/// <returns>The parametric variation with a matching name, else nullptr.</returns>
const ParametricVariation<T>* GetParametricVariation(const string& name) const
{
for (uint i = 0; i < m_ParametricVariations.size() && m_ParametricVariations[i] != nullptr; i++)
for (size_t i = 0; i < m_ParametricVariations.size() && m_ParametricVariations[i] != nullptr; i++)
if (!_stricmp(name.c_str(), m_ParametricVariations[i]->Name().c_str()))
return m_ParametricVariations[i];
@ -480,9 +480,9 @@ public:
/// <returns>The index of the variation with the matching name, else -1</returns>
int GetVariationIndex(const string& name)
{
for (uint i = 0; i < m_Variations.size() && m_Variations[i] != nullptr; i++)
for (size_t i = 0; i < m_Variations.size() && m_Variations[i] != nullptr; i++)
if (!_stricmp(name.c_str(), m_Variations[i]->Name().c_str()))
return i;
return int(i);
return -1;
}

View File

@ -277,7 +277,7 @@ public:
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T r = Zeps(pow(helper.m_PrecalcSqrtSumSquares, m_Dist));
int n = Floor<T>(m_Power * rand.Frand01<T>());
intmax_t n = Floor<T>(m_Power * rand.Frand01<T>());
T alpha = helper.m_PrecalcAtanyx + n * M_2PI / Zeps<T>(T(Floor<T>(m_Power)));
T sina = sin(alpha);
T cosa = cos(alpha);
@ -2571,7 +2571,7 @@ public:
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
int n;
intmax_t n;
T z = 4 * m_Dist / m_Power;
T r = pow(helper.m_PrecalcSqrtSumSquares, z);

View File

@ -1372,12 +1372,13 @@ public:
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
int i, j, l, k, m, m1, n, n1;
intmax_t l, k;
int i, j, m, m1, n, n1;
T r, rMin, offsetX, offsetY, x0 = 0, y0 = 0, x, y;
rMin = 20;
m = Floor<T>(helper.In.x / m_Step);
n = Floor<T>(helper.In.y / m_Step);
m = int(Floor<T>(helper.In.x / m_Step));
n = int(Floor<T>(helper.In.y / m_Step));
for (i = -1; i < 2; i++)
{

View File

@ -95,8 +95,8 @@ public:
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
int m = Floor<T>(T(0.5) * helper.In.x / m_Sc);
int n = Floor<T>(T(0.5) * helper.In.y / m_Sc);
int m = int(Floor<T>(T(0.5) * helper.In.x / m_Sc));
int n = int(Floor<T>(T(0.5) * helper.In.y / m_Sc));
T x = helper.In.x - (m * 2 + 1) * m_Sc;
T y = helper.In.y - (n * 2 + 1) * m_Sc;
T u = Zeps(Hypot(x, y));
@ -277,7 +277,7 @@ public:
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
int m, n, iters = 0;
intmax_t m, n, iters = 0;
T x, y, u;
do
@ -293,7 +293,7 @@ public:
if (++iters > 10)
break;
}
while ((DiscreteNoise2(int(m + m_Seed), n) > m_Dens) || (u > (T(0.3) + T(0.7) * DiscreteNoise2(m + 10, n + 3)) * m_Sc));
while ((DiscreteNoise2(int(m + m_Seed), int(n)) > m_Dens) || (u > (T(0.3) + T(0.7) * DiscreteNoise2(int(m + 10), int(n + 3))) * m_Sc));
helper.Out.x = m_Weight * (x + (m * 2 + 1) * m_Sc);
helper.Out.y = m_Weight * (y + (n * 2 + 1) * m_Sc);
@ -406,14 +406,14 @@ public:
Trans(m_X, m_Y, helper.In.x, helper.In.y, &ux, &uy);
int m = Floor<T>(T(0.5) * ux / m_Sc);
int n = Floor<T>(T(0.5) * uy / m_Sc);
intmax_t m = Floor<T>(T(0.5) * ux / m_Sc);
intmax_t n = Floor<T>(T(0.5) * uy / m_Sc);
x = ux - (m * 2 + 1) * m_Sc;
y = uy - (n * 2 + 1) * m_Sc;
u = Hypot(x, y);
if ((DiscreteNoise2(int(m + m_Seed), n) > m_Dens) || (u > (T(0.3) + T(0.7) * DiscreteNoise2(m + 10, n + 3)) * m_Sc))
if ((DiscreteNoise2(int(m + m_Seed), int(n)) > m_Dens) || (u > (T(0.3) + T(0.7) * DiscreteNoise2(int(m + 10), int(n + 3))) * m_Sc))
{
ux = ux;
uy = uy;
@ -548,7 +548,7 @@ private:
void CircleR(T* ux, T* vy, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
{
int m, n, iters = 0;
intmax_t m, n, iters = 0;
T x, y, alpha, u;
do
@ -558,14 +558,14 @@ private:
m = Floor<T>(T(0.5) * x / m_Sc);
n = Floor<T>(T(0.5) * y / m_Sc);
alpha = M_2PI * rand.Frand01<T>();
u = T(0.3) + T(0.7) * DiscreteNoise2(m + 10, n + 3);
u = T(0.3) + T(0.7) * DiscreteNoise2(int(m + 10), int(n + 3));
x = u * cos(alpha);
y = u * sin(alpha);
if (++iters > 10)
break;
}
while (DiscreteNoise2(int(m + m_Seed), n) > m_Dens);
while (DiscreteNoise2(int(m + m_Seed), int(n)) > m_Dens);
*ux = x + (m * 2 + 1) * m_Sc;
*vy = y + (n * 2 + 1) * m_Sc;
@ -2704,7 +2704,7 @@ public:
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
int m, n;
intmax_t m, n;
T alpha, beta, offsetAl, offsetBe, offsetGa, x, y;
//Transfer to trilinear coordinates, normalized to real distances from triangle sides.

View File

@ -878,7 +878,7 @@ public:
<< "\t\t inside = 1;\n"
<< "\t\t}\n"
<< "\n"
<< "\t\tif (" << zeroEdges << " && !inside)\n"
<< "\t\tif (" << zeroEdges << " != 0.0 && !inside)\n"
<< "\t\t{\n"
<< "\t\t u = v = 0;\n"
<< "\t\t}\n"

View File

@ -315,7 +315,7 @@ public:
//An adjustment of +/- 360 degrees is made until this is true.
if (emberSize > 1)
{
for (uint i = 1; i < emberSize; i++)
for (size_t i = 1; i < emberSize; i++)
{
//Only do this adjustment if not in compat mode..
if (embers[i - 1].m_AffineInterp != INTERP_COMPAT && embers[i - 1].m_AffineInterp != INTERP_OLDER)
@ -362,79 +362,22 @@ public:
}
/// <summary>
/// Convert the string to a floating point value and return a bool indicating success.
/// Thin wrapper around converting the string to a numeric value and return a bool indicating success.
/// See error report for errors.
/// </summary>
/// <param name="str">The string to convert</param>
/// <param name="val">The converted value</param>
/// <returns>True if success, else false.</returns>
bool Atof(const char* str, T& val)
template <typename valT>
bool Aton(const char* str, valT& val)
{
bool b = true;
char* endp;
const char* loc = __FUNCTION__;
std::istringstream istr(str);
//Reset errno.
errno = 0;//Note that this is not thread-safe.
istr >> val;
//Convert the string using strtod().
val = T(strtod(str, &endp));
//Check errno & return string.
if (endp != str + strlen(str))
{
m_ErrorReport.push_back(string(loc) + " : Error converting " + string(str) + ", extra chars");
b = false;
}
if (errno)
{
m_ErrorReport.push_back(string(loc) + " : Error converting " + string(str));
b = false;
}
return b;
}
/// <summary>
/// Thin wrapper around Atoi().
/// See error report for errors.
/// </summary>
/// <param name="str">The string to convert</param>
/// <param name="val">The converted uinteger value</param>
/// <returns>True if success, else false.</returns>
bool Atoi(const char* str, uint& val)
{
return Atoi(str, reinterpret_cast<int&>(val));
}
/// <summary>
/// Convert the string to an uinteger value and return a bool indicating success.
/// See error report for errors.
/// </summary>
/// <param name="str">The string to convert</param>
/// <param name="val">The converted uinteger value</param>
/// <returns>True if success, else false.</returns>
bool Atoi(const char* str, int& val)
{
bool b = true;
char* endp;
const char* loc = __FUNCTION__;
//Reset errno.
errno = 0;//Note that this is not thread-safe.
//Convert the string using strtod().
val = strtol(str, &endp, 10);
//Check errno & return string.
if (endp != str + strlen(str))
{
m_ErrorReport.push_back(string(loc) + " : Error converting " + string(str) + ", extra chars");
b = false;
}
if (errno)
if (istr.bad() || istr.fail())
{
m_ErrorReport.push_back(string(loc) + " : Error converting " + string(str));
b = false;
@ -443,6 +386,7 @@ public:
return b;
}
/// <summary>
/// Convert an integer to a string.
/// Just a wrapper around _itoa_s() which wraps the result in a std::string.
@ -554,11 +498,11 @@ private:
{
bool ret = true;
bool fromEmber = false;
uint newLinear = 0;
size_t newLinear = 0;
char* attStr;
const char* loc = __FUNCTION__;
int soloXform = -1;
uint i, j, count, index = 0;
size_t i, count, index = 0;
double vals[16];
xmlAttrPtr att, curAtt;
xmlNodePtr editNode, childNode, motionNode;
@ -577,38 +521,38 @@ private:
attStr = reinterpret_cast<char*>(xmlGetProp(emberNode, curAtt->name));
//First parse out simple float reads.
if (ParseAndAssignFloat(curAtt->name, attStr, "time", currentEmber.m_Time, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "scale", currentEmber.m_PixelsPerUnit, ret)) { currentEmber.m_OrigPixPerUnit = currentEmber.m_PixelsPerUnit; }
else if (ParseAndAssignFloat(curAtt->name, attStr, "rotate", currentEmber.m_Rotate, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "zoom", currentEmber.m_Zoom, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "filter", currentEmber.m_SpatialFilterRadius, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "temporal_filter_width", currentEmber.m_TemporalFilterWidth, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "temporal_filter_exp", currentEmber.m_TemporalFilterExp, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "quality", currentEmber.m_Quality, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "brightness", currentEmber.m_Brightness, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "gamma", currentEmber.m_Gamma, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "highlight_power", currentEmber.m_HighlightPower, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "vibrancy", currentEmber.m_Vibrancy, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "estimator_radius", currentEmber.m_MaxRadDE, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "estimator_minimum", currentEmber.m_MinRadDE, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "estimator_curve", currentEmber.m_CurveDE, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "gamma_threshold", currentEmber.m_GammaThresh, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "cam_zpos", currentEmber.m_CamZPos, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "cam_persp", currentEmber.m_CamPerspective, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "cam_perspective", currentEmber.m_CamPerspective, ret)) { }//Apo bug.
else if (ParseAndAssignFloat(curAtt->name, attStr, "cam_yaw", currentEmber.m_CamYaw, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "cam_pitch", currentEmber.m_CamPitch, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "cam_dof", currentEmber.m_CamDepthBlur, ret)) { }
if (ParseAndAssign(curAtt->name, attStr, "time", currentEmber.m_Time, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "scale", currentEmber.m_PixelsPerUnit, ret)) { currentEmber.m_OrigPixPerUnit = currentEmber.m_PixelsPerUnit; }
else if (ParseAndAssign(curAtt->name, attStr, "rotate", currentEmber.m_Rotate, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "zoom", currentEmber.m_Zoom, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "filter", currentEmber.m_SpatialFilterRadius, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "temporal_filter_width", currentEmber.m_TemporalFilterWidth, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "temporal_filter_exp", currentEmber.m_TemporalFilterExp, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "quality", currentEmber.m_Quality, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "brightness", currentEmber.m_Brightness, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "gamma", currentEmber.m_Gamma, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "highlight_power", currentEmber.m_HighlightPower, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "vibrancy", currentEmber.m_Vibrancy, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "estimator_radius", currentEmber.m_MaxRadDE, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "estimator_minimum", currentEmber.m_MinRadDE, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "estimator_curve", currentEmber.m_CurveDE, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "gamma_threshold", currentEmber.m_GammaThresh, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "cam_zpos", currentEmber.m_CamZPos, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "cam_persp", currentEmber.m_CamPerspective, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "cam_perspective", currentEmber.m_CamPerspective, ret)) { }//Apo bug.
else if (ParseAndAssign(curAtt->name, attStr, "cam_yaw", currentEmber.m_CamYaw, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "cam_pitch", currentEmber.m_CamPitch, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "cam_dof", currentEmber.m_CamDepthBlur, ret)) { }
//Parse simple int reads.
else if (ParseAndAssignInt(curAtt->name, attStr, "palette", currentEmber.m_Palette.m_Index, ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "oversample", currentEmber.m_Supersample , ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "supersample", currentEmber.m_Supersample , ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "temporal_samples", currentEmber.m_TemporalSamples, ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "sub_batch_size", currentEmber.m_SubBatchSize , ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "fuse", currentEmber.m_FuseCount , ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "soloxform", soloXform , ret)) { }
else if (ParseAndAssignInt(curAtt->name, attStr, "new_linear", newLinear , ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "palette", currentEmber.m_Palette.m_Index, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "oversample", currentEmber.m_Supersample , ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "supersample", currentEmber.m_Supersample , ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "temporal_samples", currentEmber.m_TemporalSamples, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "sub_batch_size", currentEmber.m_SubBatchSize , ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "fuse", currentEmber.m_FuseCount , ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "soloxform", soloXform , ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "new_linear", newLinear , ret)) { }
//Parse more complicated reads that have multiple possible values.
else if (!Compare(curAtt->name, "interpolation"))
@ -718,7 +662,7 @@ private:
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
for (glm::length_t j = 0; j < 4; j++)
{
ss >> currentEmber.m_Curves.m_Points[i][j].x;
ss >> currentEmber.m_Curves.m_Points[i][j].y;
@ -759,7 +703,7 @@ private:
if (!Compare(curAtt->name, "index"))
{
Atoi(attStr, index);
Aton(attStr, index);
}
else if(!Compare(curAtt->name, "rgb"))
{
@ -819,7 +763,7 @@ private:
if (!Compare(curAtt->name, "count"))
{
Atoi(attStr, count);
Aton(attStr, count);
}
else if (!Compare(curAtt->name, "data"))
{
@ -842,11 +786,6 @@ private:
//Make sure BOTH are not specified, otherwise either are ok.
int numColors = 0;
int numBytes = 0;
int index0, index1;
T hue0, hue1;
T blend = 0.5;
index0 = index1 = -1;
hue0 = hue1 = 0.0;
//Loop through the attributes of the palette element.
att = childNode->properties;
@ -863,7 +802,7 @@ private:
if (!Compare(curAtt->name, "count"))
{
Atoi(attStr, numColors);
Aton(attStr, numColors);
}
else if (!Compare(curAtt->name, "format"))
{
@ -915,7 +854,7 @@ private:
if (!Compare(curAtt->name, "kind"))
{
Atoi(attStr, symKind);
Aton(attStr, symKind);
}
else
{
@ -1016,8 +955,8 @@ private:
{
attStr = reinterpret_cast<char*>(xmlGetProp(childNode, curAtt->name));
if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", motion.m_MotionFreq, ret)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", motion.m_MotionOffset, ret)) { }
if (ParseAndAssign(curAtt->name, attStr, "motion_frequency", motion.m_MotionFreq, ret)) { }
else if (ParseAndAssign(curAtt->name, attStr, "motion_offset", motion.m_MotionOffset, ret)) { }
else if (!Compare(curAtt->name, "motion_function"))
{
string func(attStr);
@ -1135,7 +1074,7 @@ private:
bool r = false;
T val = 0.0;
if (Atof(attStr, val))
if (Aton(attStr, val))
{
motion.m_MotionParams.push_back(MotionParam<T>(param, val));
r = true;
@ -1160,7 +1099,7 @@ private:
bool success = true;
char* attStr;
const char* loc = __FUNCTION__;
uint j;
size_t j;
T temp;
double a, b, c, d, e, f;
double vals[10];
@ -1180,13 +1119,13 @@ private:
attStr = reinterpret_cast<char*>(xmlGetProp(childNode, curAtt->name));
//First parse out simple float reads.
if (ParseAndAssignFloat(curAtt->name, attStr, "weight", xform.m_Weight, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "color_speed", xform.m_ColorSpeed, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "animate", xform.m_Animate, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "opacity", xform.m_Opacity, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "var_color", xform.m_DirectColor, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { }
else if (ParseAndAssignFloat(curAtt->name, attStr, "motion_offset", xform.m_MotionOffset, success)) { }
if (ParseAndAssign(curAtt->name, attStr, "weight", xform.m_Weight, success)) { }
else if (ParseAndAssign(curAtt->name, attStr, "color_speed", xform.m_ColorSpeed, success)) { }
else if (ParseAndAssign(curAtt->name, attStr, "animate", xform.m_Animate, success)) { }
else if (ParseAndAssign(curAtt->name, attStr, "opacity", xform.m_Opacity, success)) { }
else if (ParseAndAssign(curAtt->name, attStr, "var_color", xform.m_DirectColor, success)) { }
else if (ParseAndAssign(curAtt->name, attStr, "motion_frequency", xform.m_MotionFreq, success)) { }
else if (ParseAndAssign(curAtt->name, attStr, "motion_offset", xform.m_MotionOffset, success)) { }
//Parse more complicated reads that have multiple possible values.
else if (!Compare(curAtt->name, "name"))
@ -1198,7 +1137,7 @@ private:
{
//Deprecated, set both color_speed and animate to this value.
//Huh? Either set it or not?
Atof(attStr, temp);
Aton(attStr, temp);
xform.m_ColorSpeed = (1 - temp) / 2;
xform.m_Animate = T(temp > 0 ? 0 : 1);
}
@ -1297,7 +1236,7 @@ private:
{
auto varCopy = var->Copy();
Atof(attStr, varCopy->m_Weight);
Aton(attStr, varCopy->m_Weight);
xform.AddVariation(varCopy);
}
//else
@ -1321,7 +1260,7 @@ private:
for (j = 0; j < xform.TotalVariationCount(); j++)
xform.GetVariation(j)->m_Weight = 0;
if (Atof(attStr, temp))
if (Aton(attStr, temp))
{
uint iTemp = static_cast<uint>(temp);
@ -1349,7 +1288,7 @@ private:
{
attStr = reinterpret_cast<char*>(xmlGetProp(childNode, curAtt->name));
if (Atof(attStr, temp))
if (Aton(attStr, temp))
{
for (j = 0; j < xform.TotalVariationCount(); j++)
xform.GetVariation(j)->m_Weight = temp;
@ -1366,7 +1305,7 @@ private:
}
//Now that all xforms have been parsed, go through and try to find params for the parametric variations.
for (uint i = 0; i < xform.TotalVariationCount(); i++)
for (size_t i = 0; i < xform.TotalVariationCount(); i++)
{
if (ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(xform.GetVariation(i)))
{
@ -1381,7 +1320,7 @@ private:
T val = 0;
attStr = CX(xmlGetProp(childNode, curAtt->name));
if (Atof(attStr, val))
if (Aton(attStr, val))
{
parVar->SetParamVal(name, val);
}
@ -1476,14 +1415,14 @@ private:
/// <param name="numColors">The number of colors present</param>
/// <param name="chan">The number of channels in each color</param>
/// <returns>True if there were no errors, else false.</returns>
bool ParseHexColors(char* colstr, Ember<T>& ember, int numColors, int chan)
bool ParseHexColors(char* colstr, Ember<T>& ember, size_t numColors, intmax_t chan)
{
int colorIndex = 0;
int colorCount = 0;
size_t colorIndex = 0;
size_t colorCount = 0;
uint r, g, b, a;
int ret;
char tmps[2];
int skip = static_cast<int>(abs(chan));
size_t skip = std::abs(chan);
bool ok = true;
const char* loc = __FUNCTION__;
@ -1539,47 +1478,25 @@ private:
}
/// <summary>
/// Wrapper to parse a floating point Xml value and convert it to float.
/// Wrapper to parse a numeric Xml string value and convert it.
/// </summary>
/// <param name="name">The xml tag to parse</param>
/// <param name="attStr">The name of the Xml attribute</param>
/// <param name="str">The name of the Xml tag</param>
/// <param name="val">The parsed value</param>
/// <param name="b">Bitwise ANDed with true if name matched str and the call to Atof() succeeded, else false. Used for keeping a running value between successive calls.</param>
/// <returns>True if the tag was matched, else false</returns>
bool ParseAndAssignFloat(const xmlChar* name, const char* attStr, const char* str, T& val, bool& b)
/// <param name="b">Bitwise ANDed with true if name matched str and the conversion succeeded, else false. Used for keeping a running value between successive calls.</param>
/// <returns>True if the tag was matched and the conversion succeeded, else false</returns>
template <typename valT>
bool ParseAndAssign(const xmlChar* name, const char* attStr, const char* str, valT& val, bool& b)
{
bool ret = false;
if (!Compare(name, str))
{
b &= Atof(attStr, val);
ret = true;//Means the strcmp() was right, but doesn't necessarily mean the conversion went ok.
}
istringstream istr(attStr);
return ret;
}
/// <summary>
/// Wrapper to parse an int Xml string value and convert it to an int.
/// </summary>
/// <param name="name">The xml tag to parse</param>
/// <param name="attStr">The name of the Xml attribute</param>
/// <param name="str">The name of the Xml tag</param>
/// <param name="val">The parsed value</param>
/// <param name="b">Bitwise ANDed with true if name matched str and the call to Atoi() succeeded, else false. Used for keeping a running value between successive calls.</param>
/// <returns>True if the tag was matched, else false</returns>
template <typename intT>
bool ParseAndAssignInt(const xmlChar* name, const char* attStr, const char* str, intT& val, bool& b)
{
bool ret = false;
T fval = 0;
if (!Compare(name, str))
{
b &= Atof(attStr, fval);
val = static_cast<intT>(fval);
ret = true;//Means the strcmp() was right, but doesn't necessarily mean the conversion went ok.
istr >> val;
ret = !istr.bad() && !istr.fail();//Means the Compare() was right, and the conversion succeeded.
}
return ret;

View File

@ -8,10 +8,10 @@
/// </summary>
/// <param name="opt">A populated EmberOptions object which specifies all program options to be used</param>
/// <returns>True if success, else false.</returns>
template <typename T, typename bucketT>
template <typename T>
bool EmberAnimate(EmberOptions& opt)
{
OpenCLWrapper wrapper;
OpenCLInfo& info(OpenCLInfo::Instance());
std::cout.imbue(std::locale(""));
@ -21,53 +21,93 @@ bool EmberAnimate(EmberOptions& opt)
if (opt.OpenCLInfo())
{
cout << "\nOpenCL Info: " << endl;
cout << wrapper.DumpInfo();
cout << info.DumpInfo();
return true;
}
//Regular variables.
Timing t;
bool unsorted = false;
bool startXml = false;
bool finishXml = false;
bool appendXml = false;
uint finalImageIndex = 0;
uint i, channels, ftime, padding;
string s, flameName, filename, inputPath = GetPath(opt.Input());
ostringstream os;
uint channels, padding;
size_t i;
string inputPath = GetPath(opt.Input());
vector<Ember<T>> embers;
EmberStats stats;
EmberReport emberReport;
EmberImageComments comments;
Ember<T> centerEmber;
XmlToEmber<T> parser;
EmberToXml<T> emberToXml;
vector<byte> finalImages[2];
std::thread writeThread;
unique_ptr<RenderProgress<T>> progress(new RenderProgress<T>());
unique_ptr<Renderer<T, bucketT>> renderer(CreateRenderer<T, bucketT>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, opt.Platform(), opt.Device(), false, 0, emberReport));
vector<string> errorReport = emberReport.ErrorReport();
EmberReport emberReport;
const vector<pair<size_t, size_t>> devices = Devices(opt.Devices());
std::atomic<size_t> atomfTime;
vector<std::thread> threadVec;
unique_ptr<RenderProgress<T>> progress;
vector<unique_ptr<Renderer<T, float>>> renderers;
vector<string> errorReport;
CriticalSection verboseCs;
if (!errorReport.empty())
emberReport.DumpErrorReport();
if (!renderer.get())
if (opt.EmberCL())
{
cout << "Renderer creation failed, exiting." << endl;
return false;
renderers = CreateRenderers<T>(OPENCL_RENDERER, devices, false, 0, emberReport);
errorReport = emberReport.ErrorReport();
if (!errorReport.empty())
emberReport.DumpErrorReport();
if (!renderers.size() || renderers.size() != devices.size())
{
cout << "Only created " << renderers.size() << " renderers out of " << devices.size() << " requested, exiting." << endl;
return false;
}
if (opt.DoProgress())
{
progress = unique_ptr<RenderProgress<T>>(new RenderProgress<T>());
renderers[0]->Callback(progress.get());
}
cout << "Using OpenCL to render." << endl;
if (opt.Verbose())
{
for (auto& device : devices)
{
cout << "Platform: " << info.PlatformName(device.first) << endl;
cout << "Device: " << info.DeviceName(device.first, device.second) << endl;
}
}
if (opt.ThreadCount() > 1)
cout << "Cannot specify threads with OpenCL, using 1 thread." << endl;
opt.ThreadCount(1);
for (auto& r : renderers)
r->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
if (opt.BitsPerChannel() != 8)
{
cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8." << endl;
opt.BitsPerChannel(8);
}
}
if (opt.EmberCL() && renderer->RendererType() != OPENCL_RENDERER)//OpenCL init failed, so fall back to CPU.
opt.EmberCL(false);
if (!InitPaletteList<T>(opt.PalettePath()))
return false;
if (!ParseEmberFile(parser, opt.Input(), embers))
return false;
if (!opt.EmberCL())
else
{
unique_ptr<Renderer<T, float>> tempRenderer(CreateRenderer<T>(CPU_RENDERER, devices, false, 0, emberReport));
errorReport = emberReport.ErrorReport();
if (!errorReport.empty())
emberReport.DumpErrorReport();
if (!tempRenderer.get())
{
cout << "Renderer creation failed, exiting." << endl;
return false;
}
if (opt.DoProgress())
{
progress = unique_ptr<RenderProgress<T>>(new RenderProgress<T>());
tempRenderer->Callback(progress.get());
}
if (opt.ThreadCount() == 0)
{
cout << "Using " << Timing::ProcessorCount() << " automatically detected threads." << endl;
@ -78,30 +118,15 @@ bool EmberAnimate(EmberOptions& opt)
cout << "Using " << opt.ThreadCount() << " manually specified threads." << endl;
}
renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
tempRenderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
renderers.push_back(std::move(tempRenderer));
}
else
{
cout << "Using OpenCL to render." << endl;
if (opt.Verbose())
{
cout << "Platform: " << wrapper.PlatformName(opt.Platform()) << endl;
cout << "Device: " << wrapper.DeviceName(opt.Platform(), opt.Device()) << endl;
}
if (!InitPaletteList<T>(opt.PalettePath()))
return false;
if (opt.ThreadCount() > 1)
cout << "Cannot specify threads with OpenCL, using 1 thread." << endl;
opt.ThreadCount(1);
renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
if (opt.BitsPerChannel() != 8)
{
cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8." << endl;
opt.BitsPerChannel(8);
}
}
if (!ParseEmberFile(parser, opt.Input(), embers))
return false;
if (opt.Format() != "jpg" &&
opt.Format() != "png" &&
@ -196,7 +221,7 @@ bool EmberAnimate(EmberOptions& opt)
//Cast to double in case the value exceeds 2^32.
double imageMem = double(channels) * double(embers[i].m_FinalRasW)
* double(embers[i].m_FinalRasH) * double(renderer->BytesPerChannel());
* double(embers[i].m_FinalRasH) * double(renderers[0]->BytesPerChannel());
double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1));
if (imageMem > maxMem)//Ensure the max amount of memory for a process isn't exceeded.
@ -236,128 +261,168 @@ bool EmberAnimate(EmberOptions& opt)
opt.FirstFrame(int(embers[0].m_Time));
if (opt.LastFrame() == UINT_MAX)
opt.LastFrame(ClampGte<uint>(uint(embers.back().m_Time - 1), opt.FirstFrame()));
opt.LastFrame(ClampGte<size_t>(size_t(embers.back().m_Time - 1), opt.FirstFrame()));
}
if (!opt.Out().empty())
{
appendXml = true;
filename = opt.Out();
cout << "Single output file " << opt.Out() << " specified for multiple images. They will be all overwritten and only the last image will remain." << endl;
cout << "Single output file " << opt.Out() << " specified for multiple images. They would be all overwritten and only the last image will remain, exiting." << endl;
return false;
}
//Final setup steps before running.
os.imbue(std::locale(""));
padding = uint(log10((double)embers.size())) + 1;
renderer->SetEmber(embers);
renderer->EarlyClip(opt.EarlyClip());
renderer->YAxisUp(opt.YAxisUp());
renderer->LockAccum(opt.LockAccum());
renderer->InsertPalette(opt.InsertPalette());
renderer->PixelAspectRatio(T(opt.AspectRatio()));
renderer->Transparency(opt.Transparency());
renderer->NumChannels(channels);
renderer->BytesPerChannel(opt.BitsPerChannel() / 8);
renderer->Priority((eThreadPriority)Clamp<int>((int)eThreadPriority::LOWEST, (int)eThreadPriority::HIGHEST, opt.Priority()));
renderer->Callback(opt.DoProgress() ? progress.get() : nullptr);
padding = uint(log10(double(embers.size()))) + 1;
std::function<void(uint)> saveFunc = [&](uint threadVecIndex)
for (auto& r : renderers)
{
r->SetEmber(embers);
r->EarlyClip(opt.EarlyClip());
r->YAxisUp(opt.YAxisUp());
r->LockAccum(opt.LockAccum());
r->InsertPalette(opt.InsertPalette());
r->PixelAspectRatio(T(opt.AspectRatio()));
r->Transparency(opt.Transparency());
r->NumChannels(channels);
r->BytesPerChannel(opt.BitsPerChannel() / 8);
r->Priority(eThreadPriority(Clamp<int>(int(opt.Priority()), int(eThreadPriority::LOWEST), int(eThreadPriority::HIGHEST))));
}
std::function<void (vector<byte>&, string, EmberImageComments, size_t, size_t, size_t)> saveFunc = [&](vector<byte>& finalImage,
string filename,//These are copies because this will be launched in a thread.
EmberImageComments comments,
size_t w,
size_t h,
size_t chan)
{
bool writeSuccess = false;
byte* finalImagep = finalImages[threadVecIndex].data();
byte* finalImagep = finalImage.data();
if ((opt.Format() == "jpg" || opt.Format() == "bmp") && renderer->NumChannels() == 4)
RgbaToRgb(finalImages[threadVecIndex], finalImages[threadVecIndex], renderer->FinalRasW(), renderer->FinalRasH());
if ((opt.Format() == "jpg" || opt.Format() == "bmp") && chan == 4)
RgbaToRgb(finalImage, finalImage, w, h);
if (opt.Format() == "png")
writeSuccess = WritePng(filename.c_str(), finalImagep, renderer->FinalRasW(), renderer->FinalRasH(), opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick());
writeSuccess = WritePng(filename.c_str(), finalImagep, w, h, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "jpg")
writeSuccess = WriteJpeg(filename.c_str(), finalImagep, renderer->FinalRasW(), renderer->FinalRasH(), opt.JpegQuality(), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
writeSuccess = WriteJpeg(filename.c_str(), finalImagep, w, h, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "ppm")
writeSuccess = WritePpm(filename.c_str(), finalImagep, renderer->FinalRasW(), renderer->FinalRasH());
writeSuccess = WritePpm(filename.c_str(), finalImagep, w, h);
else if (opt.Format() == "bmp")
writeSuccess = WriteBmp(filename.c_str(), finalImagep, renderer->FinalRasW(), renderer->FinalRasH());
writeSuccess = WriteBmp(filename.c_str(), finalImagep, w, h);
if (!writeSuccess)
cout << "Error writing " << filename << endl;/**/
cout << "Error writing " << filename << endl;
};
atomfTime.store(opt.FirstFrame());
//Begin run.
for (ftime = opt.FirstFrame(); ftime <= opt.LastFrame(); ftime += opt.Dtime())
std::function<void(size_t)> iterFunc = [&](size_t index)
{
T localTime = T(ftime);
size_t ftime, finalImageIndex = 0;
string filename, flameName;
RendererBase* renderer = renderers[index].get();
ostringstream fnstream, os;
EmberStats stats;
EmberImageComments comments;
Ember<T> centerEmber;
vector<byte> finalImages[2];
std::thread writeThread;
if ((opt.LastFrame() - opt.FirstFrame()) / opt.Dtime() >= 1)
VerbosePrint("Time = " << ftime << " / " << opt.LastFrame() << " / " << opt.Dtime());
os.imbue(std::locale(""));
renderer->Reset();
if ((renderer->Run(finalImages[finalImageIndex], localTime) != RENDER_OK) || renderer->Aborted() || finalImages[finalImageIndex].empty())
while (atomfTime.fetch_add(opt.Dtime()), ((ftime = atomfTime.load()) <= opt.LastFrame()))
{
cout << "Error: image rendering failed, skipping to next image." << endl;
renderer->DumpErrorReport();//Something went wrong, print errors.
continue;
}
T localTime = T(ftime) - 1;
if (opt.Out().empty())
{
ostringstream fnstream;
if (opt.Verbose() && ((opt.LastFrame() - opt.FirstFrame()) / opt.Dtime() >= 1))
{
verboseCs.Enter();
cout << "Time = " << ftime << " / " << opt.LastFrame() << " / " << opt.Dtime() << endl;
verboseCs.Leave();
}
renderer->Reset();
if ((renderer->Run(finalImages[finalImageIndex], localTime) != RENDER_OK) || renderer->Aborted() || finalImages[finalImageIndex].empty())
{
cout << "Error: image rendering failed, skipping to next image." << endl;
renderer->DumpErrorReport();//Something went wrong, print errors.
atomfTime.store(opt.LastFrame() + 1);//Abort all threads if any of them encounter an error.
break;
}
fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << ftime << opt.Suffix() << "." << opt.Format();
filename = fnstream.str();
}
fnstream.str("");
if (opt.WriteGenome())
{
flameName = filename.substr(0, filename.find_last_of('.')) + ".flam3";
VerbosePrint("Writing " + flameName);
Interpolater<T>::Interpolate(embers, localTime, 0, centerEmber);//Get center flame.
if (appendXml)
if (opt.WriteGenome())
{
startXml = ftime == opt.FirstFrame();
finishXml = ftime == opt.LastFrame();
flameName = filename.substr(0, filename.find_last_of('.')) + ".flam3";
if (opt.Verbose())
{
verboseCs.Enter();
cout << "Writing " << flameName << endl;
verboseCs.Leave();
}
Interpolater<T>::Interpolate(embers, localTime, 0, centerEmber);//Get center flame.
emberToXml.Save(flameName, centerEmber, opt.PrintEditDepth(), true, opt.IntPalette(), opt.HexPalette(), true, false, false);
centerEmber.Clear();
}
emberToXml.Save(flameName, centerEmber, opt.PrintEditDepth(), true, opt.IntPalette(), opt.HexPalette(), true, startXml, finishXml);
stats = renderer->Stats();
comments = renderer->ImageComments(stats, opt.PrintEditDepth(), opt.IntPalette(), opt.HexPalette());
os.str("");
size_t iterCount = renderer->TotalIterCount(1);
os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)";
if (opt.Verbose())
{
verboseCs.Enter();
cout << "\nIters ran/requested: " + os.str() << endl;
if (!opt.EmberCL()) cout << "Bad values: " << stats.m_Badvals << endl;
cout << "Render time: " << t.Format(stats.m_RenderMs) << endl;
cout << "Pure iter time: " << t.Format(stats.m_IterMs) << endl;
cout << "Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << endl;
cout << "Writing " << filename << endl << endl;
verboseCs.Leave();
}
//Run image writing in a thread. Although doing it this way duplicates the final output memory, it saves a lot of time
//when running with OpenCL. Call join() to ensure the previous thread call has completed.
if (writeThread.joinable())
writeThread.join();
auto threadVecIndex = finalImageIndex;//Cache before launching thread.
if (opt.ThreadedWrite())//Copies are passed of all but the first parameter to saveFunc(), to avoid conflicting with those values changing when starting the render for the next image.
{
writeThread = std::thread(saveFunc, std::ref(finalImages[threadVecIndex]), filename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());
finalImageIndex ^= 1;//Toggle the index.
}
else
saveFunc(finalImages[threadVecIndex], filename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());//Will always use the first index, thereby not requiring more memory.
}
stats = renderer->Stats();
comments = renderer->ImageComments(stats, opt.PrintEditDepth(), opt.IntPalette(), opt.HexPalette());
os.str("");
size_t iterCount = renderer->TotalIterCount(1);
os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)";
VerbosePrint("\nIters ran/requested: " + os.str());
VerbosePrint("Bad values: " << stats.m_Badvals);
VerbosePrint("Render time: " + t.Format(stats.m_RenderMs));
VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs));
VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << endl);
VerbosePrint("Writing " + filename);
//Run image writing in a thread. Although doing it this way duplicates the final output memory, it saves a lot of time
//when running with OpenCL. Call join() to ensure the previous thread call has completed.
if (writeThread.joinable())
if (writeThread.joinable())//One final check to make sure all writing is done before exiting this thread.
writeThread.join();
};
uint threadVecIndex = finalImageIndex;//Cache before launching thread.
threadVec.reserve(renderers.size());
if (opt.ThreadedWrite())
writeThread = std::thread(saveFunc, threadVecIndex);
else
saveFunc(threadVecIndex);
centerEmber.Clear();
finalImageIndex ^= 1;//Toggle the index.
for (size_t r = 0; r < renderers.size(); r++)
{
threadVec.push_back(std::thread([&](size_t dev)
{
iterFunc(dev);
}, r));
}
if (writeThread.joinable())
writeThread.join();
for (auto& th : threadVec)
if (th.joinable())
th.join();
VerbosePrint("Done.\n");
if (opt.Verbose())
t.Toc("\nTotal time: ", true);
t.Toc("\nFinished in: ", true);
return true;
}
@ -387,18 +452,18 @@ int _tmain(int argc, _TCHAR* argv[])
#ifdef DO_DOUBLE
if (opt.Bits() == 64)
{
b = EmberAnimate<double, float>(opt);
b = EmberAnimate<double>(opt);
}
else
#endif
if (opt.Bits() == 33)
{
b = EmberAnimate<float, float>(opt);
b = EmberAnimate<float>(opt);
}
else if (opt.Bits() == 32)
{
cout << "Bits 32/int histogram no longer supported. Using bits == 33 (float)." << endl;
b = EmberAnimate<float, float>(opt);
b = EmberAnimate<float>(opt);
}
}

View File

@ -1,4 +1,6 @@
#pragma once
#ifdef WIN32
#pragma once
#endif
/// <summary>
/// Precompiled header file. Place all system includes here with appropriate #defines for different operating systems and compilers.
@ -37,6 +39,7 @@
#include <CL/cl.hpp>
#include <algorithm>
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <fstream>

View File

@ -15,7 +15,9 @@ IterOpenCLKernelCreator<T>::IterOpenCLKernelCreator()
{
m_IterEntryPoint = "IterateKernel";
m_ZeroizeEntryPoint = "ZeroizeKernel";
m_SumHistEntryPoint = "SumHisteKernel";
m_ZeroizeKernel = CreateZeroizeKernelString();
m_SumHistKernel = CreateSumHistKernelString();
}
/// <summary>
@ -24,6 +26,8 @@ IterOpenCLKernelCreator<T>::IterOpenCLKernelCreator()
template <typename T> const string& IterOpenCLKernelCreator<T>::ZeroizeKernel() const { return m_ZeroizeKernel; }
template <typename T> const string& IterOpenCLKernelCreator<T>::ZeroizeEntryPoint() const { return m_ZeroizeEntryPoint; }
template <typename T> const string& IterOpenCLKernelCreator<T>::SumHistKernel() const { return m_SumHistKernel; }
template <typename T> const string& IterOpenCLKernelCreator<T>::SumHistEntryPoint() const { return m_SumHistEntryPoint; }
template <typename T> const string& IterOpenCLKernelCreator<T>::IterEntryPoint() const { return m_IterEntryPoint; }
/// <summary>
@ -703,6 +707,30 @@ string IterOpenCLKernelCreator<T>::CreateZeroizeKernelString()
return os.str();
}
template <typename T>
string IterOpenCLKernelCreator<T>::CreateSumHistKernelString()
{
ostringstream os;
os <<
ConstantDefinesString(typeid(T) == typeid(double)) <<//Double precision doesn't matter here since it's not used.
"__kernel void " << m_SumHistEntryPoint << "(__global real4_bucket* source, __global real4_bucket* dest, uint width, uint height, uint clear)\n"
"{\n"
" if (GLOBAL_ID_X >= width || GLOBAL_ID_Y >= height)\n"
" return;\n"
"\n"
" dest[(GLOBAL_ID_Y * width) + GLOBAL_ID_X] += source[(GLOBAL_ID_Y * width) + GLOBAL_ID_X];\n"//Can't use INDEX_IN_GRID_2D here because the grid might be larger than the buffer to make even dimensions.
"\n"
" if (clear)\n"
" source[(GLOBAL_ID_Y * width) + GLOBAL_ID_X] = 0;\n"
"\n"
" barrier(CLK_GLOBAL_MEM_FENCE);\n"//Just to be safe.
"}\n"
"\n";
return os.str();
}
/// <summary>
/// Create the string for 3D projection based on the 3D values of the ember.
/// Projection is done on the second point.

View File

@ -26,6 +26,8 @@ public:
IterOpenCLKernelCreator();
const string& ZeroizeKernel() const;
const string& ZeroizeEntryPoint() const;
const string& SumHistKernel() const;
const string& SumHistEntryPoint() const;
const string& IterEntryPoint() const;
string CreateIterKernelString(Ember<T>& ember, string& parVarDefines, bool lockAccum = false, bool doAccum = true);
static void ParVarIndexDefines(Ember<T>& ember, pair<string, vector<T>>& params, bool doVals = true, bool doString = true);
@ -33,18 +35,21 @@ public:
private:
string CreateZeroizeKernelString();
string CreateSumHistKernelString();
string CreateProjectionString(Ember<T>& ember);
string m_IterEntryPoint;
string m_ZeroizeKernel;
string m_ZeroizeEntryPoint;
string m_SumHistKernel;
string m_SumHistEntryPoint;
};
#ifdef OPEN_CL_TEST_AREA
typedef void (*KernelFuncPointer) (uint gridWidth, uint gridHeight, uint blockWidth, uint blockHeight,
uint BLOCK_ID_X, uint BLOCK_ID_Y, uint THREAD_ID_X, uint THREAD_ID_Y);
typedef void (*KernelFuncPointer) (size_t gridWidth, size_t gridHeight, size_t blockWidth, size_t blockHeight,
size_t BLOCK_ID_X, size_t BLOCK_ID_Y, size_t THREAD_ID_X, size_t THREAD_ID_Y);
static void OpenCLSim(uint gridWidth, uint gridHeight, uint blockWidth, uint blockHeight, KernelFuncPointer func)
static void OpenCLSim(size_t gridWidth, size_t gridHeight, size_t blockWidth, size_t blockHeight, KernelFuncPointer func)
{
cout << "OpenCLSim(): " << endl;
cout << " Params: " << endl;
@ -53,13 +58,13 @@ static void OpenCLSim(uint gridWidth, uint gridHeight, uint blockWidth, uint blo
cout << " blockW: " << blockWidth << endl;
cout << " blockH: " << blockHeight << endl;
for (uint i = 0; i < gridHeight; i += blockHeight)
for (size_t i = 0; i < gridHeight; i += blockHeight)
{
for (uint j = 0; j < gridWidth; j += blockWidth)
for (size_t j = 0; j < gridWidth; j += blockWidth)
{
for (uint k = 0; k < blockHeight; k++)
for (size_t k = 0; k < blockHeight; k++)
{
for (uint l = 0; l < blockWidth; l++)
for (size_t l = 0; l < blockWidth; l++)
{
func(gridWidth, gridHeight, blockWidth, blockHeight, j / blockWidth, i / blockHeight, l, k);
}

View File

@ -0,0 +1,406 @@
#include "EmberCLPch.h"
#include "OpenCLInfo.h"
namespace EmberCLns
{
/// <summary>
/// Initialize and return a reference to the one and only OpenCLInfo object.
/// </summary>
/// <returns>A reference to the only OpenCLInfo object.</returns>
OpenCLInfo& OpenCLInfo::Instance()
{
static OpenCLInfo instance;
return instance;
}
/// <summary>
/// Initialize the all platforms and devices and keep information about them in lists.
/// </summary>
OpenCLInfo::OpenCLInfo()
{
cl_int err;
vector<cl::Platform> platforms;
vector<vector<cl::Device>> devices;
intmax_t workingPlatformIndex = -1;
m_Init = false;
cl::Platform::get(&platforms);
devices.resize(platforms.size());
m_Platforms.reserve(platforms.size());
m_Devices.reserve(platforms.size());
m_DeviceNames.reserve(platforms.size());
m_AllDeviceNames.reserve(platforms.size());
m_DeviceIndices.reserve(platforms.size());
for (size_t i = 0; i < platforms.size(); i++)
platforms[i].getDevices(CL_DEVICE_TYPE_ALL, &devices[i]);
for (size_t platform = 0; platform < platforms.size(); platform++)
{
bool platformOk = false;
bool deviceOk = false;
cl::Context context;
if (CreateContext(platforms[platform], context, false))//Platform is ok, now do context. Unshared by default.
{
size_t workingDeviceIndex = 0;
for (size_t device = 0; device < devices[platform].size(); device++)//Context is ok, now do devices.
{
auto q = cl::CommandQueue(context, devices[platform][device], 0, &err);//At least one GPU device is present, so create a command queue.
if (CheckCL(err, "cl::CommandQueue()"))
{
if (!platformOk)
{
m_Platforms.push_back(platforms[platform]);
m_PlatformNames.push_back(platforms[platform].getInfo<CL_PLATFORM_VENDOR>(nullptr) + " " + platforms[platform].getInfo<CL_PLATFORM_NAME>(nullptr) + " " + platforms[platform].getInfo<CL_PLATFORM_VERSION>(nullptr));
workingPlatformIndex++;
platformOk = true;
}
if (!deviceOk)
{
m_Devices.push_back(vector<cl::Device>());
m_DeviceNames.push_back(vector<string>());
m_Devices.back().reserve(devices[platform].size());
m_DeviceNames.back().reserve(devices[platform].size());
deviceOk = true;
}
m_Devices.back().push_back(devices[platform][device]);
m_DeviceNames.back().push_back(devices[platform][device].getInfo<CL_DEVICE_VENDOR>(nullptr) + " " + devices[platform][device].getInfo<CL_DEVICE_NAME>(nullptr));// + " " + devices[platform][device].getInfo<CL_DEVICE_VERSION>());
m_AllDeviceNames.push_back(m_DeviceNames.back().back());
m_DeviceIndices.push_back(pair<size_t, size_t>(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.
}
}
}
}
}
/// <summary>
/// Get a const reference to the vector of available platforms.
/// </summary>
/// <returns>A const reference to the vector of available platforms</returns>
const vector<cl::Platform>& OpenCLInfo::Platforms() const
{
return m_Platforms;
}
/// <summary>
/// Get a const reference to the platform name at the specified index.
/// </summary>
/// <param name="i">The platform index to get the name of</param>
/// <returns>The platform name if found, else empty string</returns>
const string& OpenCLInfo::PlatformName(size_t platform) const
{
static string s;
return platform < m_PlatformNames.size() ? m_PlatformNames[platform] : s;
}
/// <summary>
/// Get a const reference to a vector of all available platform names on the system as a vector of strings.
/// </summary>
/// <returns>All available platform names on the system as a vector of strings</returns>
const vector<string>& OpenCLInfo::PlatformNames() const
{
return m_PlatformNames;
}
/// <summary>
/// Get a const reference to a vector of vectors of all available devices on the system.
/// Each outer vector is a different platform.
/// </summary>
/// <returns>All available devices on the system, grouped by platform.</returns>
const vector<vector<cl::Device>>& OpenCLInfo::Devices() const
{
return m_Devices;
}
/// <summary>
/// Get a const reference to the device name at the specified index on the platform
/// at the specified index.
/// </summary>
/// <param name="platform">The platform index of the device</param>
/// <param name="device">The device index</param>
/// <returns>The name of the device if found, else empty string</returns>
const string& OpenCLInfo::DeviceName(size_t platform, size_t device) const
{
static string s;
if (platform < m_Platforms.size() && platform < m_Devices.size())
if (device < m_Devices[platform].size())
return m_DeviceNames[platform][device];
return s;
}
/// <summary>
/// Get a const reference to a vector of pairs of uints which contain the platform,device
/// indices of all available devices on the system.
/// </summary>
/// <returns>All available devices on the system as platform,device index pairs</returns>
const vector<pair<size_t, size_t>>& OpenCLInfo::DeviceIndices() const
{
return m_DeviceIndices;
}
/// <summary>
/// Get a const reference to a vector of all available device names on the system as a vector of strings.
/// </summary>
/// <returns>All available device names on the system as a vector of strings</returns>
const vector<string>& OpenCLInfo::AllDeviceNames() const
{
return m_AllDeviceNames;
}
/// <summary>
/// Get a const reference to a vector of all available device names on the platform
/// at the specified index as a vector of strings.
/// </summary>
/// <param name="platform">The platform index whose devices names will be returned</param>
/// <returns>All available device names on the platform at the specified index as a vector of strings if within range, else empty vector.</returns>
const vector<string>& OpenCLInfo::DeviceNames(size_t platform) const
{
static vector<string> v;
if (platform < m_DeviceNames.size())
return m_DeviceNames[platform];
return v;
}
/// <summary>
/// Get the total device index at the specified platform and device index.
/// </summary>
/// <param name="platform">The platform index of the device</param>
/// <param name="device">The device index within the platform</param>
/// <returns>The total device index if found, else 0</returns>
size_t OpenCLInfo::TotalDeviceIndex(size_t platform, size_t device) const
{
size_t index = 0;
pair<size_t, size_t> p{ platform, device };
for (size_t i = 0; i < m_DeviceIndices.size(); i++)
{
if (p == m_DeviceIndices[i])
{
index = i;
break;
}
}
return index;
}
/// <summary>
/// Create a context that is optionally shared with OpenGL and plact it in the
/// passed in context ref parameter.
/// </summary>
/// <param name="platform">The platform object to create the context on</param>
/// <param name="context">The context object to store the result in</param>
/// <param name="shared">True if shared with OpenGL, else not shared.</param>
/// <returns>True if success, else false.</returns>
bool OpenCLInfo::CreateContext(const cl::Platform& platform, cl::Context& context, bool shared)
{
cl_int err;
if (shared)
{
//Define OS-specific context properties and create the OpenCL context.
#if defined (__APPLE__) || defined(MACOSX)
CGLContextObj kCGLContext = CGLGetCurrentContext();
CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
cl_context_properties props[] =
{
CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup,
0
};
context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);//May need to tinker with this on Mac.
#else
#if defined WIN32
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<cl_context_properties>((platform)()),
0
};
context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);
#else
cl_context_properties props[] =
{
CL_GL_CONTEXT_KHR, cl_context_properties(glXGetCurrentContext()),
CL_GLX_DISPLAY_KHR, cl_context_properties(glXGetCurrentDisplay()),
CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>((platform)()),
0
};
context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);
#endif
#endif
}
else
{
cl_context_properties props[3] =
{
CL_CONTEXT_PLATFORM,
reinterpret_cast<cl_context_properties>((platform)()),
0
};
context = cl::Context(CL_DEVICE_TYPE_ALL, props, nullptr, nullptr, &err);
}
return CheckCL(err, "cl::Context()");
}
/// <summary>
/// Return whether at least one device has been found and properly initialized.
/// </summary>
/// <returns>True if success, else false.</returns>
bool OpenCLInfo::Ok() const
{
return m_Init;
}
/// <summary>
/// Get all information about all platforms and devices.
/// </summary>
/// <returns>A string with all information about all platforms and devices</returns>
string OpenCLInfo::DumpInfo() const
{
ostringstream os;
vector<size_t> sizes;
os.imbue(locale(""));
for (size_t platform = 0; platform < m_Platforms.size(); platform++)
{
os << "Platform " << platform << ": " << PlatformName(platform) << endl;
for (size_t device = 0; device < m_Devices[platform].size(); device++)
{
os << "Device " << device << ": " << DeviceName(platform, device) << endl;
os << "CL_DEVICE_OPENCL_C_VERSION: " << GetInfo<string>(platform, device, CL_DEVICE_OPENCL_C_VERSION) << endl;
os << "CL_DEVICE_LOCAL_MEM_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_LOCAL_MEM_SIZE) << endl;
os << "CL_DEVICE_LOCAL_MEM_TYPE: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_LOCAL_MEM_TYPE) << endl;
os << "CL_DEVICE_MAX_COMPUTE_UNITS: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_MAX_COMPUTE_UNITS) << endl;
os << "CL_DEVICE_MAX_READ_IMAGE_ARGS: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_MAX_READ_IMAGE_ARGS) << endl;
os << "CL_DEVICE_MAX_WRITE_IMAGE_ARGS: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_MAX_WRITE_IMAGE_ARGS) << endl;
os << "CL_DEVICE_MAX_MEM_ALLOC_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_MAX_MEM_ALLOC_SIZE) << endl;
os << "CL_DEVICE_ADDRESS_BITS: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_ADDRESS_BITS) << endl;
os << "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE) << endl;
os << "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE) << endl;
os << "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE) << endl;
os << "CL_DEVICE_GLOBAL_MEM_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_GLOBAL_MEM_SIZE) << endl;
os << "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE) << endl;
os << "CL_DEVICE_MAX_CONSTANT_ARGS: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_MAX_CONSTANT_ARGS) << endl;
os << "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: " << GetInfo<cl_uint>(platform, device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS) << endl;
os << "CL_DEVICE_MAX_WORK_GROUP_SIZE: " << GetInfo<size_t>(platform, device, CL_DEVICE_MAX_WORK_GROUP_SIZE) << endl;
sizes = GetInfo<vector<size_t>>(platform, device, CL_DEVICE_MAX_WORK_ITEM_SIZES);
os << "CL_DEVICE_MAX_WORK_ITEM_SIZES: " << sizes[0] << ", " << sizes[1] << ", " << sizes[2] << endl << endl;
if (device != m_Devices[platform].size() - 1 && platform != m_Platforms.size() - 1)
os << endl;
}
os << endl;
}
return os.str();
}
/// <summary>
/// Check an OpenCL return value for errors.
/// </summary>
/// <param name="err">The error code to inspect</param>
/// <param name="name">A description of where the value was gotten from</param>
/// <returns>True if success, else false.</returns>
bool OpenCLInfo::CheckCL(cl_int err, const char* name)
{
if (err != CL_SUCCESS)
{
ostringstream ss;
ss << "ERROR: " << ErrorToStringCL(err) << " in " << name << "." << endl;
m_ErrorReport.push_back(ss.str());
}
return err == CL_SUCCESS;
}
/// <summary>
/// Translate an OpenCL error code into a human readable string.
/// </summary>
/// <param name="err">The error code to translate</param>
/// <returns>A human readable description of the error passed in</returns>
string OpenCLInfo::ErrorToStringCL(cl_int err)
{
switch (err)
{
case CL_SUCCESS: return "Success";
case CL_DEVICE_NOT_FOUND: return "Device not found";
case CL_DEVICE_NOT_AVAILABLE: return "Device not available";
case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available";
case CL_MEM_OBJECT_ALLOCATION_FAILURE: return "Memory object allocation failure";
case CL_OUT_OF_RESOURCES: return "Out of resources";
case CL_OUT_OF_HOST_MEMORY: return "Out of host memory";
case CL_PROFILING_INFO_NOT_AVAILABLE: return "Profiling information not available";
case CL_MEM_COPY_OVERLAP: return "Memory copy overlap";
case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch";
case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported";
case CL_BUILD_PROGRAM_FAILURE: return "Program build failure";
case CL_MAP_FAILURE: return "Map failure";
case CL_MISALIGNED_SUB_BUFFER_OFFSET: return "Misaligned sub buffer offset";
case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST: return "Exec status error for events in wait list";
case CL_INVALID_VALUE: return "Invalid value";
case CL_INVALID_DEVICE_TYPE: return "Invalid device type";
case CL_INVALID_PLATFORM: return "Invalid platform";
case CL_INVALID_DEVICE: return "Invalid device";
case CL_INVALID_CONTEXT: return "Invalid context";
case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties";
case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue";
case CL_INVALID_HOST_PTR: return "Invalid host pointer";
case CL_INVALID_MEM_OBJECT: return "Invalid memory object";
case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: return "Invalid image format descriptor";
case CL_INVALID_IMAGE_SIZE: return "Invalid image size";
case CL_INVALID_SAMPLER: return "Invalid sampler";
case CL_INVALID_BINARY: return "Invalid binary";
case CL_INVALID_BUILD_OPTIONS: return "Invalid build options";
case CL_INVALID_PROGRAM: return "Invalid program";
case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable";
case CL_INVALID_KERNEL_NAME: return "Invalid kernel name";
case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition";
case CL_INVALID_KERNEL: return "Invalid kernel";
case CL_INVALID_ARG_INDEX: return "Invalid argument index";
case CL_INVALID_ARG_VALUE: return "Invalid argument value";
case CL_INVALID_ARG_SIZE: return "Invalid argument size";
case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments";
case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension";
case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size";
case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size";
case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset";
case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list";
case CL_INVALID_EVENT: return "Invalid event";
case CL_INVALID_OPERATION: return "Invalid operation";
case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object";
case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size";
case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level";
case CL_INVALID_GLOBAL_WORK_SIZE: return "Invalid global work size";
case CL_INVALID_PROPERTY: return "Invalid property";
default:
{
ostringstream ss;
ss << "<Unknown error code> " << err;
return ss.str();
}
}
}
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "EmberCLPch.h"
/// <summary>
/// OpenCLInfo class.
/// </summary>
namespace EmberCLns
{
/// <summary>
/// Keeps information about all valid OpenCL devices on this system.
/// Devices which do not successfully create a test command queue are not
/// added to the list.
/// The pattern is singleton, so there is only one instance per program,
/// retreivable by reference via the Instance() function.
/// This class derives from EmberReport, so the caller is able
/// to retrieve a text dump of error information if any errors occur.
/// </summary>
class EMBERCL_API OpenCLInfo : public EmberReport
{
public:
static OpenCLInfo& Instance();
const vector<cl::Platform>& Platforms() const;
const string& PlatformName(size_t platform) const;
const vector<string>& PlatformNames() const;
const vector<vector<cl::Device>>& Devices() const;
const string& DeviceName(size_t platform, size_t device) const;
const vector<pair<size_t, size_t>>& DeviceIndices() const;
const vector<string>& AllDeviceNames() const;
const vector<string>& DeviceNames(size_t platform) const;
size_t TotalDeviceIndex(size_t platform, size_t device) const;
string DumpInfo() const;
bool Ok() const;
bool CreateContext(const cl::Platform& platform, cl::Context& context, bool shared);
bool CheckCL(cl_int err, const char* name);
string ErrorToStringCL(cl_int err);
/// <summary>
/// Get device information for the specified field.
/// Template argument expected to be cl_ulong, cl_uint or cl_int;
/// </summary>
/// <param name="platform">The index platform of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="name">The device field/feature to query</param>
/// <returns>The value of the field</returns>
template<typename T>
T GetInfo(size_t platform, size_t device, cl_device_info name) const
{
T val = T();
if (platform < m_Devices.size() && device < m_Devices[platform].size())
m_Devices[platform][device].getInfo(name, &val);
return val;
}
private:
OpenCLInfo();
bool m_Init;
vector<cl::Platform> m_Platforms;
vector<vector<cl::Device>> m_Devices;
vector<string> m_PlatformNames;
vector<vector<string>> m_DeviceNames;
vector<pair<size_t, size_t>> m_DeviceIndices;
vector<string> m_AllDeviceNames;
};
}

View File

@ -5,33 +5,23 @@ namespace EmberCLns
{
/// <summary>
/// Constructor that sets everything to an uninitialized state.
/// No OpenCL setup is done here, the caller must explicitly do it.
/// No OpenCL setup is done here other than what's done in the
/// global OpenCLInfo object. The caller must explicitly do it.
/// </summary>
OpenCLWrapper::OpenCLWrapper()
: m_Info(OpenCLInfo::Instance())
{
m_Init = false;
m_Shared = false;
m_PlatformIndex = 0;
m_DeviceIndex = 0;
m_LocalMemSize = 0;
cl::Platform::get(&m_Platforms);
m_Devices.resize(m_Platforms.size());
for (size_t i = 0; i < m_Platforms.size(); i++)
m_Platforms[i].getDevices(CL_DEVICE_TYPE_ALL, &m_Devices[i]);
}
/// <summary>
/// Determine if OpenCL is available on the system.
/// </summary>
/// <returns>True if any OpenCL platform and at least 1 device within that platform exists on the system, else false.</returns>
bool OpenCLWrapper::CheckOpenCL()
{
for (size_t i = 0; i < m_Platforms.size(); i++)
for (size_t j = 0; j < m_Devices[i].size(); j++)
return true;
return false;
//Pre-allocate some space to avoid temporary copying.
m_Programs.reserve(4);
m_Buffers.reserve(4);
m_Images.reserve(4);
m_GLImages.reserve(4);
}
/// <summary>
@ -42,35 +32,40 @@ bool OpenCLWrapper::CheckOpenCL()
/// <param name="device">The index device of the device to use</param>
/// <param name="shared">True if shared with OpenGL, else false.</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::Init(uint platform, uint device, bool shared)
bool OpenCLWrapper::Init(size_t platformIndex, size_t deviceIndex, bool shared)
{
cl_int err;
auto& platforms = m_Info.Platforms();
auto& devices = m_Info.Devices();
m_Init = false;
m_ErrorReport.clear();
if (m_Platforms.size() > 0)
if (m_Info.Ok())
{
if (platform < m_Platforms.size() && platform < m_Devices.size())
if (platformIndex < platforms.size() && platformIndex < devices.size())
{
m_PlatformIndex = platform;//Platform is ok, now do context.
cl::Context context;
if (CreateContext(shared))
if (m_Info.CreateContext(platforms[platformIndex], context, shared))//Platform index is within range, now do context.
{
//Context is ok, now do device.
if (device < m_Devices[m_PlatformIndex].size())
if (deviceIndex < devices[platformIndex].size())//Context is ok, now do device.
{
//At least one GPU device is present, so create a command queue.
m_Queue = cl::CommandQueue(m_Context, m_Devices[m_PlatformIndex][device], 0, &err);
auto q = cl::CommandQueue(context, devices[platformIndex][deviceIndex], 0, &err);//At least one GPU device is present, so create a command queue.
if (CheckCL(err, "cl::CommandQueue()"))
if (m_Info.CheckCL(err, "cl::CommandQueue()"))//Everything was successful so assign temporaries to members.
{
m_DeviceIndex = device;
m_Platform = m_Platforms[m_PlatformIndex];
m_Device = m_Devices[m_PlatformIndex][device];
m_Platform = platforms[platformIndex];
m_Device = devices[platformIndex][deviceIndex];
m_Context = context;
m_Queue = q;
m_PlatformIndex = platformIndex;
m_DeviceIndex = deviceIndex;
m_DeviceVec.clear();
m_DeviceVec.push_back(m_Device);
m_LocalMemSize = uint(GetInfo<cl_ulong>(m_PlatformIndex, m_DeviceIndex, CL_DEVICE_LOCAL_MEM_SIZE));
m_LocalMemSize = size_t(m_Info.GetInfo<cl_ulong>(m_PlatformIndex, m_DeviceIndex, CL_DEVICE_LOCAL_MEM_SIZE));
m_GlobalMemSize = size_t(m_Info.GetInfo<cl_ulong>(m_PlatformIndex, m_DeviceIndex, CL_DEVICE_GLOBAL_MEM_SIZE));
m_MaxAllocSize = size_t(m_Info.GetInfo<cl_ulong>(m_PlatformIndex, m_DeviceIndex, CL_DEVICE_MAX_MEM_ALLOC_SIZE));
m_Shared = shared;
m_Init = true;//Command queue is ok, it's now ok to begin building and running programs.
}
@ -96,11 +91,11 @@ bool OpenCLWrapper::AddProgram(const string& name, const string& program, const
if (CreateSPK(name, program, entryPoint, spk, doublePrecision))
{
for (auto& program : m_Programs)
for (auto& p : m_Programs)
{
if (name == program.m_Name)
if (name == p.m_Name)
{
program = spk;
p = spk;
return true;
}
}
@ -144,7 +139,7 @@ bool OpenCLWrapper::AddBuffer(const string& name, size_t size, cl_mem_flags flag
{
cl::Buffer buff(m_Context, flags, size, nullptr, &err);
if (!CheckCL(err, "cl::Buffer()"))
if (!m_Info.CheckCL(err, "cl::Buffer()"))
return false;
NamedBuffer nb(buff, name);
@ -157,7 +152,7 @@ bool OpenCLWrapper::AddBuffer(const string& name, size_t size, cl_mem_flags flag
cl::Buffer buff(m_Context, flags, size, nullptr, &err);//Create the new buffer.
if (!CheckCL(err, "cl::Buffer()"))
if (!m_Info.CheckCL(err, "cl::Buffer()"))
return false;
NamedBuffer nb(buff, name);//Make a named buffer out of the new buffer.
@ -215,7 +210,7 @@ bool OpenCLWrapper::WriteBuffer(const string& name, void* data, size_t size)
/// <param name="data">A pointer to the buffer</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::WriteBuffer(uint bufferIndex, void* data, size_t size)
bool OpenCLWrapper::WriteBuffer(size_t bufferIndex, void* data, size_t size)
{
if (m_Init && (bufferIndex < m_Buffers.size()) && (GetBufferSize(bufferIndex) == size))
{
@ -225,7 +220,7 @@ bool OpenCLWrapper::WriteBuffer(uint bufferIndex, void* data, size_t size)
e.wait();
m_Queue.finish();
if (CheckCL(err, "cl::CommandQueue::enqueueWriteBuffer()"))
if (m_Info.CheckCL(err, "cl::CommandQueue::enqueueWriteBuffer()"))
return true;
}
@ -253,7 +248,7 @@ bool OpenCLWrapper::ReadBuffer(const string& name, void* data, size_t size)
/// <param name="data">A pointer to a buffer to copy the data to</param>
/// <param name="size">The size in bytes of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::ReadBuffer(uint bufferIndex, void* data, size_t size)
bool OpenCLWrapper::ReadBuffer(size_t bufferIndex, void* data, size_t size)
{
if (m_Init && (bufferIndex < m_Buffers.size()) && (GetBufferSize(bufferIndex) == size))
{
@ -263,7 +258,7 @@ bool OpenCLWrapper::ReadBuffer(uint bufferIndex, void* data, size_t size)
e.wait();
m_Queue.finish();
if (CheckCL(err, "cl::CommandQueue::enqueueReadBuffer()"))
if (m_Info.CheckCL(err, "cl::CommandQueue::enqueueReadBuffer()"))
return true;
}
@ -289,7 +284,7 @@ int OpenCLWrapper::FindBufferIndex(const string& name)
/// </summary>
/// <param name="name">The name of the buffer to search for</param>
/// <returns>The size of the buffer if found, else 0.</returns>
uint OpenCLWrapper::GetBufferSize(const string& name)
size_t OpenCLWrapper::GetBufferSize(const string& name)
{
int bufferIndex = FindBufferIndex(name);
@ -301,10 +296,10 @@ uint OpenCLWrapper::GetBufferSize(const string& name)
/// </summary>
/// <param name="name">The index of the buffer to get the size of</param>
/// <returns>The size of the buffer if found, else 0.</returns>
uint OpenCLWrapper::GetBufferSize(uint bufferIndex)
size_t OpenCLWrapper::GetBufferSize(size_t bufferIndex)
{
if (m_Init && (bufferIndex < m_Buffers.size()))
return uint(m_Buffers[bufferIndex].m_Buffer.getInfo<CL_MEM_SIZE>(nullptr));
return m_Buffers[bufferIndex].m_Buffer.getInfo<CL_MEM_SIZE>(nullptr);
return 0;
}
@ -350,12 +345,12 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
IMAGEGL2D imageGL(m_Context, flags, GL_TEXTURE_2D, 0, texName, &err);
NamedImage2DGL namedImageGL(imageGL, name);
if (CheckCL(err, "cl::ImageGL()"))
if (m_Info.CheckCL(err, "cl::ImageGL()"))
{
m_GLImages.push_back(namedImageGL);
if (data)
return WriteImage2D(uint(m_GLImages.size() - 1), true, width, height, row_pitch, data);//OpenGL images/textures require a separate write.
return WriteImage2D(m_GLImages.size() - 1, true, width, height, row_pitch, data);//OpenGL images/textures require a separate write.
else
return true;
}
@ -364,7 +359,7 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
{
NamedImage2D namedImage(cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err), name);
if (CheckCL(err, "cl::Image2D()"))
if (m_Info.CheckCL(err, "cl::Image2D()"))
{
m_Images.push_back(namedImage);
return true;
@ -381,7 +376,7 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
{
NamedImage2DGL namedImageGL(IMAGEGL2D(m_Context, flags, GL_TEXTURE_2D, 0, texName, &err), name);//Sizes are different, so create new.
if (CheckCL(err, "cl::ImageGL()"))
if (m_Info.CheckCL(err, "cl::ImageGL()"))
{
m_GLImages[imageIndex] = namedImageGL;
}
@ -403,7 +398,7 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
NamedImage2D namedImage(cl::Image2D(m_Context, flags, format, width, height, row_pitch, data, &err), name);
if (CheckCL(err, "cl::Image2D()"))
if (m_Info.CheckCL(err, "cl::Image2D()"))
{
m_Images[imageIndex] = namedImage;
return true;
@ -430,7 +425,7 @@ bool OpenCLWrapper::AddAndWriteImage(const string& name, cl_mem_flags flags, con
/// <param name="row_pitch">The row pitch (usually zero)</param>
/// <param name="data">The image data</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::WriteImage2D(uint index, bool shared, ::size_t width, ::size_t height, ::size_t row_pitch, void* data)
bool OpenCLWrapper::WriteImage2D(size_t index, bool shared, ::size_t width, ::size_t height, ::size_t row_pitch, void* data)
{
if (m_Init)
{
@ -457,7 +452,7 @@ bool OpenCLWrapper::WriteImage2D(uint index, bool shared, ::size_t width, ::size
m_Queue.finish();
bool b = EnqueueReleaseGLObjects(imageGL);
return CheckCL(err, "cl::enqueueWriteImage()") && b;
return m_Info.CheckCL(err, "cl::enqueueWriteImage()") && b;
}
}
else if (!shared && index < m_Images.size())
@ -465,7 +460,7 @@ bool OpenCLWrapper::WriteImage2D(uint index, bool shared, ::size_t width, ::size
err = m_Queue.enqueueWriteImage(m_Images[index].m_Image, CL_TRUE, origin, region, row_pitch, 0, data, nullptr, &e);
e.wait();
m_Queue.finish();
return CheckCL(err, "cl::enqueueWriteImage()");
return m_Info.CheckCL(err, "cl::enqueueWriteImage()");
}
}
@ -505,7 +500,7 @@ bool OpenCLWrapper::ReadImage(const string& name, ::size_t width, ::size_t heigh
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <param name="data">A pointer to a buffer to copy the data to</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::ReadImage(uint imageIndex, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data)
bool OpenCLWrapper::ReadImage(size_t imageIndex, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data)
{
if (m_Init)
{
@ -529,13 +524,13 @@ bool OpenCLWrapper::ReadImage(uint imageIndex, ::size_t width, ::size_t height,
{
err = m_Queue.enqueueReadImage(m_GLImages[imageIndex].m_Image, true, origin, region, row_pitch, 0, data);
bool b = EnqueueReleaseGLObjects(m_GLImages[imageIndex].m_Image);
return CheckCL(err, "cl::enqueueReadImage()") && b;
return m_Info.CheckCL(err, "cl::enqueueReadImage()") && b;
}
}
else if (!shared && imageIndex < m_Images.size())
{
err = m_Queue.enqueueReadImage(m_Images[imageIndex].m_Image, true, origin, region, row_pitch, 0, data);
return CheckCL(err, "cl::enqueueReadImage()");
return m_Info.CheckCL(err, "cl::enqueueReadImage()");
}
}
@ -572,7 +567,7 @@ int OpenCLWrapper::FindImageIndex(const string& name, bool shared)
/// <param name="name">The name of the image to search for</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <returns>The size of the 2D image if found, else 0.</returns>
uint OpenCLWrapper::GetImageSize(const string& name, bool shared)
size_t OpenCLWrapper::GetImageSize(const string& name, bool shared)
{
int imageIndex = FindImageIndex(name, shared);
return GetImageSize(imageIndex, shared);
@ -584,7 +579,7 @@ uint OpenCLWrapper::GetImageSize(const string& name, bool shared)
/// <param name="imageIndex">Index of the image to search for</param>
/// <param name="shared">True if shared with an OpenGL texture, else false.</param>
/// <returns>The size of the 2D image if found, else 0.</returns>
uint OpenCLWrapper::GetImageSize(uint imageIndex, bool shared)
size_t OpenCLWrapper::GetImageSize(size_t imageIndex, bool shared)
{
size_t size = 0;
@ -593,6 +588,7 @@ uint OpenCLWrapper::GetImageSize(uint imageIndex, bool shared)
if (shared && imageIndex < m_GLImages.size())
{
vector<cl::Memory> images;
images.push_back(m_GLImages[imageIndex].m_Image);
IMAGEGL2D image = m_GLImages[imageIndex].m_Image;
@ -608,7 +604,7 @@ uint OpenCLWrapper::GetImageSize(uint imageIndex, bool shared)
}
}
return uint(size);
return size;
}
/// <summary>
@ -671,7 +667,7 @@ bool OpenCLWrapper::CreateImage2D(cl::Image2D& image2D, cl_mem_flags flags, cl::
data,
&err);
return CheckCL(err, "cl::Image2D()");
return m_Info.CheckCL(err, "cl::Image2D()");
}
return false;
@ -699,7 +695,7 @@ bool OpenCLWrapper::CreateImage2DGL(IMAGEGL2D& image2DGL, cl_mem_flags flags, GL
texobj,
&err);
return CheckCL(err, "cl::ImageGL()");
return m_Info.CheckCL(err, "cl::ImageGL()");
}
return false;
@ -734,7 +730,7 @@ bool OpenCLWrapper::EnqueueAcquireGLObjects(IMAGEGL2D& image)
images.push_back(image);
cl_int err = m_Queue.enqueueAcquireGLObjects(&images);
m_Queue.finish();
return CheckCL(err, "cl::CommandQueue::enqueueAcquireGLObjects()");
return m_Info.CheckCL(err, "cl::CommandQueue::enqueueAcquireGLObjects()");
}
return false;
@ -769,7 +765,7 @@ bool OpenCLWrapper::EnqueueReleaseGLObjects(IMAGEGL2D& image)
images.push_back(image);
cl_int err = m_Queue.enqueueReleaseGLObjects(&images);
m_Queue.finish();
return CheckCL(err, "cl::CommandQueue::enqueueReleaseGLObjects()");
return m_Info.CheckCL(err, "cl::CommandQueue::enqueueReleaseGLObjects()");
}
return false;
@ -787,7 +783,7 @@ bool OpenCLWrapper::EnqueueAcquireGLObjects(const VECTOR_CLASS<cl::Memory>* memO
cl_int err = m_Queue.enqueueAcquireGLObjects(memObjects);
m_Queue.finish();
return CheckCL(err, "cl::CommandQueue::enqueueAcquireGLObjects()");
return m_Info.CheckCL(err, "cl::CommandQueue::enqueueAcquireGLObjects()");
}
return false;
@ -805,7 +801,7 @@ bool OpenCLWrapper::EnqueueReleaseGLObjects(const VECTOR_CLASS<cl::Memory>* memO
cl_int err = m_Queue.enqueueReleaseGLObjects(memObjects);
m_Queue.finish();
return CheckCL(err, "cl::CommandQueue::enqueueReleaseGLObjects()");
return m_Info.CheckCL(err, "cl::CommandQueue::enqueueReleaseGLObjects()");
}
return false;
@ -829,7 +825,7 @@ bool OpenCLWrapper::CreateSampler(cl::Sampler& sampler, cl_bool normalizedCoords
filterMode,
&err);
return CheckCL(err, "cl::Sampler()");
return m_Info.CheckCL(err, "cl::Sampler()");
}
/// <summary>
@ -840,7 +836,7 @@ bool OpenCLWrapper::CreateSampler(cl::Sampler& sampler, cl_bool normalizedCoords
/// <param name="argIndex">Index of the argument</param>
/// <param name="name">The name of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::SetBufferArg(uint kernelIndex, uint argIndex, const string& name)
bool OpenCLWrapper::SetBufferArg(size_t kernelIndex, cl_uint argIndex, const string& name)
{
int bufferIndex = OpenCLWrapper::FindBufferIndex(name);
@ -855,7 +851,7 @@ bool OpenCLWrapper::SetBufferArg(uint kernelIndex, uint argIndex, const string&
/// <param name="argIndex">Index of the argument</param>
/// <param name="bufferIndex">Index of the buffer</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::SetBufferArg(uint kernelIndex, uint argIndex, uint bufferIndex)
bool OpenCLWrapper::SetBufferArg(size_t kernelIndex, cl_uint argIndex, size_t bufferIndex)
{
if (m_Init && bufferIndex < m_Buffers.size())
return SetArg<cl::Buffer>(kernelIndex, argIndex, m_Buffers[bufferIndex].m_Buffer);
@ -872,7 +868,7 @@ bool OpenCLWrapper::SetBufferArg(uint kernelIndex, uint argIndex, uint bufferInd
/// <param name="shared">True if shared with an OpenGL texture, else false</param>
/// <param name="name">The name of the 2D image</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::SetImageArg(uint kernelIndex, uint argIndex, bool shared, const string& name)
bool OpenCLWrapper::SetImageArg(size_t kernelIndex, cl_uint argIndex, bool shared, const string& name)
{
if (m_Init)
{
@ -892,7 +888,7 @@ bool OpenCLWrapper::SetImageArg(uint kernelIndex, uint argIndex, bool shared, co
/// <param name="shared">True if shared with an OpenGL texture, else false</param>
/// <param name="imageIndex">Index of the 2D image</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::SetImageArg(uint kernelIndex, uint argIndex, bool shared, uint imageIndex)
bool OpenCLWrapper::SetImageArg(size_t kernelIndex, cl_uint argIndex, bool shared, size_t imageIndex)
{
cl_int err;
@ -901,12 +897,12 @@ bool OpenCLWrapper::SetImageArg(uint kernelIndex, uint argIndex, bool shared, ui
if (shared && imageIndex < m_GLImages.size())
{
err = m_Programs[kernelIndex].m_Kernel.setArg(argIndex, m_GLImages[imageIndex].m_Image);
return CheckCL(err, "cl::Kernel::setArg()");
return m_Info.CheckCL(err, "cl::Kernel::setArg()");
}
else if (!shared && imageIndex < m_Images.size())
{
err = m_Programs[kernelIndex].m_Kernel.setArg(argIndex, m_Images[imageIndex].m_Image);
return CheckCL(err, "cl::Kernel::setArg()");
return m_Info.CheckCL(err, "cl::Kernel::setArg()");
}
}
@ -938,8 +934,8 @@ int OpenCLWrapper::FindKernelIndex(const string& name)
/// <param name="blockHeight">Height of each block</param>
/// <param name="blockDepth">Depth of each block</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::RunKernel(uint kernelIndex, uint totalGridWidth, uint totalGridHeight, uint totalGridDepth,
uint blockWidth, uint blockHeight, uint blockDepth)
bool OpenCLWrapper::RunKernel(size_t kernelIndex, size_t totalGridWidth, size_t totalGridHeight, size_t totalGridDepth,
size_t blockWidth, size_t blockHeight, size_t blockDepth)
{
if (m_Init && kernelIndex < m_Programs.size())
{
@ -953,183 +949,24 @@ bool OpenCLWrapper::RunKernel(uint kernelIndex, uint totalGridWidth, uint totalG
e.wait();
m_Queue.finish();
return CheckCL(err, "cl::CommandQueue::enqueueNDRangeKernel()");
return m_Info.CheckCL(err, "cl::CommandQueue::enqueueNDRangeKernel()");
}
return false;
}
/// <summary>
/// Get device information for the specified field.
/// Template argument expected to be cl_ulong, cl_uint or cl_int;
/// </summary>
/// <param name="name">The device field/feature to query</param>
/// <returns>The value of the field</returns>
template<typename T>
T OpenCLWrapper::GetInfo(size_t platform, size_t device, cl_device_info name) const
{
T val;
if (platform < m_Devices.size() && device < m_Devices[platform].size())
m_Devices[platform][device].getInfo(name, &val);
return val;
}
/// <summary>
/// Get the platform name at the specified index.
/// </summary>
/// <param name="i">The platform index to get the name of</param>
/// <returns>The platform name if found, else empty string</returns>
string OpenCLWrapper::PlatformName(size_t platform)
{
if (platform < m_Platforms.size())
return m_Platforms[platform].getInfo<CL_PLATFORM_VENDOR>(nullptr) + " " + m_Platforms[platform].getInfo<CL_PLATFORM_NAME>(nullptr) + " " + m_Platforms[platform].getInfo<CL_PLATFORM_VERSION>(nullptr);
else
return "";
}
/// <summary>
/// Get all available platform names on the system as a vector of strings.
/// </summary>
/// <returns>All available platform names on the system as a vector of strings</returns>
vector<string> OpenCLWrapper::PlatformNames()
{
vector<string> platforms;
platforms.reserve(m_Platforms.size());
for (size_t i = 0; i < m_Platforms.size(); i++)
platforms.push_back(PlatformName(i));
return platforms;
}
/// <summary>
/// Get the device name at the specified index on the platform
/// at the specified index.
/// </summary>
/// <param name="platform">The platform index of the device</param>
/// <param name="device">The device index</param>
/// <returns>The name of the device if found, else empty string</returns>
string OpenCLWrapper::DeviceName(size_t platform, size_t device)
{
string s;
if (platform < m_Platforms.size() && platform < m_Devices.size())
if (device < m_Devices[platform].size())
s = m_Devices[platform][device].getInfo<CL_DEVICE_VENDOR>(nullptr) + " " + m_Devices[platform][device].getInfo<CL_DEVICE_NAME>(nullptr);// + " " + m_Devices[platform][device].getInfo<CL_DEVICE_VERSION>();
return s;
}
/// <summary>
/// Get all available device names on the platform at the specified index as a vector of strings.
/// </summary>
/// <param name="platform">The platform index of the devices to query</param>
/// <returns>All available device names on the platform at the specified index as a vector of strings</returns>
vector<string> OpenCLWrapper::DeviceNames(size_t platform)
{
uint i = 0;
string s;
vector<string> devices;
do
{
s = DeviceName(platform, i);
if (s != "")
devices.push_back(s);
i++;
} while (s != "");
return devices;
}
/// <summary>
/// Get all availabe device and platform names as one contiguous string.
/// </summary>
/// <returns>A string with all available device and platform names</returns>
string OpenCLWrapper::DeviceAndPlatformNames()
{
ostringstream os;
vector<string> deviceNames;
for (size_t platform = 0; platform < m_Platforms.size(); platform++)
{
os << PlatformName(platform) << endl;
deviceNames = DeviceNames(platform);
for (size_t device = 0; device < m_Devices[platform].size(); device++)
os << "\t" << deviceNames[device] << endl;
}
return os.str();
}
/// <summary>
/// Get all information about the currently used device.
/// </summary>
/// <returns>A string with all information about the currently used device</returns>
string OpenCLWrapper::DumpInfo()
{
ostringstream os;
vector<size_t> sizes;
os.imbue(std::locale(""));
for (size_t platform = 0; platform < m_Platforms.size(); platform++)
{
os << "Platform " << platform << ": " << PlatformName(platform) << endl;
for (size_t device = 0; device < m_Devices[platform].size(); device++)
{
os << "Device " << device << ": " << DeviceName(platform, device) << endl;
os << "CL_DEVICE_OPENCL_C_VERSION: " << GetInfo<string> (platform, device, CL_DEVICE_OPENCL_C_VERSION) << endl;
os << "CL_DEVICE_LOCAL_MEM_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_LOCAL_MEM_SIZE) << endl;
os << "CL_DEVICE_LOCAL_MEM_TYPE: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_LOCAL_MEM_TYPE) << endl;
os << "CL_DEVICE_MAX_COMPUTE_UNITS: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_MAX_COMPUTE_UNITS) << endl;
os << "CL_DEVICE_MAX_READ_IMAGE_ARGS: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_MAX_READ_IMAGE_ARGS) << endl;
os << "CL_DEVICE_MAX_WRITE_IMAGE_ARGS: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_MAX_WRITE_IMAGE_ARGS) << endl;
os << "CL_DEVICE_MAX_MEM_ALLOC_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_MAX_MEM_ALLOC_SIZE) << endl;
os << "CL_DEVICE_ADDRESS_BITS: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_ADDRESS_BITS) << endl;
os << "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE) << endl;
os << "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE) << endl;
os << "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE) << endl;
os << "CL_DEVICE_GLOBAL_MEM_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_GLOBAL_MEM_SIZE) << endl;
os << "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE: " << GetInfo<cl_ulong>(platform, device, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE) << endl;
os << "CL_DEVICE_MAX_CONSTANT_ARGS: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_MAX_CONSTANT_ARGS) << endl;
os << "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: " << GetInfo<cl_uint> (platform, device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS) << endl;
os << "CL_DEVICE_MAX_WORK_GROUP_SIZE: " << GetInfo<::size_t>(platform, device, CL_DEVICE_MAX_WORK_GROUP_SIZE) << endl;
sizes = GetInfo<vector< ::size_t>>(platform, device, CL_DEVICE_MAX_WORK_ITEM_SIZES);
os << "CL_DEVICE_MAX_WORK_ITEM_SIZES: " << sizes[0] << ", " << sizes[1] << ", " << sizes[2] << endl << endl;
if (device != m_Devices[platform].size() - 1 && platform != m_Platforms.size() - 1)
os << endl;
}
os << endl;
}
return os.str();
}
/// <summary>
/// OpenCL properties, getters only.
/// </summary>
bool OpenCLWrapper::Ok() const { return m_Init; }
bool OpenCLWrapper::Shared() const { return m_Shared; }
cl::Context OpenCLWrapper::Context() const { return m_Context; }
uint OpenCLWrapper::PlatformIndex() const { return m_PlatformIndex; }
uint OpenCLWrapper::DeviceIndex() const { return m_DeviceIndex; }
size_t OpenCLWrapper::GlobalMemSize() const { return GetInfo<cl_ulong>(PlatformIndex(), DeviceIndex(), CL_DEVICE_GLOBAL_MEM_SIZE); }
uint OpenCLWrapper::LocalMemSize() const { return m_LocalMemSize; }
size_t OpenCLWrapper::MaxAllocSize() const { return GetInfo<cl_ulong>(PlatformIndex(), DeviceIndex(), CL_DEVICE_MAX_MEM_ALLOC_SIZE); }
const cl::Context& OpenCLWrapper::Context() const { return m_Context; }
size_t OpenCLWrapper::PlatformIndex() const { return m_PlatformIndex; }
size_t OpenCLWrapper::DeviceIndex() const { return m_DeviceIndex; }
const string& OpenCLWrapper::DeviceName() const { return m_Info.DeviceName(m_PlatformIndex, m_DeviceIndex); }
size_t OpenCLWrapper::LocalMemSize() const { return m_LocalMemSize; }
size_t OpenCLWrapper::GlobalMemSize() const { return m_GlobalMemSize; }
size_t OpenCLWrapper::MaxAllocSize() const { return m_MaxAllocSize; }
/// <summary>
/// Makes the even grid dims.
@ -1138,7 +975,7 @@ size_t OpenCLWrapper::MaxAllocSize() const { return GetInfo<cl_ulong>(PlatformIn
/// <param name="blockH">The block h.</param>
/// <param name="gridW">The grid w.</param>
/// <param name="gridH">The grid h.</param>
void OpenCLWrapper::MakeEvenGridDims(uint blockW, uint blockH, uint& gridW, uint& gridH)
void OpenCLWrapper::MakeEvenGridDims(size_t blockW, size_t blockH, size_t& gridW, size_t& gridH)
{
if (gridW % blockW != 0)
gridW += (blockW - (gridW % blockW));
@ -1147,67 +984,6 @@ void OpenCLWrapper::MakeEvenGridDims(uint blockW, uint blockH, uint& gridW, uint
gridH += (blockH - (gridH % blockH));
}
/// <summary>
/// Create a context that is optionall shared with OpenGL.
/// </summary>
/// <param name="shared">True if shared with OpenGL, else not shared.</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::CreateContext(bool shared)
{
cl_int err;
if (shared)
{
//Define OS-specific context properties and create the OpenCL context.
#if defined (__APPLE__) || defined(MACOSX)
CGLContextObj kCGLContext = CGLGetCurrentContext();
CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
cl_context_properties props[] =
{
CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup,
0
};
m_Context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);//May need to tinker with this on Mac.
#else
#if defined WIN32
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<cl_context_properties>((m_Platforms[m_PlatformIndex])()),
0
};
m_Context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);
#else
cl_context_properties props[] =
{
CL_GL_CONTEXT_KHR, cl_context_properties(glXGetCurrentContext()),
CL_GLX_DISPLAY_KHR, cl_context_properties(glXGetCurrentDisplay()),
CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>((m_Platforms[m_PlatformIndex])()),
0
};
m_Context = cl::Context(CL_DEVICE_TYPE_GPU, props, nullptr, nullptr, &err);
#endif
#endif
}
else
{
cl_context_properties props[3] =
{
CL_CONTEXT_PLATFORM,
reinterpret_cast<cl_context_properties>((m_Platforms[m_PlatformIndex])()),
0
};
m_Context = cl::Context(CL_DEVICE_TYPE_ALL, props, nullptr, nullptr, &err);
}
return CheckCL(err, "cl::Context()");
}
/// <summary>
/// Create an Spk object created by compiling the program arguments passed in.
/// </summary>
@ -1235,107 +1011,21 @@ bool OpenCLWrapper::CreateSPK(const string& name, const string& program, const s
//err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-no-signed-zeros -cl-fast-relaxed-math -cl-single-precision-constant");//This can cause some rounding.
//err = spk.m_Program.build(m_DeviceVec, "-cl-mad-enable -cl-single-precision-constant");
if (CheckCL(err, "cl::Program::build()"))
if (m_Info.CheckCL(err, "cl::Program::build()"))
{
//Building of program is ok, now create kernel with the specified entry point.
spk.m_Kernel = cl::Kernel(spk.m_Program, entryPoint.c_str(), &err);
if (CheckCL(err, "cl::Kernel()"))
if (m_Info.CheckCL(err, "cl::Kernel()"))
return true;//Everything is ok.
}
else
{
for (auto& i : m_DeviceVec)
m_ErrorReport.push_back(spk.m_Program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(i));
m_ErrorReport.push_back(spk.m_Program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(i, nullptr));
}
}
return false;
}
/// <summary>
/// Check an OpenCL return value for errors.
/// </summary>
/// <param name="err">The error code to inspect</param>
/// <param name="name">A description of where the value was gotten from</param>
/// <returns>True if success, else false.</returns>
bool OpenCLWrapper::CheckCL(cl_int err, const char* name)
{
if (err != CL_SUCCESS)
{
ostringstream ss;
ss << "ERROR: " << ErrorToStringCL(err) << " in " << name << "." << std::endl;
m_ErrorReport.push_back(ss.str());
}
return err == CL_SUCCESS;
}
/// <summary>
/// Translate an OpenCL error code into a human readable string.
/// </summary>
/// <param name="err">The error code to translate</param>
/// <returns>A human readable description of the error passed in</returns>
std::string OpenCLWrapper::ErrorToStringCL(cl_int err)
{
switch (err)
{
case CL_SUCCESS: return "Success";
case CL_DEVICE_NOT_FOUND: return "Device not found";
case CL_DEVICE_NOT_AVAILABLE: return "Device not available";
case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available";
case CL_MEM_OBJECT_ALLOCATION_FAILURE: return "Memory object allocation failure";
case CL_OUT_OF_RESOURCES: return "Out of resources";
case CL_OUT_OF_HOST_MEMORY: return "Out of host memory";
case CL_PROFILING_INFO_NOT_AVAILABLE: return "Profiling information not available";
case CL_MEM_COPY_OVERLAP: return "Memory copy overlap";
case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch";
case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported";
case CL_BUILD_PROGRAM_FAILURE: return "Program build failure";
case CL_MAP_FAILURE: return "Map failure";
case CL_MISALIGNED_SUB_BUFFER_OFFSET: return "Misaligned sub buffer offset";
case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST: return "Exec status error for events in wait list";
case CL_INVALID_VALUE: return "Invalid value";
case CL_INVALID_DEVICE_TYPE: return "Invalid device type";
case CL_INVALID_PLATFORM: return "Invalid platform";
case CL_INVALID_DEVICE: return "Invalid device";
case CL_INVALID_CONTEXT: return "Invalid context";
case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties";
case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue";
case CL_INVALID_HOST_PTR: return "Invalid host pointer";
case CL_INVALID_MEM_OBJECT: return "Invalid memory object";
case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: return "Invalid image format descriptor";
case CL_INVALID_IMAGE_SIZE: return "Invalid image size";
case CL_INVALID_SAMPLER: return "Invalid sampler";
case CL_INVALID_BINARY: return "Invalid binary";
case CL_INVALID_BUILD_OPTIONS: return "Invalid build options";
case CL_INVALID_PROGRAM: return "Invalid program";
case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable";
case CL_INVALID_KERNEL_NAME: return "Invalid kernel name";
case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition";
case CL_INVALID_KERNEL: return "Invalid kernel";
case CL_INVALID_ARG_INDEX: return "Invalid argument index";
case CL_INVALID_ARG_VALUE: return "Invalid argument value";
case CL_INVALID_ARG_SIZE: return "Invalid argument size";
case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments";
case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension";
case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size";
case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size";
case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset";
case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list";
case CL_INVALID_EVENT: return "Invalid event";
case CL_INVALID_OPERATION: return "Invalid operation";
case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object";
case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size";
case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level";
case CL_INVALID_GLOBAL_WORK_SIZE: return "Invalid global work size";
case CL_INVALID_PROPERTY: return "Invalid property";
default:
{
ostringstream ss;
ss << "<Unknown error code> " << err;
return ss.str();
}
}
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "EmberCLPch.h"
#include "OpenCLInfo.h"
/// <summary>
/// OpenCLWrapper, Spk, NamedBuffer, NamedImage2D, NamedImage2DGL classes.
@ -91,7 +92,7 @@ public:
/// <summary>
/// Running kernels in OpenCL can require quite a bit of setup, tear down and
/// general housekeeping. This class helps shield the user from such hassles.
/// It's main utility is in holding collections of programs, buffers and images
/// Its main utility is in holding collections of programs, buffers and images
/// all identified by names. That way, a user can access them as needed without
/// having to pollute their code.
/// In addition, writing to an existing object by name determines if the object
@ -103,8 +104,7 @@ class EMBERCL_API OpenCLWrapper : public EmberReport
{
public:
OpenCLWrapper();
bool CheckOpenCL();
bool Init(uint platform, uint device, bool shared = false);
bool Init(size_t platformIndex, size_t deviceIndex, bool shared = false);
//Programs.
bool AddProgram(const string& name, const string& program, const string& entryPoint, bool doublePrecision);
@ -114,22 +114,22 @@ public:
bool AddBuffer(const string& name, size_t size, cl_mem_flags flags = CL_MEM_READ_WRITE);
bool AddAndWriteBuffer(const string& name, void* data, size_t size, cl_mem_flags flags = CL_MEM_READ_WRITE);
bool WriteBuffer(const string& name, void* data, size_t size);
bool WriteBuffer(uint bufferIndex, void* data, size_t size);
bool WriteBuffer(size_t bufferIndex, void* data, size_t size);
bool ReadBuffer(const string& name, void* data, size_t size);
bool ReadBuffer(uint bufferIndex, void* data, size_t size);
bool ReadBuffer(size_t bufferIndex, void* data, size_t size);
int FindBufferIndex(const string& name);
uint GetBufferSize(const string& name);
uint GetBufferSize(uint bufferIndex);
size_t GetBufferSize(const string& name);
size_t GetBufferSize(size_t bufferIndex);
void ClearBuffers();
//Images.
bool AddAndWriteImage(const string& name, cl_mem_flags flags, const cl::ImageFormat& format, ::size_t width, ::size_t height, ::size_t row_pitch, void* data = NULL, bool shared = false, GLuint texName = 0);
bool WriteImage2D(uint index, bool shared, ::size_t width, ::size_t height, ::size_t row_pitch, void* data);
bool WriteImage2D(size_t index, bool shared, size_t width, size_t height, size_t row_pitch, void* data);
bool ReadImage(const string& name, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data);
bool ReadImage(uint imageIndex, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data);
bool ReadImage(size_t imageIndex, ::size_t width, ::size_t height, ::size_t row_pitch, bool shared, void* data);
int FindImageIndex(const string& name, bool shared);
uint GetImageSize(const string& name, bool shared);
uint GetImageSize(uint imageIndex, bool shared);
size_t GetImageSize(const string& name, bool shared);
size_t GetImageSize(size_t imageIndex, bool shared);
bool CompareImageParams(cl::Image& image, cl_mem_flags flags, const cl::ImageFormat& format, ::size_t width, ::size_t height, ::size_t row_pitch);
void ClearImages(bool shared);
bool CreateImage2D(cl::Image2D& image2D, cl_mem_flags flags, cl::ImageFormat format, ::size_t width, ::size_t height, ::size_t row_pitch = 0, void* data = NULL);
@ -143,10 +143,10 @@ public:
bool CreateSampler(cl::Sampler& sampler, cl_bool normalizedCoords, cl_addressing_mode addressingMode, cl_filter_mode filterMode);
//Arguments.
bool SetBufferArg(uint kernelIndex, uint argIndex, const string& name);
bool SetBufferArg(uint kernelIndex, uint argIndex, uint bufferIndex);
bool SetImageArg(uint kernelIndex, uint argIndex, bool shared, const string& name);
bool SetImageArg(uint kernelIndex, uint argIndex, bool shared, uint imageIndex);
bool SetBufferArg(size_t kernelIndex, cl_uint argIndex, const string& name);
bool SetBufferArg(size_t kernelIndex, cl_uint argIndex, size_t bufferIndex);
bool SetImageArg(size_t kernelIndex, cl_uint argIndex, bool shared, const string& name);
bool SetImageArg(size_t kernelIndex, cl_uint argIndex, bool shared, size_t imageIndex);
/// <summary>
/// Set an argument in the specified kernel, at the specified argument index.
@ -157,13 +157,13 @@ public:
/// <param name="arg">The argument value to set</param>
/// <returns>True if success, else false</returns>
template <typename T>
bool SetArg(uint kernelIndex, uint argIndex, T arg)
bool SetArg(size_t kernelIndex, cl_uint argIndex, T arg)
{
if (m_Init && kernelIndex < m_Programs.size())
{
cl_int err = m_Programs[kernelIndex].m_Kernel.setArg(argIndex, arg);
return CheckCL(err, "cl::Kernel::setArg()");
return m_Info.CheckCL(err, "cl::Kernel::setArg()");
}
return false;
@ -171,47 +171,37 @@ public:
//Kernels.
int FindKernelIndex(const string& name);
bool RunKernel(uint kernelIndex, uint totalGridWidth, uint totalGridHeight, uint totalGridDepth, uint blockWidth, uint blockHeight, uint blockDepth);
//Info.
template<typename T>
T GetInfo(size_t platform, size_t device, cl_device_info name) const;
string PlatformName(size_t platform);
vector<string> PlatformNames();
string DeviceName(size_t platform, size_t device);
vector<string> DeviceNames(size_t platform);
string DeviceAndPlatformNames();
string DumpInfo();
bool RunKernel(size_t kernelIndex, size_t totalGridWidth, size_t totalGridHeight, size_t totalGridDepth, size_t blockWidth, size_t blockHeight, size_t blockDepth);
//Accessors.
bool Ok() const;
bool Shared() const;
cl::Context Context() const;
uint PlatformIndex() const;
uint DeviceIndex() const;
uint LocalMemSize() const;
const cl::Context& Context() const;
size_t PlatformIndex() const;
size_t DeviceIndex() const;
const string& DeviceName() const;
size_t TotalDeviceIndex() const;
size_t LocalMemSize() const;
size_t GlobalMemSize() const;
size_t MaxAllocSize() const;
static void MakeEvenGridDims(uint blockW, uint blockH, uint& gridW, uint& gridH);
static void MakeEvenGridDims(size_t blockW, size_t blockH, size_t& gridW, size_t& gridH);
private:
bool CreateContext(bool shared);
bool CreateSPK(const string& name, const string& program, const string& entryPoint, Spk& spk, bool doublePrecision);
bool CheckCL(cl_int err, const char* name);
std::string ErrorToStringCL(cl_int err);
bool m_Init;
bool m_Shared;
uint m_PlatformIndex;
uint m_DeviceIndex;
uint m_LocalMemSize;
size_t m_PlatformIndex;
size_t m_DeviceIndex;
size_t m_LocalMemSize;
size_t m_GlobalMemSize;
size_t m_MaxAllocSize;
cl::Platform m_Platform;
cl::Context m_Context;
cl::Device m_Device;
cl::CommandQueue m_Queue;
std::vector<cl::Platform> m_Platforms;
std::vector<std::vector<cl::Device>> m_Devices;
OpenCLInfo& m_Info;
std::vector<cl::Device> m_DeviceVec;
std::vector<Spk> m_Programs;
std::vector<NamedBuffer> m_Buffers;

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,9 @@
#include "EmberCLPch.h"
#include "OpenCLWrapper.h"
#include "IterOpenCLKernelCreator.h"
#include "DEOpenCLKernelCreator.h"
#include "FinalAccumOpenCLKernelCreator.h"
#include "RendererClDevice.h"
/// <summary>
/// RendererCLBase and RendererCL classes.
@ -26,12 +26,17 @@ public:
/// <summary>
/// RendererCL is a derivation of the basic CPU renderer which
/// overrides various functions to render on the GPU using OpenCL.
/// This supports multi-GPU rendering and is done in the following manner:
/// -When rendering a single image, the iterations will be split between devices in sub batches.
/// -When animating, a renderer for each device will be created by the calling code,
/// and the frames will each be rendered by a single device as available.
/// The synchronization across devices is done through a single atomic counter.
/// Since this class derives from EmberReport and also contains an
/// OpenCLWrapper member which also derives from EmberReport, the
/// reporting functions are overridden to aggregate the errors from
/// both sources.
/// It does not support different types for T and bucketT, so it only has one template argument
/// and uses both for the base.
/// Template argument T expected to be float or double.
/// Template argument bucketT must always be float.
/// </summary>
template <typename T, typename bucketT>
class EMBERCL_API RendererCL : public Renderer<T, bucketT>, public RendererCLBase
@ -65,6 +70,8 @@ using EmberNs::Renderer<T, bucketT>::RendererBase::m_RenderTimer;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_IterTimer;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_ProgressTimer;
using EmberNs::Renderer<T, bucketT>::RendererBase::EmberReport::m_ErrorReport;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_ResizeCs;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_ProcessAction;
using EmberNs::Renderer<T, bucketT>::m_RotMat;
using EmberNs::Renderer<T, bucketT>::m_Ember;
using EmberNs::Renderer<T, bucketT>::m_Csa;
@ -82,45 +89,45 @@ using EmberNs::Renderer<T, bucketT>::GetSpatialFilter;
using EmberNs::Renderer<T, bucketT>::CoordMap;
using EmberNs::Renderer<T, bucketT>::XformDistributions;
using EmberNs::Renderer<T, bucketT>::XformDistributionsSize;
using EmberNs::Renderer<T, bucketT>::m_Dmap;
using EmberNs::Renderer<T, bucketT>::m_DensityFilter;
using EmberNs::Renderer<T, bucketT>::m_SpatialFilter;
public:
RendererCL(uint platform = 0, uint device = 0, bool shared = false, GLuint outputTexID = 0);
RendererCL(const vector<pair<size_t, size_t>>& devices, bool shared = false, GLuint outputTexID = 0);
~RendererCL();
//Non-virtual member functions for OpenCL specific tasks.
bool Init(uint platform, uint device, bool shared, GLuint outputTexID);
bool Init(const vector<pair<size_t, size_t>>& devices, bool shared, GLuint outputTexID);
bool SetOutputTexture(GLuint outputTexID);
//Iters per kernel/block/grid.
inline uint IterCountPerKernel() const;
inline uint IterCountPerBlock() const;
inline uint IterCountPerGrid() const;
inline size_t IterCountPerKernel() const;
inline size_t IterCountPerBlock() const;
inline size_t IterCountPerGrid() const;
//Kernels per block.
inline uint IterBlockKernelWidth() const;
inline uint IterBlockKernelHeight() const;
inline uint IterBlockKernelCount() const;
inline size_t IterBlockKernelWidth() const;
inline size_t IterBlockKernelHeight() const;
inline size_t IterBlockKernelCount() const;
//Kernels per grid.
inline uint IterGridKernelWidth() const;
inline uint IterGridKernelHeight() const;
inline uint IterGridKernelCount() const;
inline size_t IterGridKernelWidth() const;
inline size_t IterGridKernelHeight() const;
inline size_t IterGridKernelCount() const;
//Blocks per grid.
inline uint IterGridBlockWidth() const;
inline uint IterGridBlockHeight() const;
inline uint IterGridBlockCount() const;
inline size_t IterGridBlockWidth() const;
inline size_t IterGridBlockHeight() const;
inline size_t IterGridBlockCount() const;
uint PlatformIndex();
uint DeviceIndex();
bool ReadHist();
bool ReadHist(size_t device);
bool ReadAccum();
bool ReadPoints(vector<PointCL<T>>& vec);
bool ReadPoints(size_t device, vector<PointCL<T>>& vec);
bool ClearHist();
bool ClearHist(size_t device);
bool ClearAccum();
bool WritePoints(vector<PointCL<T>>& vec);
bool WritePoints(size_t device, vector<PointCL<T>>& vec);
#ifdef TEST_CL
bool WriteRandomPoints();
#endif
@ -136,7 +143,6 @@ public:
virtual size_t MemoryAvailable() override;
virtual bool Ok() const override;
virtual void NumChannels(size_t numChannels) override;
virtual void DumpErrorReport() override;
virtual void ClearErrorReport() override;
virtual size_t SubBatchSize() const override;
virtual size_t ThreadCount() const override;
@ -151,8 +157,7 @@ public:
protected:
#endif
//Protected virtual functions overridden from Renderer.
virtual void MakeDmap(T colorScalar) override;
virtual bool Alloc() override;
virtual bool Alloc(bool histOnly = false) override;
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true) override;
virtual eRenderStatus LogScaleDensityFilter() override;
virtual eRenderStatus GaussianDensityFilter() override;
@ -162,17 +167,19 @@ protected:
#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);
eRenderStatus RunLogScaleFilter();
eRenderStatus RunDensityFilter();
eRenderStatus RunFinalAccum();
bool ClearBuffer(const string& bufferName, uint width, uint height, uint elementSize);
bool RunDensityFilterPrivate(uint kernelIndex, uint gridW, uint gridH, uint blockW, uint blockH, uint chunkSizeW, uint chunkSizeH, uint chunkW, uint chunkH);
bool ClearBuffer(size_t device, const string& bufferName, uint width, uint height, uint elementSize);
bool RunDensityFilterPrivate(size_t kernelIndex, size_t gridW, size_t gridH, size_t blockW, size_t blockH, uint chunkSizeW, uint chunkSizeH, uint chunkW, uint chunkH);
int MakeAndGetDensityFilterProgram(size_t ss, uint filterWidth);
int MakeAndGetFinalAccumProgram(double& alphaBase, double& alphaScale);
int MakeAndGetGammaCorrectionProgram();
bool SumDeviceHist();
void FillSeeds();
//Private functions passing data to OpenCL programs.
@ -182,15 +189,12 @@ private:
void ConvertCarToRas(const CarToRas<T>& carToRas);
bool m_Init;
bool m_NVidia;
bool m_DoublePrecision;
uint m_IterCountPerKernel;
uint m_IterBlocksWide, m_IterBlockWidth;
uint m_IterBlocksHigh, m_IterBlockHeight;
uint m_MaxDEBlockSizeW;
uint m_MaxDEBlockSizeH;
uint m_WarpSize;
size_t m_Calls;
size_t m_IterCountPerKernel;
size_t m_IterBlocksWide, m_IterBlockWidth;
size_t m_IterBlocksHigh, m_IterBlockHeight;
size_t m_MaxDEBlockSizeW;
size_t m_MaxDEBlockSizeH;
//Buffer names.
string m_EmberBufferName;
@ -214,7 +218,6 @@ private:
//Kernels.
string m_IterKernel;
OpenCLWrapper m_Wrapper;
cl::ImageFormat m_PaletteFormat;
cl::ImageFormat m_FinalFormat;
cl::Image2D m_Palette;
@ -222,8 +225,7 @@ private:
GLuint m_OutputTexID;
EmberCL<T> m_EmberCL;
vector<XformCL<T>> m_XformsCL;
vector<glm::highp_uvec2> m_Seeds;
Palette<float> m_DmapCL;//Used instead of the base class' m_Dmap because OpenCL only supports float textures. Likely not needed if we switch to float only hist.
vector<vector<glm::highp_uvec2>> m_Seeds;
CarToRasCL<T> m_CarToRasCL;
DensityFilterCL<bucketT> m_DensityFilterCL;
SpatialFilterCL<bucketT> m_SpatialFilterCL;
@ -231,6 +233,7 @@ private:
DEOpenCLKernelCreator m_DEOpenCLKernelCreator;
FinalAccumOpenCLKernelCreator m_FinalAccumOpenCLKernelCreator;
pair<string, vector<T>> m_Params;
vector<unique_ptr<RendererClDevice>> m_Devices;
Ember<T> m_LastBuiltEmber;
};
}

View File

@ -0,0 +1,60 @@
#include "EmberCLPch.h"
#include "RendererClDevice.h"
namespace EmberCLns
{
/// <summary>
/// Constructor that assigns members.
/// The object is not fully initialized at this point, the caller
/// must manually call Init().
/// </summary>
/// <param name="platform">The index of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="shared">True if shared with OpenGL, else false.</param>
/// <returns>True if success, else false.</returns>
RendererClDevice::RendererClDevice(bool doublePrec, size_t platform, size_t device, bool shared)
: m_Info(OpenCLInfo::Instance())
{
m_Init = false;
m_Shared = shared;
m_NVidia = false;
m_WarpSize = 0;
m_Calls = 0;
m_PlatformIndex = platform;
m_DeviceIndex = device;
}
/// <summary>
/// Initialization of the OpenCLWrapper member.
/// </summary>
/// <returns>True if success, else false.</returns>
bool RendererClDevice::Init()
{
bool b = true;
if (!m_Wrapper.Ok())
{
m_Init = false;
b = m_Wrapper.Init(m_PlatformIndex, m_DeviceIndex, m_Shared);
}
if (b && m_Wrapper.Ok() && !m_Init)
{
m_NVidia = ToLower(m_Info.PlatformName(m_PlatformIndex)).find_first_of("nvidia") != string::npos && m_Wrapper.LocalMemSize() > (32 * 1024);
m_WarpSize = m_NVidia ? 32 : 64;
m_Init = true;
}
return b;
}
/// <summary>
/// OpenCL property accessors, getters only.
/// </summary>
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; }
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "EmberCLPch.h"
#include "OpenCLWrapper.h"
#include "IterOpenCLKernelCreator.h"
/// <summary>
/// RendererClDevice class.
/// </summary>
namespace EmberCLns
{
/// <summary>
/// Class to manage a device that does the iteration portion of
/// the rendering process. Having a separate class for this purpose
/// enables multi-GPU support.
/// </summary>
class EMBERCL_API RendererClDevice : public EmberReport
{
public:
RendererClDevice(bool doublePrec, size_t platform, size_t device, bool shared);
bool Init();
bool Ok() const;
bool Shared() const;
bool Nvidia() const;
size_t WarpSize() const;
size_t PlatformIndex() const;
size_t DeviceIndex() const;
size_t m_Calls;
OpenCLWrapper m_Wrapper;
private:
bool m_Init;
bool m_Shared;
bool m_NVidia;
size_t m_WarpSize;
size_t m_PlatformIndex;
size_t m_DeviceIndex;
OpenCLInfo& m_Info;
};
}

View File

@ -84,7 +84,7 @@ private:
/// <param name="useDefaults">True to use defaults if they are not present in the file, else false to use invalid values as placeholders to indicate the values were not present. Default: true.</param>
/// <returns>True if success, else false.</returns>
template <typename T>
static bool ParseEmberFile(XmlToEmber<T>& parser, string filename, vector<Ember<T>>& embers, bool useDefaults = true)
static bool ParseEmberFile(XmlToEmber<T>& parser, const string& filename, vector<Ember<T>>& embers, bool useDefaults = true)
{
if (!parser.Parse(filename.c_str(), embers, useDefaults))
{
@ -138,7 +138,7 @@ static void RgbaToRgb(vector<byte>& rgba, vector<byte>& rgb, size_t width, size_
if (rgba.data() != rgb.data())//Only resize the destination buffer if they are different.
rgb.resize(width * height * 3);
for (uint i = 0, j = 0; i < (width * height * 4); i += 4, j += 3)
for (size_t i = 0, j = 0; i < (width * height * 4); i += 4, j += 3)
{
rgb[j] = rgba[i];
rgb[j + 1] = rgba[i + 1];
@ -231,35 +231,52 @@ static T NextLowestEvenDiv(T numerator, T denominator)
return result;
}
/// <summary>
/// Wrapper for converting a vector of absolute device indices to a vector
/// of platform,device index pairs.
/// </summary>
/// <param name="selectedDevices">The vector of absolute device indices to convert</param>
/// <returns>The converted vector of platform,device index pairs</returns>
static vector<pair<size_t, size_t>> Devices(const vector<size_t>& selectedDevices)
{
vector<pair<size_t, size_t>> vec;
OpenCLInfo& info = OpenCLInfo::Instance();
auto& devices = info.DeviceIndices();
vec.reserve(selectedDevices.size());
for (size_t i = 0; i < selectedDevices.size(); i++)
{
auto index = selectedDevices[i];
if (index < devices.size())
vec.push_back(devices[index]);
}
return vec;
}
/// <summary>
/// Wrapper for creating a renderer of the specified type.
/// First template argument expected to be float or double for CPU renderer,
/// Second argument expected to be float or double for CPU renderer, and only float for OpenCL renderer.
/// </summary>
/// <param name="renderType">Type of renderer to create</param>
/// <param name="platform">The index platform of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="devices">The vector of platform/device indices to use</param>
/// <param name="shared">True if shared with OpenGL, else false.</param>
/// <param name="texId">The texture ID of the shared OpenGL texture if shared</param>
/// <param name="errorReport">The error report for holding errors if anything goes wrong</param>
/// <returns>A pointer to the created renderer if successful, else false.</returns>
template <typename T, typename bucketT>
static Renderer<T, bucketT>* CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared, GLuint texId, EmberReport& errorReport)
template <typename T>
static Renderer<T, float>* CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared, GLuint texId, EmberReport& errorReport)
{
string s;
unique_ptr<Renderer<T, bucketT>> renderer;
unique_ptr<Renderer<T, float>> renderer;
try
{
if (renderType == CPU_RENDERER)
{
s = "CPU";
renderer = unique_ptr<Renderer<T, bucketT>>(new Renderer<T, bucketT>());
}
else if (renderType == OPENCL_RENDERER)
if (renderType == OPENCL_RENDERER && !devices.empty())
{
s = "OpenCL";
renderer = unique_ptr<Renderer<T, bucketT>>(new RendererCL<T, bucketT>(platform, device, shared, texId));
renderer = unique_ptr<Renderer<T, float>>(new RendererCL<T, float>(devices, shared, texId));
if (!renderer.get() || !renderer->Ok())
{
@ -267,9 +284,18 @@ static Renderer<T, bucketT>* CreateRenderer(eRendererType renderType, uint platf
errorReport.AddToReport(renderer->ErrorReport());
errorReport.AddToReport("Error initializing OpenCL renderer, using CPU renderer instead.");
renderer = unique_ptr<Renderer<T, bucketT>>(new Renderer<T, bucketT>());
renderer = unique_ptr<Renderer<T, float>>(new Renderer<T, float>());
}
}
else
{
s = "CPU";
renderer = unique_ptr<Renderer<T, float>>(new Renderer<T, float>());
}
}
catch (const std::exception& e)
{
errorReport.AddToReport("Error creating " + s + " renderer: " + e.what() + "\n");
}
catch (...)
{
@ -279,6 +305,100 @@ static Renderer<T, bucketT>* CreateRenderer(eRendererType renderType, uint platf
return renderer.release();
}
/// <summary>
/// Wrapper for creating a vector of renderers of the specified type for each passed in device.
/// If shared is true, only the first renderer will be shared with OpenGL.
/// Although a fallback GPU renderer will be created if a failure occurs, it doesn't really
/// make sense since the concept of devices only applies to OpenCL renderers.
/// </summary>
/// <param name="renderType">Type of renderer to create</param>
/// <param name="devices">The vector of platform/device indices to use</param>
/// <param name="shared">True if shared with OpenGL, else false.</param>
/// <param name="texId">The texture ID of the shared OpenGL texture if shared</param>
/// <param name="errorReport">The error report for holding errors if anything goes wrong</param>
/// <returns>The vector of created renderers if successful, else false.</returns>
template <typename T>
static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared, GLuint texId, EmberReport& errorReport)
{
string s;
vector<unique_ptr<Renderer<T, float>>> v;
try
{
if (renderType == OPENCL_RENDERER && !devices.empty())
{
s = "OpenCL";
v.reserve(devices.size());
for (size_t i = 0; i < devices.size(); i++)
{
vector<pair<size_t, size_t>> tempDevices{ devices[i] };
auto renderer = unique_ptr<Renderer<T, float>>(new RendererCL<T, float>(tempDevices, !i ? shared : false, texId));
if (!renderer.get() || !renderer->Ok())
{
ostringstream os;
if (renderer.get())
errorReport.AddToReport(renderer->ErrorReport());
os << "Error initializing OpenCL renderer for platform " << devices[i].first << ", " << devices[i].second;
errorReport.AddToReport(os.str());
}
else
v.push_back(std::move(renderer));
}
}
else
{
s = "CPU";
v.push_back(std::move(unique_ptr<Renderer<T, float>>(::CreateRenderer<T>(CPU_RENDERER, devices, shared, texId, errorReport))));
}
}
catch (const std::exception& e)
{
errorReport.AddToReport("Error creating " + s + " renderer: " + e.what() + "\n");
}
catch (...)
{
errorReport.AddToReport("Error creating " + s + " renderer.\n");
}
if (v.empty() && s != "CPU")//OpenCL creation failed and CPU creation has not been attempted, so just create one CPU renderer and place it in the vector.
{
try
{
s = "CPU";
v.push_back(std::move(unique_ptr<Renderer<T, float>>(::CreateRenderer<T>(CPU_RENDERER, devices, shared, texId, errorReport))));
}
catch (const std::exception& e)
{
errorReport.AddToReport("Error creating fallback" + s + " renderer: " + e.what() + "\n");
}
catch (...)
{
errorReport.AddToReport("Error creating fallback " + s + " renderer.\n");
}
}
return v;
}
/// <summary>
/// Perform a render which allows for using strips or not.
/// If an error occurs while rendering any strip, the rendering process stops.
/// </summary>
/// <param name="renderer">The renderer to use</param>
/// <param name="ember">The ember to render</param>
/// <param name="finalImage">The vector to place the final output in</param>
/// <param name="time">The time position to use, only valid for animation</param>
/// <param name="strips">The number of strips to use. This must be validated before calling this function.</param>
/// <param name="yAxisUp">True to flip the Y axis, else false.</param>
/// <param name="perStripStart">Function called before the start of the rendering of each strip</param>
/// <param name="perStripFinish">Function called after the end of the rendering of each strip</param>
/// <param name="perStripError">Function called if there is an error rendering a strip</param>
/// <param name="allStripsFinished">Function called when all strips successfully finish rendering</param>
/// <returns>True if all rendering was successful, else false.</returns>
template <typename T>
static bool StripsRender(RendererBase* renderer, Ember<T>& ember, vector<byte>& finalImage, double time, size_t strips, bool yAxisUp,
std::function<void(size_t strip)> perStripStart,
@ -354,6 +474,17 @@ static bool StripsRender(RendererBase* renderer, Ember<T>& ember, vector<byte>&
return success;
}
/// <summary>
/// Verify that the specified number of strips is valid for the given height.
/// The passed in error functions will be called if the number of strips needs
/// to be modified for the given height.
/// </summary>
/// <param name="height">The height in pixels of the image to be rendered</param>
/// <param name="strips">The number of strips to split the render into</param>
/// <param name="stripError1">Function called if the number of strips exceeds the height of the image</param>
/// <param name="stripError2">Function called if the number of strips does not divide evently into the height of the image</param>
/// <param name="stripError3">Called if for any reason the number of strips used will differ from the value passed in</param>
/// <returns>The actual number of strips that will be used</returns>
static size_t VerifyStrips(size_t height, size_t strips,
std::function<void(const string& s)> stripError1,
std::function<void(const string& s)> stripError2,

View File

@ -1,4 +1,6 @@
#pragma once
#ifdef WIN32
#pragma once
#endif
/// <summary>
/// Precompiled header file. Place all system includes here with appropriate #defines for different operating systems and compilers.

View File

@ -56,9 +56,7 @@ enum eOptionIDs
OPT_DUMP_KERNEL,
//Value args.
OPT_OPENCL_PLATFORM,//Int value args.
OPT_OPENCL_DEVICE,
OPT_SEED,
OPT_SEED,//Int value args.
OPT_NTHREADS,
OPT_STRIPS,
OPT_SUPERSAMPLE,
@ -94,7 +92,8 @@ enum eOptionIDs
OPT_USEMEM,
OPT_LOOPS,
OPT_ISAAC_SEED,//String value args.
OPT_OPENCL_DEVICE,//String value args.
OPT_ISAAC_SEED,
OPT_IN,
OPT_OUT,
OPT_PREFIX,
@ -158,7 +157,7 @@ public:
/// <param name="defaultVal">The default value to use the option was not given on the command line</param>
/// <param name="argType">The format the argument should be given in</param>
/// <param name="docString">The documentation string describing what the argument means</param>
EmberOptionEntry(eOptionUse optUsage, eOptionIDs optId, const CharT* arg, T defaultVal, ESOArgType argType, string docString)
EmberOptionEntry(eOptionUse optUsage, eOptionIDs optId, const CharT* arg, T defaultVal, ESOArgType argType, const string& docString)
{
m_OptionUse = optUsage;
m_Option.nId = int(optId);
@ -235,25 +234,25 @@ private:
break
//Int.
#define Eoi EmberOptionEntry<int>
#define Eoi EmberOptionEntry<intmax_t>
#define INITINTOPTION(member, option) \
member = option; \
m_IntArgs.push_back(&member)
#define PARSEINTOPTION(opt, member) \
case (opt): \
sscanf_s(args.OptionArg(), "%d", &member.m_Val); \
sscanf_s(args.OptionArg(), "%ld", &member.m_Val); \
break
//Uint.
#define Eou EmberOptionEntry<uint>
#define Eou EmberOptionEntry<size_t>
#define INITUINTOPTION(member, option) \
member = option; \
m_UintArgs.push_back(&member)
#define PARSEUINTOPTION(opt, member) \
case (opt): \
sscanf_s(args.OptionArg(), "%u", &member.m_Val); \
sscanf_s(args.OptionArg(), "%lu", &member.m_Val); \
break
//Double.
@ -318,7 +317,7 @@ public:
INITBOOLOPTION(JpegComments, Eob(OPT_RENDER_ANIM, OPT_JPEG_COMMENTS, _T("--enable_jpeg_comments"), true, SO_OPT, "\t--enable_jpeg_comments Enables comments in the jpeg header [default: true].\n"));
INITBOOLOPTION(PngComments, Eob(OPT_RENDER_ANIM, OPT_PNG_COMMENTS, _T("--enable_png_comments"), true, SO_OPT, "\t--enable_png_comments Enables comments in the png header [default: true].\n"));
INITBOOLOPTION(WriteGenome, Eob(OPT_USE_ANIMATE, OPT_WRITE_GENOME, _T("--write_genome"), false, SO_NONE, "\t--write_genome Write out flame associated with center of motion blur window [default: false].\n"));
INITBOOLOPTION(ThreadedWrite, Eob(OPT_RENDER_ANIM, OPT_THREADED_WRITE, _T("--threaded_write"), true, SO_OPT, "\t--threaded_write Use a separate thread to write images to disk. This doubles the memory required for the final output buffer. [default: true].\n"));
INITBOOLOPTION(ThreadedWrite, Eob(OPT_RENDER_ANIM, OPT_THREADED_WRITE, _T("--threaded_write"), true, SO_OPT, "\t--threaded_write Use a separate thread to write images to disk. This gives better performance, but doubles the memory required for the final output buffer. [default: true].\n"));
INITBOOLOPTION(Enclosed, Eob(OPT_USE_GENOME, OPT_ENCLOSED, _T("--enclosed"), true, SO_OPT, "\t--enclosed Use enclosing XML tags [default: true].\n"));
INITBOOLOPTION(NoEdits, Eob(OPT_USE_GENOME, OPT_NO_EDITS, _T("--noedits"), false, SO_NONE, "\t--noedits Exclude edit tags when writing Xml [default: false].\n"));
INITBOOLOPTION(UnsmoothEdge, Eob(OPT_USE_GENOME, OPT_UNSMOOTH_EDGE, _T("--unsmoother"), false, SO_NONE, "\t--unsmoother Do not use smooth blending for sheep edges [default: false].\n"));
@ -336,8 +335,6 @@ public:
#endif
//Uint.
INITUINTOPTION(Platform, Eou(OPT_USE_ALL, OPT_OPENCL_PLATFORM, _T("--platform"), 0, SO_REQ_SEP, "\t--platform The OpenCL platform index to use [default: 0].\n"));
INITUINTOPTION(Device, Eou(OPT_USE_ALL, OPT_OPENCL_DEVICE, _T("--device"), 0, SO_REQ_SEP, "\t--device The OpenCL device index within the specified platform to use [default: 0].\n"));
INITUINTOPTION(Seed, Eou(OPT_USE_ALL, OPT_SEED, _T("--seed"), 0, SO_REQ_SEP, "\t--seed=<val> Integer seed to use for the random number generator [default: random].\n"));
INITUINTOPTION(ThreadCount, Eou(OPT_USE_ALL, OPT_NTHREADS, _T("--nthreads"), 0, SO_REQ_SEP, "\t--nthreads=<val> The number of threads to use [default: use all available cores].\n"));
INITUINTOPTION(Strips, Eou(OPT_USE_RENDER, OPT_STRIPS, _T("--nstrips"), 1, SO_REQ_SEP, "\t--nstrips=<val> The number of fractions to split a single render frame into. Useful for print size renders or low memory systems [default: 1].\n"));
@ -378,9 +375,10 @@ public:
INITDOUBLEOPTION(Loops, Eod(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1.0, SO_REQ_SEP, "\t--loops=<val> Number of times to rotate each control point in sequence [default: 1].\n"));
//String.
INITSTRINGOPTION(Device, Eos(OPT_USE_ALL, OPT_OPENCL_DEVICE, _T("--device"), "0", SO_REQ_SEP, "\t--device The comma-separated OpenCL device indices to use. Single device: 0 Multi device: 0,1,3,4 [default: 0].\n"));
INITSTRINGOPTION(IsaacSeed, Eos(OPT_USE_ALL, OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed=<val> Character-based seed for the random number generator [default: random].\n"));
INITSTRINGOPTION(Input, Eos(OPT_RENDER_ANIM, OPT_IN, _T("--in"), "", SO_REQ_SEP, "\t--in=<val> Name of the input file.\n"));
INITSTRINGOPTION(Out, Eos(OPT_RENDER_ANIM, OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out=<val> Name of a single output file. Not recommended when rendering more than one image.\n"));
INITSTRINGOPTION(Out, Eos(OPT_USE_RENDER, OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out=<val> Name of a single output file. Not recommended when rendering more than one image.\n"));
INITSTRINGOPTION(Prefix, Eos(OPT_RENDER_ANIM, OPT_PREFIX, _T("--prefix"), "", SO_REQ_SEP, "\t--prefix=<val> Prefix to prepend to all output files.\n"));
INITSTRINGOPTION(Suffix, Eos(OPT_RENDER_ANIM, OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, "\t--suffix=<val> Suffix to append to all output files.\n"));
INITSTRINGOPTION(Format, Eos(OPT_RENDER_ANIM, OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, "\t--format=<val> Format of the output file. Valid values are: bmp, jpg, png, ppm [default: jpg].\n"));
@ -470,9 +468,7 @@ public:
PARSEINTOPTION(OPT_SHEEP_GEN, SheepGen);
PARSEINTOPTION(OPT_SHEEP_ID, SheepId);
PARSEINTOPTION(OPT_PRIORITY, Priority);
PARSEUINTOPTION(OPT_OPENCL_PLATFORM, Platform);//uint args.
PARSEUINTOPTION(OPT_OPENCL_DEVICE, Device);
PARSEUINTOPTION(OPT_SEED, Seed);
PARSEUINTOPTION(OPT_SEED, Seed);//uint args.
PARSEUINTOPTION(OPT_NTHREADS, ThreadCount);
PARSEUINTOPTION(OPT_STRIPS, Strips);
PARSEUINTOPTION(OPT_SUPERSAMPLE, Supersample);
@ -504,7 +500,8 @@ public:
PARSEDOUBLEOPTION(OPT_USEMEM, UseMem);
PARSEDOUBLEOPTION(OPT_LOOPS, Loops);
PARSESTRINGOPTION(OPT_ISAAC_SEED, IsaacSeed);//String args.
PARSESTRINGOPTION(OPT_OPENCL_DEVICE, Device);//String args.
PARSESTRINGOPTION(OPT_ISAAC_SEED, IsaacSeed);
PARSESTRINGOPTION(OPT_IN, Input);
PARSESTRINGOPTION(OPT_OUT, Out);
PARSESTRINGOPTION(OPT_PREFIX, Prefix);
@ -545,9 +542,36 @@ public:
}
}
auto strings = Split(Device(), ',');
if (!strings.empty())
{
for (auto& s : strings)
{
size_t device = 0;
istringstream istr(s);
istr >> device;
if (!istr.bad() && !istr.fail())
m_Devices.push_back(device);
else
cout << "Failed to parse device index " << s;
}
}
return false;
}
/// <summary>
/// Return a const ref to m_Devices.
/// </summary>
/// <returns>A const ref to the vector of absolute device indices to be used</returns>
const vector<size_t>& Devices()
{
return m_Devices;
}
/// <summary>
/// Return a vector of all available options for the specified program.
/// </summary>
@ -656,103 +680,103 @@ public:
//Break from the usual m_* notation for members here because
//each of these is a functor, so it looks nicer and is less typing
//to just say opt.Member().
EmberOptionEntry<bool> Help;//Diagnostic bool.
EmberOptionEntry<bool> Version;
EmberOptionEntry<bool> Verbose;
EmberOptionEntry<bool> Debug;
EmberOptionEntry<bool> DumpArgs;
EmberOptionEntry<bool> DoProgress;
EmberOptionEntry<bool> OpenCLInfo;
Eob Help;//Diagnostic bool.
Eob Version;
Eob Verbose;
Eob Debug;
Eob DumpArgs;
Eob DoProgress;
Eob OpenCLInfo;
EmberOptionEntry<bool> EmberCL;//Value bool.
EmberOptionEntry<bool> EarlyClip;
EmberOptionEntry<bool> YAxisUp;
EmberOptionEntry<bool> Transparency;
EmberOptionEntry<bool> NameEnable;
EmberOptionEntry<bool> IntPalette;
EmberOptionEntry<bool> HexPalette;
EmberOptionEntry<bool> InsertPalette;
EmberOptionEntry<bool> JpegComments;
EmberOptionEntry<bool> PngComments;
EmberOptionEntry<bool> WriteGenome;
EmberOptionEntry<bool> ThreadedWrite;
EmberOptionEntry<bool> Enclosed;
EmberOptionEntry<bool> NoEdits;
EmberOptionEntry<bool> UnsmoothEdge;
EmberOptionEntry<bool> LockAccum;
EmberOptionEntry<bool> DumpKernel;
Eob EmberCL;//Value bool.
Eob EarlyClip;
Eob YAxisUp;
Eob Transparency;
Eob NameEnable;
Eob IntPalette;
Eob HexPalette;
Eob InsertPalette;
Eob JpegComments;
Eob PngComments;
Eob WriteGenome;
Eob ThreadedWrite;
Eob Enclosed;
Eob NoEdits;
Eob UnsmoothEdge;
Eob LockAccum;
Eob DumpKernel;
EmberOptionEntry<int> Symmetry;//Value int.
EmberOptionEntry<int> SheepGen;
EmberOptionEntry<int> SheepId;
EmberOptionEntry<int> Priority;
EmberOptionEntry<uint> Platform;//Value uint.
EmberOptionEntry<uint> Device;
EmberOptionEntry<uint> Seed;
EmberOptionEntry<uint> ThreadCount;
EmberOptionEntry<uint> Strips;
EmberOptionEntry<uint> Supersample;
EmberOptionEntry<uint> BitsPerChannel;
EmberOptionEntry<uint> SubBatchSize;
EmberOptionEntry<uint> Bits;
EmberOptionEntry<uint> PrintEditDepth;
EmberOptionEntry<uint> JpegQuality;
EmberOptionEntry<uint> FirstFrame;
EmberOptionEntry<uint> LastFrame;
EmberOptionEntry<uint> Frame;
EmberOptionEntry<uint> Time;
EmberOptionEntry<uint> Dtime;
EmberOptionEntry<uint> Frames;
EmberOptionEntry<uint> Repeat;
EmberOptionEntry<uint> Tries;
EmberOptionEntry<uint> MaxXforms;
Eoi Symmetry;//Value int.
Eoi SheepGen;
Eoi SheepId;
Eoi Priority;
Eou Seed;//Value uint.
Eou ThreadCount;
Eou Strips;
Eou Supersample;
Eou BitsPerChannel;
Eou SubBatchSize;
Eou Bits;
Eou PrintEditDepth;
Eou JpegQuality;
Eou FirstFrame;
Eou LastFrame;
Eou Frame;
Eou Time;
Eou Dtime;
Eou Frames;
Eou Repeat;
Eou Tries;
Eou MaxXforms;
EmberOptionEntry<double> SizeScale;//Value double.
EmberOptionEntry<double> QualityScale;
EmberOptionEntry<double> AspectRatio;
EmberOptionEntry<double> Stagger;
EmberOptionEntry<double> AvgThresh;
EmberOptionEntry<double> BlackThresh;
EmberOptionEntry<double> WhiteLimit;
EmberOptionEntry<double> Speed;
EmberOptionEntry<double> OffsetX;
EmberOptionEntry<double> OffsetY;
EmberOptionEntry<double> UseMem;
EmberOptionEntry<double> Loops;
Eod SizeScale;//Value double.
Eod QualityScale;
Eod AspectRatio;
Eod Stagger;
Eod AvgThresh;
Eod BlackThresh;
Eod WhiteLimit;
Eod Speed;
Eod OffsetX;
Eod OffsetY;
Eod UseMem;
Eod Loops;
EmberOptionEntry<string> IsaacSeed;//Value string.
EmberOptionEntry<string> Input;
EmberOptionEntry<string> Out;
EmberOptionEntry<string> Prefix;
EmberOptionEntry<string> Suffix;
EmberOptionEntry<string> Format;
EmberOptionEntry<string> PalettePath;
//EmberOptionEntry<string> PaletteImage;
EmberOptionEntry<string> Id;
EmberOptionEntry<string> Url;
EmberOptionEntry<string> Nick;
EmberOptionEntry<string> Comment;
EmberOptionEntry<string> TemplateFile;
EmberOptionEntry<string> Clone;
EmberOptionEntry<string> CloneAll;
EmberOptionEntry<string> CloneAction;
EmberOptionEntry<string> Animate;
EmberOptionEntry<string> Mutate;
EmberOptionEntry<string> Cross0;
EmberOptionEntry<string> Cross1;
EmberOptionEntry<string> Method;
EmberOptionEntry<string> Inter;
EmberOptionEntry<string> Rotate;
EmberOptionEntry<string> Strip;
EmberOptionEntry<string> Sequence;
EmberOptionEntry<string> UseVars;
EmberOptionEntry<string> DontUseVars;
EmberOptionEntry<string> Extras;
Eos Device;//Value string.
Eos IsaacSeed;
Eos Input;
Eos Out;
Eos Prefix;
Eos Suffix;
Eos Format;
Eos PalettePath;
//Eos PaletteImage;
Eos Id;
Eos Url;
Eos Nick;
Eos Comment;
Eos TemplateFile;
Eos Clone;
Eos CloneAll;
Eos CloneAction;
Eos Animate;
Eos Mutate;
Eos Cross0;
Eos Cross1;
Eos Method;
Eos Inter;
Eos Rotate;
Eos Strip;
Eos Sequence;
Eos UseVars;
Eos DontUseVars;
Eos Extras;
private:
vector<EmberOptionEntry<bool>*> m_BoolArgs;
vector<EmberOptionEntry<int>*> m_IntArgs;
vector<EmberOptionEntry<uint>*> m_UintArgs;
vector<EmberOptionEntry<double>*> m_DoubleArgs;
vector<EmberOptionEntry<string>*> m_StringArgs;
vector<size_t> m_Devices;
vector<Eob*> m_BoolArgs;
vector<Eoi*> m_IntArgs;
vector<Eou*> m_UintArgs;
vector<Eod*> m_DoubleArgs;
vector<Eos*> m_StringArgs;
};

View File

@ -44,7 +44,7 @@ static bool WritePpm(const char* filename, byte* image, size_t width, size_t hei
/// <param name="url">Url of the author</param>
/// <param name="nick">Nickname of the author</param>
/// <returns>True if success, else false</returns>
static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t height, int quality, bool enableComments, EmberImageComments& comments, string id, string url, string nick)
static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t height, int quality, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
{
bool b = false;
FILE* file;
@ -135,7 +135,7 @@ static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t he
/// <param name="url">Url of the author</param>
/// <param name="nick">Nickname of the author</param>
/// <returns>True if success, else false</returns>
static bool WritePng(const char* filename, byte* image, size_t width, size_t height, size_t bytesPerChannel, bool enableComments, EmberImageComments& comments, string id, string url, string nick)
static bool WritePng(const char* filename, byte* image, size_t width, size_t height, size_t bytesPerChannel, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
{
bool b = false;
FILE* file;

View File

@ -601,7 +601,7 @@ private:
CSimpleGlobTempl(const CSimpleGlobTempl &); // disabled
CSimpleGlobTempl & operator=(const CSimpleGlobTempl &); // disabled
/*! @brief The argv array has it's members stored as either an offset into
/*! @brief The argv array has its members stored as either an offset into
the string buffer, or as pointers to their string in the buffer. The
offsets are used because if the string buffer is dynamically resized,
all pointers into that buffer would become invalid.

View File

@ -1,4 +1,5 @@
#include "EmberCommonPch.h"
#include "EmberGenome.h"
#include "JpegUtils.h"
@ -39,10 +40,11 @@ void SetDefaultTestValues(Ember<T>& ember)
/// </summary>
/// <param name="opt">A populated EmberOptions object which specifies all program options to be used</param>
/// <returns>True if success, else false.</returns>
template <typename T, typename bucketT>
template <typename T>
bool EmberGenome(EmberOptions& opt)
{
OpenCLWrapper wrapper;
OpenCLInfo& info(OpenCLInfo::Instance());
std::cout.imbue(std::locale(""));
if (opt.DumpArgs())
@ -51,15 +53,15 @@ bool EmberGenome(EmberOptions& opt)
if (opt.OpenCLInfo())
{
cerr << "\nOpenCL Info: " << endl;
cerr << wrapper.DumpInfo();
cerr << info.DumpInfo();
return true;
}
//Regular variables.
Timing t;
bool exactTimeMatch, randomMode, didColor, seqFlag;
uint i, j, i0, i1, rep, val, frame, frameCount, count = 0;
uint ftime, firstFrame, lastFrame;
size_t i, j, i0, i1, rep, val, frame, frameCount, count = 0;
size_t ftime, firstFrame, lastFrame;
size_t n, tot, totb, totw;
T avgPix, fractionBlack, fractionWhite, blend, spread, mix0, mix1;
string token, filename;
@ -76,8 +78,9 @@ bool EmberGenome(EmberOptions& opt)
EmberToXml<T> emberToXml;
VariationList<T> varList;
EmberReport emberReport, emberReport2;
const vector<pair<size_t, size_t>> devices = Devices(opt.Devices());
unique_ptr<RenderProgress<T>> progress(new RenderProgress<T>());
unique_ptr<Renderer<T, bucketT>> renderer(CreateRenderer<T, bucketT>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, opt.Platform(), opt.Device(), false, 0, emberReport));
unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, devices, false, 0, emberReport));
QTIsaac<ISAAC_SIZE, ISAAC_INT> rand(ISAAC_INT(t.Tic()), ISAAC_INT(t.Tic() * 2), ISAAC_INT(t.Tic() * 3));
vector<string> errorReport = emberReport.ErrorReport();
@ -107,13 +110,16 @@ bool EmberGenome(EmberOptions& opt)
if (opt.Verbose())
{
cerr << "Platform: " << wrapper.PlatformName(opt.Platform()) << endl;
cerr << "Device: " << wrapper.DeviceName(opt.Platform(), opt.Device()) << endl;
for (auto& device : devices)
{
cerr << "Platform: " << info.PlatformName(device.first) << endl;
cerr << "Device: " << info.DeviceName(device.first, device.second) << endl;
}
}
}
//SheepTools will own the created renderer and will take care of cleaning it up.
SheepTools<T, bucketT> tools(opt.PalettePath(), CreateRenderer<T, bucketT>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, opt.Platform(), opt.Device(), false, 0, emberReport2));
SheepTools<T, float> tools(opt.PalettePath(), CreateRenderer<T>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, devices, false, 0, emberReport2));
tools.SetSpinParams(!opt.UnsmoothEdge(),
T(opt.Stagger()),
@ -169,7 +175,7 @@ bool EmberGenome(EmberOptions& opt)
while (std::getline(iss, token, ','))
{
if (parser.Atoi(token.c_str(), val))
if (parser.Aton(token.c_str(), val))
{
if (val < varList.Size())
vars.push_back(static_cast<eVariationId>(val));
@ -182,7 +188,7 @@ bool EmberGenome(EmberOptions& opt)
while (std::getline(iss, token, ','))
{
if (parser.Atoi(token.c_str(), val))
if (parser.Aton(token.c_str(), val))
{
if (val < varList.Size())
noVars.push_back(static_cast<eVariationId>(val));
@ -787,18 +793,18 @@ int _tmain(int argc, _TCHAR* argv[])
#ifdef DO_DOUBLE
if (opt.Bits() == 64)
{
b = EmberGenome<double, float>(opt);
b = EmberGenome<double>(opt);
}
else
#endif
if (opt.Bits() == 33)
{
b = EmberGenome<float, float>(opt);
b = EmberGenome<float>(opt);
}
else if (opt.Bits() == 32)
{
cerr << "Bits 32/int histogram no longer supported. Using bits == 33 (float)." << endl;
b = EmberGenome<float, float>(opt);
b = EmberGenome<float>(opt);
}
}

View File

@ -8,10 +8,10 @@
/// </summary>
/// <param name="opt">A populated EmberOptions object which specifies all program options to be used</param>
/// <returns>True if success, else false.</returns>
template <typename T, typename bucketT>
template <typename T>
bool EmberRender(EmberOptions& opt)
{
OpenCLWrapper wrapper;
EmberCLns::OpenCLInfo& info(EmberCLns::OpenCLInfo::Instance());
std::cout.imbue(std::locale(""));
@ -21,7 +21,7 @@ bool EmberRender(EmberOptions& opt)
if (opt.OpenCLInfo())
{
cout << "\nOpenCL Info: " << endl;
cout << wrapper.DumpInfo();
cout << info.DumpInfo();
return true;
}
@ -44,8 +44,9 @@ bool EmberRender(EmberOptions& opt)
XmlToEmber<T> parser;
EmberToXml<T> emberToXml;
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> randVec;
const vector<pair<size_t, size_t>> devices = Devices(opt.Devices());
unique_ptr<RenderProgress<T>> progress(new RenderProgress<T>());
unique_ptr<Renderer<T, bucketT>> renderer(CreateRenderer<T, bucketT>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, opt.Platform(), opt.Device(), false, 0, emberReport));
unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, devices, false, 0, emberReport));
vector<string> errorReport = emberReport.ErrorReport();
if (!errorReport.empty())
@ -86,8 +87,11 @@ bool EmberRender(EmberOptions& opt)
if (opt.Verbose())
{
cout << "Platform: " << wrapper.PlatformName(opt.Platform()) << endl;
cout << "Device: " << wrapper.DeviceName(opt.Platform(), opt.Device()) << endl;
for (auto& device : devices)
{
cout << "Platform: " << info.PlatformName(device.first) << endl;
cout << "Device: " << info.DeviceName(device.first, device.second) << endl;
}
}
if (opt.ThreadCount() > 1)
@ -145,7 +149,7 @@ bool EmberRender(EmberOptions& opt)
//Final setup steps before running.
os.imbue(std::locale(""));
padding = uint(log10((double)embers.size())) + 1;
padding = uint(log10(double(embers.size()))) + 1;
renderer->EarlyClip(opt.EarlyClip());
renderer->YAxisUp(opt.YAxisUp());
renderer->LockAccum(opt.LockAccum());
@ -154,7 +158,7 @@ bool EmberRender(EmberOptions& opt)
renderer->Transparency(opt.Transparency());
renderer->NumChannels(channels);
renderer->BytesPerChannel(opt.BitsPerChannel() / 8);
renderer->Priority((eThreadPriority)Clamp<int>((int)eThreadPriority::LOWEST, (int)eThreadPriority::HIGHEST, opt.Priority()));
renderer->Priority(eThreadPriority(Clamp<int>(int(opt.Priority()), int(eThreadPriority::LOWEST), int(eThreadPriority::HIGHEST))));
renderer->Callback(opt.DoProgress() ? progress.get() : nullptr);
for (i = 0; i < embers.size(); i++)
@ -276,7 +280,7 @@ bool EmberRender(EmberOptions& opt)
os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)";
VerbosePrint("\nIters ran/requested: " + os.str());
VerbosePrint("Bad values: " << stats.m_Badvals);
if (!opt.EmberCL()) VerbosePrint("Bad values: " << stats.m_Badvals);
VerbosePrint("Render time: " + t.Format(stats.m_RenderMs));
VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs));
VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << endl);
@ -291,7 +295,7 @@ bool EmberRender(EmberOptions& opt)
if (opt.Format() == "png")
writeSuccess = WritePng(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "jpg")
writeSuccess = WriteJpeg(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.JpegQuality(), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
writeSuccess = WriteJpeg(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "ppm")
writeSuccess = WritePpm(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
else if (opt.Format() == "bmp")
@ -303,7 +307,7 @@ bool EmberRender(EmberOptions& opt)
if (opt.EmberCL() && opt.DumpKernel())
{
if (auto rendererCL = dynamic_cast<RendererCL<T, bucketT>*>(renderer.get()))
if (auto rendererCL = dynamic_cast<RendererCL<T, float>*>(renderer.get()))
{
cout << "Iteration kernel: \n" <<
rendererCL->IterKernel() << "\n\n" <<
@ -315,8 +319,7 @@ bool EmberRender(EmberOptions& opt)
VerbosePrint("Done.");
}
if (opt.Verbose())
t.Toc("\nTotal time: ", true);
t.Toc("\nFinished in: ", true);
return true;
}
@ -347,18 +350,18 @@ int _tmain(int argc, _TCHAR* argv[])
#ifdef DO_DOUBLE
if (opt.Bits() == 64)
{
b = EmberRender<double, float>(opt);
b = EmberRender<double>(opt);
}
else
#endif
if (opt.Bits() == 33)
{
b = EmberRender<float, float>(opt);
b = EmberRender<float>(opt);
}
else if (opt.Bits() == 32)
{
cout << "Bits 32/int histogram no longer supported. Using bits == 33 (float)." << endl;
b = EmberRender<float, float>(opt);
b = EmberRender<float>(opt);
}
}

View File

@ -714,7 +714,7 @@ bool TestParVars()
names.reserve(parVar->ParamCount());
addresses.reserve(parVar->ParamCount());
for (uint j = 0; j < parVar->ParamCount(); j++)
for (size_t j = 0; j < parVar->ParamCount(); j++)
{
if (std::find(names.begin(), names.end(), params[j].Name()) != names.end())
{
@ -1449,13 +1449,13 @@ void TestVarsSimilar()
if (parVar)
{
for (uint v = 0; v < parVar->ParamCount(); v++)
for (size_t v = 0; v < parVar->ParamCount(); v++)
parVar->SetParamVal(v, (T)iter);
}
if (parVarComp)
{
for (uint v = 0; v < parVarComp->ParamCount(); v++)
for (size_t v = 0; v < parVarComp->ParamCount(); v++)
parVarComp->SetParamVal(v, (T)iter);
}
@ -1878,6 +1878,46 @@ double RandD(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
// double points[4];
//};
void TestThreadedKernel()
{
OpenCLWrapper wrapper1, wrapper2;
if (wrapper1.Init(1, 0) && wrapper2.Init(2, 0))
{
string k = ConstantDefinesString(false) + "\n__kernel void Kern()\n"
"{\n"
" int gid = GLOBAL_ID_X + GLOBAL_ID_Y;\n"
"}\n"
"\n";
if (wrapper1.AddProgram("prog1", k, "Kern", false) &&
wrapper2.AddProgram("prog1", k, "Kern", false))
{
cout << "Builds ok, now run..." << endl;
std::thread th1([&]()
{
if (wrapper1.RunKernel(0, 256, 16, 1, 16, 16, 1))
{
cout << "Successful run inside thread 1..." << endl;
}
});
std::thread th2([&]()
{
if (wrapper2.RunKernel(0, 256, 16, 1, 16, 16, 1))
{
cout << "Successful run inside thread 2..." << endl;
}
});
th1.join();
th2.join();
cout << "Successful join of kernel thread..." << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//int i;
@ -1885,12 +1925,14 @@ int _tmain(int argc, _TCHAR* argv[])
QTIsaac<ISAAC_SIZE, ISAAC_INT> rand(1, 2, 3);
mt19937 meow(1729);
PaletteList<float> palf;
TestThreadedKernel();
/*PaletteList<float> palf;
Palette<float>* pal = palf.GetRandomPalette();
cout << pal->Size() << endl;
/*double d = 1;
double d = 1;
for (int i = 0; i < 10; i++)
{

View File

@ -6,12 +6,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>595</height>
<width>596</width>
<height>622</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -31,223 +31,248 @@
<property name="windowTitle">
<string>About</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>6</number>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<widget class="QLabel" name="DescriptionLabel">
<property name="geometry">
<rect>
<x>6</x>
<y>5</y>
<width>583</width>
<height>151</height>
</rect>
</property>
<property name="topMargin">
<number>6</number>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="rightMargin">
<number>6</number>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="bottomMargin">
<number>6</number>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<item>
<widget class="QLabel" name="DescriptionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;br/&gt;Fractorium 0.4.1.9 Beta&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;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.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Lead: Matt Feemster&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Contributors: Simon Detheridge&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="CodeCopiedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Code Copied</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="topMargin">
<number>4</number>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;br/&gt;Fractorium 0.4.1.9 Beta&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;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.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Lead: Matt Feemster&lt;br/&gt;Contributors: Simon Detheridge&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>1</number>
</property>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>5</x>
<y>156</y>
<width>585</width>
<height>458</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="CodeCopiedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="bottomMargin">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Code Copied</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="topMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="CodeCopiedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://code.google.com/p/flam3&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;flam3&lt;/span&gt;&lt;/a&gt;: Scott Draves, Erik Reckase (GPL v2)&lt;br/&gt;&lt;a href=&quot;http://github.com/stevenrobertson/cuburn&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;cuburn&lt;/span&gt;&lt;/a&gt;: Steven Robertson, Michael Semeniuk, Matthew Znoj, Nicolas Mejia (GPL v3)&lt;br/&gt;&lt;a href=&quot;http://fractron9000.sourceforge.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Fractron 9000&lt;/span&gt;&lt;/a&gt;: Mike Thiesen (GPL)&lt;br/&gt;&lt;a href=&quot;http://sourceforge.net/projects/apophysis7x&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Apophysis&lt;/span&gt;&lt;/a&gt;: Mark Townsend, Ronald Hordijk, Peter Sdobnov, Piotr Borys, Georg Kiehne (GPL)&lt;br/&gt;&lt;a href=&quot;http://jwildfire.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;JWildfire&lt;/span&gt;&lt;/a&gt;: Andreas Maschke (LGPL)&lt;br/&gt;Numerous Apophysis plugin developers (GPL)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="margin">
<number>1</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="LibrariesLinkedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Libraries Linked</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="topMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="LibrariesLinkedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://qt-project.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Qt&lt;/span&gt;&lt;/a&gt;: Digia Plc (GPL v3, LGPL v2)&lt;br/&gt;&lt;a href=&quot;http://g-truc.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;glm&lt;/span&gt;&lt;/a&gt;: Christophe Riccio (MIT License)&lt;br/&gt;&lt;a href=&quot;http://threadingbuildingblocks.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Threading Building Blocks&lt;/span&gt;&lt;/a&gt;: Intel Corporation (GPLv2)&lt;br/&gt;&lt;a href=&quot;http://libjpeg.sourceforge.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;libjpeg&lt;/span&gt;&lt;/a&gt;: Independent JPEG Group (Free Software License)&lt;br/&gt;&lt;a href=&quot;http://libpng.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;libpng&lt;/span&gt;&lt;/a&gt;: Glenn Randers-Pehrson et al (Libpng License)&lt;br/&gt;&lt;a href=&quot;http://xmlsoft.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;libxml2&lt;/span&gt;&lt;/a&gt;: Daniel Veillard (MIT License)&lt;br/&gt;&lt;a href=&quot;http://zlib.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;zlib&lt;/span&gt;&lt;/a&gt;: Jean-loup Gailly, Mark Adler (Zlib License)&lt;br/&gt;&lt;a href=&quot;http://burtleburtle.net/bob/rand/isaac.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;QTIsaac&lt;/span&gt;&lt;/a&gt;: Robert J. Jenkins, Quinn Tyler Jackson (Public Domain)&lt;br/&gt;&lt;a href=&quot;http://cas.ee.ic.ac.uk/people/dt10/index.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;MWC64X Random Number Generator&lt;/span&gt;&lt;/a&gt;: David Thomas (Public Domain)&lt;br/&gt;&lt;a href=&quot;http://code.jellycan.com/simpleopt/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SimpleOpt&lt;/span&gt;&lt;/a&gt;: Brodie Thiesfield (MIT License)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="margin">
<number>1</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="IconsUsedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Icons Used</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="IconsUsedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://famfamfam.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Silk&lt;/span&gt;&lt;/a&gt;: Mark James (Creative Commons Attribution 2.5 License)&lt;br/&gt;&lt;a href=&quot;http://momentumdesignlab.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Momentum&lt;/span&gt;&lt;/a&gt;: Momentum Design Lab (Creative Commons Attribution-ShareAlike 3.5 License)&lt;br/&gt;&lt;a href=&quot;http://everaldo.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Crystal Clear&lt;/span&gt;&lt;/a&gt;: Everaldo Coelho (LGPL)&lt;br/&gt;&lt;a href=&quot;http://openiconlibrary.sourceforge.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Open Icon Library&lt;/span&gt;&lt;/a&gt;: Jeff Israel (GPL, LGPL, Creative Commons, Public Domain)&lt;br/&gt;&lt;a href=&quot;http://icons.mysitemyway.com/category/3d-transparent-glass-icons/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;3D Transparent Glass&lt;/span&gt;&lt;/a&gt;: iconsETC (Public Domain)&lt;br/&gt;&lt;a href=&quot;http://p.yusukekamiyamane.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Fugue&lt;/span&gt;&lt;/a&gt;: Yusuke Kamiyamane (Creative Commons Attribution 3.0 License)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="margin">
<number>1</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout" rowminimumheight="0">
<property name="verticalSpacing">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="CodeCopiedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="okButton">
<property name="minimumSize">
<size>
<width>0</width>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://code.google.com/p/flam3&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;flam3&lt;/span&gt;&lt;/a&gt;: Scott Draves, Erik Reckase (GPL v2)&lt;br/&gt;&lt;a href=&quot;http://github.com/stevenrobertson/cuburn&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;cuburn&lt;/span&gt;&lt;/a&gt;: Steven Robertson, Michael Semeniuk, Matthew Znoj, Nicolas Mejia (GPL v3)&lt;br/&gt;&lt;a href=&quot;http://fractron9000.sourceforge.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Fractron 9000&lt;/span&gt;&lt;/a&gt;: Mike Thiesen (GPL)&lt;br/&gt;&lt;a href=&quot;http://sourceforge.net/projects/apophysis7x&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Apophysis&lt;/span&gt;&lt;/a&gt;: Mark Townsend, Ronald Hordijk, Peter Sdobnov, Piotr Borys, Georg Kiehne (GPL)&lt;br/&gt;&lt;a href=&quot;http://jwildfire.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;JWildfire&lt;/span&gt;&lt;/a&gt;: Andreas Maschke (LGPL)&lt;br/&gt;Numerous Apophysis plugin developers (GPL)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<string>OK</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="LibrariesLinkedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Libraries Linked</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="topMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="LibrariesLinkedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://qt-project.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Qt&lt;/span&gt;&lt;/a&gt;: Digia Plc (GPL v3, LGPL v2)&lt;br/&gt;&lt;a href=&quot;http://g-truc.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;glm&lt;/span&gt;&lt;/a&gt;: Christophe Riccio (MIT License)&lt;br/&gt;&lt;a href=&quot;http://threadingbuildingblocks.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Threading Building Blocks&lt;/span&gt;&lt;/a&gt;: Intel Corporation (GPLv2)&lt;br/&gt;&lt;a href=&quot;http://libjpeg.sourceforge.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;libjpeg&lt;/span&gt;&lt;/a&gt;: Independent JPEG Group (Free Software License)&lt;br/&gt;&lt;a href=&quot;http://libpng.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;libpng&lt;/span&gt;&lt;/a&gt;: Glenn Randers-Pehrson et al (Libpng License)&lt;br/&gt;&lt;a href=&quot;http://xmlsoft.org&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;libxml2&lt;/span&gt;&lt;/a&gt;: Daniel Veillard (MIT License)&lt;br/&gt;&lt;a href=&quot;http://zlib.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;zlib&lt;/span&gt;&lt;/a&gt;: Jean-loup Gailly, Mark Adler (Zlib License)&lt;br/&gt;&lt;a href=&quot;http://burtleburtle.net/bob/rand/isaac.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;QTIsaac&lt;/span&gt;&lt;/a&gt;: Robert J. Jenkins, Quinn Tyler Jackson (Public Domain)&lt;br/&gt;&lt;a href=&quot;http://cas.ee.ic.ac.uk/people/dt10/index.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;MWC64X Random Number Generator&lt;/span&gt;&lt;/a&gt;: David Thomas (Public Domain)&lt;br/&gt;&lt;a href=&quot;http://code.jellycan.com/simpleopt/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;SimpleOpt&lt;/span&gt;&lt;/a&gt;: Brodie Thiesfield (MIT License)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="IconsUsedGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Icons Used</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="IconsUsedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://famfamfam.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Silk&lt;/span&gt;&lt;/a&gt;: Mark James (Creative Commons Attribution 2.5 License)&lt;br/&gt;&lt;a href=&quot;http://momentumdesignlab.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Momentum&lt;/span&gt;&lt;/a&gt;: Momentum Design Lab (Creative Commons Attribution-ShareAlike 3.5 License)&lt;br/&gt;&lt;a href=&quot;http://everaldo.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Crystal Clear&lt;/span&gt;&lt;/a&gt;: Everaldo Coelho (LGPL)&lt;br/&gt;&lt;a href=&quot;http://openiconlibrary.sourceforge.net&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Open Icon Library&lt;/span&gt;&lt;/a&gt;: Jeff Israel (GPL, LGPL, Creative Commons, Public Domain)&lt;br/&gt;&lt;a href=&quot;http://icons.mysitemyway.com/category/3d-transparent-glass-icons/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;3D Transparent Glass&lt;/span&gt;&lt;/a&gt;: iconsETC (Public Domain)&lt;br/&gt;&lt;a href=&quot;http://p.yusukekamiyamane.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Fugue&lt;/span&gt;&lt;/a&gt;: Yusuke Kamiyamane (Creative Commons Attribution 3.0 License)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout" rowminimumheight="0">
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="okButton">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>

View File

@ -124,7 +124,7 @@ void CurvesGraphicsView::Set(int curveIndex, int pointIndex, const QPointF& poin
/// <param name="curveIndex">The curve to set</param>
void CurvesGraphicsView::SetTop(CurveIndex curveIndex)
{
int index;
size_t index;
switch (curveIndex)
{
@ -142,7 +142,7 @@ void CurvesGraphicsView::SetTop(CurveIndex curveIndex)
index = 3;
}
for (int i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
if (i == index)
{

View File

@ -76,7 +76,7 @@ public:
/// <param name="param">The name of the parameter this is for</param>
/// <param name="h">The height of the spin box. Default: 16.</param>
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05.</param>
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, string param, int h = 16, double step = 0.05)
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05)
: DoubleSpinBox(p, h, step)
{
m_WidgetItem = widgetItem;

View File

@ -11,7 +11,8 @@
/// <param name="p">The parent widget</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* settings, QWidget* p, Qt::WindowFlags f)
: QDialog(p, f)
: QDialog(p, f),
m_Info(OpenCLInfo::Instance())
{
ui.setupUi(this);
@ -29,18 +30,18 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
connect(ui.FinalRenderTransparencyCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnTransparencyCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderOpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderDoublePrecisionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoublePrecisionCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderPlatformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnPlatformComboCurrentIndexChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderDoAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderDoSequenceCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnDoSequenceCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderCurrentSpin, SIGNAL(valueChanged(int)), this, SLOT(OnFinalRenderCurrentSpinChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderCurrentSpin, SIGNAL(valueChanged(int)), this, SLOT(OnCurrentSpinChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderApplyToAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnApplyAllCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderKeepAspectCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnKeepAspectCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.FinalRenderScaleNoneRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
connect(ui.FinalRenderScaleWidthRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
connect(ui.FinalRenderScaleHeightRadioButton, SIGNAL(toggled(bool)), this, SLOT(OnScaleRadioButtonChanged(bool)), Qt::QueuedConnection);
SetupSpinner<DoubleSpinBox, double>(ui.FinalRenderSizeTable, this, row, 1, m_WidthScaleSpin, spinHeight, 0.001, 99.99, 0.1, SIGNAL(valueChanged(double)), SLOT(OnFinalRenderWidthScaleChanged(double)), true, 1.0, 1.0, 1.0);
SetupSpinner<DoubleSpinBox, double>(ui.FinalRenderSizeTable, this, row, 1, m_HeightScaleSpin, spinHeight, 0.001, 99.99, 0.1, SIGNAL(valueChanged(double)), SLOT(OnFinalRenderHeightScaleChanged(double)), true, 1.0, 1.0, 1.0);
connect(ui.DeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
SetupSpinner<DoubleSpinBox, double>(ui.FinalRenderSizeTable, this, row, 1, m_WidthScaleSpin, spinHeight, 0.001, 99.99, 0.1, SIGNAL(valueChanged(double)), SLOT(OnWidthScaleChanged(double)), true, 1.0, 1.0, 1.0);
SetupSpinner<DoubleSpinBox, double>(ui.FinalRenderSizeTable, this, row, 1, m_HeightScaleSpin, spinHeight, 0.001, 99.99, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHeightScaleChanged(double)), true, 1.0, 1.0, 1.0);
m_WidthScaleSpin->setDecimals(3);
m_HeightScaleSpin->setDecimals(3);
m_WidthScaleSpin->setSuffix(" ( )");
@ -68,43 +69,35 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
connect(m_Tbcw->m_Button1, SIGNAL(clicked(bool)), this, SLOT(OnFileButtonClicked(bool)), Qt::QueuedConnection);
connect(m_Tbcw->m_Button2, SIGNAL(clicked(bool)), this, SLOT(OnShowFolderButtonClicked(bool)), Qt::QueuedConnection);
connect(m_Tbcw->m_Combo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFinalRenderExtIndexChanged(int)), Qt::QueuedConnection);
connect(m_Tbcw->m_Combo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnExtIndexChanged(int)), Qt::QueuedConnection);
m_PrefixEdit = new QLineEdit(table);
table->setCellWidget(row++, 1, m_PrefixEdit);
m_SuffixEdit = new QLineEdit(table);
table->setCellWidget(row++, 1, m_SuffixEdit);
connect(m_PrefixEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnFinalRenderPrefixChanged(const QString&)), Qt::QueuedConnection);
connect(m_SuffixEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnFinalRenderSuffixChanged(const QString&)), Qt::QueuedConnection);
connect(m_PrefixEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPrefixChanged(const QString&)), Qt::QueuedConnection);
connect(m_SuffixEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnSuffixChanged(const QString&)), Qt::QueuedConnection);
ui.StartRenderButton->disconnect(SIGNAL(clicked(bool)));
connect(ui.StartRenderButton, SIGNAL(clicked(bool)), this, SLOT(OnRenderClicked(bool)), Qt::QueuedConnection);
connect(ui.StopRenderButton, SIGNAL(clicked(bool)), this, SLOT(OnCancelRenderClicked(bool)), Qt::QueuedConnection);
if (m_Wrapper.CheckOpenCL())
table = ui.DeviceTable;
if (m_Info.Ok() && !m_Info.Devices().empty())
{
vector<string> platforms = m_Wrapper.PlatformNames();
SetupDeviceTable(table, m_Settings->FinalDevices());
//Populate combo boxes with available OpenCL platforms and devices.
for (i = 0; i < platforms.size(); i++)
ui.FinalRenderPlatformCombo->addItem(QString::fromStdString(platforms[i]));
for (int i = 0; i < table->rowCount(); i++)
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(i, 1)))
connect(radio, SIGNAL(toggled(bool)), this, SLOT(OnDeviceTableRadioToggled(bool)), Qt::QueuedConnection);
//If init succeeds, set the selected platform and device combos to match what was saved in the settings.
if (m_Wrapper.Init(m_Settings->FinalPlatformIndex(), m_Settings->FinalDeviceIndex()))
{
ui.FinalRenderOpenCLCheckBox->setChecked( m_Settings->FinalOpenCL());
ui.FinalRenderPlatformCombo->setCurrentIndex(m_Settings->FinalPlatformIndex());
ui.FinalRenderDeviceCombo->setCurrentIndex( m_Settings->FinalDeviceIndex());
}
else
{
OnPlatformComboCurrentIndexChanged(0);
ui.FinalRenderOpenCLCheckBox->setChecked(false);
}
ui.FinalRenderOpenCLCheckBox->setChecked(m_Settings->FinalOpenCL());
}
else
{
table->setEnabled(false);
ui.FinalRenderOpenCLCheckBox->setChecked(false);
ui.FinalRenderOpenCLCheckBox->setEnabled(false);
}
@ -128,7 +121,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
else if (m_Settings->FinalThreadPriority() == THREAD_PRIORITY_HIGHEST)
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(tpc);
else
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(Clamp<int>(0, tpc, m_Settings->FinalThreadPriority() / 25));
ui.FinalRenderThreadPriorityComboBox->setCurrentIndex(Clamp<int>(m_Settings->FinalThreadPriority() / 25, 0, tpc));
#endif
m_QualitySpin->setValue(m_Settings->FinalQuality());
@ -167,8 +160,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
w = SetTabOrder(this, w, ui.FinalRenderDoAllCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoSequenceCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderCurrentSpin);
w = SetTabOrder(this, w, ui.FinalRenderPlatformCombo);
w = SetTabOrder(this, w, ui.FinalRenderDeviceCombo);
w = SetTabOrder(this, w, ui.DeviceTable);
w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin);
w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox);
w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox);
@ -214,8 +206,6 @@ void FractoriumFinalRenderDialog::Path(const QString& s) { ui.FinalRenderParamsT
QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); }
QString FractoriumFinalRenderDialog::Suffix() { return m_SuffixEdit->text(); }
uint FractoriumFinalRenderDialog::Current() { return ui.FinalRenderCurrentSpin->value(); }
uint FractoriumFinalRenderDialog::PlatformIndex() { return ui.FinalRenderPlatformCombo->currentIndex(); }
uint FractoriumFinalRenderDialog::DeviceIndex() { return ui.FinalRenderDeviceCombo->currentIndex(); }
uint FractoriumFinalRenderDialog::ThreadCount() { return ui.FinalRenderThreadCountSpin->value(); }
#ifdef _WIN32
int FractoriumFinalRenderDialog::ThreadPriority() { return ui.FinalRenderThreadPriorityComboBox->currentIndex() - 2; }
@ -236,6 +226,7 @@ double FractoriumFinalRenderDialog::Quality() { return m_QualitySpin->value(); }
uint FractoriumFinalRenderDialog::TemporalSamples() { return m_TemporalSamplesSpin->value(); }
uint FractoriumFinalRenderDialog::Supersample() { return m_SupersampleSpin->value(); }
uint FractoriumFinalRenderDialog::Strips() { return m_StripsSpin->value(); }
QList<QVariant> FractoriumFinalRenderDialog::Devices() { return DeviceTableToSettings(ui.DeviceTable); }
/// <summary>
/// Capture the current state of the Gui.
@ -260,8 +251,7 @@ FinalRenderGuiState FractoriumFinalRenderDialog::State()
state.m_Ext = Ext();
state.m_Prefix = Prefix();
state.m_Suffix = Suffix();
state.m_PlatformIndex = PlatformIndex();
state.m_DeviceIndex = DeviceIndex();
state.m_Devices = Devices();
state.m_ThreadCount = ThreadCount();
state.m_ThreadPriority = ThreadPriority();
state.m_WidthScale = WidthScale();
@ -348,14 +338,14 @@ void FractoriumFinalRenderDialog::OnTransparencyCheckBoxStateChanged(int state)
/// <summary>
/// Set whether to use OpenCL in the rendering process or not.
/// Also disable or enable the CPU and OpenCL related controls based on the state passed in.
/// </summary>
/// <param name="state">Use OpenCL if state == Qt::Checked, else don't.</param>
void FractoriumFinalRenderDialog::OnOpenCLCheckBoxStateChanged(int state)
{
bool checked = state == Qt::Checked;
ui.FinalRenderPlatformCombo->setEnabled(checked);
ui.FinalRenderDeviceCombo->setEnabled(checked);
ui.DeviceTable->setEnabled(checked);
ui.FinalRenderThreadCountSpin->setEnabled(!checked);
ui.FinalRenderThreadPriorityComboBox->setEnabled(!checked);
SetMemory();
@ -379,6 +369,9 @@ void FractoriumFinalRenderDialog::OnDoublePrecisionCheckBoxStateChanged(int stat
/// <param name="state">The state of the checkbox</param>
void FractoriumFinalRenderDialog::OnDoAllCheckBoxStateChanged(int state)
{
if (!state)
ui.FinalRenderDoSequenceCheckBox->setChecked(false);
ui.FinalRenderDoSequenceCheckBox->setEnabled(ui.FinalRenderDoAllCheckBox->isChecked());
}
@ -390,36 +383,28 @@ void FractoriumFinalRenderDialog::OnDoAllCheckBoxStateChanged(int state)
/// <param name="state">The state of the checkbox</param>
void FractoriumFinalRenderDialog::OnDoSequenceCheckBoxStateChanged(int state)
{
m_TemporalSamplesSpin->setEnabled(ui.FinalRenderDoSequenceCheckBox->isChecked());
bool checked = ui.FinalRenderDoSequenceCheckBox->isChecked();
m_TemporalSamplesSpin->setEnabled(checked);
if (checked)
m_StripsSpin->setValue(1);
m_StripsSpin->setEnabled(!checked);
SetMemory();
}
/// <summary>
/// The current ember spinner was changed, update fields.
/// </summary>
/// <param name="d">Ignored</param>
void FractoriumFinalRenderDialog::OnFinalRenderCurrentSpinChanged(int d)
void FractoriumFinalRenderDialog::OnCurrentSpinChanged(int d)
{
m_Controller->SetEmber(d - 1);
m_Controller->SyncCurrentToGui();
SetMemory();
}
/// <summary>
/// Populate the the device combo box with all available
/// OpenCL devices for the selected platform.
/// Called when the platform combo box index changes.
/// </summary>
/// <param name="index">The selected index of the combo box</param>
void FractoriumFinalRenderDialog::OnPlatformComboCurrentIndexChanged(int index)
{
vector<string> devices = m_Wrapper.DeviceNames(index);
ui.FinalRenderDeviceCombo->clear();
for (auto& device : devices)
ui.FinalRenderDeviceCombo->addItem(QString::fromStdString(device));
}
/// <summary>
/// The apply all checkbox was changed.
/// If checked, set values for all embers in the file to the values specified in the GUI.
@ -437,7 +422,7 @@ void FractoriumFinalRenderDialog::OnApplyAllCheckBoxStateChanged(int state)
/// the height spinner as well to be in proportion.
/// </summary>
/// <param name="d">Ignored</param>
void FractoriumFinalRenderDialog::OnFinalRenderWidthScaleChanged(double d)
void FractoriumFinalRenderDialog::OnWidthScaleChanged(double d)
{
if (ui.FinalRenderKeepAspectCheckBox->isChecked() && m_Controller.get())
m_HeightScaleSpin->SetValueStealth(m_WidthScaleSpin->value());
@ -452,7 +437,7 @@ void FractoriumFinalRenderDialog::OnFinalRenderWidthScaleChanged(double d)
/// the width spinner as well to be in proportion.
/// </summary>
/// <param name="d">Ignored</param>
void FractoriumFinalRenderDialog::OnFinalRenderHeightScaleChanged(double d)
void FractoriumFinalRenderDialog::OnHeightScaleChanged(double d)
{
if (ui.FinalRenderKeepAspectCheckBox->isChecked() && m_Controller.get())
m_WidthScaleSpin->SetValueStealth(m_HeightScaleSpin->value());
@ -484,6 +469,50 @@ void FractoriumFinalRenderDialog::OnScaleRadioButtonChanged(bool checked)
SetMemory();
}
/// <summary>
/// The check state of one of the OpenCL devices was changed.
/// This does a special check to always ensure at least one device,
/// as well as one primary is checked.
/// </summary>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
void FractoriumFinalRenderDialog::OnDeviceTableCellChanged(int row, int col)
{
if (auto item = ui.DeviceTable->item(row, col))
{
HandleDeviceTableCheckChanged(ui.DeviceTable, row, col);
SetMemory();
}
}
/// <summary>
/// The primary device radio button selection was changed.
/// If the device was specified as primary, but was not selected
/// for inclusion, it will automatically be selected for inclusion.
/// </summary>
/// <param name="checked">The state of the radio button</param>
void FractoriumFinalRenderDialog::OnDeviceTableRadioToggled(bool checked)
{
int row;
auto s = sender();
auto table = ui.DeviceTable;
QRadioButton* radio = nullptr;
if (s)
{
for (row = 0; row < table->rowCount(); row++)
if (radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1)))
if (s == radio)
{
HandleDeviceTableCheckChanged(ui.DeviceTable, row, 1);
break;
}
}
if (checked)
SetMemory();
}
/// <summary>
/// The quality spinner was changed, recompute required memory.
/// </summary>
@ -558,7 +587,7 @@ void FractoriumFinalRenderDialog::OnShowFolderButtonClicked(bool checked)
/// number of channels used in the final output buffer.
/// </summary>
/// <param name="d">Ignored</param>
void FractoriumFinalRenderDialog::OnFinalRenderExtIndexChanged(int d)
void FractoriumFinalRenderDialog::OnExtIndexChanged(int d)
{
if (SetMemory())
Path(m_Controller->ComposePath(m_Controller->Name()));
@ -568,7 +597,7 @@ void FractoriumFinalRenderDialog::OnFinalRenderExtIndexChanged(int d)
/// Change the prefix prepended to the output file name.
/// </summary>
/// <param name="s">Ignored</param>
void FractoriumFinalRenderDialog::OnFinalRenderPrefixChanged(const QString& s)
void FractoriumFinalRenderDialog::OnPrefixChanged(const QString& s)
{
Path(m_Controller->ComposePath(m_Controller->Name()));
}
@ -577,7 +606,7 @@ void FractoriumFinalRenderDialog::OnFinalRenderPrefixChanged(const QString& s)
/// Change the suffix appended to the output file name.
/// </summary>
/// <param name="s">Ignored</param>
void FractoriumFinalRenderDialog::OnFinalRenderSuffixChanged(const QString& s)
void FractoriumFinalRenderDialog::OnSuffixChanged(const QString& s)
{
Path(m_Controller->ComposePath(m_Controller->Name()));
}
@ -637,7 +666,7 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
ui.FinalRenderCurrentSpin->blockSignals(true);
ui.FinalRenderCurrentSpin->setValue(index);//Set the currently selected ember to the one that was being edited.
ui.FinalRenderCurrentSpin->blockSignals(false);
OnFinalRenderCurrentSpinChanged(index);//Force update in case the ember was new, but at the same index as the previous one.
OnCurrentSpinChanged(index);//Force update in case the ember was new, but at the same index as the previous one.
m_Controller->m_ImageCount = 0;
SetMemory();
m_Controller->ResetProgress();
@ -655,7 +684,7 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
/// <summary>
/// Close the dialog without running, or if running, cancel and exit.
/// Settings will not be saved.
/// Control will be returned to Fractorium::OnActionFinalRender().
/// Control will be returned to Fractorium::OnActiOn().
/// </summary>
void FractoriumFinalRenderDialog::reject()
{
@ -736,48 +765,58 @@ bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
/// <summary>
/// Compute the amount of memory needed via call to SyncAndComputeMemory(), then
/// assign the result to the table cell as text.
/// Report errors if not enough memory is available for any of the selected devices.
/// </summary>
/// <returns>True devices and a controller is present, else false.</returns>
bool FractoriumFinalRenderDialog::SetMemory()
{
if (isVisible() && CreateControllerFromGUI(true))
{
bool error = false;
tuple<size_t, size_t, size_t> p = m_Controller->SyncAndComputeMemory();
QString s;
ui.FinalRenderParamsTable->item(m_MemoryCellIndex, 1)->setText(ToString<qulonglong>(get<1>(p)));
ui.FinalRenderParamsTable->item(m_ItersCellIndex, 1)->setText(ToString<qulonglong>(get<2>(p)));
if (OpenCL())
if (OpenCL() && !m_Wrappers.empty())
{
if (!m_Wrapper.Ok() || PlatformIndex() != m_Wrapper.PlatformIndex() || DeviceIndex() != m_Wrapper.DeviceIndex())
m_Wrapper.Init(PlatformIndex(), DeviceIndex());
auto devices = Devices();
if (m_Wrapper.Ok())
for (size_t i = 0; i < m_Wrappers.size(); i++)
{
size_t histSize = get<0>(p);
size_t totalSize = get<1>(p);
size_t maxAlloc = m_Wrapper.MaxAllocSize();
size_t totalAvail = m_Wrapper.GlobalMemSize();
QString s;
if (histSize > maxAlloc)
if (devices.contains(int(i)))
{
s = "Histogram/Accumulator memory size of " + ToString<qulonglong>(histSize) +
" is greater than the max OpenCL allocation size of " + ToString<qulonglong>(maxAlloc);
}
size_t histSize = get<0>(p);
size_t totalSize = get<1>(p);
size_t maxAlloc = m_Wrappers[i].MaxAllocSize();
size_t totalAvail = m_Wrappers[i].GlobalMemSize();
QString temp;
if (totalSize > totalAvail)
{
s += "\n\nTotal required memory size of " + ToString<qulonglong>(totalSize) +
" is greater than the max OpenCL available memory of " + ToString<qulonglong>(totalAvail);
}
if (histSize > maxAlloc)
{
temp = "Histogram/Accumulator memory size of " + ToString<qulonglong>(histSize) +
" is greater than the max OpenCL allocation size of " + ToString<qulonglong>(maxAlloc);
}
if (!s.isEmpty())
{
error = true;
ui.FinalRenderTextOutput->setText(s + ".\n\nRendering will most likely fail.");
if (totalSize > totalAvail)
{
temp += "\n\nTotal required memory size of " + ToString<qulonglong>(totalSize) +
" is greater than the max OpenCL available memory of " + ToString<qulonglong>(totalAvail);
}
if (!temp.isEmpty())
{
error = true;
s += QString::fromStdString(m_Wrappers[i].DeviceName()) + ":\n" + temp + "\n\n";
}
}
}
if (!s.isEmpty())
s += "Rendering will most likely fail.";
ui.FinalRenderTextOutput->setText(s);
}
if (!error)

View File

@ -65,8 +65,6 @@ public:
QString Prefix();
QString Suffix();
uint Current();
uint PlatformIndex();
uint DeviceIndex();
uint ThreadCount();
int ThreadPriority();
double WidthScale();
@ -75,6 +73,7 @@ public:
uint TemporalSamples();
uint Supersample();
uint Strips();
QList<QVariant> Devices();
FinalRenderGuiState State();
public slots:
@ -86,22 +85,23 @@ public slots:
void OnDoublePrecisionCheckBoxStateChanged(int state);
void OnDoAllCheckBoxStateChanged(int state);
void OnDoSequenceCheckBoxStateChanged(int state);
void OnFinalRenderCurrentSpinChanged(int d);
void OnPlatformComboCurrentIndexChanged(int index);
void OnCurrentSpinChanged(int d);
void OnApplyAllCheckBoxStateChanged(int state);
void OnFinalRenderWidthScaleChanged(double d);
void OnFinalRenderHeightScaleChanged(double d);
void OnWidthScaleChanged(double d);
void OnHeightScaleChanged(double d);
void OnKeepAspectCheckBoxStateChanged(int state);
void OnScaleRadioButtonChanged(bool checked);
void OnDeviceTableCellChanged(int row, int col);
void OnDeviceTableRadioToggled(bool checked);
void OnQualityChanged(double d);
void OnTemporalSamplesChanged(int d);
void OnSupersampleChanged(int d);
void OnStripsChanged(int d);
void OnFileButtonClicked(bool checked);
void OnShowFolderButtonClicked(bool checked);
void OnFinalRenderExtIndexChanged(int d);
void OnFinalRenderPrefixChanged(const QString& s);
void OnFinalRenderSuffixChanged(const QString& s);
void OnExtIndexChanged(int d);
void OnPrefixChanged(const QString& s);
void OnSuffixChanged(const QString& s);
void OnRenderClicked(bool checked);
void OnCancelRenderClicked(bool checked);
virtual void reject() override;
@ -116,7 +116,6 @@ private:
int m_MemoryCellIndex;
int m_ItersCellIndex;
int m_PathCellIndex;
OpenCLWrapper m_Wrapper;
Timing m_RenderTimer;
DoubleSpinBox* m_WidthScaleSpin;
DoubleSpinBox* m_HeightScaleSpin;
@ -129,6 +128,8 @@ private:
QLineEdit* m_SuffixEdit;
FractoriumSettings* m_Settings;
Fractorium* m_Fractorium;
OpenCLInfo& m_Info;
vector<OpenCLWrapper> m_Wrappers;
unique_ptr<FinalRenderEmberControllerBase> m_Controller;
Ui::FinalRenderDialog ui;
};

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>519</width>
<height>897</height>
<height>899</height>
</rect>
</property>
<property name="sizePolicy">
@ -64,7 +64,7 @@
<x>0</x>
<y>0</y>
<width>507</width>
<height>885</height>
<height>887</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -240,9 +240,9 @@
</layout>
</item>
<item>
<widget class="QComboBox" name="FinalRenderPlatformCombo">
<widget class="QTableWidget" name="DeviceTable">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -250,37 +250,81 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
<height>91</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
<height>91</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="FinalRenderDeviceCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>true</bool>
</property>
<property name="columnCount">
<number>3</number>
</property>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>60</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>22</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>22</number>
</attribute>
<row>
<property name="text">
<string>AMD</string>
</property>
</row>
<row>
<property name="text">
<string>Nvidia</string>
</property>
</row>
<row>
<property name="text">
<string>Intel</string>
</property>
</row>
<column>
<property name="text">
<string>Use</string>
</property>
</column>
<column>
<property name="text">
<string>Primary</string>
</property>
</column>
<column>
<property name="text">
<string>Device</string>
</property>
</column>
</widget>
</item>
<item>
@ -1119,8 +1163,6 @@
<tabstop>FinalRenderYAxisUpCheckBox</tabstop>
<tabstop>FinalRenderTransparencyCheckBox</tabstop>
<tabstop>FinalRenderOpenCLCheckBox</tabstop>
<tabstop>FinalRenderPlatformCombo</tabstop>
<tabstop>FinalRenderDeviceCombo</tabstop>
<tabstop>FinalRenderParamsTable</tabstop>
<tabstop>FinalRenderTextOutput</tabstop>
<tabstop>StartRenderButton</tabstop>

View File

@ -15,7 +15,7 @@ FinalRenderEmberControllerBase::FinalRenderEmberControllerBase(FractoriumFinalRe
m_Run = false;
m_PreviewRun = false;
m_ImageCount = 0;
m_FinishedImageCount = 0;
m_FinishedImageCount.store(0);
m_FinalRenderDialog = finalRenderDialog;
m_Settings = m_Fractorium->m_Settings;
}
@ -26,7 +26,8 @@ FinalRenderEmberControllerBase::FinalRenderEmberControllerBase(FractoriumFinalRe
/// It should never take longer than a few milliseconds because the
/// renderer checks the m_Abort flag in many places during the process.
/// </summary>
void FinalRenderEmberControllerBase::CancelRender()
template <typename T>
void FinalRenderEmberController<T>::CancelRender()
{
if (m_Result.isRunning())
{
@ -48,6 +49,21 @@ void FinalRenderEmberControllerBase::CancelRender()
m_Renderer->LeaveFinalAccum();
m_Renderer->LeaveRender();
}
else
{
for (auto& renderer : m_Renderers)
{
renderer->Abort();
while (renderer->InRender())
QApplication::processEvents();
renderer->EnterRender();
renderer->EnterFinalAccum();
renderer->LeaveFinalAccum();
renderer->LeaveRender();
}
}
});
g.wait();
@ -66,11 +82,11 @@ void FinalRenderEmberControllerBase::CancelRender()
/// <returns>True if a valid renderer is created or if no action is taken, else false.</returns>
bool FinalRenderEmberControllerBase::CreateRendererFromGUI()
{
bool useOpenCL = m_Wrapper.CheckOpenCL() && m_FinalRenderDialog->OpenCL();
bool useOpenCL = m_Info.Ok() && m_FinalRenderDialog->OpenCL();
auto v = Devices(m_FinalRenderDialog->Devices());
return CreateRenderer(useOpenCL ? OPENCL_RENDERER : CPU_RENDERER,
m_FinalRenderDialog->PlatformIndex(),
m_FinalRenderDialog->DeviceIndex(),
return CreateRenderer((useOpenCL && !v.empty()) ? OPENCL_RENDERER : CPU_RENDERER,
v,
false);//Not shared.
}
@ -102,9 +118,9 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
m_PreviewRun = true;
m_FinalPreviewRenderer->Abort();
QLabel* widget = m_FinalRenderDialog->ui.FinalRenderPreviewLabel;
size_t maxDim = 100;
T scalePercentage;
size_t maxDim = 100;
QLabel* widget = m_FinalRenderDialog->ui.FinalRenderPreviewLabel;
//Determine how to scale the scaled ember to fit in the label with a max of 100x100.
if (m_Ember->m_FinalRasW >= m_Ember->m_FinalRasH)
@ -125,7 +141,7 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
m_FinalPreviewRenderer->SetEmber(m_PreviewEmber);
m_FinalPreviewRenderer->PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
uint strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, m_FinalRenderDialog->Strips(),
auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, m_FinalRenderDialog->Strips(),
[&](const string& s) { }, [&](const string& s) { }, [&](const string& s) { });
StripsRender<T>(m_FinalPreviewRenderer.get(), m_PreviewEmber, m_PreviewFinalImage, 0, strips, m_FinalRenderDialog->YAxisUp(),
@ -152,11 +168,10 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
m_Run = true;
m_TotalTimer.Tic();//Begin timing for progress of all operations.
m_GuiState = m_FinalRenderDialog->State();//Cache render settings from the GUI before running.
m_FinalImageIndex = 0;
size_t i;
bool doAll = m_GuiState.m_DoAll && m_EmberFile.Size() > 1;
uint currentStripForProgress = 0;//Sort of a hack to get the strip value to the progress function.
size_t currentStripForProgress = 0;//Sort of a hack to get the strip value to the progress function.
QString path = doAll ? ComposePath(QString::fromStdString(m_EmberFile.m_Embers[0].m_Name)) : ComposePath(Name());
QString backup = path + "_backup.flame";
@ -166,32 +181,23 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
else
m_XmlWriter.Save(backup.toStdString().c_str(), *m_Ember, 0, true, false, true);
m_FinishedImageCount = 0;
m_Renderer->EarlyClip(m_GuiState.m_EarlyClip);
m_Renderer->YAxisUp(m_GuiState.m_YAxisUp);
m_Renderer->ThreadCount(m_GuiState.m_ThreadCount);
m_Renderer->Priority((eThreadPriority)m_GuiState.m_ThreadPriority);
m_Renderer->Transparency(m_GuiState.m_Transparency);
m_Renderer->m_ProgressParameter = reinterpret_cast<void*>(&currentStripForProgress);
if (path.endsWith(".png", Qt::CaseInsensitive) || m_Renderer->RendererType() == OPENCL_RENDERER)
m_Renderer->NumChannels(4);
else
m_Renderer->NumChannels(3);
m_FinishedImageCount.store(0);
SyncGuiToRenderer();
FirstOrDefaultRenderer()->m_ProgressParameter = reinterpret_cast<void*>(&currentStripForProgress);//When animating, only the first (primary) device has a progress parameter.
m_GuiState.m_Strips = VerifyStrips(m_Ember->m_FinalRasH, m_GuiState.m_Strips,
[&](const string& s) { Output(QString::fromStdString(s)); },//Greater than height.
[&](const string& s) { Output(QString::fromStdString(s)); },//Mod height != 0.
[&](const string& s) { Output(QString::fromStdString(s) + "\n"); });//Final strips value to be set.
ResetProgress();
//The rendering process is different between doing a single image, and doing multiple.
if (doAll)
{
m_ImageCount = m_EmberFile.Size();
ResetProgress();
//Different action required for rendering as animation or not.
if (m_GuiState.m_DoSequence)
if (m_GuiState.m_DoSequence && !m_Renderers.empty())
{
Ember<T>* firstEmber = &m_EmberFile.m_Embers[0];
@ -213,58 +219,107 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
m_EmberFile.m_Embers[i].m_TemporalSamples = m_GuiState.m_TemporalSamples;
}
//Not supporting strips with motion blur.
//Shouldn't be a problem because animations will be at max 4k x 4k which will take about 1.1GB
//even when using double precision, which most cards at the time of this writing already exceed.
std::atomic<size_t> atomfTime;
vector<std::thread> threadVec;
//Not supporting strips with animation.
//Shouldn't be a problem because animations will be at max 4k x 2k which will take about 1GB
//even when using double precision, which most cards at the time of this writing already exceed.
m_GuiState.m_Strips = 1;
m_Renderer->SetEmber(m_EmberFile.m_Embers);//Copy all embers to the local storage inside the renderer.
uint finalImageIndex = m_FinalImageIndex;
atomfTime.store(0);
//Render each image, cancelling if m_Run ever gets set to false.
for (i = 0; i < m_EmberFile.Size() && m_Run; i++)
std::function<void(size_t)> iterFunc = [&](size_t index)
{
Output("Image " + ToString(m_FinishedImageCount) + ":\n" + ComposePath(QString::fromStdString(m_EmberFile.m_Embers[i].m_Name)));
m_Renderer->Reset();//Have to manually set this since the ember is not set each time through.
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
size_t ftime;
size_t finalImageIndex = 0;
std::thread writeThread;
vector<byte> finalImages[2];
EmberStats stats;
EmberImageComments comments;
Timing renderTimer;
auto renderer = m_Renderers[index].get();
renderer->SetEmber(m_EmberFile.m_Embers);//Copy all embers to the local storage inside the renderer.
//Can't use strips render here. Run() must be called directly for animation.
if (m_Renderer->Run(m_FinalImage[finalImageIndex], i) != RENDER_OK)
//Render each image, cancelling if m_Run ever gets set to false.
while (atomfTime.fetch_add(1), ((ftime = atomfTime.load() - 1) < m_EmberFile.Size()) && m_Run)//Needed to set 1 to claim this iter from other threads, so decrement it to be zero-indexed here.
{
Output("Rendering failed.\n");
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRenderDialog->ui.FinalRenderTextOutput, false);//Internally calls invoke.
}
else
{
if (m_WriteThread.joinable())
m_WriteThread.join();
T localTime = T(ftime);
SetProgressComplete(100);
m_Stats = m_Renderer->Stats();
m_FinalImageIndex = finalImageIndex;//Will be used inside of RenderComplete(). Set here when no threads are running.
//RenderComplete(m_EmberFile.m_Embers[i]);//Non-threaded version for testing.
m_WriteThread = std::thread([&] { RenderComplete(m_EmberFile.m_Embers[i]); });
Output("Image " + ToString(ftime + 1ULL) + ":\n" + ComposePath(QString::fromStdString(m_EmberFile.m_Embers[ftime].m_Name)));
renderer->Reset();//Have to manually set this since the ember is not set each time through.
renderTimer.Tic();//Toc() is called in RenderComplete().
//Can't use strips render here. Run() must be called directly for animation.
if (renderer->Run(finalImages[finalImageIndex], localTime) != RENDER_OK)
{
Output("Rendering failed.\n");
m_Fractorium->ErrorReportToQTextEdit(renderer->ErrorReport(), m_FinalRenderDialog->ui.FinalRenderTextOutput, false);//Internally calls invoke.
atomfTime.store(m_EmberFile.Size() + 1);//Abort all threads if any of them encounter an error.
break;
}
else
{
if (writeThread.joinable())
writeThread.join();
stats = renderer->Stats();
comments = renderer->ImageComments(stats, 0, false, true);
writeThread = std::thread([&](size_t tempTime, size_t threadFinalImageIndex)
{
SaveCurrentRender(m_EmberFile.m_Embers[tempTime],
comments,//These all don't change during the renders, so it's ok to access them in the thread.
finalImages[threadFinalImageIndex],
renderer->FinalRasW(),
renderer->FinalRasH(),
renderer->NumChannels(),
renderer->BytesPerChannel());
}, ftime, finalImageIndex);
m_FinishedImageCount.fetch_add(1);
RenderComplete(m_EmberFile.m_Embers[ftime], stats, renderTimer);
if (!index)//Only first device has a progress callback, so it also makes sense to only manually set the progress on the first device as well.
HandleFinishedProgress();
}
finalImageIndex ^= 1;//Toggle the index.
}
finalImageIndex ^= 1;//Toggle the index.
if (writeThread.joinable())//One final check to make sure all writing is done before exiting this thread.
writeThread.join();
};
threadVec.reserve(m_Renderers.size());
for (size_t r = 0; r < m_Renderers.size(); r++)
{
threadVec.push_back(std::thread([&](size_t index)
{
iterFunc(index);
}, r));
}
if (m_WriteThread.joinable())
m_WriteThread.join();
for (auto& th : threadVec)
if (th.joinable())
th.join();
HandleFinishedProgress();//One final check that all images were finished.
}
else//Render all images, but not as an animation sequence (without temporal samples motion blur).
else if (m_Renderer.get())//Make sure a renderer was created and render all images, but not as an animation sequence (without temporal samples motion blur).
{
//Render each image, cancelling if m_Run ever gets set to false.
for (i = 0; i < m_EmberFile.Size() && m_Run; i++)
{
Output("Image " + ToString(m_FinishedImageCount) + ":\n" + ComposePath(QString::fromStdString(m_EmberFile.m_Embers[i].m_Name)));
Output("Image " + ToString<qulonglong>(m_FinishedImageCount.load() + 1) + ":\n" + ComposePath(QString::fromStdString(m_EmberFile.m_Embers[i].m_Name)));
m_EmberFile.m_Embers[i].m_TemporalSamples = 1;//No temporal sampling.
m_Renderer->SetEmber(m_EmberFile.m_Embers[i]);
m_Renderer->PrepFinalAccumVector(m_FinalImage[m_FinalImageIndex]);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
m_Renderer->PrepFinalAccumVector(m_FinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
m_Stats.Clear();
Memset(m_FinalImage[m_FinalImageIndex]);
Memset(m_FinalImage);
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
StripsRender<T>(m_Renderer.get(), m_EmberFile.m_Embers[i], m_FinalImage[m_FinalImageIndex], 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
StripsRender<T>(m_Renderer.get(), m_EmberFile.m_Embers[i], m_FinalImage, 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
[&](size_t strip) { currentStripForProgress = strip; },//Pre strip.
[&](size_t strip) { m_Stats += m_Renderer->Stats(); },//Post strip.
[&](size_t strip)//Error.
@ -272,23 +327,32 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
Output("Rendering failed.\n");
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRenderDialog->ui.FinalRenderTextOutput, false);//Internally calls invoke.
},
[&](Ember<T>& finalEmber) { RenderComplete(finalEmber); });//Final strip.
[&](Ember<T>& finalEmber)
{
m_FinishedImageCount.fetch_add(1);
SaveCurrentRender(finalEmber);
RenderComplete(finalEmber);
HandleFinishedProgress();
});//Final strip.
}
}
else
{
Output("No renderer present, aborting.");
}
}
else//Render a single image.
else if (m_Renderer.get())//Render a single image.
{
m_ImageCount = 1;
ResetProgress();
m_Ember->m_TemporalSamples = 1;
m_Renderer->SetEmber(*m_Ember);
m_Renderer->PrepFinalAccumVector(m_FinalImage[m_FinalImageIndex]);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
m_Renderer->PrepFinalAccumVector(m_FinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
m_Stats.Clear();
Memset(m_FinalImage[m_FinalImageIndex]);
Memset(m_FinalImage);
Output(ComposePath(QString::fromStdString(m_Ember->m_Name)));
m_RenderTimer.Tic();//Toc() is called in RenderComplete().
StripsRender<T>(m_Renderer.get(), *m_Ember, m_FinalImage[m_FinalImageIndex], 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
StripsRender<T>(m_Renderer.get(), *m_Ember, m_FinalImage, 0, m_GuiState.m_Strips, m_GuiState.m_YAxisUp,
[&](size_t strip) { currentStripForProgress = strip; },//Pre strip.
[&](size_t strip) { m_Stats += m_Renderer->Stats(); },//Post strip.
[&](size_t strip)//Error.
@ -296,10 +360,19 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
Output("Rendering failed.\n");
m_Fractorium->ErrorReportToQTextEdit(m_Renderer->ErrorReport(), m_FinalRenderDialog->ui.FinalRenderTextOutput, false);//Internally calls invoke.
},
[&](Ember<T>& finalEmber) { RenderComplete(finalEmber); });//Final strip.
[&](Ember<T>& finalEmber)
{
m_FinishedImageCount.fetch_add(1);
SaveCurrentRender(finalEmber);
RenderComplete(finalEmber);
HandleFinishedProgress();
});//Final strip.
}
else
{
Output("No renderer present, aborting.");
}
m_FinalImageIndex = 0;
QString totalTimeString = "All renders completed in: " + QString::fromStdString(m_TotalTimer.Format(m_TotalTimer.Toc())) + ".";
Output(totalTimeString);
@ -403,35 +476,43 @@ bool FinalRenderEmberController<T>::Render()
/// Stop rendering and initialize a new renderer, using the specified type and the options on the final render dialog.
/// </summary>
/// <param name="renderType">The type of render to create</param>
/// <param name="platform">The index platform of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="outputTexID">The texture ID of the shared OpenGL texture if shared</param>
/// <param name="shared">True if shared with OpenGL, else false. Default: true.</param>
/// <param name="devices">The platform,device index pairs of the devices to use</param>
/// <param name="shared">True if shared with OpenGL, else false. Always false in this case.</param>
/// <returns>True if nothing went wrong, else false.</returns>
template <typename T>
bool FinalRenderEmberController<T>::CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared)
bool FinalRenderEmberController<T>::CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared)
{
bool ok = true;
uint channels = m_FinalRenderDialog->Ext() == "png" ? 4 : 3;
bool deviceDiff = false;
//uint channels = m_FinalRenderDialog->Ext().endsWith("png", Qt::CaseInsensitive) ? 4 : 3;
bool renderTypeMismatch = (m_Renderer.get() && (m_Renderer->RendererType() != renderType)) ||
(!m_Renderers.empty() && (m_Renderers[0]->RendererType() != renderType));
CancelRender();
if (!m_Renderer.get() ||
!m_Renderer->Ok() ||
m_Renderer->RendererType() != renderType ||
m_Platform != platform ||
m_Device != device ||
m_Shared != shared)
if ((!m_FinalRenderDialog->DoSequence() && (!m_Renderer.get() || !m_Renderer->Ok())) ||
(m_FinalRenderDialog->DoSequence() && m_Renderers.empty()) ||
renderTypeMismatch ||
!Equal(m_Devices, devices))
{
EmberReport emberReport;
vector<string> errorReport;
m_Platform = platform;//Store values for re-creation later on.
m_Device = device;
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;
m_Shared = shared;//So shared is of course false.
if (m_FinalRenderDialog->DoSequence())
{
m_Renderer.reset();
m_Renderers = ::CreateRenderers<T>(renderType, m_Devices, shared, m_OutputTexID, emberReport);
}
else
{
m_Renderers.clear();
m_Renderer = unique_ptr<EmberNs::RendererBase>(::CreateRenderer<T>(renderType, m_Devices, shared, m_OutputTexID, emberReport));
}
m_Renderer = unique_ptr<EmberNs::RendererBase>(::CreateRenderer<T, float>(renderType, platform, device, shared, m_OutputTexID, emberReport));
errorReport = emberReport.ErrorReport();
if (!errorReport.empty())
@ -442,30 +523,13 @@ bool FinalRenderEmberController<T>::CreateRenderer(eRendererType renderType, uin
}
}
if (m_Renderer.get())
{
if (m_Renderer->RendererType() == OPENCL_RENDERER)
channels = 4;//Always using 4 since the GL texture is RGBA.
m_Renderer->Callback(this);
m_Renderer->NumChannels(channels);
m_Renderer->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_Renderer->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_Renderer->ThreadCount(m_FinalRenderDialog->ThreadCount());
m_Renderer->Transparency(m_FinalRenderDialog->Transparency());
}
else
{
ok = false;
m_Fractorium->ShowCritical("Renderer Creation Error", "Could not create renderer, aborting. See info tab for details.");
}
return ok;
return SyncGuiToRenderer() && ok;
}
/// <summary>
/// Progress function.
/// Take special action to sync options upon finishing.
/// Note this is only called on the primary renderer.
/// </summary>
/// <param name="ember">The ember currently being rendered</param>
/// <param name="foo">An extra dummy parameter</param>
@ -477,7 +541,7 @@ template <typename T>
int FinalRenderEmberController<T>::ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs)
{
static int count = 0;
uint strip = *(reinterpret_cast<uint*>(m_Renderer->m_ProgressParameter));
size_t strip = *(reinterpret_cast<size_t*>(FirstOrDefaultRenderer()->m_ProgressParameter));
double fracPerStrip = ceil(100.0 / m_GuiState.m_Strips);
double stripsfrac = ceil(fracPerStrip * strip) + ceil(fraction / m_GuiState.m_Strips);
int intFract = int(stripsfrac);
@ -489,7 +553,7 @@ int FinalRenderEmberController<T>::ProgressFunc(Ember<T>& ember, void* foo, doub
else if (stage == 2)
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderAccumProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, intFract));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString(m_FinishedImageCount) + " / " + ToString(m_ImageCount) + " Eta: " + QString::fromStdString(m_RenderTimer.Format(etaMs))));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString<qulonglong>(m_FinishedImageCount.load() + 1) + " / " + ToString<qulonglong>(m_ImageCount) + " Eta: " + QString::fromStdString(m_RenderTimer.Format(etaMs))));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTextOutput, "update", Qt::QueuedConnection);
return m_Run ? 1 : 0;
@ -533,6 +597,53 @@ void FinalRenderEmberController<T>::SyncGuiToEmbers(size_t widthOverride, size_t
}
}
/// <summary>
/// Copy GUI values to the renderers.
/// </summary>
template <typename T>
bool FinalRenderEmberController<T>::SyncGuiToRenderer()
{
bool ok = true;
uint channels = m_FinalRenderDialog->Ext().endsWith("png", Qt::CaseInsensitive) ? 4 : 3;
if (m_Renderer.get())
{
if (m_Renderer->RendererType() == OPENCL_RENDERER)
channels = 4;//Always using 4 since the GL texture is RGBA.
m_Renderer->Callback(this);
m_Renderer->NumChannels(channels);
m_Renderer->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_Renderer->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_Renderer->ThreadCount(m_FinalRenderDialog->ThreadCount());
m_Renderer->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority());
m_Renderer->Transparency(m_FinalRenderDialog->Transparency());
}
else if (!m_Renderers.empty())
{
for (size_t i = 0; i < m_Renderers.size(); i++)
{
if (m_Renderers[i]->RendererType() == OPENCL_RENDERER)
channels = 4;//Always using 4 since the GL texture is RGBA.
m_Renderers[i]->Callback(!i ? this : nullptr);
m_Renderers[i]->NumChannels(channels);
m_Renderers[i]->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_Renderers[i]->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_Renderers[i]->ThreadCount(m_FinalRenderDialog->ThreadCount());
m_Renderers[i]->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority());
m_Renderers[i]->Transparency(m_FinalRenderDialog->Transparency());
}
}
else
{
ok = false;
m_Fractorium->ShowCritical("Renderer Creation Error", "No renderer present, aborting. See info tab for details.");
}
return ok;
}
/// <summary>
/// Set values for scale spinners based on the ratio of the original dimensions to the current dimensions
/// of the current ember. Also update the size suffix text.
@ -564,7 +675,7 @@ void FinalRenderEmberController<T>::ResetProgress(bool total)
{
if (total)
{
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, "0 / " + ToString(m_ImageCount)));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, "0 / " + ToString<qulonglong>(m_ImageCount)));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, 0));
}
@ -574,7 +685,7 @@ void FinalRenderEmberController<T>::ResetProgress(bool total)
}
/// <summary>
/// Set various parameters in the renderer and current ember with the values
/// Set various parameters in the renderers and current ember with the values
/// specified in the widgets and compute the amount of memory required to render.
/// This includes the memory needed for the final output image.
/// </summary>
@ -584,16 +695,17 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
{
size_t iterCount;
pair<size_t, size_t> p(0, 0);
size_t strips;
bool b = false;
uint channels = m_FinalRenderDialog->Ext() == "png" ? 4 : 3;//4 channels for Png, else 3.
SyncGuiToEmbers();
if (m_Renderer.get())
{
bool b = false;
uint channels = m_FinalRenderDialog->Ext() == "png" ? 4 : 3;//4 channels for Png, else 3.
size_t strips = VerifyStrips(m_Ember->m_FinalRasH, m_FinalRenderDialog->Strips(),
[&](const string& s) { }, [&](const string& s) { }, [&](const string& s) { });
strips = VerifyStrips(m_Ember->m_FinalRasH, m_FinalRenderDialog->Strips(),
[&](const string& s) {}, [&](const string& s) {}, [&](const string& s) {});
SyncGuiToEmbers();
m_FinalRenderDialog->m_StripsSpin->setSuffix(" (" + ToString<qulonglong>(strips) + ")");
m_Renderer->SetEmber(*m_Ember);
m_Renderer->CreateSpatialFilter(b);
m_Renderer->CreateTemporalFilter(b);
@ -607,6 +719,28 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderer->TotalIterCount(strips);
}
else if (!m_Renderers.empty())
{
for (auto& renderer : m_Renderers)
{
renderer->SetEmber(*m_Ember);
renderer->CreateSpatialFilter(b);
renderer->CreateTemporalFilter(b);
renderer->NumChannels(channels);
renderer->ComputeBounds();
renderer->ComputeQuality();
renderer->ComputeCamera();
}
CancelPreviewRender();
m_FinalPreviewRenderFunc();
strips = 1;
p = m_Renderers[0]->MemoryRequired(1, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderers[0]->TotalIterCount(strips);
}
m_FinalRenderDialog->m_StripsSpin->setSuffix(" (" + ToString<qulonglong>(strips) +")");
return tuple<size_t, size_t, size_t>(p.first, p.second, iterCount);
}
@ -631,6 +765,25 @@ QString FinalRenderEmberController<T>::ComposePath(const QString& name)
/// Non-virtual functions declared in FinalRenderEmberController<T>.
/// </summary>
/// <summary>
/// Return either m_Renderer in the case of running a CPU renderer, else
/// m_Renderers[0] in the case of running OpenCL.
/// </summary>
/// <returns>The primary renderer</returns>
template <typename T>
EmberNs::Renderer<T, float>* FinalRenderEmberController<T>::FirstOrDefaultRenderer()
{
if (m_Renderer.get())
return dynamic_cast<EmberNs::Renderer<T, float>*>(m_Renderer.get());
else if (!m_Renderers.empty())
return dynamic_cast<EmberNs::Renderer<T, float>*>(m_Renderers[0].get());
else
{
throw "No final renderer, exiting.";
return nullptr;
}
}
/// <summary>
/// Stop the preview renderer.
/// This is meant to only be called programatically and never by the user.
@ -645,40 +798,80 @@ void FinalRenderEmberController<T>::CancelPreviewRender()
while (m_FinalPreviewResult.isRunning()) { QApplication::processEvents(); }
}
/// <summary>
/// Save the output of the render.
/// </summary>
/// <param name="ember">The ember whose rendered output will be saved</param>
template<typename T>
void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember)
{
auto comments = m_Renderer->ImageComments(m_Stats, 0, false, true);
SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_Renderer->NumChannels(), m_Renderer->BytesPerChannel());
}
/// <summary>
/// Save the output of the render.
/// </summary>
/// <param name="ember">The ember whose rendered output will be saved</param>
/// <param name="comments">The comments to save in the png or jpg</param>
/// <param name="pixels">The buffer containing the pixels</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="channels">The number of channels, 3 or 4.</param>
/// <param name="bpc">The bytes per channel, almost always 1.</param>
template<typename T>
void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc)
{
QString filename = ComposePath(QString::fromStdString(ember.m_Name));
FractoriumEmberControllerBase::SaveCurrentRender(filename, comments, pixels, width, height, channels, bpc);
}
/// <summary>
/// Action to take when rendering an image completes.
/// Thin wrapper around the function of the same name that takes more arguments.
/// Just passes m_Renderer and m_FinalImage.
/// </summary>
/// <param name="ember">The ember currently being rendered</param>
template<typename T>
void FinalRenderEmberController<T>::RenderComplete(Ember<T>& ember)
{
string renderTimeString = m_RenderTimer.Format(m_RenderTimer.Toc()), totalTimeString;
if (auto renderer = dynamic_cast<EmberNs::Renderer<T, float>*>(m_Renderer.get()))
RenderComplete(ember, m_Stats, m_RenderTimer);
}
/// <summary>
/// Handle setting the appropriate progress bar values when an image render has finished.
/// This handles single image, animations, and strips.
/// </summary>
template<typename T>
void FinalRenderEmberController<T>::HandleFinishedProgress()
{
auto finishedCountCached = m_FinishedImageCount.load();//Make sure to use the same value throughout this function even if the atomic is changing.
if (m_FinishedImageCount.load() != m_ImageCount)
ResetProgress(false);
else
SetProgressComplete(100);//Just to be safe.
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int((float(finishedCountCached) / float(m_ImageCount)) * 100)));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString<qulonglong>(finishedCountCached) + " / " + ToString<qulonglong>(m_ImageCount)));
}
/// <summary>
/// Action to take when rendering an image completes.
/// </summary>
/// <param name="ember">The ember currently being rendered</param>
/// <param name="stats">The renderer stats</param>
/// <param name="pixels">The timer which was started at the beginning of the render</param>
template<typename T>
void FinalRenderEmberController<T>::RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer)
{
m_ProgressCs.Enter();
string renderTimeString = renderTimer.Format(renderTimer.Toc()), totalTimeString;
QString status, filename = ComposePath(QString::fromStdString(ember.m_Name));
QString itersString = ToString<qulonglong>(m_Stats.m_Iters);
QString itersPerSecString = ToString<qulonglong>(size_t(m_Stats.m_Iters / (m_Stats.m_IterMs / 1000.0)));
//Save whatever options were specified on the GUI to the settings.
m_Settings->FinalEarlyClip(m_GuiState.m_EarlyClip);
m_Settings->FinalYAxisUp(m_GuiState.m_YAxisUp);
m_Settings->FinalTransparency(m_GuiState.m_Transparency);
m_Settings->FinalOpenCL(m_GuiState.m_OpenCL);
m_Settings->FinalDouble(m_GuiState.m_Double);
m_Settings->FinalPlatformIndex(m_GuiState.m_PlatformIndex);
m_Settings->FinalDeviceIndex(m_GuiState.m_DeviceIndex);
m_Settings->FinalSaveXml(m_GuiState.m_SaveXml);
m_Settings->FinalDoAll(m_GuiState.m_DoAll);
m_Settings->FinalDoSequence(m_GuiState.m_DoSequence);
m_Settings->FinalKeepAspect(m_GuiState.m_KeepAspect);
m_Settings->FinalScale(m_GuiState.m_Scale);
m_Settings->FinalExt(m_GuiState.m_Ext);
m_Settings->FinalThreadCount(m_GuiState.m_ThreadCount);
m_Settings->FinalThreadPriority(m_GuiState.m_ThreadPriority);
m_Settings->FinalQuality(m_GuiState.m_Quality);
m_Settings->FinalTemporalSamples(m_GuiState.m_TemporalSamples);
m_Settings->FinalSupersample(m_GuiState.m_Supersample);
m_Settings->FinalStrips(m_GuiState.m_Strips);
SaveCurrentRender(filename, false);//Don't pull from the card, the rendering process already did it.
QString itersString = ToString<qulonglong>(stats.m_Iters);
QString itersPerSecString = ToString<qulonglong>(size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)));
if (m_GuiState.m_SaveXml)
{
QFileInfo xmlFileInfo(filename);//Create another one in case it was modified for batch rendering.
@ -692,31 +885,38 @@ void FinalRenderEmberController<T>::RenderComplete(Ember<T>& ember)
xmlFreeDoc(tempEdit);
}
m_FinishedImageCount++;
//In a thread if animating, so don't set to complete because it'll be out of sync with the rest of the progress bars.
if (!m_GuiState.m_DoSequence)
{
SetProgressComplete(100);//Just to be safe.
}
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTotalProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, int((float(m_FinishedImageCount) / float(m_ImageCount)) * 100)));
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderImageCountLabel, "setText", Qt::QueuedConnection, Q_ARG(const QString&, ToString(m_FinishedImageCount) + " / " + ToString(m_ImageCount)));
status = "Pure render time: " + QString::fromStdString(renderTimeString);
Output(status);
totalTimeString = m_RenderTimer.Format(m_RenderTimer.Toc());
totalTimeString = renderTimer.Format(renderTimer.Toc());
status = "Total time: " + QString::fromStdString(totalTimeString) + "\nTotal iters: " + itersString + "\nIters/second: " + itersPerSecString + "\n";
Output(status);
QMetaObject::invokeMethod(m_FinalRenderDialog, "MoveCursorToEnd", Qt::QueuedConnection);
if (m_FinishedImageCount != m_ImageCount)
if (m_FinishedImageCount.load() == m_ImageCount)//Finished, save whatever options were specified on the GUI to the settings.
{
ResetProgress(false);
m_Settings->FinalEarlyClip(m_GuiState.m_EarlyClip);
m_Settings->FinalYAxisUp(m_GuiState.m_YAxisUp);
m_Settings->FinalTransparency(m_GuiState.m_Transparency);
m_Settings->FinalOpenCL(m_GuiState.m_OpenCL);
m_Settings->FinalDouble(m_GuiState.m_Double);
m_Settings->FinalDevices(m_GuiState.m_Devices);
m_Settings->FinalSaveXml(m_GuiState.m_SaveXml);
m_Settings->FinalDoAll(m_GuiState.m_DoAll);
m_Settings->FinalDoSequence(m_GuiState.m_DoSequence);
m_Settings->FinalKeepAspect(m_GuiState.m_KeepAspect);
m_Settings->FinalScale(m_GuiState.m_Scale);
m_Settings->FinalExt(m_GuiState.m_Ext);
m_Settings->FinalThreadCount(m_GuiState.m_ThreadCount);
m_Settings->FinalThreadPriority(m_GuiState.m_ThreadPriority);
m_Settings->FinalQuality(m_GuiState.m_Quality);
m_Settings->FinalTemporalSamples(m_GuiState.m_TemporalSamples);
m_Settings->FinalSupersample(m_GuiState.m_Supersample);
m_Settings->FinalStrips(m_GuiState.m_Strips);
}
QMetaObject::invokeMethod(m_FinalRenderDialog->ui.FinalRenderTextOutput, "update", Qt::QueuedConnection);
m_ProgressCs.Leave();
}
/// <summary>

View File

@ -35,8 +35,7 @@ struct FinalRenderGuiState
QString m_Ext;
QString m_Prefix;
QString m_Suffix;
uint m_PlatformIndex;
uint m_DeviceIndex;
QList<QVariant> m_Devices;
uint m_ThreadCount;
int m_ThreadPriority;
double m_WidthScale;
@ -68,16 +67,16 @@ public:
virtual tuple<size_t, size_t, size_t> SyncAndComputeMemory() { return tuple<size_t, size_t, size_t>(0, 0, 0); }
virtual double OriginalAspect() { return 1; }
virtual QString ComposePath(const QString& name) { return ""; }
virtual void CancelRender() { }
void CancelRender();
bool CreateRendererFromGUI();
void Output(const QString& s);
protected:
bool m_Run;
bool m_PreviewRun;
uint m_ImageCount;
uint m_FinishedImageCount;
size_t m_ImageCount;
std::atomic<size_t> m_FinishedImageCount;
QFuture<void> m_Result;
QFuture<void> m_FinalPreviewResult;
@ -87,15 +86,14 @@ protected:
FractoriumSettings* m_Settings;
FractoriumFinalRenderDialog* m_FinalRenderDialog;
FinalRenderGuiState m_GuiState;
OpenCLWrapper m_Wrapper;
CriticalSection m_PreviewCs;
CriticalSection m_PreviewCs, m_ProgressCs;
Timing m_RenderTimer;
Timing m_TotalTimer;
};
/// <summary>
/// Templated derived class which implements all interaction functionality between the embers
/// of a specific template type and the final render dialog;
/// of a specific template type and the final render dialog.
/// </summary>
template<typename T>
class FinalRenderEmberController : public FinalRenderEmberControllerBase
@ -113,7 +111,7 @@ public:
#endif
virtual void SetEmber(size_t index) override;
virtual bool Render() override;
virtual bool CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared = true) override;
virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared = true) override;
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
virtual size_t Index() const override { return m_Ember->m_Index; }
virtual uint SizeOfT() const override { return sizeof(T); }
@ -127,11 +125,20 @@ public:
virtual double OriginalAspect() override { return double(m_Ember->m_OrigFinalRasW) / m_Ember->m_OrigFinalRasH; }
virtual QString Name() const override { return QString::fromStdString(m_Ember->m_Name); }
virtual QString ComposePath(const QString& name) override;
virtual void CancelRender() override;
//Non Virtual functions.
EmberNs::Renderer<T, float>* FirstOrDefaultRenderer();
protected:
void CancelPreviewRender();
void HandleFinishedProgress();
void SaveCurrentRender(Ember<T>& ember);
void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc);
void RenderComplete(Ember<T>& ember);
void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer);
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0);
bool SyncGuiToRenderer();
void SetProgressComplete(int val);
Ember<T>* m_Ember;
@ -139,5 +146,6 @@ protected:
EmberFile<T> m_EmberFile;
EmberToXml<T> m_XmlWriter;
unique_ptr<EmberNs::Renderer<T, float>> m_FinalPreviewRenderer;
vector<unique_ptr<EmberNs::Renderer<T, float>>> m_Renderers;
};

View File

@ -14,7 +14,8 @@
/// </summary>
/// <param name="p">The parent widget of this item</param>
Fractorium::Fractorium(QWidget* p)
: QMainWindow(p)
: QMainWindow(p),
m_Info(OpenCLInfo::Instance())
{
int spinHeight = 20, iconSize_ = 9;
size_t i = 0;
@ -58,9 +59,6 @@ Fractorium::Fractorium(QWidget* p)
const QRect screen = QApplication::desktop()->screenGeometry();
m_AboutDialog->move(screen.center() - m_AboutDialog->rect().center());
//The options dialog should be a fixed size without a size grip, however even if it's here, it still shows up. Perhaps Qt will fix it some day.
m_OptionsDialog->layout()->setSizeConstraint(QLayout::SetFixedSize);
m_OptionsDialog->setSizeGripEnabled(false);
connect(m_ColorDialog, SIGNAL(colorSelected(const QColor&)), this, SLOT(OnColorSelected(const QColor&)), Qt::QueuedConnection);
m_XformComboColors[i++] = QColor(0XFF, 0X00, 0X00);
@ -119,8 +117,8 @@ Fractorium::Fractorium(QWidget* p)
m_Controller->SetupVariationTree();
m_Controller->FilteredVariations();
if (m_Wrapper.CheckOpenCL() && m_Settings->OpenCL() && m_QualitySpin->value() < 30)
m_QualitySpin->setValue(30);
if (m_Info.Ok() && m_Settings->OpenCL() && m_QualitySpin->value() < (30 * m_Settings->Devices().size()))
m_QualitySpin->setValue(30 * m_Settings->Devices().size());
int statusBarHeight = 20 * devicePixelRatio();
ui.statusBar->setMinimumHeight(statusBarHeight);

View File

@ -369,7 +369,7 @@ private:
void ShutdownAndRecreateFromOptions();
bool CreateRendererFromOptions();
bool CreateControllerFromOptions();
//Dialogs.
QStringList SetupOpenXmlDialog();
QString SetupSaveXmlDialog(const QString& defaultFilename);
@ -495,7 +495,7 @@ private:
int m_VarSortMode;
int m_PaletteSortMode;
int m_PreviousPaletteRow;
OpenCLWrapper m_Wrapper;
OpenCLInfo& m_Info;
unique_ptr<FractoriumEmberControllerBase> m_Controller;
Ui::FractoriumClass ui;
};

View File

@ -185,4 +185,175 @@ static intmax_t IsXformLinked(Ember<T>& ember, Xform<T>* xform)
}
return linked;
}
}
/// <summary>
/// Convert the passed in QList<QVariant> of absolute device indices to a vector<pair<size_t, size_t>> of platform,device
/// index pairs.
/// </summary>
/// <param name="selectedDevices">The absolute device indices</param>
/// <returns>The converted device vector of platform,device index pairs</returns>
static vector<pair<size_t, size_t>> Devices(const QList<QVariant>& selectedDevices)
{
vector<pair<size_t, size_t>> vec;
OpenCLInfo& info = OpenCLInfo::Instance();
auto& devices = info.DeviceIndices();
vec.reserve(selectedDevices.size());
for (size_t i = 0; i < selectedDevices.size(); i++)
{
auto index = selectedDevices[i].toUInt();
if (index < devices.size())
vec.push_back(devices[index]);
}
return vec;
}
/// <summary>
/// Setup a table showing all available OpenCL devices on the system.
/// Create checkboxes and radio buttons which allow the user to specify
/// which devices to use, and which one to make the primary device.
/// Used in the options dialog and the final render dialog.
/// </summary>
/// <param name="table">The QTableWidget to setup</param>
/// <param name="settingsDevices">The absolute indices of the devices to use, with the first being the primary.</param>
static void SetupDeviceTable(QTableWidget* table, const QList<QVariant>& settingsDevices)
{
bool primary = false;
auto& deviceNames = OpenCLInfo::Instance().AllDeviceNames();
table->clearContents();
table->setRowCount(deviceNames.size());
for (size_t i = 0; i < deviceNames.size(); i++)
{
auto checkItem = new QTableWidgetItem();
auto radio = new QRadioButton();
auto deviceItem = new QTableWidgetItem(QString::fromStdString(deviceNames[i]));
table->setItem(i, 0, checkItem);
table->setCellWidget(i, 1, radio);
table->setItem(i, 2, deviceItem);
if (settingsDevices.contains(QVariant::fromValue(i)))
{
checkItem->setCheckState(Qt::Checked);
if (!primary)
{
radio->setChecked(true);
primary = true;
}
}
else
checkItem->setCheckState(Qt::Unchecked);
}
if (!primary && table->rowCount() > 0)//Primary was never set, so just default to the first device and hope it was the one detected as the main display.
{
table->item(0, 0)->setCheckState(Qt::Checked);
qobject_cast<QRadioButton*>(table->cellWidget(0, 1))->setChecked(true);
}
}
/// <summary>
/// Copy the passed in selected absolute device indices to the controls on the passed in table.
/// Used in the options dialog and the final render dialog.
/// </summary>
/// <param name="table">The QTableWidget to copy values to</param>
/// <param name="settingsDevices">The absolute indices of the devices to use, with the first being the primary.</param>
static void SettingsToDeviceTable(QTableWidget* table, QList<QVariant>& settingsDevices)
{
if (settingsDevices.empty() && table->rowCount() > 0)
{
table->item(0, 0)->setCheckState(Qt::Checked);
qobject_cast<QRadioButton*>(table->cellWidget(0, 1))->setChecked(true);
for (int row = 1; row < table->rowCount(); row++)
if (auto item = table->item(row, 0))
item->setCheckState(Qt::Unchecked);
}
else
{
for (int row = 0; row < table->rowCount(); row++)
{
if (auto item = table->item(row, 0))
{
if (settingsDevices.contains(row))
{
item->setCheckState(Qt::Checked);
if (!settingsDevices.indexOf(QVariant::fromValue(row)))
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1)))
radio->setChecked(true);
}
else
{
item->setCheckState(Qt::Unchecked);
}
}
}
}
}
/// <summary>
/// Copy the values of the controls on the passed in table to a list of absolute device indices.
/// Used in the options dialog and the final render dialog.
/// </summary>
/// <param name="table">The QTableWidget to copy values from</param>
/// <returns>The list of absolute device indices</returns>
static QList<QVariant> DeviceTableToSettings(QTableWidget* table)
{
QList<QVariant> devices;
auto rows = table->rowCount();
for (int row = 0; row < rows; row++)
{
auto checkItem = table->item(row, 0);
auto radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1));
if (checkItem->checkState() == Qt::Checked)
{
if (radio && radio->isChecked())
devices.push_front(row);
else
devices.push_back(row);
}
}
return devices;
}
/// <summary>
/// Ensure device selection on the passed in table make sense.
/// </summary>
/// <param name="table">The QTableWidget to setup</param>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
static void HandleDeviceTableCheckChanged(QTableWidget* table, int row, int col)
{
int primaryRow = -1;
QRadioButton* primaryRadio = nullptr;
for (int i = 0; i < table->rowCount(); i++)
{
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(i, 1)))
{
if (radio->isChecked())
{
primaryRow = i;
primaryRadio = radio;
break;
}
}
}
if (primaryRow == -1) primaryRow = 0;
if (auto primaryItem = table->item(primaryRow, 0))
if (primaryItem->checkState() == Qt::Unchecked)
primaryItem->setCheckState(Qt::Checked);
}

View File

@ -11,19 +11,17 @@
/// </summary>
/// <param name="fractorium">Pointer to the main window.</param>
FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractorium)
: m_Info(OpenCLInfo::Instance())
{
Timing t;
m_Rendering = false;
m_Shared = true;
m_Platform = 0;
m_Device = 0;
m_FailedRenders = 0;
m_UndoIndex = 0;
m_RenderType = CPU_RENDERER;
m_OutputTexID = 0;
m_SubBatchCount = 1;//Will be ovewritten by the options on first render.
m_FinalImageIndex = 0;
m_Fractorium = fractorium;
m_RenderTimer = nullptr;
m_RenderRestartTimer = nullptr;

View File

@ -72,16 +72,16 @@ public:
virtual size_t TotalXformCount() const { return 0; }
virtual QString Name() const { return ""; }
virtual void Name(const string& s) { }
virtual uint FinalRasW() const { return 0; }
virtual void FinalRasW(uint w) { }
virtual uint FinalRasH() const { return 0; }
virtual void FinalRasH(uint h) { }
virtual size_t FinalRasW() const { return 0; }
virtual void FinalRasW(size_t w) { }
virtual size_t FinalRasH() const { return 0; }
virtual void FinalRasH(size_t h) { }
virtual size_t Index() const { return 0; }
virtual void AddSymmetry(int sym, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) { }
virtual void CalcNormalizedWeights() { }
//Menu.
virtual void NewFlock(uint count) { }//File.
virtual void NewFlock(size_t count) { }//File.
virtual void NewEmptyFlameInCurrentFile() { }
virtual void NewRandomFlameInCurrentFile() { }
virtual void CopyFlameInCurrentFile() { }
@ -209,7 +209,7 @@ public:
//Rendering/progress.
virtual bool Render() { return false; }
virtual bool CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared = true) { return false; }
virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared = true) { return false; }
virtual uint SizeOfT() const { return 0; }
virtual void ClearUndo() { }
virtual GLEmberControllerBase* GLController() { return nullptr; }
@ -221,10 +221,11 @@ public:
void Shutdown();
void UpdateRender(eProcessAction action = FULL_RENDER);
void DeleteRenderer();
void SaveCurrentRender(const QString& filename, bool forcePull);
void SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc);
RendererBase* Renderer() { return m_Renderer.get(); }
vector<byte>* FinalImage() { return &(m_FinalImage[m_FinalImageIndex]); }
vector<byte>* FinalImage() { return &(m_FinalImage); }
vector<byte>* PreviewFinalImage() { return &m_PreviewFinalImage; }
EmberStats Stats() { return m_Stats; }
protected:
//Rendering/progress.
@ -236,9 +237,7 @@ protected:
bool m_Rendering;
bool m_Shared;
bool m_LastEditWasUndoRedo;
uint m_FinalImageIndex;
uint m_Platform;
uint m_Device;
vector<pair<size_t, size_t>> m_Devices;
uint m_SubBatchCount;
uint m_FailedRenders;
uint m_UndoIndex;
@ -253,7 +252,7 @@ protected:
string m_CurrentPaletteFilePath;
CriticalSection m_Cs;
std::thread m_WriteThread;
vector<byte> m_FinalImage[2];
vector<byte> m_FinalImage;
vector<byte> m_PreviewFinalImage;
vector<eProcessAction> m_ProcessActions;
vector<eVariationId> m_FilteredVariations;
@ -262,6 +261,7 @@ protected:
Fractorium* m_Fractorium;
QTimer* m_RenderTimer;
QTimer* m_RenderRestartTimer;
OpenCLInfo& m_Info;
};
/// <summary>
@ -306,10 +306,10 @@ public:
virtual size_t TotalXformCount() const override { return m_Ember.TotalXformCount(); }
virtual QString Name() const override { return QString::fromStdString(m_Ember.m_Name); }
virtual void Name(const string& s) override { m_Ember.m_Name = s; }
virtual uint FinalRasW() const override { return m_Ember.m_FinalRasW; }
virtual void FinalRasW(uint w) override { m_Ember.m_FinalRasW = w; }
virtual uint FinalRasH() const override { return m_Ember.m_FinalRasH; }
virtual void FinalRasH(uint h) override { m_Ember.m_FinalRasH = h; }
virtual size_t FinalRasW() const override { return m_Ember.m_FinalRasW; }
virtual void FinalRasW(size_t w) override { m_Ember.m_FinalRasW = w; }
virtual size_t FinalRasH() const override { return m_Ember.m_FinalRasH; }
virtual void FinalRasH(size_t h) override { m_Ember.m_FinalRasH = h; }
virtual size_t Index() const override { return m_Ember.m_Index; }
virtual void AddSymmetry(int sym, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override { m_Ember.AddSymmetry(sym, rand); }
virtual void CalcNormalizedWeights() override { m_Ember.CalcNormalizedWeights(m_NormalizedWeights); }
@ -317,7 +317,7 @@ public:
Ember<T>* CurrentEmber();
//Menu.
virtual void NewFlock(uint count) override;
virtual void NewFlock(size_t count) override;
virtual void NewEmptyFlameInCurrentFile() override;
virtual void NewRandomFlameInCurrentFile() override;
virtual void CopyFlameInCurrentFile() override;
@ -447,7 +447,7 @@ public:
//Rendering/progress.
virtual bool Render() override;
virtual bool CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared = true) override;
virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared = true) override;
virtual uint SizeOfT() const override { return sizeof(T); }
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
virtual void ClearUndo() override;

View File

@ -33,7 +33,7 @@ void Fractorium::InitInfoUI()
/// <param name="newSize">Ignored</param>
void Fractorium::OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize)
{
QPixmap pixmap = QPixmap::fromImage(m_Controller->FinalPaletteImage());//Create a QPixmap out of the QImage.
QPixmap pixmap = QPixmap::fromImage(m_Controller->FinalPaletteImage());//Create a QPixmap out of the QImage, will be empty on startup.
SetPaletteTableItem(&pixmap, ui.SummaryTableWidget, m_InfoPaletteItem, 1, 0);
}

View File

@ -240,7 +240,7 @@ void FractoriumEmberController<T>::EmberTreeItemChanged(QTreeWidgetItem* item, i
}
}
}
catch(std::exception& e)
catch(const std::exception& e)
{
qDebug() << "FractoriumEmberController<T>::EmberTreeItemChanged() : Exception thrown: " << e.what();
}

View File

@ -19,13 +19,13 @@ void Fractorium::InitMenusUI()
connect(ui.ActionExit, SIGNAL(triggered(bool)), this, SLOT(OnActionExit(bool)), Qt::QueuedConnection);
//Edit menu.
connect(ui.ActionUndo, SIGNAL(triggered(bool)), this, SLOT(OnActionUndo(bool)), Qt::QueuedConnection);
connect(ui.ActionRedo, SIGNAL(triggered(bool)), this, SLOT(OnActionRedo(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyXml(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyAllXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyAllXml(bool)), Qt::QueuedConnection);
connect(ui.ActionPasteXmlAppend, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlAppend(bool)), Qt::QueuedConnection);
connect(ui.ActionPasteXmlOver, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(bool)), Qt::QueuedConnection);
connect(ui.ActionCopySelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionCopySelectedXforms(bool)), Qt::QueuedConnection);
connect(ui.ActionUndo, SIGNAL(triggered(bool)), this, SLOT(OnActionUndo(bool)), Qt::QueuedConnection);
connect(ui.ActionRedo, SIGNAL(triggered(bool)), this, SLOT(OnActionRedo(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyXml(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyAllXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyAllXml(bool)), Qt::QueuedConnection);
connect(ui.ActionPasteXmlAppend, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlAppend(bool)), Qt::QueuedConnection);
connect(ui.ActionPasteXmlOver, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(bool)), Qt::QueuedConnection);
connect(ui.ActionCopySelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionCopySelectedXforms(bool)), Qt::QueuedConnection);
connect(ui.ActionPasteSelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteSelectedXforms(bool)), Qt::QueuedConnection);
ui.ActionPasteSelectedXforms->setEnabled(false);
@ -54,7 +54,7 @@ void Fractorium::InitMenusUI()
/// </summary>
/// <param name="count">The number of embers to include in the flock</param>
template <typename T>
void FractoriumEmberController<T>::NewFlock(uint count)
void FractoriumEmberController<T>::NewFlock(size_t count)
{
Ember<T> ember;
@ -63,12 +63,12 @@ void FractoriumEmberController<T>::NewFlock(uint count)
m_EmberFile.m_Embers.reserve(count);
m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename();
for (uint i = 0; i < count; i++)
for (size_t i = 0; i < count; i++)
{
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0);
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<intmax_t>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0);
ParamsToEmber(ember);
ember.m_Index = i;
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "-" + ToString(i + 1).toStdString();
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "-" + ToString(i + 1ULL).toStdString();
m_EmberFile.m_Embers.push_back(ember);
}
@ -353,8 +353,22 @@ void Fractorium::OnActionSaveEntireFileAsXml(bool checked) { m_Controller->SaveE
void Fractorium::OnActionSaveCurrentScreen(bool checked)
{
QString filename = SetupSaveImageDialog(m_Controller->Name());
auto renderer = m_Controller->Renderer();
auto& pixels = *m_Controller->FinalImage();
RendererCLBase* rendererCL = dynamic_cast<RendererCLBase*>(m_Controller->Renderer());
auto stats = m_Controller->Stats();
EmberImageComments comments = renderer->ImageComments(stats, 0, false, true);
m_Controller->SaveCurrentRender(filename, true);
if (rendererCL && renderer->PrepFinalAccumVector(pixels))
{
if (!rendererCL->ReadFinal(pixels.data()))
{
ShowCritical("GPU Read Error", "Could not read image from the GPU, aborting image save.", false);
return;
}
}
m_Controller->SaveCurrentRender(filename, comments, pixels, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels(), renderer->BytesPerChannel());
}
/// <summary>

View File

@ -341,7 +341,7 @@ void Fractorium::OnPaletteFilterLineEditTextChanged(const QString& text)
table->setUpdatesEnabled(false);
for (uint i = 0; i < uint(table->rowCount()); i++)
for (int i = 0; i < table->rowCount(); i++)
{
if (auto item = table->item(i, 0))
{

View File

@ -78,8 +78,7 @@ void FractoriumEmberControllerBase::Shutdown()
/// </summary>
void FractoriumEmberControllerBase::ClearFinalImages()
{
Memset(m_FinalImage[0]);
Memset(m_FinalImage[1]);
Memset(m_FinalImage);
//Unsure if we should also call RendererCL::ClearFinal() as well. At the moment it seems unnecessary.
}
@ -114,48 +113,42 @@ void FractoriumEmberControllerBase::DeleteRenderer()
/// <summary>
/// Save the current render results to a file.
/// This could benefit from QImageWriter, however it's compression capabilities are
/// This could benefit from QImageWriter, however its compression capabilities are
/// severely lacking. A Png file comes out larger than a bitmap, so instead use the
/// Png and Jpg wrapper functions from the command line programs.
/// This will embed the id, url and nick fields from the options in the image comments.
/// </summary>
/// <param name="filename">The full path and filename</param>
void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, bool forcePull)
/// <param name="comments">The comments to save in the png or jpg</param>
/// <param name="pixels">The buffer containing the pixels</param>
/// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param>
/// <param name="channels">The number of channels, 3 or 4.</param>
/// <param name="bpc">The bytes per channel, almost always 1.</param>
void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc)
{
if (filename != "")
{
bool b = false;
uint i, j;
uint width = m_Renderer->FinalRasW();
uint height = m_Renderer->FinalRasH();
byte* data = nullptr;
vector<byte> vecRgb;
QFileInfo fileInfo(filename);
QString suffix = fileInfo.suffix();
FractoriumSettings* settings = m_Fractorium->m_Settings;
RendererCLBase* rendererCL = dynamic_cast<RendererCLBase*>(m_Renderer.get());
if (forcePull && rendererCL && m_Renderer->PrepFinalAccumVector(m_FinalImage[m_FinalImageIndex]))
{
if (!rendererCL->ReadFinal(m_FinalImage[m_FinalImageIndex].data()))
{
m_Fractorium->ShowCritical("GPU Read Error", "Could not read image from the GPU, aborting image save.", true);
return;
}
}
//Ensure dimensions are valid.
if (m_FinalImage[m_FinalImageIndex].size() < (width * height * m_Renderer->NumChannels() * m_Renderer->BytesPerChannel()))
if (pixels.size() < (width * height * channels * bpc))
{
m_Fractorium->ShowCritical("Save Failed", "Dimensions didn't match, not saving.", true);
return;
}
data = m_FinalImage[m_FinalImageIndex].data();//Png and channels == 4.
data = pixels.data();//Png and channels == 4.
if ((suffix == "jpg" || suffix == "bmp") && m_Renderer->NumChannels() == 4)
if ((suffix == "jpg" || suffix == "bmp") && channels)
{
RgbaToRgb(m_FinalImage[m_FinalImageIndex], vecRgb, width, height);
RgbaToRgb(pixels, vecRgb, width, height);
data = vecRgb.data();
}
@ -164,7 +157,6 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, b
string id = settings->Id().toStdString();
string url = settings->Url().toStdString();
string nick = settings->Nick().toStdString();
EmberImageComments comments = m_Renderer->ImageComments(m_Stats, 0, false, true);
if (suffix == "png")
b = WritePng(s.c_str(), data, width, height, 1, true, comments, id, url, nick);
@ -369,14 +361,14 @@ bool FractoriumEmberController<T>::Render()
if (ProcessState() != ACCUM_DONE)
{
//if (m_Renderer->Run(m_FinalImage, 0) == RENDER_OK)//Full, non-incremental render for debugging.
if (m_Renderer->Run(m_FinalImage[m_FinalImageIndex], 0, m_SubBatchCount, (iterBegin || m_Fractorium->m_Settings->ContinuousUpdate())) == RENDER_OK)//Force output on iterBegin or if the settings specify to always do it.
if (m_Renderer->Run(m_FinalImage, 0, m_SubBatchCount, (iterBegin || m_Fractorium->m_Settings->ContinuousUpdate())) == RENDER_OK)//Force output on iterBegin or if the settings specify to always do it.
{
//The amount to increment sub batch while rendering proceeds is purely empirical.
//Change later if better values can be derived/observed.
if (m_Renderer->RendererType() == OPENCL_RENDERER)
{
if (m_SubBatchCount < 3)//More than 3 with OpenCL gives a sluggish UI.
m_SubBatchCount++;
if (m_SubBatchCount < (4 * m_Devices.size()))//More than 3 with OpenCL gives a sluggish UI.
m_SubBatchCount += m_Devices.size();
}
else
{
@ -445,7 +437,7 @@ bool FractoriumEmberController<T>::Render()
//Update it on finish because the rendering process is completely done.
if (iterBegin || ProcessState() == ACCUM_DONE)
{
if (m_FinalImage[m_FinalImageIndex].size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed.
if (m_FinalImage.size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed.
gl->update();
//gl->repaint();
@ -504,19 +496,17 @@ bool FractoriumEmberController<T>::Render()
/// Rendering will be left in a stopped state. The caller is responsible for restarting the render loop again.
/// </summary>
/// <param name="renderType">The type of render to create</param>
/// <param name="platform">The index platform of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="outputTexID">The texture ID of the shared OpenGL texture if shared</param>
/// <param name="devices">The platform,device index pairs of the devices to use</param>
/// <param name="shared">True if shared with OpenGL, else false. Default: true.</param>
/// <returns>True if nothing went wrong, else false.</returns>
template <typename T>
bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, uint platform, uint device, bool shared)
bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared)
{
bool ok = true;
FractoriumSettings* s = m_Fractorium->m_Settings;
GLWidget* gl = m_Fractorium->ui.GLDisplay;
if (!m_Renderer.get() || (m_Renderer->RendererType() != renderType) || (m_Platform != platform) || (m_Device != device))
if (!m_Renderer.get() || (m_Renderer->RendererType() != renderType) || !Equal(m_Devices, devices))
{
EmberReport emberReport;
vector<string> errorReport;
@ -524,13 +514,12 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, uint
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<EmberNs::RendererBase>(::CreateRenderer<T, float>(renderType, platform, device, shared, gl->OutputTexID(), emberReport));//Always make bucket type float.
m_Renderer = unique_ptr<EmberNs::RendererBase>(::CreateRenderer<T>(renderType, devices, shared, gl->OutputTexID(), emberReport));//Always make bucket type float.
errorReport = emberReport.ErrorReport();
if (errorReport.empty())
{
m_Platform = platform;//Store values for re-creation later on.
m_Device = device;
m_Devices = devices;
m_OutputTexID = gl->OutputTexID();
m_Shared = shared;
}
@ -548,11 +537,13 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, uint
if (m_RenderType == OPENCL_RENDERER)
{
m_Fractorium->m_QualitySpin->DoubleClickZero(30);
m_Fractorium->m_QualitySpin->DoubleClickNonZero(30);
auto val = 30 * m_Fractorium->m_Settings->Devices().size();
if (m_Fractorium->m_QualitySpin->value() < 30)
m_Fractorium->m_QualitySpin->setValue(30);
m_Fractorium->m_QualitySpin->DoubleClickZero(val);
m_Fractorium->m_QualitySpin->DoubleClickNonZero(val);
if (m_Fractorium->m_QualitySpin->value() < val)
m_Fractorium->m_QualitySpin->setValue(val);
}
else
{
@ -618,12 +609,11 @@ void Fractorium::ShutdownAndRecreateFromOptions()
bool Fractorium::CreateRendererFromOptions()
{
bool ok = true;
bool useOpenCL = m_Wrapper.CheckOpenCL() && m_Settings->OpenCL();
bool useOpenCL = m_Info.Ok() && m_Settings->OpenCL();
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 ? OPENCL_RENDERER : CPU_RENDERER,
m_Settings->PlatformIndex(),
m_Settings->DeviceIndex()))
if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? OPENCL_RENDERER : CPU_RENDERER, v))
{
//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.");

View File

@ -21,7 +21,7 @@ void FractoriumSettings::EnsureDefaults()
FinalQuality(1000);
if (FinalTemporalSamples() == 0)
FinalTemporalSamples(1000);
FinalTemporalSamples(100);
if (FinalSupersample() == 0)
FinalSupersample(2);
@ -30,7 +30,7 @@ void FractoriumSettings::EnsureDefaults()
FinalStrips(1);
if (XmlTemporalSamples() == 0)
XmlTemporalSamples(1000);
XmlTemporalSamples(100);
if (XmlQuality() == 0)
XmlQuality(1000);
@ -44,7 +44,7 @@ void FractoriumSettings::EnsureDefaults()
if (FinalThreadCount() == 0 || FinalThreadCount() > Timing::ProcessorCount())
FinalThreadCount(Timing::ProcessorCount());
FinalThreadPriority(Clamp<int>((int)eThreadPriority::LOWEST, (int)eThreadPriority::HIGHEST, FinalThreadPriority()));
FinalThreadPriority(Clamp<int>(FinalThreadPriority(), (int)eThreadPriority::LOWEST, (int)eThreadPriority::HIGHEST));
if (CpuSubBatch() < 1)
CpuSubBatch(1);
@ -52,24 +52,9 @@ void FractoriumSettings::EnsureDefaults()
if (OpenCLSubBatch() < 1)
OpenCLSubBatch(1);
//There normally wouldn't be any more than 10 OpenCL platforms and devices
//on the system, so if a value greater than that is read, then the settings file
//was corrupted.
if (PlatformIndex() > 10)
PlatformIndex(0);
if (DeviceIndex() > 10)
DeviceIndex(0);
if (FinalScale() > SCALE_HEIGHT)
FinalScale(0);
if (FinalPlatformIndex() > 10)
FinalPlatformIndex(0);
if (FinalDeviceIndex() > 10)
FinalDeviceIndex(0);
if (OpenXmlExt() == "")
OpenXmlExt("Flame (*.flame)");
@ -101,155 +86,149 @@ void FractoriumSettings::EnsureDefaults()
/// Interactive renderer settings.
/// </summary>
bool FractoriumSettings::EarlyClip() { return value(EARLYCLIP).toBool(); }
void FractoriumSettings::EarlyClip(bool b) { setValue(EARLYCLIP, b); }
bool FractoriumSettings::EarlyClip() { return value(EARLYCLIP).toBool(); }
void FractoriumSettings::EarlyClip(bool b) { setValue(EARLYCLIP, b); }
bool FractoriumSettings::YAxisUp() { return value(YAXISUP).toBool(); }
void FractoriumSettings::YAxisUp(bool b) { setValue(YAXISUP, b); }
bool FractoriumSettings::Transparency() { return value(TRANSPARENCY).toBool(); }
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::Double() { return value(DOUBLEPRECISION).toBool(); }
void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b); }
bool FractoriumSettings::ShowAllXforms() { return value(SHOWALLXFORMS).toBool(); }
void FractoriumSettings::ShowAllXforms(bool b) { setValue(SHOWALLXFORMS, b); }
bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); }
void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); }
bool FractoriumSettings::YAxisUp() { return value(YAXISUP).toBool(); }
void FractoriumSettings::YAxisUp(bool b) { setValue(YAXISUP, b); }
QList<QVariant> FractoriumSettings::Devices() { return value(DEVICES).toList(); }
void FractoriumSettings::Devices(const QList<QVariant>& devices) { setValue(DEVICES, devices); }
bool FractoriumSettings::Transparency() { return value(TRANSPARENCY).toBool(); }
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::Double() { return value(DOUBLEPRECISION).toBool(); }
void FractoriumSettings::Double(bool b) { setValue(DOUBLEPRECISION, b); }
bool FractoriumSettings::ShowAllXforms() { return value(SHOWALLXFORMS).toBool(); }
void FractoriumSettings::ShowAllXforms(bool b) { setValue(SHOWALLXFORMS, b); }
bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); }
void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); }
uint FractoriumSettings::PlatformIndex() { return value(PLATFORMINDEX).toUInt(); }
void FractoriumSettings::PlatformIndex(uint i) { setValue(PLATFORMINDEX, i); }
uint FractoriumSettings::DeviceIndex() { return value(DEVICEINDEX).toUInt(); }
void FractoriumSettings::DeviceIndex(uint i) { setValue(DEVICEINDEX, i); }
uint FractoriumSettings::ThreadCount() { return value(THREADCOUNT).toUInt(); }
void FractoriumSettings::ThreadCount(uint i) { setValue(THREADCOUNT, i); }
bool FractoriumSettings::CpuDEFilter() { return value(CPUDEFILTER).toBool(); }
void FractoriumSettings::CpuDEFilter(bool b) { setValue(CPUDEFILTER, b); }
bool FractoriumSettings::OpenCLDEFilter() { return value(OPENCLDEFILTER).toBool(); }
void FractoriumSettings::OpenCLDEFilter(bool b) { setValue(OPENCLDEFILTER, b); }
uint FractoriumSettings::CpuSubBatch() { return value(CPUSUBBATCH).toUInt(); }
void FractoriumSettings::CpuSubBatch(uint b) { setValue(CPUSUBBATCH, b); }
uint FractoriumSettings::OpenCLSubBatch() { return value(OPENCLSUBBATCH).toUInt(); }
void FractoriumSettings::OpenCLSubBatch(uint b) { setValue(OPENCLSUBBATCH, b); }
uint FractoriumSettings::ThreadCount() { return value(THREADCOUNT).toUInt(); }
void FractoriumSettings::ThreadCount(uint i) { setValue(THREADCOUNT, i); }
bool FractoriumSettings::CpuDEFilter() { return value(CPUDEFILTER).toBool(); }
void FractoriumSettings::CpuDEFilter(bool b) { setValue(CPUDEFILTER, b); }
bool FractoriumSettings::OpenCLDEFilter() { return value(OPENCLDEFILTER).toBool(); }
void FractoriumSettings::OpenCLDEFilter(bool b) { setValue(OPENCLDEFILTER, b); }
uint FractoriumSettings::CpuSubBatch() { return value(CPUSUBBATCH).toUInt(); }
void FractoriumSettings::CpuSubBatch(uint i) { setValue(CPUSUBBATCH, i); }
uint FractoriumSettings::OpenCLSubBatch() { return value(OPENCLSUBBATCH).toUInt(); }
void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH, i); }
/// <summary>
/// Final render settings.
/// </summary>
bool FractoriumSettings::FinalEarlyClip() { return value(FINALEARLYCLIP).toBool(); }
void FractoriumSettings::FinalEarlyClip(bool b) { setValue(FINALEARLYCLIP, b); }
bool FractoriumSettings::FinalYAxisUp() { return value(FINALYAXISUP).toBool(); }
void FractoriumSettings::FinalYAxisUp(bool b) { setValue(FINALYAXISUP, b); }
bool FractoriumSettings::FinalTransparency() { return value(FINALTRANSPARENCY).toBool(); }
void FractoriumSettings::FinalTransparency(bool b) { setValue(FINALTRANSPARENCY, b); }
bool FractoriumSettings::FinalOpenCL() { return value(FINALOPENCL).toBool(); }
void FractoriumSettings::FinalOpenCL(bool b) { setValue(FINALOPENCL, b); }
bool FractoriumSettings::FinalEarlyClip() { return value(FINALEARLYCLIP).toBool(); }
void FractoriumSettings::FinalEarlyClip(bool b) { setValue(FINALEARLYCLIP, b); }
bool FractoriumSettings::FinalYAxisUp() { return value(FINALYAXISUP).toBool(); }
void FractoriumSettings::FinalYAxisUp(bool b) { setValue(FINALYAXISUP, b); }
bool FractoriumSettings::FinalTransparency() { return value(FINALTRANSPARENCY).toBool(); }
void FractoriumSettings::FinalTransparency(bool b) { setValue(FINALTRANSPARENCY, b); }
bool FractoriumSettings::FinalOpenCL() { return value(FINALOPENCL).toBool(); }
void FractoriumSettings::FinalOpenCL(bool b) { setValue(FINALOPENCL, b); }
bool FractoriumSettings::FinalDouble() { return value(FINALDOUBLEPRECISION).toBool(); }
void FractoriumSettings::FinalDouble(bool b) { setValue(FINALDOUBLEPRECISION, b); }
bool FractoriumSettings::FinalSaveXml() { return value(FINALSAVEXML).toBool(); }
void FractoriumSettings::FinalSaveXml(bool b) { setValue(FINALSAVEXML, b); }
bool FractoriumSettings::FinalDoAll() { return value(FINALDOALL).toBool(); }
void FractoriumSettings::FinalDoAll(bool b) { setValue(FINALDOALL, b); }
bool FractoriumSettings::FinalDoSequence() { return value(FINALDOSEQUENCE).toBool(); }
void FractoriumSettings::FinalDoSequence(bool b) { setValue(FINALDOSEQUENCE, b); }
bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); }
void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); }
uint FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); }
void FractoriumSettings::FinalScale(uint i) { setValue(FINALSCALE, i); }
QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); }
void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); }
bool FractoriumSettings::FinalDouble() { return value(FINALDOUBLEPRECISION).toBool(); }
void FractoriumSettings::FinalDouble(bool b) { setValue(FINALDOUBLEPRECISION, b); }
QList<QVariant> FractoriumSettings::FinalDevices() { return value(FINALDEVICES).toList(); }
void FractoriumSettings::FinalDevices(const QList<QVariant>& devices) { setValue(FINALDEVICES, devices); }
bool FractoriumSettings::FinalSaveXml() { return value(FINALSAVEXML).toBool(); }
void FractoriumSettings::FinalSaveXml(bool b) { setValue(FINALSAVEXML, b); }
bool FractoriumSettings::FinalDoAll() { return value(FINALDOALL).toBool(); }
void FractoriumSettings::FinalDoAll(bool b) { setValue(FINALDOALL, b); }
bool FractoriumSettings::FinalDoSequence() { return value(FINALDOSEQUENCE).toBool(); }
void FractoriumSettings::FinalDoSequence(bool b) { setValue(FINALDOSEQUENCE, b); }
bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); }
void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); }
uint FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); }
void FractoriumSettings::FinalScale(uint i) { setValue(FINALSCALE, i); }
QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); }
void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); }
uint FractoriumSettings::FinalPlatformIndex() { return value(FINALPLATFORMINDEX).toUInt(); }
void FractoriumSettings::FinalPlatformIndex(uint i) { setValue(FINALPLATFORMINDEX, i); }
uint FractoriumSettings::FinalDeviceIndex() { return value(FINALDEVICEINDEX).toUInt(); }
void FractoriumSettings::FinalDeviceIndex(uint i) { setValue(FINALDEVICEINDEX, i); }
uint FractoriumSettings::FinalThreadCount() { return value(FINALTHREADCOUNT).toUInt(); }
void FractoriumSettings::FinalThreadCount(uint i) { setValue(FINALTHREADCOUNT, i); }
uint FractoriumSettings::FinalThreadPriority() { return value(FINALTHREADPRIORITY).toInt(); }
void FractoriumSettings::FinalThreadPriority(int i) { setValue(FINALTHREADPRIORITY, i); }
uint FractoriumSettings::FinalQuality() { return value(FINALQUALITY).toUInt(); }
void FractoriumSettings::FinalQuality(uint i) { setValue(FINALQUALITY, i); }
uint FractoriumSettings::FinalTemporalSamples() { return value(FINALTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::FinalTemporalSamples(uint i) { setValue(FINALTEMPORALSAMPLES, i); }
uint FractoriumSettings::FinalSupersample() { return value(FINALSUPERSAMPLE).toUInt(); }
void FractoriumSettings::FinalSupersample(uint i) { setValue(FINALSUPERSAMPLE, i); }
uint FractoriumSettings::FinalStrips() { return value(FINALSTRIPS).toUInt(); }
void FractoriumSettings::FinalStrips(uint i) { setValue(FINALSTRIPS, i); }
uint FractoriumSettings::FinalThreadCount() { return value(FINALTHREADCOUNT).toUInt(); }
void FractoriumSettings::FinalThreadCount(uint i) { setValue(FINALTHREADCOUNT, i); }
int FractoriumSettings::FinalThreadPriority() { return value(FINALTHREADPRIORITY).toInt(); }
void FractoriumSettings::FinalThreadPriority(int i) { setValue(FINALTHREADPRIORITY, i); }
uint FractoriumSettings::FinalQuality() { return value(FINALQUALITY).toUInt(); }
void FractoriumSettings::FinalQuality(uint i) { setValue(FINALQUALITY, i); }
uint FractoriumSettings::FinalTemporalSamples() { return value(FINALTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::FinalTemporalSamples(uint i) { setValue(FINALTEMPORALSAMPLES, i); }
uint FractoriumSettings::FinalSupersample() { return value(FINALSUPERSAMPLE).toUInt(); }
void FractoriumSettings::FinalSupersample(uint i) { setValue(FINALSUPERSAMPLE, i); }
uint FractoriumSettings::FinalStrips() { return value(FINALSTRIPS).toUInt(); }
void FractoriumSettings::FinalStrips(uint i) { setValue(FINALSTRIPS, i); }
/// <summary>
/// Xml file saving settings.
/// </summary>
uint FractoriumSettings::XmlTemporalSamples() { return value(XMLTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::XmlTemporalSamples(uint i) { setValue(XMLTEMPORALSAMPLES, i); }
uint FractoriumSettings::XmlTemporalSamples() { return value(XMLTEMPORALSAMPLES).toUInt(); }
void FractoriumSettings::XmlTemporalSamples(uint i) { setValue(XMLTEMPORALSAMPLES, i); }
uint FractoriumSettings::XmlQuality() { return value(XMLQUALITY).toUInt(); }
void FractoriumSettings::XmlQuality(uint i) { setValue(XMLQUALITY, i); }
uint FractoriumSettings::XmlQuality() { return value(XMLQUALITY).toUInt(); }
void FractoriumSettings::XmlQuality(uint i) { setValue(XMLQUALITY, i); }
uint FractoriumSettings::XmlSupersample() { return value(XMLSUPERSAMPLE).toUInt(); }
void FractoriumSettings::XmlSupersample(uint i) { setValue(XMLSUPERSAMPLE, i); }
uint FractoriumSettings::XmlSupersample() { return value(XMLSUPERSAMPLE).toUInt(); }
void FractoriumSettings::XmlSupersample(uint i) { setValue(XMLSUPERSAMPLE, i); }
QString FractoriumSettings::Id() { return value(IDENTITYID).toString(); }
void FractoriumSettings::Id(const QString& s) { setValue(IDENTITYID, s); }
QString FractoriumSettings::Id() { return value(IDENTITYID).toString(); }
void FractoriumSettings::Id(const QString& s) { setValue(IDENTITYID, s); }
QString FractoriumSettings::Url() { return value(IDENTITYURL).toString(); }
void FractoriumSettings::Url(const QString& s) { setValue(IDENTITYURL, s); }
QString FractoriumSettings::Url() { return value(IDENTITYURL).toString(); }
void FractoriumSettings::Url(const QString& s) { setValue(IDENTITYURL, s); }
QString FractoriumSettings::Nick() { return value(IDENTITYNICK).toString(); }
void FractoriumSettings::Nick(const QString& s) { setValue(IDENTITYNICK, s); }
QString FractoriumSettings::Nick() { return value(IDENTITYNICK).toString(); }
void FractoriumSettings::Nick(const QString& s) { setValue(IDENTITYNICK, s); }
/// <summary>
/// General operations settings.
/// </summary>
QString FractoriumSettings::OpenFolder() { return value(OPENFOLDER).toString(); }
void FractoriumSettings::OpenFolder(const QString& s) { setValue(OPENFOLDER, s); }
QString FractoriumSettings::OpenFolder() { return value(OPENFOLDER).toString(); }
void FractoriumSettings::OpenFolder(const QString& s) { setValue(OPENFOLDER, s); }
QString FractoriumSettings::SaveFolder() { return value(SAVEFOLDER).toString(); }
void FractoriumSettings::SaveFolder(const QString& s) { setValue(SAVEFOLDER, s); }
QString FractoriumSettings::SaveFolder() { return value(SAVEFOLDER).toString(); }
void FractoriumSettings::SaveFolder(const QString& s) { setValue(SAVEFOLDER, s); }
QString FractoriumSettings::OpenXmlExt() { return value(OPENXMLEXT).toString(); }
void FractoriumSettings::OpenXmlExt(const QString& s) { setValue(OPENXMLEXT, s); }
QString FractoriumSettings::OpenXmlExt() { return value(OPENXMLEXT).toString(); }
void FractoriumSettings::OpenXmlExt(const QString& s) { setValue(OPENXMLEXT, s); }
QString FractoriumSettings::SaveXmlExt() { return value(SAVEXMLEXT).toString(); }
void FractoriumSettings::SaveXmlExt(const QString& s) { setValue(SAVEXMLEXT, s); }
QString FractoriumSettings::SaveXmlExt() { return value(SAVEXMLEXT).toString(); }
void FractoriumSettings::SaveXmlExt(const QString& s) { setValue(SAVEXMLEXT, s); }
QString FractoriumSettings::OpenImageExt() { return value(OPENIMAGEEXT).toString(); }
void FractoriumSettings::OpenImageExt(const QString& s) { setValue(OPENIMAGEEXT, s); }
QString FractoriumSettings::OpenImageExt() { return value(OPENIMAGEEXT).toString(); }
void FractoriumSettings::OpenImageExt(const QString& s) { setValue(OPENIMAGEEXT, s); }
QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); }
void FractoriumSettings::SaveImageExt(const QString& s) { setValue(SAVEIMAGEEXT, s); }
QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); }
void FractoriumSettings::SaveImageExt(const QString& s) { setValue(SAVEIMAGEEXT, s); }
bool FractoriumSettings::SaveAutoUnique() { return value(AUTOUNIQUE).toBool(); }
void FractoriumSettings::SaveAutoUnique(bool b) { setValue(AUTOUNIQUE, b); }
bool FractoriumSettings::SaveAutoUnique() { return value(AUTOUNIQUE).toBool(); }
void FractoriumSettings::SaveAutoUnique(bool b) { setValue(AUTOUNIQUE, b); }
QMap<QString, QVariant> FractoriumSettings::Variations() { return value(UIVARIATIONS).toMap(); }
void FractoriumSettings::Variations(const QMap<QString, QVariant>& m) { setValue(UIVARIATIONS, m); }
QMap<QString, QVariant> FractoriumSettings::Variations() { return value(UIVARIATIONS).toMap(); }
void FractoriumSettings::Variations(const QMap<QString, QVariant>& m) { setValue(UIVARIATIONS, m); }

View File

@ -13,8 +13,7 @@
#define DOUBLEPRECISION "render/dp64"
#define CONTUPDATE "render/continuousupdate"
#define SHOWALLXFORMS "render/dragshowallxforms"
#define PLATFORMINDEX "render/platformindex"
#define DEVICEINDEX "render/deviceindex"
#define DEVICES "render/devices"
#define THREADCOUNT "render/threadcount"
#define CPUDEFILTER "render/cpudefilter"
#define OPENCLDEFILTER "render/opencldefilter"
@ -32,8 +31,7 @@
#define FINALKEEPASPECT "finalrender/keepaspect"
#define FINALSCALE "finalrender/scale"
#define FINALEXT "finalrender/ext"
#define FINALPLATFORMINDEX "finalrender/platformindex"
#define FINALDEVICEINDEX "finalrender/deviceindex"
#define FINALDEVICES "finalrender/devices"
#define FINALTHREADCOUNT "finalrender/threadcount"
#define FINALTHREADPRIORITY "finalrender/threadpriority"
#define FINALQUALITY "finalrender/quality"
@ -95,14 +93,11 @@ public:
bool ContinuousUpdate();
void ContinuousUpdate(bool b);
uint PlatformIndex();
void PlatformIndex(uint b);
uint DeviceIndex();
void DeviceIndex(uint b);
QList<QVariant> Devices();
void Devices(const QList<QVariant>& devices);
uint ThreadCount();
void ThreadCount(uint b);
void ThreadCount(uint i);
bool CpuDEFilter();
void CpuDEFilter(bool b);
@ -111,10 +106,10 @@ public:
void OpenCLDEFilter(bool b);
uint CpuSubBatch();
void CpuSubBatch(uint b);
void CpuSubBatch(uint i);
uint OpenCLSubBatch();
void OpenCLSubBatch(uint b);
void OpenCLSubBatch(uint i);
bool FinalEarlyClip();
void FinalEarlyClip(bool b);
@ -149,16 +144,13 @@ public:
QString FinalExt();
void FinalExt(const QString& s);
uint FinalPlatformIndex();
void FinalPlatformIndex(uint b);
uint FinalDeviceIndex();
void FinalDeviceIndex(uint b);
QList<QVariant> FinalDevices();
void FinalDevices(const QList<QVariant>& devices);
uint FinalThreadCount();
void FinalThreadCount(uint b);
void FinalThreadCount(uint i);
uint FinalThreadPriority();
int FinalThreadPriority();
void FinalThreadPriority(int b);
uint FinalQuality();

View File

@ -79,7 +79,14 @@ void Fractorium::OnActionDP(bool checked)
/// </summary>
void Fractorium::SyncOptionsToToolbar()
{
if (m_Settings->OpenCL())
static bool openCL = !OpenCLInfo::Instance().Devices().empty();
if (!openCL)
{
ui.ActionCL->setEnabled(false);
}
if (openCL && m_Settings->OpenCL())
{
ui.ActionCpu->setChecked(false);
ui.ActionCL->setChecked(true);

View File

@ -240,9 +240,9 @@ void FractoriumEmberController<T>::FillCurvesControl()
{
m_Fractorium->ui.CurvesView->blockSignals(true);
for (int i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
{
for (int j = 1; j < 3; j++)//Only do middle points.
for (size_t j = 1; j < 3; j++)//Only do middle points.
{
QPointF point(m_Ember.m_Curves.m_Points[i][j].x, m_Ember.m_Curves.m_Points[i][j].y);
@ -280,7 +280,7 @@ void FractoriumEmberController<T>::FillColorWithXform(Xform<T>* xform)
/// <param name="col">The column of the cell</param>
void Fractorium::SetPaletteTableItem(QPixmap* pixmap, QTableWidget* table, QTableWidgetItem* item, int row, int col)
{
if (pixmap)
if (pixmap && !pixmap->isNull())
{
QSize size(table->columnWidth(col), table->rowHeight(row) + 1);
item->setData(Qt::DecorationRole, pixmap->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));

View File

@ -50,7 +50,7 @@ void FractoriumEmberController<T>::Filter(const QString& text)
tree->setUpdatesEnabled(false);
for (uint i = 0; i < uint(tree->topLevelItemCount()); i++)
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
if (auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i)))
{
@ -181,14 +181,14 @@ void FractoriumEmberController<T>::ClearVariationsTree()
{
QTreeWidget* tree = m_Fractorium->ui.VariationsTree;
for (uint i = 0; i < tree->topLevelItemCount(); i++)
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
QTreeWidgetItem* item = tree->topLevelItem(i);
auto* spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1));
spinBox->SetValueStealth(0);
for (uint j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params.
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params.
{
if ((spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item->child(j), 1))))//Cast the child widget to the VariationTreeDoubleSpinBox type.
spinBox->SetValueStealth(0);
@ -301,7 +301,7 @@ void FractoriumEmberController<T>::FillVariationTreeWithXform(Xform<T>* xform)
tree->blockSignals(true);
m_Fractorium->Filter();
for (uint i = 0; i < tree->topLevelItemCount(); i++)
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i));
auto var = xform->GetVariationById(item->Id());//See if this variation in the tree was contained in the xform.
@ -317,7 +317,7 @@ void FractoriumEmberController<T>::FillVariationTreeWithXform(Xform<T>* xform)
//item->setBackgroundColor(0, var ? Qt::darkGray : Qt::lightGray);//Ensure background is always white if the value goes to zero, else gray if var present.
item->setBackgroundColor(0, var ? QColor(200, 200, 200) : QColor(255, 255, 255));//Ensure background is always white if the value goes to zero, else gray if var present.
for (uint j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
{
T* param = nullptr;
auto childItem = item->child(j);//Get the child.

View File

@ -252,13 +252,13 @@ void GLEmberController<T>::QueryMatrices(bool print)
if (print)
{
for (int i = 0; i < 4; i++)
for (size_t i = 0; i < 4; i++)
qDebug() << "Viewport[" << i << "] = " << m_Viewport[i] << endl;
for (int i = 0; i < 16; i++)
for (size_t i = 0; i < 16; i++)
qDebug() << "Modelview[" << i << "] = " << glm::value_ptr(m_Modelview)[i] << endl;
for (int i = 0; i < 16; i++)
for (size_t i = 0; i < 16; i++)
qDebug() << "Projection[" << i << "] = " << glm::value_ptr(m_Projection)[i] << endl;
}
}

View File

@ -313,7 +313,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{
if (pre && m_Fractorium->DrawAllPre())//Draw all pre affine if specified.
{
for (uint i = 0; i < ember->TotalXformCount(); i++)
for (size_t i = 0; i < ember->TotalXformCount(); i++)
{
Xform<T>* xform = ember->GetTotalXform(i);
bool selected = dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform);
@ -328,7 +328,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
if (post && m_Fractorium->DrawAllPost())//Draw all post affine if specified.
{
for (uint i = 0; i < ember->TotalXformCount(); i++)
for (size_t i = 0; i < ember->TotalXformCount(); i++)
{
Xform<T>* xform = ember->GetTotalXform(i);
bool selected = dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform);
@ -1025,7 +1025,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final,
if (selected)
{
for (int i = 1; i <= 64; i++)//The circle.
for (size_t i = 1; i <= 64; i++)//The circle.
{
float theta = float(M_PI) * 2.0f * float(i % 64) / 64.0f;
float fx = float(cos(theta));
@ -1100,7 +1100,7 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
}
//Check all xforms.
for (uint i = 0; i < ember->TotalXformCount(); i++)
for (size_t i = 0; i < ember->TotalXformCount(); i++)
{
Xform<T>* xform = ember->GetTotalXform(i);

View File

@ -90,6 +90,10 @@ int main(int argc, char *argv[])
a.installEventFilter(&w);
rv = a.exec();
}
catch (const std::exception& e)
{
QMessageBox::critical(0, "Fatal Error", QString::fromStdString(e.what()));
}
catch (const char* e)
{
QMessageBox::critical(0, "Fatal Error", e);

View File

@ -9,17 +9,17 @@
/// <param name="p">The parent widget. Default: nullptr.</param>
/// <param name="f">The window flags. Default: 0.</param>
FractoriumOptionsDialog::FractoriumOptionsDialog(FractoriumSettings* settings, QWidget* p, Qt::WindowFlags f)
: QDialog(p, f)
: QDialog(p, f),
m_Info(OpenCLInfo::Instance())
{
int row = 0, spinHeight = 20;
uint i;
int i, row = 0, spinHeight = 20;
ui.setupUi(this);
m_Settings = settings;
QTableWidget* table = ui.OptionsXmlSavingTable;
ui.ThreadCountSpin->setRange(1, Timing::ProcessorCount());
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.PlatformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnPlatformComboCurrentIndexChanged(int)), Qt::QueuedConnection);
connect(ui.OpenCLCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnOpenCLCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.DeviceTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnDeviceTableCellChanged(int, int)), Qt::QueuedConnection);
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlTemporalSamplesSpin, spinHeight, 1, 1000, 100, "", "", true, 1000);
SetupSpinner<SpinBox, int>(table, this, row, 1, m_XmlQualitySpin, spinHeight, 1, 200000, 50, "", "", true, 1000);
@ -34,63 +34,28 @@ FractoriumOptionsDialog::FractoriumOptionsDialog(FractoriumSettings* settings, Q
m_NickEdit = new QLineEdit(ui.OptionsIdentityTable);
ui.OptionsIdentityTable->setCellWidget(2, 1, m_NickEdit);
m_IdEdit->setText(m_Settings->Id());
m_UrlEdit->setText(m_Settings->Url());
m_NickEdit->setText(m_Settings->Nick());
table = ui.DeviceTable;
if (m_Wrapper.CheckOpenCL())
if (m_Info.Ok() && !m_Info.Devices().empty())
{
vector<string> platforms = m_Wrapper.PlatformNames();
SetupDeviceTable(table, m_Settings->Devices());
//Populate combo boxes with available OpenCL platforms and devices.
for (i = 0; i < platforms.size(); i++)
ui.PlatformCombo->addItem(QString::fromStdString(platforms[i]));
//If init succeeds, set the selected platform and device combos to match what was saved in the settings.
if (m_Wrapper.Init(m_Settings->PlatformIndex(), m_Settings->DeviceIndex()))
{
ui.OpenCLCheckBox->setChecked( m_Settings->OpenCL());
ui.PlatformCombo->setCurrentIndex(m_Settings->PlatformIndex());
ui.DeviceCombo->setCurrentIndex( m_Settings->DeviceIndex());
}
else
{
OnPlatformComboCurrentIndexChanged(0);
ui.OpenCLCheckBox->setChecked(false);
}
for (i = 0; i < table->rowCount(); i++)
if (auto radio = qobject_cast<QRadioButton*>(table->cellWidget(i, 1)))
connect(radio, SIGNAL(toggled(bool)), this, SLOT(OnDeviceTableRadioToggled(bool)), Qt::QueuedConnection);
}
else
{
ui.DeviceTable->setEnabled(false);
ui.OpenCLCheckBox->setChecked(false);
ui.OpenCLCheckBox->setEnabled(false);
ui.OpenCLSubBatchSpin->setEnabled(false);
ui.OpenCLFilteringDERadioButton->setEnabled(false);
ui.OpenCLFilteringLogRadioButton->setEnabled(false);
ui.InteraciveGpuFilteringGroupBox->setEnabled(false);
}
ui.EarlyClipCheckBox->setChecked( m_Settings->EarlyClip());
ui.YAxisUpCheckBox->setChecked( m_Settings->YAxisUp());
ui.TransparencyCheckBox->setChecked( m_Settings->Transparency());
ui.ContinuousUpdateCheckBox->setChecked(m_Settings->ContinuousUpdate());
ui.DoublePrecisionCheckBox->setChecked( m_Settings->Double());
ui.ShowAllXformsCheckBox->setChecked( m_Settings->ShowAllXforms());
ui.ThreadCountSpin->setValue( m_Settings->ThreadCount());
if (m_Settings->CpuDEFilter())
ui.CpuFilteringDERadioButton->setChecked(true);
else
ui.CpuFilteringLogRadioButton->setChecked(true);
if (m_Settings->OpenCLDEFilter())
ui.OpenCLFilteringDERadioButton->setChecked(true);
else
ui.OpenCLFilteringLogRadioButton->setChecked(true);
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
m_XmlQualitySpin->setValue(m_Settings->XmlQuality());
m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample());
ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique());
DataToGui();
OnOpenCLCheckBoxStateChanged(ui.OpenCLCheckBox->isChecked());
}
@ -106,12 +71,48 @@ bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked();
bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); }
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
uint FractoriumOptionsDialog::PlatformIndex() { return ui.PlatformCombo->currentIndex(); }
uint FractoriumOptionsDialog::DeviceIndex() { return ui.DeviceCombo->currentIndex(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
/// <summary>
/// Disable or enable the OpenCL related controls based on the state passed in.
/// The check state of one of the OpenCL devices was changed.
/// This does a special check to always ensure at least one device,
/// as well as one primary is checked.
/// </summary>
/// <param name="row">The row of the cell</param>
/// <param name="col">The column of the cell</param>
void FractoriumOptionsDialog::OnDeviceTableCellChanged(int row, int col)
{
if (auto item = ui.DeviceTable->item(row, col))
HandleDeviceTableCheckChanged(ui.DeviceTable, row, col);
}
/// <summary>
/// The primary device radio button selection was changed.
/// If the device was specified as primary, but was not selected
/// for inclusion, it will automatically be selected for inclusion.
/// </summary>
/// <param name="checked">The state of the radio button</param>
void FractoriumOptionsDialog::OnDeviceTableRadioToggled(bool checked)
{
int row;
auto s = sender();
auto table = ui.DeviceTable;
QRadioButton* radio = nullptr;
if (s)
{
for (row = 0; row < table->rowCount(); row++)
if (radio = qobject_cast<QRadioButton*>(table->cellWidget(row, 1)))
if (s == radio)
{
HandleDeviceTableCheckChanged(ui.DeviceTable, row, 1);
break;
}
}
}
/// <summary>
/// Disable or enable the CPU and OpenCL related controls based on the state passed in.
/// Called when the state of the OpenCL checkbox is changed.
/// </summary>
/// <param name="state">The state of the checkbox</param>
@ -119,28 +120,15 @@ void FractoriumOptionsDialog::OnOpenCLCheckBoxStateChanged(int state)
{
bool checked = state == Qt::Checked;
ui.PlatformCombo->setEnabled(checked);
ui.DeviceCombo->setEnabled(checked);
ui.DeviceTable->setEnabled(checked);
ui.ThreadCountSpin->setEnabled(!checked);
}
/// <summary>
/// Populate the the device combo box with all available
/// OpenCL devices for the selected platform.
/// Called when the platform combo box index changes.
/// </summary>
/// <param name="index">The selected index of the combo box</param>
void FractoriumOptionsDialog::OnPlatformComboCurrentIndexChanged(int index)
{
vector<string> devices = m_Wrapper.DeviceNames(index);
ui.DeviceCombo->clear();
for (auto& device : devices)
ui.DeviceCombo->addItem(QString::fromStdString(device));
if (ui.PlatformCombo->currentIndex() == m_Settings->PlatformIndex())
ui.DeviceCombo->setCurrentIndex(m_Settings->DeviceIndex());
ui.CpuSubBatchSpin->setEnabled(!checked);
ui.OpenCLSubBatchSpin->setEnabled(checked);
ui.CpuFilteringDERadioButton->setEnabled(!checked);
ui.CpuFilteringLogRadioButton->setEnabled(!checked);
ui.OpenCLFilteringDERadioButton->setEnabled(checked);
ui.OpenCLFilteringLogRadioButton->setEnabled(checked);
ui.InteraciveGpuFilteringGroupBox->setEnabled(checked);
}
/// <summary>
@ -187,13 +175,12 @@ void FractoriumOptionsDialog::GuiToData()
m_Settings->OpenCL(OpenCL());
m_Settings->Double(Double());
m_Settings->ShowAllXforms(ShowAllXforms());
m_Settings->PlatformIndex(PlatformIndex());
m_Settings->DeviceIndex(DeviceIndex());
m_Settings->ThreadCount(ThreadCount());
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
m_Settings->OpenCLSubBatch(ui.OpenCLSubBatchSpin->value());
m_Settings->CpuDEFilter(ui.CpuFilteringDERadioButton->isChecked());
m_Settings->OpenCLDEFilter(ui.OpenCLFilteringDERadioButton->isChecked());
m_Settings->Devices(DeviceTableToSettings(ui.DeviceTable));
//Xml saving.
m_Settings->XmlTemporalSamples(m_XmlTemporalSamplesSpin->value());
@ -213,6 +200,8 @@ void FractoriumOptionsDialog::GuiToData()
void FractoriumOptionsDialog::DataToGui()
{
//Interactive rendering.
auto devices = m_Settings->Devices();
ui.EarlyClipCheckBox->setChecked(m_Settings->EarlyClip());
ui.YAxisUpCheckBox->setChecked(m_Settings->YAxisUp());
ui.TransparencyCheckBox->setChecked(m_Settings->Transparency());
@ -220,13 +209,20 @@ void FractoriumOptionsDialog::DataToGui()
ui.OpenCLCheckBox->setChecked(m_Settings->OpenCL());
ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double());
ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms());
ui.PlatformCombo->setCurrentIndex(m_Settings->PlatformIndex());
ui.DeviceCombo->setCurrentIndex(m_Settings->DeviceIndex());
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
ui.CpuFilteringDERadioButton->setChecked(m_Settings->CpuDEFilter());
ui.OpenCLFilteringDERadioButton->setChecked(m_Settings->OpenCLDEFilter());
SettingsToDeviceTable(ui.DeviceTable, devices);
if (m_Settings->CpuDEFilter())
ui.CpuFilteringDERadioButton->setChecked(true);
else
ui.CpuFilteringLogRadioButton->setChecked(true);
if (m_Settings->OpenCLDEFilter())
ui.OpenCLFilteringDERadioButton->setChecked(true);
else
ui.OpenCLFilteringLogRadioButton->setChecked(true);
//Xml saving.
m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples());
@ -238,4 +234,4 @@ void FractoriumOptionsDialog::DataToGui()
m_IdEdit->setText(m_Settings->Id());
m_UrlEdit->setText(m_Settings->Url());
m_NickEdit->setText(m_Settings->Nick());
}
}

View File

@ -28,7 +28,8 @@ public:
public slots:
void OnOpenCLCheckBoxStateChanged(int state);
void OnPlatformComboCurrentIndexChanged(int index);
void OnDeviceTableCellChanged(int row, int col);
void OnDeviceTableRadioToggled(bool checked);
virtual void accept() override;
virtual void reject() override;
@ -45,14 +46,12 @@ private:
bool Double();
bool ShowAllXforms();
bool AutoUnique();
uint PlatformIndex();
uint DeviceIndex();
uint ThreadCount();
void DataToGui();
void GuiToData();
Ui::OptionsDialog ui;
OpenCLWrapper m_Wrapper;
OpenCLInfo& m_Info;
SpinBox* m_XmlTemporalSamplesSpin;
SpinBox* m_XmlQualitySpin;
SpinBox* m_XmlSupersampleSpin;

View File

@ -6,31 +6,34 @@
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>368</height>
<width>427</width>
<height>415</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>368</height>
<width>427</width>
<height>415</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>368</height>
<width>16777215</width>
<height>415</height>
</size>
</property>
<property name="windowTitle">
<string>Options</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>6</number>
@ -72,6 +75,9 @@
<string>Interactive Rendering</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
@ -103,6 +109,36 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="OpenCLCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenCL to render if your video card supports it.&lt;/p&gt;&lt;p&gt;This is highly recommended as it will give fluid, real-time interactive editing.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use OpenCL</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="YAxisUpCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: Positive Y direction is up.&lt;/p&gt;&lt;p&gt;Unchecked: Positive Y direction is down.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Positive Y Up</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="DoublePrecisionCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: use 64-bit double precision numbers (slower, but better image quality).&lt;/p&gt;&lt;p&gt;Unchecked: use 32-bit single precision numbers (faster, but worse image quality).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Double Precision</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="TransparencyCheckBox">
<property name="toolTip">
@ -113,13 +149,115 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="ShowAllXformsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: show all xforms while dragging.&lt;/p&gt;&lt;p&gt;Unchecked: only show current xform while dragging.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show All Xforms</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="ContinuousUpdateCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Continually update output image during interactive rendering.&lt;/p&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Continuous Update</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QComboBox" name="PlatformCombo"/>
<widget class="QTableWidget" name="DeviceTable">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>91</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>91</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>true</bool>
</property>
<property name="columnCount">
<number>3</number>
</property>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>60</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>22</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>22</number>
</attribute>
<row>
<property name="text">
<string>AMD</string>
</property>
</row>
<row>
<property name="text">
<string>Nvidia</string>
</property>
</row>
<row>
<property name="text">
<string>Intel</string>
</property>
</row>
<column>
<property name="text">
<string>Use</string>
</property>
</column>
<column>
<property name="text">
<string>Primary</string>
</property>
</column>
<column>
<property name="text">
<string>Device</string>
</property>
</column>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QComboBox" name="DeviceCombo"/>
</item>
<item row="8" column="0">
<item row="7" column="0">
<widget class="QSpinBox" name="ThreadCountSpin">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of threads to use with CPU rendering.&lt;/p&gt;&lt;p&gt;Decrease for more responsive editing, increase for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -135,7 +273,41 @@
</property>
</widget>
</item>
<item row="11" column="0">
<item row="8" column="0">
<widget class="QSpinBox" name="CpuSubBatchSpin">
<property name="toolTip">
<string>The number of 10,000 iteration chunks ran per thread on the CPU
in interactive mode for each mouse movement</string>
</property>
<property name="prefix">
<string>CPU Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QSpinBox" name="OpenCLSubBatchSpin">
<property name="toolTip">
<string>The number of ~8M iteration chunks ran using OpenCL
in interactive mode for each mouse movement</string>
</property>
<property name="prefix">
<string>OpenCL Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QGroupBox" name="InteraciveCpuFilteringGroupBox">
<property name="title">
<string>CPU Filtering</string>
@ -182,7 +354,7 @@
</layout>
</widget>
</item>
<item row="12" column="0">
<item row="11" column="0">
<widget class="QGroupBox" name="InteraciveGpuFilteringGroupBox">
<property name="title">
<string>OpenCL Filtering</string>
@ -226,90 +398,6 @@
</layout>
</widget>
</item>
<item row="9" column="0">
<widget class="QSpinBox" name="CpuSubBatchSpin">
<property name="toolTip">
<string>The number of 10,000 iteration chunks ran per thread on the CPU
in interactive mode for each mouse movement</string>
</property>
<property name="prefix">
<string>CPU Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QSpinBox" name="OpenCLSubBatchSpin">
<property name="toolTip">
<string>The number of ~8M iteration chunks ran using OpenCL
in interactive mode for each mouse movement</string>
</property>
<property name="prefix">
<string>OpenCL Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="YAxisUpCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: Positive Y direction is up.&lt;/p&gt;&lt;p&gt;Unchecked: Positive Y direction is down.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Positive Y Up</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="OpenCLCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenCL to render if your video card supports it.&lt;/p&gt;&lt;p&gt;This is highly recommended as it will give fluid, real-time interactive editing.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use OpenCL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="DoublePrecisionCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: use 64-bit double precision numbers (slower, but better image quality).&lt;/p&gt;&lt;p&gt;Unchecked: use 32-bit single precision numbers (faster, but worse image quality).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Double Precision</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="ShowAllXformsCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: show all xforms while dragging.&lt;/p&gt;&lt;p&gt;Unchecked: only show current xform while dragging.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show All Xforms</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="ContinuousUpdateCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Continually update output image during interactive rendering.&lt;/p&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Continuous Update</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="OptionsXmlSavingTab">
@ -732,8 +820,6 @@ in interactive mode for each mouse movement</string>
<tabstop>TransparencyCheckBox</tabstop>
<tabstop>ShowAllXformsCheckBox</tabstop>
<tabstop>ContinuousUpdateCheckBox</tabstop>
<tabstop>PlatformCombo</tabstop>
<tabstop>DeviceCombo</tabstop>
<tabstop>ThreadCountSpin</tabstop>
<tabstop>CpuSubBatchSpin</tabstop>
<tabstop>OpenCLSubBatchSpin</tabstop>