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> ®ion, 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> ®ion, 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