1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2015-21 Mandelbulber Team     §R-==%w["'~5]m%=L.=~5N
5  *                                        ,=mm=§M ]=4 yJKA"/-Nsaj  "Bw,==,,
6  * This file is part of Mandelbulber.    §R.r= jw",M  Km .mM  FW ",§=ß., ,TN
7  *                                     ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M
8  * Mandelbulber is free software:     §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw
9  * you can redistribute it and/or     §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" §
10  * modify it under the terms of the    "§M=M =D=4"N #"%==A%p M§ M6  R' #"=~.4M
11  * GNU General Public License as        §W =, ][T"]C  §  § '§ e===~ U  !§[Z ]N
12  * published by the                    4M",,Jm=,"=e~  §  §  j]]""N  BmM"py=ßM
13  * Free Software Foundation,          ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§,
14  * either version 3 of the License,    TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N
15  * or (at your option)                   TW=,-#"%=;[  =Q:["V""  ],,M.m == ]N
16  * any later version.                      J§"mr"] ,=,," =="""J]= M"M"]==ß"
17  *                                          §= "=C=4 §"eM "=B:m|4"]#F,§~
18  * Mandelbulber is distributed in            "9w=,,]w em%wJ '"~" ,=,,ß"
19  * the hope that it will be useful,                 . "K=  ,=RMMMßM"""
20  * but WITHOUT ANY WARRANTY;                            .'''
21  * without even the implied warranty
22  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23  *
24  * See the GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with Mandelbulber. If not, see <http://www.gnu.org/licenses/>.
27  *
28  * ###########################################################################
29  *
30  * Authors: Krzysztof Marczak (buddhi1980@gmail.com), Sebastian Jennen (jenzebas@gmail.com),
31  *  Robert Pancoast (RobertPancoast77@gmail.com)
32  *
33  * cCommandLineInterface - CLI Input handler
34  */
35 
36 #include "command_line_interface.hpp"
37 
38 #include <ctime>
39 
40 #include <QList>
41 
42 #include "animation_frames.hpp"
43 #include "error_message.hpp"
44 #include "file_image.hpp"
45 #include "fractal_container.hpp"
46 #include "global_data.hpp"
47 #include "headless.h"
48 #include "initparameters.hpp"
49 #include "interface.hpp"
50 #include "keyframes.hpp"
51 #include "netrender.hpp"
52 #include "old_settings.hpp"
53 #include "opencl_global.h"
54 #include "opencl_hardware.h"
55 #include "queue.hpp"
56 #include "settings.hpp"
57 #include "system_data.hpp"
58 #include "system_directories.hpp"
59 #include "test.hpp"
60 #include "write_log.hpp"
61 
cCommandLineInterface(QCoreApplication * _qApplication)62 cCommandLineInterface::cCommandLineInterface(QCoreApplication *_qApplication)
63 		: settingsSpecified(false)
64 {
65 	qApplication = _qApplication;
66 	// text from http://sourceforge.net/projects/mandelbulber/
67 	parser.setApplicationDescription(QCoreApplication::translate("main",
68 		"Mandelbulber is an easy to use, "
69 		"handy application designed to help you render 3D Mandelbrot fractals called Mandelbulb "
70 		"and some other kind of 3D fractals like Mandelbox, Bulbbox, Juliabulb, Menger Sponge"));
71 
72 	parser.addHelpOption();
73 	parser.addVersionOption();
74 
75 	const QCommandLineOption noguiOption(QStringList({"n", "nogui"}),
76 		QCoreApplication::translate("main", "Starts the program without a GUI."));
77 
78 	const QCommandLineOption keyframeOption(QStringList({"K", "keyframe"}),
79 		QCoreApplication::translate("main", "Renders keyframe animation."));
80 
81 	const QCommandLineOption flightOption(
82 		QStringList({"F", "flight"}), QCoreApplication::translate("main", "Renders flight animation."));
83 
84 	const QCommandLineOption silentOption(QStringList({"X", "never-delete"}),
85 		QCoreApplication::translate("main", "Never delete data, instead Exit CLI application."));
86 
87 	const QCommandLineOption startOption(QStringList({"s", "start"}),
88 		QCoreApplication::translate("main", "Starts rendering from frame number <N>."),
89 		QCoreApplication::translate("main", "N"));
90 
91 	const QCommandLineOption endOption(QStringList({"e", "end"}),
92 		QCoreApplication::translate("main", "Stops rendering on frame number <N>."),
93 		QCoreApplication::translate("main", "N"));
94 
95 	const QCommandLineOption overrideOption(QStringList({"O", "override"}),
96 		QCoreApplication::translate("main",
97 			"<KEY=VALUE> overrides item '<KEY>' from settings file with new value '<VALUE>'.\n"
98 			"Specify multiple KEY=VALUE pairs by separating with a '#': <KEY1=VALUE1#KEY2=VALUE2>. Quote "
99 			"whole expression to avoid whitespace parsing issues\n"
100 			"Override fractal parameter in the form 'fractal<N>_KEY=VALUE' with <N> being index of "
101 			"fractal"),
102 		QCoreApplication::translate("main", "..."));
103 
104 	const QCommandLineOption listOption(QStringList({"L", "list"}),
105 		QCoreApplication::translate(
106 			"main", "Lists all possible parameters '<KEY>' with corresponding default value '<VALUE>'."));
107 
108 	const QCommandLineOption formatOption(QStringList({"f", "format"}),
109 		QCoreApplication::translate("main",
110 			"Image output format:\n"
111 			"  jpg  - JPEG format (default)\n"
112 			"  png  - PNG format\n"
113 			"  exr  - EXR format\n"
114 			"  tiff - TIFF format\n"
115 			" Legacy formats for still frames:\n"
116 			"  png16 - 16-bit PNG format\n"
117 			"  png16alpha - 16-bit PNG with alpha channel format"),
118 		QCoreApplication::translate("main", "FORMAT"));
119 
120 	const QCommandLineOption resOption(QStringList({"r", "res"}),
121 		QCoreApplication::translate(
122 			"main", "Overrides image resolution. Specify as width and height separated by 'x'"),
123 		QCoreApplication::translate("main", "WxH"));
124 
125 	const QCommandLineOption fpkOption("fpk",
126 		QCoreApplication::translate("main", "Overrides frames per key parameter."),
127 		QCoreApplication::translate("main", "N"));
128 
129 	const QCommandLineOption serverOption(QStringList({"S", "server"}),
130 		QCoreApplication::translate("main", "Sets application as a server listening for clients."));
131 
132 	const QCommandLineOption hostOption(QStringList({"H", "host"}),
133 		QCoreApplication::translate("main",
134 			"Sets application as a client connected to server of given host address"
135 			" (Host can be of type IPv4, IPv6 and Domain name address)."),
136 		QCoreApplication::translate("main", "N.N.N.N"));
137 
138 	const QCommandLineOption portOption(QStringList({"p", "port"}),
139 		QCoreApplication::translate("main", "Sets network port number for netrender (default 5555)."),
140 		QCoreApplication::translate("main", "N"));
141 
142 	const QCommandLineOption noColorOption(QStringList({"C", "no-cli-color"}),
143 		QCoreApplication::translate(
144 			"main", "Starts program without ANSI colors, when execution on CLI."));
145 
146 	const QCommandLineOption outputOption(QStringList({"o", "output"}),
147 		QCoreApplication::translate("main", "Saves rendered image(s) to this file / folder."),
148 		QCoreApplication::translate("main", "N"));
149 
150 	const QCommandLineOption logFilepathOption(QStringList({"logfilepath"}),
151 		QCoreApplication::translate(
152 			"main", "Specify custom system log filepath (default is: ~/.mandelbulber_log.txt)."),
153 		QCoreApplication::translate("main", "N"));
154 
155 	const QCommandLineOption queueOption(QStringList({"q", "queue"}),
156 		QCoreApplication::translate("main", "Renders all images from common queue."));
157 
158 	const QCommandLineOption testOption(QStringList({"t", "test"}),
159 		QCoreApplication::translate("main", "Runs testcases on the mandelbulber instance"));
160 
161 	const QCommandLineOption benchmarkOption(QStringList({"b", "benchmark"}),
162 		QCoreApplication::translate("main",
163 			"Runs benchmarks on the mandelbulber instance, specify optional"
164 			" parameter difficulty (1 -> very easy, > 20 -> very hard, 10 -> default)."
165 			" When [output] option is set to a folder, the example-test images will be stored there."));
166 
167 	const QCommandLineOption gpuOption(QStringList({"g", "gpu"}),
168 		QCoreApplication::translate(
169 			"main", "Runs the program in opencl mode and selects first available gpu device."));
170 
171 	const QCommandLineOption gpuAllOption(QStringList({"G", "gpuall"}),
172 		QCoreApplication::translate(
173 			"main", "Runs the program in opencl mode and selects all available gpu devices."));
174 
175 	const QCommandLineOption touchOption(QStringList({"T", "touch"}),
176 		QCoreApplication::translate(
177 			"main", "Resaves a settings file (can be used to update a settings file)"));
178 
179 	const QCommandLineOption voxelOption(QStringList({"V", "voxel"}),
180 		QCoreApplication::translate("main",
181 			"Renders the voxel volume. Output formats are:\n"
182 			"  slice - stack of PNG images into one folder (default)\n"
183 			"  ply   - Polygon File Format (single 3d file)\n"),
184 		QCoreApplication::translate("main", "FORMAT"));
185 
186 	const QCommandLineOption statsOption(QStringList({"stats"}),
187 		QCoreApplication::translate("main", "Shows statistics while rendering in CLI mode."));
188 
189 	const QCommandLineOption helpInputOption(
190 		QStringList({"help-input"}), QCoreApplication::translate("main", "Shows help about input."));
191 	const QCommandLineOption helpExamplesOption(
192 		QStringList({"help-examples"}), QCoreApplication::translate("main", "Shows example commands."));
193 	const QCommandLineOption helpOpenClOption(QStringList({"help-opencl"}),
194 		QCoreApplication::translate("main", "Shows commands regarding OpenCL."));
195 
196 	parser.addPositionalArgument("settings_file",
197 		QCoreApplication::translate("main",
198 			"file with fractal settings (program also tries\nto find file in ./mandelbulber/settings "
199 			"directory)\n"
200 			"When settings_file is put as a command line argument then program will start in noGUI mode"
201 			"<settings_file> can also be specified as a list, see all options with --help-input"));
202 
203 	parser.addOption(noguiOption);
204 	parser.addOption(outputOption);
205 	parser.addOption(logFilepathOption);
206 	parser.addOption(keyframeOption);
207 	parser.addOption(flightOption);
208 	parser.addOption(silentOption);
209 	parser.addOption(startOption);
210 	parser.addOption(endOption);
211 	parser.addOption(listOption);
212 	parser.addOption(formatOption);
213 	parser.addOption(resOption);
214 	parser.addOption(fpkOption);
215 	parser.addOption(serverOption);
216 	parser.addOption(hostOption);
217 	parser.addOption(portOption);
218 	parser.addOption(noColorOption);
219 	parser.addOption(queueOption);
220 	parser.addOption(testOption);
221 	parser.addOption(benchmarkOption);
222 	parser.addOption(touchOption);
223 	parser.addOption(voxelOption);
224 	parser.addOption(overrideOption);
225 	parser.addOption(statsOption);
226 	parser.addOption(gpuOption);
227 	parser.addOption(gpuAllOption);
228 	parser.addOption(helpInputOption);
229 	parser.addOption(helpExamplesOption);
230 	parser.addOption(helpOpenClOption);
231 
232 	// Process the actual command line arguments given by the user
233 	parser.process(*qApplication);
234 	args = parser.positionalArguments();
235 	if (!args.empty() && args[0] == "-")
236 	{
237 		// "-" marks stdin to be used as the input for the program (filename or content)
238 		// use the input as first argument
239 		QTextStream qin(stdin);
240 		args[0] = qin.readAll();
241 	}
242 
243 	cliData.nogui = parser.isSet(noguiOption);
244 	cliData.keyframe = parser.isSet(keyframeOption);
245 	cliData.flight = parser.isSet(flightOption);
246 	cliData.silent = parser.isSet(silentOption);
247 	cliData.startFrameText = parser.value(startOption);
248 	cliData.endFrameText = parser.value(endOption);
249 	cliData.overrideParametersText = parser.value(overrideOption);
250 	cliData.imageFileFormat = parser.value(formatOption);
251 	cliData.resolution = parser.value(resOption);
252 	cliData.fpkText = parser.value(fpkOption);
253 	cliData.server = parser.isSet(serverOption);
254 	cliData.host = parser.value(hostOption);
255 	cliData.portText = parser.value(portOption);
256 	cliData.outputText = parser.value(outputOption);
257 	cliData.logFilepathText = parser.value(logFilepathOption);
258 	cliData.listParameters = parser.isSet(listOption);
259 	cliData.queue = parser.isSet(queueOption);
260 	cliData.voxel = parser.isSet(voxelOption);
261 	cliData.voxelFormat = parser.value(voxelOption);
262 	cliData.test = parser.isSet(testOption);
263 	cliData.benchmark = parser.isSet(benchmarkOption);
264 	cliData.touch = parser.isSet(touchOption);
265 	cliData.gpu = parser.isSet(gpuOption);
266 	cliData.gpuAll = parser.isSet(gpuAllOption);
267 	cliData.showInputHelp = parser.isSet(helpInputOption);
268 	cliData.showExampleHelp = parser.isSet(helpExamplesOption);
269 	cliData.showOpenCLHelp = parser.isSet(helpOpenClOption);
270 
271 	systemData.statsOnCLI = parser.isSet(statsOption);
272 
273 #ifdef _WIN32 /* WINDOWS */
274 	systemData.useColor = false;
275 #else
276 	systemData.useColor = !parser.isSet(noColorOption);
277 #endif /* WINDOWS */
278 
279 	if (cliData.listParameters) cliData.nogui = true;
280 	if (cliData.queue) cliData.nogui = true;
281 	if (cliData.test) cliData.nogui = true;
282 	if (cliData.benchmark) cliData.nogui = true;
283 	cliOperationalMode = modeBootOnly;
284 }
285 
286 cCommandLineInterface::~cCommandLineInterface() = default;
287 
ReadCLI()288 void cCommandLineInterface::ReadCLI()
289 {
290 	settingsSpecified = false;
291 
292 	// show example help only
293 	if (cliData.showExampleHelp) printExampleHelpAndExit();
294 	// show input help only
295 	if (cliData.showInputHelp) printInputHelpAndExit();
296 	// list parameters only
297 	if (cliData.listParameters) printParametersAndExit();
298 
299 	// log filepath if it is specified
300 	if (cliData.logFilepathText != "")
301 	{
302 		systemData.SetLogfileName(cliData.logFilepathText);
303 	}
304 
305 	// run test cases
306 	if (cliData.test) runTestCasesAndExit();
307 	// run benchmarks
308 	if (cliData.benchmark) runBenchmarksAndExit();
309 
310 	// check netrender server / client
311 	if (cliData.server)
312 		handleServer();
313 	else if (cliData.host != "")
314 	{
315 		handleClient();
316 		return;
317 	}
318 
319 	if (cliData.queue)
320 		handleQueue();
321 	else
322 		handleArgs();
323 
324 	// overwriting parameters
325 	if (cliData.overrideParametersText != "") handleOverrideParameters();
326 
327 	// specified resolution
328 	if (cliData.resolution != "") handleResolution();
329 
330 	// specified frames per keyframe
331 	if (cliData.fpkText != "") handleFpk();
332 
333 	// specified image file format
334 	if (cliData.imageFileFormat != "")
335 		handleImageFileFormat();
336 	else
337 		cliData.imageFileFormat = "jpg";
338 
339 	// silent mode
340 	if (cliData.silent) systemData.silent = true;
341 
342 	// flight animation
343 	if (cliData.flight) handleFlight();
344 
345 	// keyframe animation
346 	if (cliData.keyframe) handleKeyframe();
347 
348 	// start frame of animation
349 	if (cliData.startFrameText != "") handleStartFrame();
350 
351 	// end frame of animation
352 	if (cliData.endFrameText != "") handleEndFrame();
353 
354 	// voxel export
355 	if (cliData.voxel) handleVoxel();
356 
357 	// folder for animation frames
358 	if (cliData.outputText != "" && cliOperationalMode == modeFlight)
359 	{
360 		gPar->Set("anim_flight_dir", cliData.outputText);
361 	}
362 	if (cliData.outputText != "" && cliOperationalMode == modeKeyframe)
363 	{
364 		gPar->Set("anim_keyframe_dir", cliData.outputText);
365 	}
366 
367 	// gpu
368 	if (cliData.gpu) handleGpu();
369 
370 	// gpuAll
371 	if (cliData.gpuAll) handleGpuAll();
372 
373 	// show opencl help only (requires previous handling of override parameters)
374 	if (cliData.showOpenCLHelp) printOpenCLHelpAndExit();
375 
376 	if (!settingsSpecified && cliData.nogui && cliOperationalMode != modeNetrender)
377 	{
378 		cErrorMessage::showMessage(
379 			QObject::tr("You have to specify a settings file, for this configuration!"),
380 			cErrorMessage::errorMessage);
381 		parser.showHelp(cliErrorSettingsFileNotSpecified);
382 	}
383 
384 	if (cliData.nogui && cliOperationalMode != modeKeyframe && cliOperationalMode != modeFlight
385 			&& cliOperationalMode != modeQueue && cliOperationalMode != modeVoxel)
386 	{
387 		// creating output filename if it's not specified
388 		if (cliData.outputText == "")
389 		{
390 			cliData.outputText = gPar->Get<QString>("default_image_path") + QDir::separator();
391 			cliData.outputText += QFileInfo(systemData.lastSettingsFile).completeBaseName();
392 		}
393 		cliOperationalMode = modeStill;
394 		return;
395 	}
396 }
397 
ProcessCLI() const398 void cCommandLineInterface::ProcessCLI() const
399 {
400 	switch (cliOperationalMode)
401 	{
402 		case modeNetrender:
403 		{
404 			gNetRender->SetClient(gPar->Get<QString>("netrender_client_remote_address"),
405 				gPar->Get<int>("netrender_client_remote_port"));
406 			gApplication->exec();
407 			break;
408 		}
409 		case modeFlight:
410 		{
411 			gMainInterface->headless = new cHeadless(gMainInterface);
412 			gMainInterface->headless->RenderFlightAnimation();
413 			break;
414 		}
415 		case modeKeyframe:
416 		{
417 			gMainInterface->headless = new cHeadless(gMainInterface);
418 			gMainInterface->headless->RenderKeyframeAnimation();
419 			break;
420 		}
421 		case modeStill:
422 		{
423 			gMainInterface->headless = new cHeadless(gMainInterface);
424 			gMainInterface->headless->RenderStillImage(cliData.outputText, cliData.imageFileFormat);
425 			break;
426 		}
427 		case modeQueue:
428 		{
429 			gMainInterface->headless = new cHeadless(gMainInterface);
430 			gMainInterface->headless->RenderQueue();
431 			break;
432 		}
433 		case modeVoxel:
434 		{
435 			gMainInterface->headless = new cHeadless(gMainInterface);
436 			gMainInterface->headless->RenderVoxel(cliData.voxelFormat);
437 			break;
438 		}
439 		case modeBootOnly:
440 		{
441 			// nothing to be done
442 			break;
443 		}
444 	}
445 }
446 
printExampleHelpAndExit()447 void cCommandLineInterface::printExampleHelpAndExit()
448 {
449 	QTextStream out(stdout);
450 	out << cHeadless::colorize(QObject::tr("Some useful example commands:"), cHeadless::ansiRed)
451 			<< "\n\n";
452 
453 	out << cHeadless::colorize(QObject::tr("Simple render"), cHeadless::ansiBlue) << "\n";
454 	out << cHeadless::colorize("mandelbulber2 -n path/to/fractal.fract", cHeadless::ansiYellow)
455 			<< "\n";
456 	out << QObject::tr("Renders the file on the cli (no window required).") << "\n\n";
457 
458 	out << cHeadless::colorize(QObject::tr("Animation render"), cHeadless::ansiBlue) << "\n";
459 	out << cHeadless::colorize(
460 		"mandelbulber2 -n -K -s 200 -e 300 path/to/keyframe_fractal.fract", cHeadless::ansiYellow)
461 			<< "\n";
462 	out << QObject::tr(
463 		"Renders the keyframe animation of the file keyframe_fractal.fract "
464 		"within frames 200 till 300.")
465 			<< "\n\n";
466 
467 	out << cHeadless::colorize(QObject::tr("Network render"), cHeadless::ansiBlue) << "\n";
468 	out << cHeadless::colorize("mandelbulber2 -n --host 192.168.100.1", cHeadless::ansiYellow)
469 			<< cHeadless::colorize(" # (1) client", cHeadless::ansiGreen) << "\n";
470 	out << cHeadless::colorize(
471 		"mandelbulber2 -n --server path/to/fractal.fract", cHeadless::ansiYellow)
472 			<< cHeadless::colorize(" # (2) server", cHeadless::ansiGreen) << "\n";
473 	out << QObject::tr(
474 		"In a network you can render on multiple machines. One is a server (2) and multiple "
475 		"clients (1) can connect to help rendering.\n"
476 		"On each client run (1), 192.168.100.1 should be substituted with the IP address of "
477 		"the server.\nOn the server run (2) with the settings required for the render and "
478 		"additionally '--server'.\nThe server will start and wait a short time for the "
479 		"clients to connect. Then the whole system will start rendering.")
480 			<< "\n\n";
481 
482 	out << cHeadless::colorize(QObject::tr("Voxel volume render"), cHeadless::ansiBlue) << "\n";
483 	out << cHeadless::colorize(
484 		"mandelbulber2 --voxel ply -n path/to/voxel_fractal.fract"
485 		" -O 'voxel_custom_limit_enabled=1#voxel_limit_min=-1 -1 -1#voxel_limit_max=1 1 "
486 		"1#voxel_samples_x=200'",
487 		cHeadless::ansiYellow)
488 			<< "\n";
489 	out << QObject::tr(
490 		"Renders the voxel volume in the bounding box of [x(-1 - 1); y(-1 - 1); z(-1 - 1)] "
491 		"with a resolution of 200x200x200 in the ply format "
492 		"and saves as working folder/slices/output.ply.")
493 			<< "\n\n";
494 
495 	out << cHeadless::colorize(QObject::tr("Queue render"), cHeadless::ansiBlue) << "\n";
496 	out << cHeadless::colorize(
497 		"nohup mandelbulber2 -q > /tmp/queue.log 2>&1 &", cHeadless::ansiYellow)
498 			<< "\n";
499 	out << QObject::tr(
500 		"Runs the mandelbulber instance in queue mode and daemonizes it.\n"
501 		"Mandelbulber runs in background and waits for jobs.\n"
502 		"The output will be written to /tmp/queue.log.\n"
503 		"(will not work under Windows)")
504 			<< "\n\n";
505 
506 	out.flush();
507 	exit(0);
508 }
509 
printInputHelpAndExit()510 void cCommandLineInterface::printInputHelpAndExit()
511 {
512 	QTextStream out(stdout);
513 	out << QObject::tr(
514 		"Mandelbulber also accepts an arbitrary number of input files\n"
515 		"These files can be of type:\n"
516 		"  .fract File - An ordinary fractal file\n"
517 		"  .fractlist File - A queue file, all entries inside the queue file will be added to the "
518 		"current queue\n"
519 		"  Folder - if the specified argument is a folder all .fract files inside the folder will be "
520 		"added to the queue\n"
521 		"You can also use \"-\" as a special argument to indicate that the filename, or the whole "
522 		"file contents are specified in stdin, example: cat example.fract | mandelbulber2 -\n");
523 	out.flush();
524 	exit(0);
525 }
526 
printOpenCLHelpAndExit()527 void cCommandLineInterface::printOpenCLHelpAndExit()
528 {
529 	QTextStream out(stdout);
530 #ifdef USE_OPENCL
531 	gOpenCl = new cGlobalOpenCl(qApplication);
532 	gOpenCl->InitPlatfromAndDevices();
533 
534 	out << QObject::tr(
535 		"Mandelbulber can utilize OpenCL to accelerate rendering.\n"
536 		"When Mandelbulber is already configured to use OpenCL, it will also run OpenCL from "
537 		"commandline by default.\nThe configuration can also be done directly from this "
538 		"commandline by setting the optional settings directly.\nThese can be given by the "
539 		"default --override option, available opencl specific options are:")
540 			<< "\n";
541 	out << " * opencl_enabled      - " << QObject::tr("boolean to enable OpenCL") << "\n";
542 	out << " * opencl_platform     - "
543 			<< QObject::tr("platform index to use, see available platforms below") << "\n";
544 	out << " * opencl_device_type  - " << QObject::tr("Possible device types of the platform to use")
545 			<< QString("\n  `- %1\n")
546 					 .arg(QObject::tr("possible values: [%1]")
547 									.arg(gPar->GetAsOneParameter("opencl_device_type").GetEnumLookup().join(", ")));
548 	out << " * opencl_device_list  - "
549 			<< QObject::tr("right now only one device at a time is supported.") << "\n"
550 			<< " Specify the device hash of the device to use, see available devices below\n";
551 	out << " * opencl_mode         - "
552 			<< QObject::tr(
553 					 "Mode of the render engine, 'fast' has no effects, 'limited' has "
554 					 "basic effects, 'full' contains all shaders.")
555 			<< QString("\n  `- %1\n")
556 					 .arg(QObject::tr("possible values: [%1]")
557 									.arg(gPar->GetAsOneParameter("opencl_mode").GetEnumLookup().join(", ")));
558 	out << " * opencl_precision    - "
559 			<< QObject::tr(
560 					 "Floating point precision of Render (single is faster, but "
561 					 "less accurate)")
562 			<< QString("\n  `- %1\n")
563 					 .arg(QObject::tr("possible values: [%1]")
564 									.arg(gPar->GetAsOneParameter("opencl_precision").GetEnumLookup().join(", ")));
565 	out << " * opencl_memory_limit - " << QObject::tr("Memory limit in MB") << "\n";
566 
567 	// print available platforms
568 	out << "\n"
569 			<< cHeadless::colorize(QObject::tr("Available platforms are:"), cHeadless::ansiBlue) << "\n";
570 	const QList<cOpenClHardware::sPlatformInformation> platforms =
571 		gOpenCl->openClHardware->getPlatformsInformation();
572 	for (int i = 0; i < platforms.size(); i++)
573 	{
574 		const cOpenClHardware::sPlatformInformation platform = platforms[i];
575 		out << (gOpenCl->openClHardware->getSelectedPlatformIndex() == i ? "> " : "  ");
576 		out << "index: " << i << " | name: " << platform.name << "\n";
577 	}
578 
579 	// print available devices
580 	out << "\n"
581 			<< cHeadless::colorize(QObject::tr("Available devices for the selected platform (%1) are:")
582 															 .arg(gOpenCl->openClHardware->getSelectedPlatformIndex()),
583 					 cHeadless::ansiBlue)
584 			<< "\n";
585 	const QList<cOpenClDevice::sDeviceInformation> devices =
586 		gOpenCl->openClHardware->getDevicesInformation();
587 	for (int i = 0; i < devices.size(); i++)
588 	{
589 		cOpenClDevice::sDeviceInformation device = devices[i];
590 		if (gOpenCl->openClHardware->getSelectedDevicesIndices().contains(i))
591 			out << "> ";
592 		else
593 			out << "  ";
594 		out << "index: " << i << " | hash: " << device.hash.toHex() << " | name: " << device.deviceName
595 				<< "\n";
596 	}
597 
598 	out << "\n"
599 			<< cHeadless::colorize(QObject::tr("Example invocation:"), cHeadless::ansiBlue) << "\n";
600 	out << cHeadless::colorize(
601 		"mandelbulber2	 -n path/to/fractal.fract"
602 		" -O 'opencl_enabled=1#opencl_platform=1#opencl_device_list=14be3d'",
603 		cHeadless::ansiYellow)
604 			<< "\n";
605 #else
606 	out << "not supported, this version is not compiled with OpenCL support.";
607 #endif
608 
609 	out.flush();
610 	exit(0);
611 }
612 
printParametersAndExit()613 void cCommandLineInterface::printParametersAndExit()
614 {
615 	QTextStream out(stdout);
616 	InitMaterialParams(1, gPar);
617 	QList<QString> listOfParameters = gPar->GetListOfParameters();
618 	out << cHeadless::colorize(
619 		"\nList of main parameters:\n", cHeadless::ansiYellow, cHeadless::noExplicitColor, true);
620 	out << "KEY=VALUE\n";
621 	for (auto &parameterName : listOfParameters)
622 	{
623 		const QString defaultValue = gPar->GetDefault<QString>(parameterName);
624 		out << parameterName + "=" + defaultValue + "\n";
625 	}
626 
627 	QList<QString> listOfFractalParameters = gParFractal->at(0)->GetListOfParameters();
628 	out << cHeadless::colorize(QObject::tr("\nList of fractal parameters:\n"), cHeadless::ansiYellow,
629 		cHeadless::noExplicitColor, true);
630 
631 	for (auto &parameterName : listOfFractalParameters)
632 	{
633 		const QString defaultValue = gParFractal->at(0)->GetDefault<QString>(parameterName);
634 		out << parameterName + "=" + defaultValue + "\n";
635 	}
636 
637 	out.flush();
638 	exit(0);
639 }
640 
runTestCasesAndExit()641 void cCommandLineInterface::runTestCasesAndExit()
642 {
643 	systemData.noGui = true;
644 	QStringList arguments = gApplication->arguments();
645 	arguments.removeOne(QString("--test"));
646 	arguments.removeOne(QString("-t"));
647 
648 	QStringList outputStrings({"-o", "--output", "--logfilepath"});
649 	for (int i = 0; i < outputStrings.size(); i++)
650 	{
651 		const int index = arguments.indexOf(outputStrings[i]);
652 		if (index >= 0)
653 		{
654 			arguments.removeAt(index);
655 			arguments.removeAt(index);
656 		}
657 	}
658 
659 	int status = 0;
660 	Test test(Test::simpleTestMode);
661 	status |= QTest::qExec(&test, arguments);
662 	exit(status);
663 }
664 
runBenchmarksAndExit()665 void cCommandLineInterface::runBenchmarksAndExit()
666 {
667 	// Set 24 hour test timeout
668 	qputenv("QTEST_FUNCTION_TIMEOUT", QByteArray("86400000"));
669 
670 	systemData.noGui = true;
671 	QStringList arguments = gApplication->arguments();
672 	arguments.removeOne(QString("--benchmark"));
673 	arguments.removeOne(QString("-b"));
674 	int difficulty = 10;
675 	QString exampleOutputPath = "";
676 
677 	if (args.size() > 0)
678 	{
679 		if (args[0] != "")
680 		{
681 			bool checkParse = false;
682 			const int difficultyTemp = args[0].toInt(&checkParse);
683 			if (checkParse && difficultyTemp > 0)
684 			{
685 				difficulty = difficultyTemp;
686 			}
687 			arguments.removeOne(args[0]);
688 		}
689 	}
690 
691 	if (cliData.outputText != "")
692 	{
693 		// Invalid output path specified on CLI
694 		exampleOutputPath = cliData.outputText;
695 		if (!QDir(exampleOutputPath).exists())
696 		{
697 			cErrorMessage::showMessage(
698 				QObject::tr("Example output path invalid\n"), cErrorMessage::errorMessage);
699 			parser.showHelp(cliErrorBenchmarkOutputFolderInvalid);
700 		}
701 
702 		// Add Timestamp to exampleOutputPath directory path
703 		time_t rawTime;
704 		char timeBuffer[80];
705 		time(&rawTime);
706 		struct tm *timeInfo = localtime(&rawTime);
707 		strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d-%H-%M-%S", timeInfo);
708 		QString timestamp(timeBuffer);
709 		exampleOutputPath += "/" + timestamp;
710 
711 		// Create the timestamped folder for rendering the examples
712 		if (!QDir(exampleOutputPath).exists())
713 		{
714 			CreateFolder(exampleOutputPath);
715 		}
716 
717 		// Create the text log in exampleOutputPath
718 		if (cliData.logFilepathText == "")
719 		{
720 			systemData.SetLogfileName(exampleOutputPath + "/" + "_bulb_log_" + timestamp + ".txt");
721 		}
722 	}
723 
724 	QStringList outputStrings({"-o", "--output", "--logfilepath"});
725 	for (int i = 0; i < outputStrings.size(); i++)
726 	{
727 		const int index = arguments.indexOf(outputStrings[i]);
728 		if (index >= 0)
729 		{
730 			arguments.removeAt(index);
731 			arguments.removeAt(index);
732 		}
733 	}
734 
735 	int status = 0;
736 	WriteLogCout(QString("Starting benchmark with difficulty [%1] and example output path [%2]")
737 									 .arg(difficulty)
738 									 .arg(exampleOutputPath)
739 								 + "\n",
740 		1);
741 
742 #ifdef USE_OPENCL
743 	if (gPar->Get<bool>("opencl_enabled"))
744 	{
745 		WriteLogCout(QString("opencl enabled: GPU Benchmark\n"), 1);
746 	}
747 	else
748 	{
749 		WriteLogCout(QString("opencl disabled: CPU benchmark\n"), 1);
750 	}
751 #else
752 	WriteLogCout(QString("this version is not compiled with OpenCL support.\n"), 1);
753 #endif
754 	Test test(Test::benchmarkTestMode, difficulty, exampleOutputPath);
755 	status |= QTest::qExec(&test, arguments);
756 	exit(status);
757 }
758 
handleServer()759 void cCommandLineInterface::handleServer()
760 {
761 	QTextStream out(stdout);
762 	bool checkParse = true;
763 	if (cliData.portText != "")
764 	{
765 		const int port = cliData.portText.toInt(&checkParse);
766 		if (!checkParse || port <= 0)
767 		{
768 			cErrorMessage::showMessage(
769 				QObject::tr("Specified server port is invalid\n"), cErrorMessage::errorMessage);
770 			parser.showHelp(cliErrorServerInvalidPort);
771 		}
772 		gPar->Set("netrender_server_local_port", port);
773 	}
774 	cliData.nogui = true;
775 	systemData.noGui = true;
776 	gNetRender->SetServer(gPar->Get<int>("netrender_server_local_port"));
777 	QElapsedTimer timer;
778 	timer.start();
779 
780 	if (systemData.noGui)
781 	{
782 		out << QObject::tr("NetRender - Waiting for clients\n");
783 		out.flush();
784 	}
785 
786 	while (timer.elapsed() < 5000)
787 	{
788 		gApplication->processEvents();
789 	}
790 }
791 
handleClient()792 void cCommandLineInterface::handleClient()
793 {
794 	bool checkParse = true;
795 	gPar->Set("netrender_client_remote_address", cliData.host);
796 	if (cliData.portText != "")
797 	{
798 		const int port = cliData.portText.toInt(&checkParse);
799 		if (!checkParse || port <= 0)
800 		{
801 			cErrorMessage::showMessage(
802 				QObject::tr("Specified client port is invalid\n"), cErrorMessage::errorMessage);
803 			parser.showHelp(cliErrorClientInvalidPort);
804 		}
805 		gPar->Set("netrender_client_remote_port", port);
806 	}
807 	cliData.nogui = true;
808 	systemData.noGui = true;
809 	cliOperationalMode = modeNetrender;
810 }
811 
handleQueue()812 void cCommandLineInterface::handleQueue()
813 {
814 	cliOperationalMode = modeQueue;
815 	settingsSpecified = true;
816 	cliData.nogui = true;
817 	systemData.noGui = true;
818 	try
819 	{
820 		gQueue = new cQueue(gMainInterface, systemDirectories.GetQueueFractlistFile(),
821 			systemDirectories.GetQueueFolder(), gMainInterface);
822 	}
823 	catch (QString &ex)
824 	{
825 		cErrorMessage::showMessage(
826 			QObject::tr("Cannot init queue: ") + ex, cErrorMessage::errorMessage);
827 		parser.showHelp(cliErrorQueueInit);
828 	}
829 }
830 
handleArgs()831 void cCommandLineInterface::handleArgs()
832 {
833 	if (args.size() > 0)
834 	{
835 		// file specified -> load it
836 		if (args.size() == 1 && QFileInfo(args[0]).suffix() != QString("fractlist")
837 				&& !QDir(args[0]).exists())
838 		{
839 			QString filename = args[0];
840 			cSettings parSettings(cSettings::formatFullText);
841 			if (!QFile::exists(filename))
842 			{
843 				// try to find settings in default settings path
844 				filename = systemDirectories.GetSettingsFolder() + QDir::separator() + filename;
845 			}
846 			if (QFile::exists(filename))
847 			{
848 				// hint: do not use auto loading of v1 files in batch mode
849 				parSettings.LoadFromFile(filename);
850 				parSettings.Decode(gPar, gParFractal, gAnimFrames, gKeyframes);
851 				settingsSpecified = true;
852 				systemData.lastSettingsFile = filename;
853 				systemData.settingsLoadedFromCLI = true;
854 				if (cliData.touch)
855 				{
856 					parSettings.CreateText(gPar, gParFractal, gAnimFrames, gKeyframes);
857 					parSettings.SaveToFile(filename);
858 					WriteLogString("cCommandLineInterface::handleArgs(): touched file", filename, 3);
859 					exit(0);
860 				}
861 			}
862 			else if (parSettings.LoadFromString(args[0]))
863 			{
864 				// the whole settings file is specified as an argument from stdin
865 				parSettings.Decode(gPar, gParFractal, gAnimFrames, gKeyframes);
866 				settingsSpecified = true;
867 				systemData.lastSettingsFile = "from stdin";
868 				systemData.settingsLoadedFromCLI = true;
869 			}
870 			else
871 			{
872 				cErrorMessage::showMessage(QObject::tr("Cannot load file!\n"), cErrorMessage::errorMessage);
873 				qCritical() << "\nSetting file " << filename << " not found\n";
874 				parser.showHelp(cliErrorLoadSettingsFile);
875 			}
876 		}
877 		else
878 		{
879 			// queue render
880 			cliOperationalMode = modeQueue;
881 			cliData.nogui = true;
882 			systemData.noGui = true;
883 			try
884 			{
885 				gQueue = new cQueue(gMainInterface, systemDirectories.GetQueueFractlistFile(),
886 					systemDirectories.GetQueueFolder(), gMainInterface);
887 			}
888 			catch (QString &ex)
889 			{
890 				cErrorMessage::showMessage(
891 					QObject::tr("Cannot init queue: ") + ex, cErrorMessage::errorMessage);
892 				parser.showHelp(cliErrorQueueInit);
893 			}
894 			for (int i = 0; i < args.size(); i++)
895 			{
896 				const QString filename = args[i];
897 				if (QDir(args[i]).exists())
898 				{
899 					// specified input is a folder, load all fractal files contained in this folder
900 					gQueue->AppendFolder(filename);
901 					settingsSpecified = true;
902 				}
903 				else if (QFileInfo(filename).suffix() == QString("fractlist"))
904 				{
905 					// specified input is a queue list file, append all entries to the current queue
906 					gQueue->AppendList(filename);
907 					settingsSpecified = true;
908 				}
909 				else
910 				{
911 					// specified input can only be an ordinary fract file, try to append to queuelist
912 					gQueue->Append(filename);
913 					settingsSpecified = true;
914 				}
915 			}
916 		}
917 	}
918 }
919 
handleOverrideParameters() const920 void cCommandLineInterface::handleOverrideParameters() const
921 {
922 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
923 	QStringList overrideParameters =
924 		cliData.overrideParametersText.split("#", QString::SkipEmptyParts);
925 #else
926 	QStringList overrideParameters = cliData.overrideParametersText.split("#", Qt::SkipEmptyParts);
927 #endif
928 
929 	for (int i = 0; i < overrideParameters.size(); i++)
930 	{
931 		int fractalIndex = -1;
932 		QRegularExpression reType("^fractal([0-9]+)_(.*)$");
933 		QRegularExpressionMatch matchType = reType.match(overrideParameters[i]);
934 		if (matchType.hasMatch())
935 		{
936 			fractalIndex = matchType.captured(1).toInt() - 1;
937 			overrideParameters[i] = matchType.captured(2);
938 		}
939 		QStringList overrideParameter = overrideParameters[i].split(QRegExp("\\="));
940 		if (overrideParameter.size() == 2)
941 		{
942 			if (fractalIndex >= 0 && fractalIndex < NUMBER_OF_FRACTALS)
943 			{
944 				gParFractal->at(fractalIndex)
945 					->Set(overrideParameter[0].trimmed(), overrideParameter[1].trimmed());
946 			}
947 			else
948 			{
949 				gPar->Set(overrideParameter[0].trimmed(), overrideParameter[1].trimmed());
950 			}
951 		}
952 	}
953 }
954 
handleResolution()955 void cCommandLineInterface::handleResolution()
956 {
957 	bool checkParse = true;
958 	QStringList resolutionParameters = cliData.resolution.split(QRegExp("x"));
959 	if (resolutionParameters.size() == 2)
960 	{
961 		const int xRes = resolutionParameters[0].toInt(&checkParse);
962 		const int yRes = resolutionParameters[1].toInt(&checkParse);
963 		if (!checkParse || xRes <= 0 || yRes <= 0)
964 		{
965 			cErrorMessage::showMessage(QObject::tr("Specified resolution not valid\n"
966 																						 "both dimensions need to be > 0"),
967 				cErrorMessage::errorMessage);
968 			parser.showHelp(cliErrorResolutionInvalid);
969 		}
970 		gPar->Set("image_width", xRes);
971 		gPar->Set("image_height", yRes);
972 	}
973 	else
974 	{
975 		cErrorMessage::showMessage(QObject::tr("Specified resolution not valid\n"
976 																					 "resolution has to be in the form WIDTHxHEIGHT"),
977 			cErrorMessage::errorMessage);
978 		parser.showHelp(cliErrorResolutionInvalid);
979 	}
980 }
981 
handleFpk()982 void cCommandLineInterface::handleFpk()
983 {
984 	bool checkParse = true;
985 	const int fpk = cliData.fpkText.toInt(&checkParse);
986 	if (!checkParse || fpk <= 0)
987 	{
988 		cErrorMessage::showMessage(QObject::tr("Specified frames per key not valid\n"
989 																					 "need to be > 0"),
990 			cErrorMessage::errorMessage);
991 		parser.showHelp(cliErrorFPKInvalid);
992 	}
993 	gPar->Set("frames_per_keyframe", fpk);
994 }
995 
handleImageFileFormat()996 void cCommandLineInterface::handleImageFileFormat()
997 {
998 	QStringList allowedImageFileFormat({"jpg", "png", "png16", "png16alpha", "exr", "tiff"});
999 	if (!allowedImageFileFormat.contains(cliData.imageFileFormat))
1000 	{
1001 		cErrorMessage::showMessage(QObject::tr("Specified imageFileFormat is not valid\n"
1002 																					 "allowed formats are: ")
1003 																 + allowedImageFileFormat.join(", "),
1004 			cErrorMessage::errorMessage);
1005 		parser.showHelp(cliErrorImageFileFormatInvalid);
1006 	}
1007 	else
1008 	{
1009 		const ImageFileSave::enumImageFileType fileType =
1010 			ImageFileSave::ImageFileType(cliData.imageFileFormat);
1011 		gPar->Set("keyframe_animation_image_type", int(fileType));
1012 		gPar->Set("flight_animation_image_type", int(fileType));
1013 	}
1014 }
1015 
handleFlight()1016 void cCommandLineInterface::handleFlight()
1017 {
1018 	if (gAnimFrames->GetNumberOfFrames() > 0)
1019 	{
1020 		cliOperationalMode = modeFlight;
1021 		cliData.nogui = true;
1022 		systemData.noGui = true;
1023 	}
1024 	else
1025 	{
1026 		cErrorMessage::showMessage(
1027 			QObject::tr("There are no flight animation frames in specified settings file"),
1028 			cErrorMessage::errorMessage);
1029 		parser.showHelp(cliErrorFlightNoFrames);
1030 	}
1031 }
1032 
handleKeyframe()1033 void cCommandLineInterface::handleKeyframe()
1034 {
1035 	if (cliOperationalMode == modeFlight)
1036 	{
1037 		cErrorMessage::showMessage(
1038 			QObject::tr("You cannot render keyframe animation at the same time as flight animation"),
1039 			cErrorMessage::errorMessage);
1040 	}
1041 	else
1042 	{
1043 		if (gKeyframes->GetNumberOfFrames() > 0)
1044 		{
1045 			cliOperationalMode = modeKeyframe;
1046 			cliData.nogui = true;
1047 			systemData.noGui = true;
1048 		}
1049 		else
1050 		{
1051 			cErrorMessage::showMessage(QObject::tr("There are no keyframes in specified settings file"),
1052 				cErrorMessage::errorMessage);
1053 			parser.showHelp(cliErrorKeyframeNoFrames);
1054 		}
1055 	}
1056 }
1057 
handleStartFrame()1058 void cCommandLineInterface::handleStartFrame()
1059 {
1060 	bool checkParse = true;
1061 	const int startFrame = cliData.startFrameText.toInt(&checkParse);
1062 	if (cliOperationalMode == modeFlight)
1063 	{
1064 		if (startFrame <= gAnimFrames->GetNumberOfFrames())
1065 		{
1066 			gPar->Set("flight_first_to_render", startFrame);
1067 		}
1068 		else
1069 		{
1070 			cErrorMessage::showMessage(
1071 				QObject::tr("Animation has only %1 frames").arg(gAnimFrames->GetNumberOfFrames()),
1072 				cErrorMessage::errorMessage);
1073 			parser.showHelp(cliErrorFlightStartFrameOutOfRange);
1074 		}
1075 	}
1076 
1077 	if (cliOperationalMode == modeKeyframe)
1078 	{
1079 		int numberOfFrames =
1080 			(gKeyframes->GetNumberOfFrames() - 1) * gPar->Get<int>("frames_per_keyframe");
1081 		if (numberOfFrames < 0) numberOfFrames = 0;
1082 
1083 		if (startFrame <= numberOfFrames)
1084 		{
1085 			gPar->Set("keyframe_first_to_render", startFrame);
1086 		}
1087 		else
1088 		{
1089 			cErrorMessage::showMessage(QObject::tr("Animation has only %1 frames").arg(numberOfFrames),
1090 				cErrorMessage::errorMessage);
1091 			parser.showHelp(cliErrorKeyframeStartFrameOutOfRange);
1092 		}
1093 	}
1094 }
1095 
handleEndFrame()1096 void cCommandLineInterface::handleEndFrame()
1097 {
1098 	bool checkParse = true;
1099 	const int endFrame = cliData.endFrameText.toInt(&checkParse);
1100 	if (cliOperationalMode == modeFlight)
1101 	{
1102 		if (endFrame <= gAnimFrames->GetNumberOfFrames())
1103 		{
1104 			if (endFrame > gPar->Get<int>("flight_first_to_render"))
1105 			{
1106 				gPar->Set("flight_last_to_render", endFrame);
1107 			}
1108 			else
1109 			{
1110 				cErrorMessage::showMessage(
1111 					QObject::tr("End frame has to be greater than start frame which is %1")
1112 						.arg(gPar->Get<int>("flight_first_to_render")),
1113 					cErrorMessage::errorMessage);
1114 				parser.showHelp(cliErrorFlightEndFrameSmallerStartFrame);
1115 			}
1116 		}
1117 		else
1118 		{
1119 			cErrorMessage::showMessage(
1120 				QObject::tr("Animation has only %1 frames").arg(gAnimFrames->GetNumberOfFrames()),
1121 				cErrorMessage::errorMessage);
1122 			parser.showHelp(cliErrorFlightEndFrameOutOfRange);
1123 		}
1124 	}
1125 
1126 	if (cliOperationalMode == modeKeyframe)
1127 	{
1128 		int numberOfFrames =
1129 			(gKeyframes->GetNumberOfFrames() - 1) * gPar->Get<int>("frames_per_keyframe");
1130 		if (numberOfFrames < 0) numberOfFrames = 0;
1131 
1132 		if (endFrame <= numberOfFrames)
1133 		{
1134 			if (endFrame > gPar->Get<int>("keyframe_first_to_render"))
1135 			{
1136 				gPar->Set("keyframe_last_to_render", endFrame);
1137 			}
1138 			else
1139 			{
1140 				cErrorMessage::showMessage(
1141 					QObject::tr("End frame has to be greater than start frame which is %1")
1142 						.arg(gPar->Get<int>("keyframe_first_to_render")),
1143 					cErrorMessage::errorMessage);
1144 				parser.showHelp(cliErrorKeyframeEndFrameSmallerStartFrame);
1145 			}
1146 		}
1147 		else
1148 		{
1149 			cErrorMessage::showMessage(QObject::tr("Animation has only %1 frames").arg(numberOfFrames),
1150 				cErrorMessage::errorMessage);
1151 			parser.showHelp(cliErrorKeyframeEndFrameOutOfRange);
1152 		}
1153 	}
1154 }
1155 
handleVoxel()1156 void cCommandLineInterface::handleVoxel()
1157 {
1158 	QStringList allowedVoxelFormat({"ply", "slice"});
1159 	WriteLogString(
1160 		"CommandLineInterface::handleVoxel(): cliData.voxelFormat", cliData.voxelFormat, 3);
1161 	if (!allowedVoxelFormat.contains(cliData.voxelFormat))
1162 	{
1163 		cErrorMessage::showMessage(QObject::tr("Specified voxel format is not valid\n"
1164 																					 "allowed formats are: ")
1165 																 + allowedVoxelFormat.join(", "),
1166 			cErrorMessage::errorMessage);
1167 		parser.showHelp(cliErrorVoxelOutputFormatInvalid);
1168 	}
1169 
1170 	const QString folderString = gPar->Get<QString>("voxel_image_path");
1171 	QDir folder(folderString);
1172 	if (!folder.exists())
1173 	{
1174 		cErrorMessage::showMessage(
1175 			QObject::tr("Cannot start voxel export. Specified folder (%1) does not exist.")
1176 				.arg(folderString),
1177 			cErrorMessage::errorMessage);
1178 		parser.showHelp(cliErrorVoxelOutputFolderDoesNotExists);
1179 	}
1180 	cliOperationalMode = modeVoxel;
1181 	cliData.nogui = true;
1182 	systemData.noGui = true;
1183 }
1184 
handleGpu()1185 void cCommandLineInterface::handleGpu()
1186 {
1187 #ifdef USE_OPENCL
1188 	QTextStream out(stdout);
1189 	gOpenCl->Reset();
1190 	gOpenCl->InitPlatfromAndDevices();
1191 
1192 	gPar->Set("opencl_enabled", true);
1193 
1194 	// print available platforms
1195 	const QList<cOpenClHardware::sPlatformInformation> platforms =
1196 		gOpenCl->openClHardware->getPlatformsInformation();
1197 	if (platforms.size() == 0)
1198 	{
1199 		cErrorMessage::showMessage(
1200 			QObject::tr("No opencl platforms found"), cErrorMessage::errorMessage);
1201 		parser.showHelp(cliErrorOpenClNoPlatform);
1202 	}
1203 	gPar->Set("opencl_platform", 0);
1204 
1205 	const QList<cOpenClDevice::sDeviceInformation> devices =
1206 		gOpenCl->openClHardware->getDevicesInformation();
1207 	if (devices.size() == 0)
1208 	{
1209 		cErrorMessage::showMessage(QObject::tr("No opencl devices found"), cErrorMessage::errorMessage);
1210 		parser.showHelp(cliErrorOpenClNoDevice);
1211 	}
1212 	else
1213 	{
1214 		gPar->Set("opencl_device_list", QString(devices[0].hash.toHex()));
1215 		out << "Use " << devices[0].deviceName << " for OpenCL rendering\n";
1216 	}
1217 #else
1218 	cErrorMessage::showMessage(QObject::tr("Not compiled for opencl"), cErrorMessage::errorMessage);
1219 	parser.showHelp(cliErrorOpenClNotCompiled);
1220 #endif
1221 }
1222 
handleGpuAll()1223 void cCommandLineInterface::handleGpuAll()
1224 {
1225 #ifdef USE_OPENCL
1226 	QTextStream out(stdout);
1227 	gOpenCl->Reset();
1228 	gOpenCl->InitPlatfromAndDevices();
1229 
1230 	gPar->Set("opencl_enabled", true);
1231 
1232 	// print available platforms
1233 	const QList<cOpenClHardware::sPlatformInformation> platforms =
1234 		gOpenCl->openClHardware->getPlatformsInformation();
1235 	if (platforms.size() == 0)
1236 	{
1237 		cErrorMessage::showMessage(
1238 			QObject::tr("No opencl platforms found"), cErrorMessage::errorMessage);
1239 		parser.showHelp(cliErrorOpenClNoPlatform);
1240 	}
1241 	gPar->Set("opencl_platform", 0);
1242 
1243 	const QList<cOpenClDevice::sDeviceInformation> devices =
1244 		gOpenCl->openClHardware->getDevicesInformation();
1245 	if (devices.size() == 0)
1246 	{
1247 		cErrorMessage::showMessage(QObject::tr("No opencl devices found"), cErrorMessage::errorMessage);
1248 		parser.showHelp(cliErrorOpenClNoDevice);
1249 	}
1250 	else
1251 	{
1252 		QString listHashCodes;
1253 		for (int i = 0; i < devices.size(); i++)
1254 		{
1255 			if (i > 0) listHashCodes += "|";
1256 			listHashCodes += QString(devices[i].hash.toHex());
1257 			out << "Use " << devices[i].deviceName << " for OpenCL rendering\n";
1258 		}
1259 		gPar->Set("opencl_device_list", listHashCodes);
1260 	}
1261 #else
1262 	cErrorMessage::showMessage(QObject::tr("Not compiled for opencl"), cErrorMessage::errorMessage);
1263 	parser.showHelp(cliErrorOpenClNotCompiled);
1264 #endif
1265 }
1266