1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2014-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)
31  *
32  * cRenderJob class - prepare and coordinate rendering of single or multiple images (animation)
33  */
34 
35 #include "render_job.hpp"
36 
37 #include <algorithm>
38 
39 #include <QWidget>
40 
41 #include "ao_modes.h"
42 #include "cimage.hpp"
43 #include "fractparams.hpp"
44 #include "global_data.hpp"
45 #include "image_scale.hpp"
46 #include "netrender.hpp"
47 #include "nine_fractals.hpp"
48 #include "opencl_engine_render_dof.h"
49 #include "opencl_engine_render_fractal.h"
50 #include "opencl_engine_render_post_filter.h"
51 #include "opencl_engine_render_ssao.h"
52 #include "opencl_global.h"
53 #include "progress_text.hpp"
54 #include "render_data.hpp"
55 #include "render_image.hpp"
56 #include "render_ssao.h"
57 #include "rendering_configuration.hpp"
58 #include "stereo.h"
59 #include "system_data.hpp"
60 #include "write_log.hpp"
61 
cRenderJob(const std::shared_ptr<cParameterContainer> _params,const std::shared_ptr<cFractalContainer> _fractal,std::shared_ptr<cImage> _image,bool * _stopRequest,QWidget * _qWidget)62 cRenderJob::cRenderJob(const std::shared_ptr<cParameterContainer> _params,
63 	const std::shared_ptr<cFractalContainer> _fractal, std::shared_ptr<cImage> _image,
64 	bool *_stopRequest, QWidget *_qWidget)
65 		: QObject()
66 {
67 	WriteLog("cRenderJob::cRenderJob", 2);
68 	image = _image;
69 
70 	// create new copy of parameter container
71 	paramsContainer.reset(new cParameterContainer());
72 	*paramsContainer = *_params;
73 	fractalContainer.reset(new cFractalContainer());
74 	*fractalContainer = *_fractal;
75 	canUseNetRender = false;
76 
77 	width = 0;
78 	height = 0;
79 	mode = still;
80 	ready = false;
81 	inProgress = false;
82 	if (_qWidget)
83 	{
84 		imageWidget = _qWidget;
85 		hasQWidget = true;
86 		connect(this, SIGNAL(SetMinimumWidgetSize(int, int)), imageWidget,
87 			SLOT(slotSetMinimumSize(int, int)));
88 	}
89 	else
90 	{
91 		imageWidget = nullptr;
92 		hasQWidget = false;
93 	}
94 
95 	totalNumberOfCPUs = systemData.numberOfThreads;
96 	renderData = nullptr;
97 	useSizeFromImage = false;
98 	stopRequest = _stopRequest;
99 
100 	id++;
101 	// qDebug() << "Id" << id;
102 }
103 int cRenderJob::id = 0;
104 int cRenderJob::runningJobs = 0;
105 
~cRenderJob()106 cRenderJob::~cRenderJob()
107 {
108 	id--;
109 	// qDebug() << "Id" << id;
110 
111 	if (canUseNetRender) gNetRender->Release();
112 
113 	WriteLog("Job finished and closed", 2);
114 }
115 
Init(enumMode _mode,const cRenderingConfiguration & config)116 bool cRenderJob::Init(enumMode _mode, const cRenderingConfiguration &config)
117 {
118 	WriteLog("cRenderJob::Init id = " + QString::number(id), 2);
119 
120 	mode = _mode;
121 
122 	if (config.UseNetRender()) canUseNetRender = gNetRender->Block();
123 
124 	cStereo stereo;
125 	stereo.SetMode(cStereo::enumStereoMode(paramsContainer->Get<int>("stereo_mode")));
126 	if (!paramsContainer->Get<bool>("stereo_enabled")) stereo.SetMode(cStereo::stereoDisabled);
127 	if (paramsContainer->Get<bool>("stereo_swap_eyes")) stereo.SwapEyes();
128 
129 	// needed when image has to fit in widget
130 	if (useSizeFromImage)
131 	{
132 		paramsContainer->Set("image_width", int(image->GetWidth()));
133 		paramsContainer->Set("image_height", int(image->GetHeight()));
134 	}
135 	width = paramsContainer->Get<int>("image_width");
136 	height = paramsContainer->Get<int>("image_height");
137 
138 	if (stereo.isEnabled() && (!gNetRender->IsClient() || gNetRender->IsAnimation()))
139 	{
140 		CVector2<int> modifiedResolution = stereo.ModifyImageResolution(CVector2<int>(width, height));
141 		width = modifiedResolution.x;
142 		height = modifiedResolution.y;
143 		paramsContainer->Set("image_width", width);
144 		paramsContainer->Set("image_height", height);
145 	}
146 
147 	sImageOptional imageOptional;
148 	imageOptional.optionalNormal = paramsContainer->Get<bool>("normal_enabled");
149 	imageOptional.optionalNormalWorld = paramsContainer->Get<bool>("normalWorld_enabled");
150 	imageOptional.optionalSpecular = paramsContainer->Get<bool>("specular_enabled");
151 	imageOptional.optionalWorld = paramsContainer->Get<bool>("world_enabled");
152 	imageOptional.optionalDiffuse = paramsContainer->Get<bool>("diffuse_enabled");
153 
154 	// FIXME: option for optionalNormal (denoiser)
155 	imageOptional.optionalNormalWorld = true;
156 
157 	emit updateProgressAndStatus(
158 		QObject::tr("Initialization"), QObject::tr("Setting up image buffers"), 0.0);
159 	// gApplication->processEvents();
160 
161 	if (!InitImage(width, height, imageOptional))
162 	{
163 		ready = false;
164 		return false;
165 	}
166 
167 	image->SetStereoLeftRight(stereo.GetMode() == cStereo::stereoLeftRight);
168 
169 	if (image->IsMainImage())
170 	{
171 		// clear image before start rendering
172 		if ((gNetRender->IsClient() || gNetRender->IsServer()) && canUseNetRender)
173 		{
174 			image->ClearImage();
175 		}
176 	}
177 
178 	if (config.UseNetRender() && canUseNetRender)
179 	{
180 		// connect signals
181 		if (gNetRender->IsServer() && !gNetRender->IsAnimation())
182 		{
183 			connect(this, &cRenderJob::SendNetRenderJob, gNetRender, &cNetRender::SetCurrentJob);
184 			connect(this, &cRenderJob::SendNetRenderSetup, gNetRender, &cNetRender::SendSetup);
185 		}
186 	}
187 
188 	totalNumberOfCPUs = systemData.numberOfThreads;
189 	// totalNumberOfCPUs = 1;
190 	// systemData.numberOfThreads = 1;
191 
192 	// aux renderer data
193 	renderData.reset(new sRenderData);
194 
195 	renderData->stereo = stereo;
196 	renderData->configuration = config;
197 
198 	ready = true;
199 
200 	return true;
201 }
202 
InitImage(int w,int h,const sImageOptional & optional)203 bool cRenderJob::InitImage(int w, int h, const sImageOptional &optional)
204 {
205 	WriteLog("cRenderJob::InitImage", 2);
206 
207 	if (!image->ChangeSize(w, h, optional))
208 	{
209 		printf("Cannot allocate memory. Maybe image is too big");
210 		return false;
211 	}
212 	else
213 	{
214 		WriteLog("complexImage allocated", 2);
215 		if (hasQWidget)
216 		{
217 			double scale =
218 				ImageScaleComboSelection2Double(paramsContainer->Get<int>("image_preview_scale"));
219 			if (useSizeFromImage) scale = 0.0;
220 			scale = CalcMainImageScale(
221 				scale, image->GetPreviewVisibleWidth(), image->GetPreviewVisibleHeight(), image);
222 			image->CreatePreview(
223 				scale, image->GetPreviewVisibleWidth(), image->GetPreviewVisibleHeight(), imageWidget);
224 			// image->UpdatePreview();
225 			emit SetMinimumWidgetSize(image->GetPreviewWidth(), image->GetPreviewHeight());
226 		}
227 
228 		// qDebug() << "Memory for image: " << image->GetUsedMB() << " MB" << endl;
229 		return true;
230 	}
231 }
232 
LoadTextures(int frameNo,const cRenderingConfiguration & config)233 void cRenderJob::LoadTextures(int frameNo, const cRenderingConfiguration &config)
234 {
235 	//	if (gNetRender->IsClient() && renderData->configuration.UseNetRender())
236 	//	{
237 	//		// get received textures from NetRender buffer
238 	//		if (paramsContainer->Get<bool>("textured_background"))
239 	//			renderData->textures.backgroundTexture.FromQByteArray(
240 	//				gNetRender->GetTexture(paramsContainer->Get<QString>("file_background"), frameNo),
241 	//				cTexture::doNotUseMipmaps);
242 	//
243 	//		if (paramsContainer->Get<bool>("env_mapping_enable"))
244 	//			renderData->textures.envmapTexture.FromQByteArray(
245 	//				gNetRender->GetTexture(paramsContainer->Get<QString>("file_envmap"), frameNo),
246 	//				cTexture::doNotUseMipmaps);
247 	//
248 	//		if (paramsContainer->Get<int>("ambient_occlusion_mode") == params::AOModeMultipleRays
249 	//				&& paramsContainer->Get<bool>("ambient_occlusion_enabled"))
250 	//			renderData->textures.lightmapTexture.FromQByteArray(
251 	//				gNetRender->GetTexture(paramsContainer->Get<QString>("file_lightmap"), frameNo),
252 	//				cTexture::doNotUseMipmaps);
253 	//	}
254 	//	else
255 	//	{
256 	if (paramsContainer->Get<bool>("textured_background"))
257 		renderData->textures.backgroundTexture =
258 			cTexture(paramsContainer->Get<QString>("file_background"), cTexture::doNotUseMipmaps, frameNo,
259 				config.UseIgnoreErrors(), config.UseNetRender());
260 
261 	if (paramsContainer->Get<bool>("env_mapping_enable"))
262 		renderData->textures.envmapTexture = cTexture(paramsContainer->Get<QString>("file_envmap"),
263 			cTexture::doNotUseMipmaps, frameNo, config.UseIgnoreErrors(), config.UseNetRender());
264 
265 	if (paramsContainer->Get<int>("ambient_occlusion_mode") == params::AOModeMultipleRays
266 			&& paramsContainer->Get<bool>("ambient_occlusion_enabled"))
267 		renderData->textures.lightmapTexture = cTexture(paramsContainer->Get<QString>("file_lightmap"),
268 			cTexture::doNotUseMipmaps, frameNo, config.UseIgnoreErrors(), config.UseNetRender());
269 	//	}
270 }
271 
PrepareData()272 void cRenderJob::PrepareData()
273 {
274 	WriteLog("Init renderData", 2);
275 	renderData->rendererID = id;
276 
277 	if (!canUseNetRender) renderData->configuration.DisableNetRender();
278 
279 	// set image region to render
280 	if (paramsContainer->Get<bool>("legacy_coordinate_system"))
281 	{
282 		renderData->imageRegion.Set(-0.5, -0.5, 0.5, 0.5);
283 	}
284 	else
285 	{
286 		renderData->imageRegion.Set(-0.5, 0.5, 0.5, -0.5);
287 	}
288 
289 	// renderData->screenRegion.Set(width*0.15, height*0.15, width*0.85, height*0.85);
290 	renderData->screenRegion.Set(0, 0, width, height);
291 	// TODO to correct resolution and aspect ratio according to region data
292 
293 	// textures are deleted with destruction of renderData
294 
295 	emit updateProgressAndStatus(QObject::tr("Initialization"), QObject::tr("Loading textures"), 0.0);
296 	// gApplication->processEvents();
297 
298 	int frameNo = paramsContainer->Get<int>("frame_no");
299 
300 	// don't load textures if fast or medium OpenCL mode
301 	bool loadTextures = !(
302 		paramsContainer->Get<bool>("opencl_enabled")
303 		&& (cOpenClEngineRenderFractal::enumClRenderEngineMode(paramsContainer->Get<int>("opencl_mode"))
304 					== cOpenClEngineRenderFractal::clRenderEngineTypeFast
305 				|| cOpenClEngineRenderFractal::enumClRenderEngineMode(
306 						 paramsContainer->Get<int>("opencl_mode"))
307 						 == cOpenClEngineRenderFractal::clRenderEngineTypeLimited));
308 
309 	if (loadTextures)
310 	{
311 		LoadTextures(frameNo, renderData->configuration);
312 	}
313 
314 	// assign stop handler
315 	renderData->stopRequest = stopRequest;
316 
317 	CreateMaterialsMap(paramsContainer, &renderData->materials, loadTextures,
318 		renderData->configuration.UseIgnoreErrors(), renderData->configuration.UseNetRender());
319 
320 	// preparation of lights
321 	// connect signal for progress bar update
322 	connect(&renderData->lights,
323 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
324 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)),
325 		Qt::UniqueConnection);
326 
327 	renderData->lights.Set(paramsContainer, fractalContainer, loadTextures,
328 		renderData->configuration.UseIgnoreErrors(), renderData->configuration.UseNetRender());
329 
330 	renderData->objectData.resize(NUMBER_OF_FRACTALS); // reserve first items for fractal formulas
331 }
332 
Execute()333 bool cRenderJob::Execute()
334 {
335 	image->BlockImage();
336 
337 	runningJobs++;
338 
339 	bool result = false;
340 
341 	bool twoPassStereo = false;
342 	int noOfRepeats = GetNumberOfRepeatsOfStereoLoop(&twoPassStereo);
343 	QElapsedTimer totalTime;
344 	totalTime.start();
345 
346 	PrepareData();
347 
348 	if (!paramsContainer->Get<bool>("opencl_enabled") || !gOpenCl
349 			|| cOpenClEngineRenderFractal::enumClRenderEngineMode(
350 					 paramsContainer->Get<int>("opencl_mode"))
351 					 == cOpenClEngineRenderFractal::clRenderEngineTypeNone)
352 	{
353 		for (int repeat = 0; repeat < noOfRepeats; repeat++)
354 		{
355 			emit updateProgressAndStatus(
356 				QObject::tr("Rendering image"), QObject::tr("Starting rendering of image"), 0.0);
357 
358 			SetupStereoEyes(repeat, twoPassStereo);
359 
360 			// send settings to all NetRender clients
361 			if (renderData->configuration.UseNetRender())
362 			{
363 				InitNetRender();
364 			}
365 
366 			// qDebug() << "runningJobs" << runningJobs;
367 
368 			inProgress = true;
369 			*renderData->stopRequest = false;
370 
371 			WriteLog("cRenderJob::Execute(void): running jobs = " + QString::number(runningJobs), 2);
372 
373 			// move parameters from containers to structures
374 			std::shared_ptr<sParamRender> params(
375 				new sParamRender(paramsContainer, &renderData->objectData));
376 			std::shared_ptr<cNineFractals> fractals(new cNineFractals(fractalContainer, paramsContainer));
377 
378 			renderData->ValidateObjects();
379 
380 			// recalculation of some parameters;
381 			params->resolution = 1.0 / image->GetHeight();
382 			ReduceDetail();
383 
384 			InitStatistics(fractals.get());
385 
386 			// initialize histograms
387 			renderData->statistics.histogramIterations.Resize(paramsContainer->Get<int>("N"));
388 			renderData->statistics.histogramStepCount.Resize(1000);
389 			renderData->statistics.Reset();
390 			renderData->statistics.usedDEType = fractals->GetDETypeString();
391 
392 			// create and execute renderer
393 			std::unique_ptr<cRenderer> renderer(new cRenderer(params, fractals, renderData, image));
394 
395 			ConnectUpdateSinalsSlots(renderer.get());
396 
397 			if (renderData->configuration.UseNetRender())
398 			{
399 				ConnectNetRenderSignalsSlots(renderer.get());
400 			}
401 
402 			result = renderer->RenderImage();
403 
404 			if (twoPassStereo && repeat == 0) renderData->stereo.StoreImageInBuffer(image);
405 		}
406 	}
407 
408 #ifdef USE_OPENCL
409 	if (paramsContainer->Get<bool>("opencl_enabled")
410 			&& cOpenClEngineRenderFractal::enumClRenderEngineMode(
411 					 paramsContainer->Get<int>("opencl_mode"))
412 					 != cOpenClEngineRenderFractal::clRenderEngineTypeNone)
413 	{
414 		cProgressText progressText;
415 		progressText.ResetTimer();
416 
417 		*renderData->stopRequest = false;
418 
419 		for (int repeat = 0; repeat < noOfRepeats; repeat++)
420 		{
421 			SetupStereoEyes(repeat, twoPassStereo);
422 
423 			// move parameters from containers to structures
424 			std::shared_ptr<sParamRender> params(
425 				new sParamRender(paramsContainer, &renderData->objectData));
426 			std::shared_ptr<cNineFractals> fractals(new cNineFractals(fractalContainer, paramsContainer));
427 
428 			renderData->ValidateObjects();
429 
430 			image->SetImageParameters(params->imageAdjustments);
431 
432 			InitStatistics(fractals.get());
433 			emit updateStatistics(renderData->statistics);
434 
435 			image->SetFastPreview(true);
436 
437 			// render all with OpenCL
438 			result = RenderFractalWithOpenCl(params, fractals, &progressText);
439 
440 			if (renderData->stereo.isEnabled()
441 					&& (renderData->stereo.GetMode() == cStereo::stereoLeftRight
442 							|| renderData->stereo.GetMode() == cStereo::stereoTopBottom))
443 			{
444 				// stereoscopic rendering of SSAO (separate for each half of image)
445 				cRegion<int> region;
446 				region = renderData->stereo.GetRegion(
447 					CVector2<int>(image->GetWidth(), image->GetHeight()), cStereo::eyeLeft);
448 				RenderSSAOWithOpenCl(params, region, &progressText, &result);
449 
450 				region = renderData->stereo.GetRegion(
451 					CVector2<int>(image->GetWidth(), image->GetHeight()), cStereo::eyeRight);
452 				RenderSSAOWithOpenCl(params, region, &progressText, &result);
453 			}
454 			else
455 			{
456 				RenderSSAOWithOpenCl(params, renderData->screenRegion, &progressText, &result);
457 			}
458 
459 			RenderDOFWithOpenCl(params, &result);
460 
461 			if (renderData->stereo.isEnabled()
462 					&& (renderData->stereo.GetMode() == cStereo::stereoLeftRight
463 							|| renderData->stereo.GetMode() == cStereo::stereoTopBottom))
464 			{
465 				// stereoscopic rendering of SSAO (separate for each half of image)
466 				cRegion<int> region;
467 				region = renderData->stereo.GetRegion(
468 					CVector2<int>(image->GetWidth(), image->GetHeight()), cStereo::eyeLeft);
469 				RenderPostFiltersWithOpenCl(params, region, &progressText, &result);
470 
471 				region = renderData->stereo.GetRegion(
472 					CVector2<int>(image->GetWidth(), image->GetHeight()), cStereo::eyeRight);
473 				RenderPostFiltersWithOpenCl(params, region, &progressText, &result);
474 			}
475 			else
476 			{
477 				RenderPostFiltersWithOpenCl(params, renderData->screenRegion, &progressText, &result);
478 			}
479 
480 			if (!*renderData->stopRequest)
481 			{
482 				if (cOpenClEngineRenderFractal::enumClRenderEngineMode(
483 							paramsContainer->Get<int>("opencl_mode"))
484 							== cOpenClEngineRenderFractal::clRenderEngineTypeFast
485 						|| mode == flightAnimRecord)
486 					image->SetFastPreview(true);
487 				else
488 					image->SetFastPreview(false);
489 
490 				if (image->IsPreview())
491 				{
492 					image->UpdatePreview();
493 					WriteLog("image->GetImageWidget()->update()", 2);
494 					emit updateImage();
495 				}
496 			}
497 
498 			if (twoPassStereo && repeat == 0) renderData->stereo.StoreImageInBuffer(image);
499 		} // next repeat
500 
501 		image->SetFastPreview(false);
502 
503 		gApplication->processEvents();
504 		emit updateProgressAndStatus(
505 			tr("OpenCl - rendering - all finished"), progressText.getText(1.0), 1.0);
506 		emit signalTotalRenderTime(progressText.getTime());
507 	}
508 
509 #endif
510 
511 	if (twoPassStereo)
512 	{
513 		renderData->stereo.MixImages(image);
514 		if (image->IsPreview())
515 		{
516 			WriteLog("image->ConvertTo8bit()", 2);
517 			image->ConvertTo8bitChar();
518 			WriteLog("image->UpdatePreview()", 2);
519 			image->UpdatePreview();
520 			WriteLog("image->GetImageWidget()->update()", 2);
521 			emit updateImage();
522 		}
523 	}
524 
525 	if (result)
526 	{
527 		emit fullyRendered(tr("Finished Render"), tr("The image has been rendered completely."));
528 		emit fullyRenderedTime(totalTime.elapsed() / 1000.0);
529 	}
530 
531 	inProgress = false;
532 
533 	WriteLog("cRenderJob::Execute(void): finished", 2);
534 
535 	image->setMeta(paramsContainer->getImageMeta());
536 	image->ReleaseImage();
537 
538 	runningJobs--;
539 	// qDebug() << "runningJobs" << runningJobs;
540 
541 	return result;
542 }
543 
GetNumberOfRepeatsOfStereoLoop(bool * twoPassStereo)544 int cRenderJob::GetNumberOfRepeatsOfStereoLoop(bool *twoPassStereo)
545 {
546 	int noOfRepeats = 1;
547 	if ((!gNetRender->IsClient() || gNetRender->IsAnimation())
548 			&& paramsContainer->Get<bool>("stereo_enabled")
549 			&& paramsContainer->Get<int>("stereo_mode") == cStereo::stereoRedCyan
550 			&& ((paramsContainer->Get<bool>("ambient_occlusion_enabled")
551 						&& paramsContainer->Get<int>("ambient_occlusion_mode") == params::AOModeScreenSpace)
552 					|| (paramsContainer->Get<bool>("DOF_enabled")
553 							&& !paramsContainer->Get<bool>("DOF_monte_carlo"))))
554 	{
555 		noOfRepeats = 2;
556 		*twoPassStereo = true;
557 	}
558 	return noOfRepeats;
559 }
560 
SetupStereoEyes(int repeat,bool twoPassStereo)561 void cRenderJob::SetupStereoEyes(int repeat, bool twoPassStereo)
562 {
563 	// stereo rendering with SSAO or DOF (2 passes)
564 	if (twoPassStereo)
565 	{
566 		cStereo::enumEye eye;
567 		if (repeat == 0)
568 			eye = cStereo::eyeLeft;
569 		else
570 			eye = cStereo::eyeRight;
571 
572 		renderData->stereo.ForceEye(eye);
573 		paramsContainer->Set("stereo_actual_eye", int(eye));
574 	}
575 	else if (!gNetRender->IsClient() || gNetRender->IsAnimation())
576 	{
577 		paramsContainer->Set("stereo_actual_eye", int(cStereo::eyeNone));
578 	}
579 
580 	if (gNetRender->IsClient() && !gNetRender->IsAnimation())
581 	{
582 		cStereo::enumEye eye = cStereo::enumEye(paramsContainer->Get<int>("stereo_actual_eye"));
583 		if (eye != cStereo::eyeNone)
584 		{
585 			renderData->stereo.ForceEye(eye);
586 		}
587 	}
588 }
589 
InitNetRender()590 void cRenderJob::InitNetRender()
591 {
592 	if (gNetRender->IsServer() && !gNetRender->IsAnimation())
593 	{
594 		// new random id for NetRender
595 		qint32 renderId = rand();
596 		gNetRender->SetCurrentRenderId(renderId);
597 
598 		// calculation of starting positions list and sending id to clients
599 		renderData->netRenderStartingPositions.clear();
600 
601 		int clientIndex = 0;
602 		int clientWorkerIndex = 0;
603 
604 		int workersCount =
605 			gNetRender->getTotalWorkerCount() + renderData->configuration.GetNumberOfThreads();
606 
607 		QList<int> startingPositionsToSend;
608 
609 		for (int i = 0; i < workersCount; i++)
610 		{
611 			// FIXME to correct starting positions considering region data
612 			if (i < renderData->configuration.GetNumberOfThreads())
613 			{
614 				renderData->netRenderStartingPositions.append(i * image->GetHeight() / workersCount);
615 			}
616 			else
617 			{
618 				startingPositionsToSend.append(i * image->GetHeight() / workersCount);
619 				clientWorkerIndex++;
620 
621 				if (clientWorkerIndex >= gNetRender->GetWorkerCount(clientIndex))
622 				{
623 					emit SendNetRenderSetup(clientIndex, startingPositionsToSend);
624 					clientIndex++;
625 					clientWorkerIndex = 0;
626 					startingPositionsToSend.clear();
627 				}
628 			}
629 		}
630 
631 		QStringList listOfUsedTextures = CreateListOfUsedTextures();
632 
633 		// send settings to all clients
634 		emit SendNetRenderJob(paramsContainer, fractalContainer, listOfUsedTextures);
635 	}
636 
637 	// get starting positions received from server
638 	if (gNetRender->IsClient() && !gNetRender->IsAnimation())
639 	{
640 		renderData->netRenderStartingPositions = gNetRender->GetStartingPositions();
641 	}
642 }
643 
InitStatistics(const cNineFractals * fractals)644 void cRenderJob::InitStatistics(const cNineFractals *fractals)
645 {
646 	// initialize histograms
647 	renderData->statistics.histogramIterations.Resize(paramsContainer->Get<int>("N"));
648 	renderData->statistics.histogramStepCount.Resize(1000);
649 	renderData->statistics.Reset();
650 	renderData->statistics.usedDEType = fractals->GetDETypeString();
651 }
652 
653 #ifdef USE_OPENCL
RenderFractalWithOpenCl(std::shared_ptr<sParamRender> params,std::shared_ptr<cNineFractals> fractals,cProgressText * progressText)654 bool cRenderJob::RenderFractalWithOpenCl(std::shared_ptr<sParamRender> params,
655 	std::shared_ptr<cNineFractals> fractals, cProgressText *progressText)
656 {
657 	bool result = false;
658 	connect(gOpenCl->openClEngineRenderFractal, SIGNAL(updateStatistics(cStatistics)), this,
659 		SIGNAL(updateStatistics(cStatistics)), Qt::UniqueConnection);
660 	connect(gOpenCl->openClEngineRenderFractal, SIGNAL(updateImage()), this, SIGNAL(updateImage()),
661 		Qt::UniqueConnection);
662 	connect(gOpenCl->openClEngineRenderFractal,
663 		SIGNAL(sendRenderedTilesList(QList<sRenderedTileData>)), this,
664 		SIGNAL(sendRenderedTilesList(QList<sRenderedTileData>)), Qt::UniqueConnection);
665 	connect(gOpenCl->openClEngineRenderFractal,
666 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
667 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)),
668 		Qt::UniqueConnection);
669 
670 	gOpenCl->openClEngineRenderFractal->Lock();
671 	progressText->ResetTimer();
672 	gOpenCl->openClEngineRenderFractal->SetParameters(
673 		paramsContainer, fractalContainer, params, fractals, renderData, false);
674 	if (gOpenCl->openClEngineRenderFractal->LoadSourcesAndCompile(paramsContainer))
675 	{
676 		gOpenCl->openClEngineRenderFractal->CreateKernel4Program(paramsContainer);
677 		WriteLogDouble("OpenCl render fractal - needed mem:",
678 			gOpenCl->openClEngineRenderFractal->CalcNeededMemory() / 1048576.0, 2);
679 		gOpenCl->openClEngineRenderFractal->PreAllocateBuffers(paramsContainer);
680 		gOpenCl->openClEngineRenderFractal->CreateCommandQueue();
681 		result = gOpenCl->openClEngineRenderFractal->RenderMulti(
682 			image, renderData->stopRequest, renderData.get());
683 	}
684 	WriteLog("OpenCL RenderMulti exited", 2);
685 	gOpenCl->openClEngineRenderFractal->ReleaseMemory();
686 	WriteLog("OpenCL memory released", 2);
687 	gOpenCl->openClEngineRenderFractal->Unlock();
688 
689 	emit updateProgressAndStatus(tr("OpenCl - rendering finished"), progressText->getText(1.0), 1.0);
690 	return result;
691 }
692 
RenderSSAOWithOpenCl(std::shared_ptr<sParamRender> params,const cRegion<int> & region,cProgressText * progressText,bool * result)693 void cRenderJob::RenderSSAOWithOpenCl(std::shared_ptr<sParamRender> params,
694 	const cRegion<int> &region, cProgressText *progressText, bool *result)
695 {
696 	if (!*renderData->stopRequest && *result == true)
697 	{
698 		if (params->ambientOcclusionEnabled && params->ambientOcclusionMode == params::AOModeScreenSpace
699 				&& cOpenClEngineRenderFractal::enumClRenderEngineMode(
700 						 paramsContainer->Get<int>("opencl_mode"))
701 						 != cOpenClEngineRenderFractal::clRenderEngineTypeFast)
702 		{
703 			connect(gOpenCl->openClEngineRenderSSAO, SIGNAL(updateImage()), this, SIGNAL(updateImage()));
704 			connect(gOpenCl->openClEngineRenderSSAO,
705 				SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
706 				SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
707 
708 			gOpenCl->openClEngineRenderSSAO->Lock();
709 			gOpenCl->openClEngineRenderSSAO->SetParameters(params.get(), region);
710 			if (gOpenCl->openClEngineRenderSSAO->LoadSourcesAndCompile(paramsContainer))
711 			{
712 				gOpenCl->openClEngineRenderSSAO->CreateKernel4Program(paramsContainer);
713 				qint64 neededMem = gOpenCl->openClEngineRenderSSAO->CalcNeededMemory();
714 				WriteLogDouble("OpenCl render SSAO - needed mem:", neededMem / 1048576.0, 2);
715 				if (neededMem / 1048576 < paramsContainer->Get<int>("opencl_memory_limit"))
716 				{
717 					gOpenCl->openClEngineRenderSSAO->PreAllocateBuffers(paramsContainer);
718 					gOpenCl->openClEngineRenderSSAO->CreateCommandQueue();
719 					*result = gOpenCl->openClEngineRenderSSAO->Render(image, renderData->stopRequest);
720 				}
721 				else
722 				{
723 					qCritical() << "Not enough GPU mem!";
724 					*result = false;
725 				}
726 
727 				if (!*result)
728 				{
729 					cRenderSSAO rendererSSAO(params, renderData, image);
730 					connect(&rendererSSAO,
731 						SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
732 						SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
733 					connect(&rendererSSAO, SIGNAL(updateImage()), this, SIGNAL(updateImage()));
734 					rendererSSAO.SetRegion(region);
735 					rendererSSAO.RenderSSAO();
736 
737 					// refresh image at end
738 					WriteLog("image->CompileImage()", 2);
739 					image->CompileImage();
740 
741 					if (image->IsPreview())
742 					{
743 						WriteLog("image->ConvertTo8bit()", 2);
744 						image->ConvertTo8bitChar();
745 						WriteLog("image->UpdatePreview()", 2);
746 						image->UpdatePreview();
747 						WriteLog("image->GetImageWidget()->update()", 2);
748 						emit updateImage();
749 					}
750 					*result = true;
751 				}
752 			}
753 			gOpenCl->openClEngineRenderSSAO->ReleaseMemory();
754 			gOpenCl->openClEngineRenderSSAO->Unlock();
755 
756 			emit updateProgressAndStatus(
757 				tr("OpenCl - rendering SSAO finished"), progressText->getText(1.0), 1.0);
758 		}
759 	}
760 }
761 
RenderPostFiltersWithOpenCl(std::shared_ptr<sParamRender> params,const cRegion<int> & region,cProgressText * progressText,bool * result)762 void cRenderJob::RenderPostFiltersWithOpenCl(std::shared_ptr<sParamRender> params,
763 	const cRegion<int> &region, cProgressText *progressText, bool *result)
764 {
765 	if (!*renderData->stopRequest && *result == true)
766 	{
767 		if (cOpenClEngineRenderFractal::enumClRenderEngineMode(paramsContainer->Get<int>("opencl_mode"))
768 				!= cOpenClEngineRenderFractal::clRenderEngineTypeFast)
769 		{
770 			connect(gOpenCl->openclEngineRenderPostFilter, &cOpenClEngineRenderPostFilter::updateImage,
771 				this, &cRenderJob::updateImage);
772 			connect(gOpenCl->openclEngineRenderPostFilter,
773 				&cOpenClEngineRenderPostFilter::updateProgressAndStatus, this,
774 				&cRenderJob::updateProgressAndStatus);
775 
776 			for (int i = cOpenClEngineRenderPostFilter::hdrBlur;
777 					 i <= cOpenClEngineRenderPostFilter::chromaticAberration; i++)
778 			{
779 
780 				bool skip = false;
781 				switch (cOpenClEngineRenderPostFilter::enumPostEffectType(i))
782 				{
783 					case cOpenClEngineRenderPostFilter::hdrBlur:
784 					{
785 						if (!params->hdrBlurEnabled) skip = true;
786 						break;
787 					}
788 					case cOpenClEngineRenderPostFilter::chromaticAberration:
789 					{
790 						if (!params->postChromaticAberrationEnabled) skip = true;
791 						break;
792 					}
793 				}
794 
795 				if (skip) continue;
796 
797 				gOpenCl->openclEngineRenderPostFilter->Lock();
798 				gOpenCl->openclEngineRenderPostFilter->SetParameters(
799 					params.get(), region, cOpenClEngineRenderPostFilter::enumPostEffectType(i));
800 				if (gOpenCl->openclEngineRenderPostFilter->LoadSourcesAndCompile(paramsContainer))
801 				{
802 					gOpenCl->openclEngineRenderPostFilter->CreateKernel4Program(paramsContainer);
803 					qint64 neededMem = gOpenCl->openclEngineRenderPostFilter->CalcNeededMemory();
804 					WriteLogDouble("OpenCl render Post Filter - needed mem:", neededMem / 1048576.0, 2);
805 					if (neededMem / 1048576 < paramsContainer->Get<int>("opencl_memory_limit"))
806 					{
807 						gOpenCl->openclEngineRenderPostFilter->PreAllocateBuffers(paramsContainer);
808 						gOpenCl->openclEngineRenderPostFilter->CreateCommandQueue();
809 						*result = gOpenCl->openclEngineRenderPostFilter->Render(image, renderData->stopRequest);
810 					}
811 					else
812 					{
813 						qCritical() << "Not enough GPU mem!";
814 						*result = false;
815 					}
816 				}
817 				gOpenCl->openclEngineRenderPostFilter->ReleaseMemory();
818 				gOpenCl->openclEngineRenderPostFilter->Unlock();
819 			}
820 			emit updateProgressAndStatus(
821 				tr("OpenCl - rendering Post Filter finished"), progressText->getText(1.0), 1.0);
822 		}
823 	}
824 }
825 
RenderDOFWithOpenCl(std::shared_ptr<sParamRender> params,bool * result)826 void cRenderJob::RenderDOFWithOpenCl(std::shared_ptr<sParamRender> params, bool *result)
827 {
828 	if (!*renderData->stopRequest)
829 	{
830 		if (params->DOFEnabled && !params->DOFMonteCarlo)
831 		{
832 			connect(gOpenCl->openclEngineRenderDOF, SIGNAL(updateImage()), this, SIGNAL(updateImage()));
833 			connect(gOpenCl->openclEngineRenderDOF,
834 				SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
835 				SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
836 
837 			if (renderData->stereo.isEnabled()
838 					&& (renderData->stereo.GetMode() == cStereo::stereoLeftRight
839 							|| renderData->stereo.GetMode() == cStereo::stereoTopBottom))
840 			{
841 				cRegion<int> region;
842 				region = renderData->stereo.GetRegion(
843 					CVector2<int>(image->GetWidth(), image->GetHeight()), cStereo::eyeLeft);
844 				*result = gOpenCl->openclEngineRenderDOF->RenderDOF(
845 					params.get(), paramsContainer, image, renderData->stopRequest, region);
846 
847 				region = renderData->stereo.GetRegion(
848 					CVector2<int>(image->GetWidth(), image->GetHeight()), cStereo::eyeRight);
849 				*result = gOpenCl->openclEngineRenderDOF->RenderDOF(
850 					params.get(), paramsContainer, image, renderData->stopRequest, region);
851 			}
852 			else
853 			{
854 				*result = gOpenCl->openclEngineRenderDOF->RenderDOF(
855 					params.get(), paramsContainer, image, renderData->stopRequest, renderData->screenRegion);
856 			}
857 		}
858 	}
859 }
860 #endif // USE_OPENCL
861 
ConnectUpdateSinalsSlots(const cRenderer * renderer)862 void cRenderJob::ConnectUpdateSinalsSlots(const cRenderer *renderer)
863 {
864 	// connect signal for progress bar update
865 	connect(renderer, SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)), this,
866 		SIGNAL(updateProgressAndStatus(const QString &, const QString &, double)));
867 	connect(
868 		renderer, SIGNAL(updateStatistics(cStatistics)), this, SIGNAL(updateStatistics(cStatistics)));
869 	connect(renderer, SIGNAL(updateImage()), this, SIGNAL(updateImage()));
870 	connect(renderer, &cRenderer::signalTotalRenderTime, this, &cRenderJob::signalTotalRenderTime);
871 }
872 
ConnectNetRenderSignalsSlots(const cRenderer * renderer)873 void cRenderJob::ConnectNetRenderSignalsSlots(const cRenderer *renderer)
874 {
875 	if (gNetRender->IsClient() && !gNetRender->IsAnimation())
876 	{
877 		connect(renderer, &cRenderer::sendRenderedLines, gNetRender, &cNetRender::SendRenderedLines);
878 		connect(gNetRender, &cNetRender::ToDoListArrived, renderer, &cRenderer::ToDoListArrived);
879 		connect(renderer, &cRenderer::NotifyClientStatus, gNetRender, &cNetRender::NotifyStatus);
880 		connect(gNetRender, &cNetRender::AckReceived, renderer, &cRenderer::AckReceived);
881 	}
882 
883 	if (gNetRender->IsServer() && !gNetRender->IsAnimation())
884 	{
885 		connect(gNetRender, &cNetRender::NewLinesArrived, renderer, &cRenderer::NewLinesArrived);
886 		connect(renderer, &cRenderer::SendToDoList, gNetRender, &cNetRender::SendToDoList);
887 		connect(renderer, &cRenderer::StopAllClients, gNetRender, &cNetRender::StopAllClients);
888 	}
889 }
890 
ChangeCameraTargetPosition(cCameraTarget & cameraTarget) const891 void cRenderJob::ChangeCameraTargetPosition(cCameraTarget &cameraTarget) const
892 {
893 	paramsContainer->Set("camera", cameraTarget.GetCamera());
894 	paramsContainer->Set("target", cameraTarget.GetTarget());
895 	paramsContainer->Set("camera_top", cameraTarget.GetTopVector());
896 	paramsContainer->Set("camera_rotation", cameraTarget.GetRotation() * 180.0 / M_PI);
897 	paramsContainer->Set("camera_distance_to_target", cameraTarget.GetDistance());
898 }
899 
UpdateParameters(const std::shared_ptr<cParameterContainer> _params,const std::shared_ptr<cFractalContainer> _fractal)900 void cRenderJob::UpdateParameters(const std::shared_ptr<cParameterContainer> _params,
901 	const std::shared_ptr<cFractalContainer> _fractal)
902 {
903 	*paramsContainer = *_params;
904 	*fractalContainer = *_fractal;
905 
906 	if (renderData->stereo.isEnabled() && !gNetRender->IsClient())
907 	{
908 		paramsContainer->Set("image_width", width);
909 		paramsContainer->Set("image_height", height);
910 	}
911 }
912 
ReduceDetail() const913 void cRenderJob::ReduceDetail() const
914 {
915 	if (mode == flightAnimRecord)
916 	{
917 		renderData->reduceDetail = sqrt(renderData->lastPercentage);
918 		if (renderData->reduceDetail < 0.1) renderData->reduceDetail = 0.1;
919 	}
920 	else
921 	{
922 		renderData->reduceDetail = 1.0;
923 	}
924 }
925 
slotExecute()926 void cRenderJob::slotExecute()
927 {
928 	Execute();
929 	emit finished();
930 }
931 
CreateListOfUsedTextures() const932 QStringList cRenderJob::CreateListOfUsedTextures() const
933 {
934 	QSet<QString> listOfTextures;
935 	if (renderData)
936 	{
937 		QList<int> keys;
938 		for (auto const &element : renderData->materials)
939 		{
940 			keys.push_back(element.first);
941 		}
942 
943 		for (int matIndex : keys)
944 		{
945 			if (renderData->materials[matIndex].colorTexture.IsLoaded())
946 				listOfTextures.insert(renderData->materials[matIndex].colorTexture.GetFileName());
947 
948 			if (renderData->materials[matIndex].diffusionTexture.IsLoaded())
949 				listOfTextures.insert(renderData->materials[matIndex].diffusionTexture.GetFileName());
950 
951 			if (renderData->materials[matIndex].normalMapTexture.IsLoaded())
952 				listOfTextures.insert(renderData->materials[matIndex].normalMapTexture.GetFileName());
953 
954 			if (renderData->materials[matIndex].displacementTexture.IsLoaded())
955 				listOfTextures.insert(renderData->materials[matIndex].displacementTexture.GetFileName());
956 
957 			if (renderData->materials[matIndex].luminosityTexture.IsLoaded())
958 				listOfTextures.insert(renderData->materials[matIndex].luminosityTexture.GetFileName());
959 		}
960 
961 		for (auto &texture : renderData->textures.textureList)
962 		{
963 			if (texture->IsLoaded()) listOfTextures.insert(texture->GetFileName());
964 		}
965 
966 		return listOfTextures.values();
967 	}
968 	return QStringList();
969 }
970 
UpdateConfig(const cRenderingConfiguration & config) const971 void cRenderJob::UpdateConfig(const cRenderingConfiguration &config) const
972 {
973 	renderData->configuration = config;
974 }
975 
GetStatistics() const976 cStatistics cRenderJob::GetStatistics() const
977 {
978 	return renderData->statistics;
979 }
980