1 //******************************************************************************
2 ///
3 /// @file backend/control/scene.cpp
4 ///
5 /// @todo What's in here?
6 ///
7 /// @copyright
8 /// @parblock
9 ///
10 /// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
11 /// Copyright 1991-2021 Persistence of Vision Raytracer Pty. Ltd.
12 ///
13 /// POV-Ray is free software: you can redistribute it and/or modify
14 /// it under the terms of the GNU Affero General Public License as
15 /// published by the Free Software Foundation, either version 3 of the
16 /// License, or (at your option) any later version.
17 ///
18 /// POV-Ray is distributed in the hope that it will be useful,
19 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
20 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 /// GNU Affero General Public License for more details.
22 ///
23 /// You should have received a copy of the GNU Affero General Public License
24 /// along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ///
26 /// ----------------------------------------------------------------------------
27 ///
28 /// POV-Ray is based on the popular DKB raytracer version 2.12.
29 /// DKBTrace was originally written by David K. Buck.
30 /// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
31 ///
32 /// @endparblock
33 ///
34 //------------------------------------------------------------------------------
35 // SPDX-License-Identifier: AGPL-3.0-or-later
36 //******************************************************************************
37
38 // Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
39 #include "backend/control/scene.h"
40
41 // Boost header files
42 #include <boost/bind.hpp>
43 #if POV_MULTITHREADED
44 #include <boost/thread.hpp>
45 #endif
46
47 // POV-Ray header files (base module)
48
49 // POV-Ray header files (core module)
50 #include "core/scene/tracethreaddata.h"
51
52 // POV-Ray header files (parser module)
53 #include "parser/parser.h"
54
55 // POV-Ray header files (backend module)
56 #include "backend/bounding/boundingtask.h"
57 #include "backend/scene/view.h"
58
59 // this must be the last file included
60 #include "base/povdebug.h"
61
62 namespace pov
63 {
64
Scene(POVMSAddress backendAddr,POVMSAddress frontendAddr,RenderBackend::SceneId sid)65 Scene::Scene(POVMSAddress backendAddr, POVMSAddress frontendAddr, RenderBackend::SceneId sid) :
66 sceneData(new BackendSceneData()),
67 stopRequsted(false),
68 parserControlThread(nullptr)
69 {
70 sceneData->tree = nullptr;
71 sceneData->sceneId = sid;
72 sceneData->backendAddress = backendAddr;
73 sceneData->frontendAddress = frontendAddr;
74 }
75
~Scene()76 Scene::~Scene()
77 {
78 stopRequsted = true; // NOTE: Order is important here, set this before stopping the queue!
79 parserTasks.Stop();
80
81 if (parserControlThread != nullptr)
82 parserControlThread->join();
83 delete parserControlThread;
84
85 for(vector<TraceThreadData *>::iterator i(sceneThreadData.begin()); i != sceneThreadData.end(); i++)
86 delete (*i);
87 sceneThreadData.clear();
88 }
89
StartParser(POVMS_Object & parseOptions)90 void Scene::StartParser(POVMS_Object& parseOptions)
91 {
92 size_t seed = 0; // TODO
93
94 // A scene can only be parsed once
95 if (parserControlThread == nullptr)
96 parserControlThread = Task::NewBoostThread(boost::bind(&Scene::ParserControlThread, this), POV_THREAD_STACK_SIZE);
97 else
98 return;
99
100 if (parseOptions.Exist(kPOVAttrib_Version))
101 {
102 sceneData->languageVersion = POVRayVersion(parseOptions.GetFloat(kPOVAttrib_Version));
103 sceneData->languageVersionSet = true;
104 }
105
106 sceneData->warningLevel = clip(parseOptions.TryGetInt(kPOVAttrib_WarningLevel, 9), 0, 9);
107
108 sceneData->inputFile = parseOptions.TryGetUCS2String(kPOVAttrib_InputFile, "object.pov");
109 sceneData->headerFile = parseOptions.TryGetUCS2String(kPOVAttrib_IncludeHeader, "");
110
111 DBL outputWidth = parseOptions.TryGetFloat(kPOVAttrib_Width, 160);
112 DBL outputHeight = parseOptions.TryGetFloat(kPOVAttrib_Height, 120);
113 sceneData->aspectRatio = outputWidth / outputHeight;
114
115 sceneData->defaultFileType = parseOptions.TryGetInt(kPOVAttrib_OutputFileType, DEFAULT_OUTPUT_FORMAT); // TODO - should get DEFAULT_OUTPUT_FORMAT from the front-end
116 sceneData->clocklessAnimation = parseOptions.TryGetBool(kPOVAttrib_ClocklessAnimation, false); // TODO - experimental code
117
118 sceneData->splitUnions = parseOptions.TryGetBool(kPOVAttrib_SplitUnions, false);
119 sceneData->removeBounds = parseOptions.TryGetBool(kPOVAttrib_RemoveBounds, true);
120 sceneData->boundingMethod = clip<int>(parseOptions.TryGetInt(kPOVAttrib_BoundingMethod, 1), 1, 2);
121 if(parseOptions.TryGetBool(kPOVAttrib_Bounding, true) == false)
122 sceneData->boundingMethod = 0;
123
124 sceneData->outputAlpha = parseOptions.TryGetBool(kPOVAttrib_OutputAlpha, false);
125 if (!sceneData->outputAlpha)
126 // if we're not outputting an alpha channel, precompose the scene background against a black "background behind the background"
127 // (NB: Here, background color is still at its default of <0,0,0,0,1> = full transparency; we're changing that to opaque black.)
128 sceneData->backgroundColour.Clear();
129
130 // NB a value of '0' for any of the BSP parameters tells the BSP code to use its internal default
131 sceneData->bspMaxDepth = parseOptions.TryGetInt(kPOVAttrib_BSP_MaxDepth, 0);
132 sceneData->bspObjectIsectCost = clip<float>(parseOptions.TryGetFloat(kPOVAttrib_BSP_ISectCost, 0.0f), 0.0f, HUGE_VAL);
133 sceneData->bspBaseAccessCost = clip<float>(parseOptions.TryGetFloat(kPOVAttrib_BSP_BaseAccessCost, 0.0f), 0.0f, HUGE_VAL);
134 sceneData->bspChildAccessCost = clip<float>(parseOptions.TryGetFloat(kPOVAttrib_BSP_ChildAccessCost, 0.0f), 0.0f, HUGE_VAL);
135 sceneData->bspMissChance = clip<float>(parseOptions.TryGetFloat(kPOVAttrib_BSP_MissChance, 0.0f), 0.0f, 1.0f - EPSILON);
136
137 sceneData->realTimeRaytracing = parseOptions.TryGetBool(kPOVAttrib_RealTimeRaytracing, false);
138
139 if(parseOptions.Exist(kPOVAttrib_Declare) == true)
140 {
141 POVMS_List ds;
142
143 parseOptions.Get(kPOVAttrib_Declare, ds);
144 for(int i = 1; i <= ds.GetListSize(); i++)
145 {
146 POVMS_Attribute a;
147 POVMS_Object d;
148
149 ds.GetNth(i, d);
150 std::string ident = d.GetString(kPOVAttrib_Identifier);
151 d.Get(kPOVAttrib_Value, a);
152 switch (a.Type())
153 {
154 case kPOVMSType_CString:
155 sceneData->declaredStrings.insert(make_pair(ident, d.TryGetString(kPOVAttrib_Value, "")));
156 break;
157
158 case kPOVMSType_Float:
159 sceneData->declaredNumbers.insert(make_pair(ident, d.TryGetFloat(kPOVAttrib_Value, 0.0)));
160 break;
161
162 default:
163 // shouldn't happen unless we make a coding error
164 throw POV_EXCEPTION(kParamErr, "Invalid type passed in declare list");
165 }
166 }
167 }
168
169 // do parsing
170 sceneThreadData.push_back(dynamic_cast<TraceThreadData *>(parserTasks.AppendTask(new pov_parser::Parser(
171 sceneData, bool(parseOptions.Exist(kPOVAttrib_Clock)), parseOptions.TryGetFloat(kPOVAttrib_Clock, 0.0), seed
172 ))));
173
174 // wait for parsing
175 parserTasks.AppendSync();
176
177 // do bounding - we always call this even if the bounding is turned off
178 // because it also generates object statistics
179 sceneThreadData.push_back(dynamic_cast<TraceThreadData *>(parserTasks.AppendTask(new BoundingTask(
180 sceneData,
181 clip<int>(parseOptions.TryGetInt(kPOVAttrib_BoundingThreshold, DEFAULT_AUTO_BOUNDINGTHRESHOLD),1,SIGNED16_MAX),
182 seed
183 ))));
184
185 // wait for bounding
186 parserTasks.AppendSync();
187
188 // wait for bounding to finish
189 parserTasks.AppendSync();
190
191 // send statistics
192 parserTasks.AppendFunction(boost::bind(&Scene::SendStatistics, this, _1));
193
194 // send done message and compatibility data
195 parserTasks.AppendFunction(boost::bind(&Scene::SendDoneMessage, this, _1));
196 }
197
StopParser()198 void Scene::StopParser()
199 {
200 parserTasks.Stop();
201
202 RenderBackend::SendSceneFailedResult(sceneData->sceneId, kUserAbortErr, sceneData->frontendAddress);
203 }
204
PauseParser()205 void Scene::PauseParser()
206 {
207 parserTasks.Pause();
208 }
209
ResumeParser()210 void Scene::ResumeParser()
211 {
212 parserTasks.Resume();
213 }
214
IsParsing()215 bool Scene::IsParsing()
216 {
217 return parserTasks.IsRunning();
218 }
219
IsPaused()220 bool Scene::IsPaused()
221 {
222 return parserTasks.IsPaused();
223 }
224
Failed()225 bool Scene::Failed()
226 {
227 return parserTasks.Failed();
228 }
229
NewView(unsigned int width,unsigned int height,RenderBackend::ViewId vid)230 shared_ptr<View> Scene::NewView(unsigned int width, unsigned int height, RenderBackend::ViewId vid)
231 {
232 if(parserTasks.IsDone() == false)
233 throw POV_EXCEPTION_CODE(kNotNowErr);
234
235 if((parserTasks.IsDone() == false) || (parserTasks.Failed() == true))
236 throw POV_EXCEPTION_CODE(kNotNowErr);
237
238 return shared_ptr<View>(new View(sceneData, width, height, vid));
239 }
240
GetStatistics(POVMS_Object & parserStats)241 void Scene::GetStatistics(POVMS_Object& parserStats)
242 {
243 struct TimeData
244 {
245 POV_LONG cpuTime;
246 POV_LONG realTime;
247 size_t samples;
248
249 TimeData() : cpuTime(0), realTime(0), samples(0) { }
250 };
251
252 TimeData timeData[TraceThreadData::kMaxTimeType];
253
254 for(vector<TraceThreadData *>::iterator i(sceneThreadData.begin()); i != sceneThreadData.end(); i++)
255 {
256 timeData[(*i)->timeType].realTime = max(timeData[(*i)->timeType].realTime, (*i)->realTime);
257 timeData[(*i)->timeType].cpuTime += (*i)->cpuTime;
258 timeData[(*i)->timeType].samples++;
259 }
260
261 for(size_t i = TraceThreadData::kUnknownTime; i < TraceThreadData::kMaxTimeType; i++)
262 {
263 if(timeData[i].samples > 0)
264 {
265 POVMS_Object elapsedTime(kPOVObjectClass_ElapsedTime);
266
267 elapsedTime.SetLong(kPOVAttrib_RealTime, timeData[i].realTime);
268 elapsedTime.SetLong(kPOVAttrib_CPUTime, timeData[i].cpuTime);
269 elapsedTime.SetInt(kPOVAttrib_TimeSamples, POVMSInt(timeData[i].samples));
270
271 switch(i)
272 {
273 case TraceThreadData::kParseTime:
274 parserStats.Set(kPOVAttrib_ParseTime, elapsedTime);
275 break;
276 case TraceThreadData::kBoundingTime:
277 parserStats.Set(kPOVAttrib_BoundingTime, elapsedTime);
278 break;
279 }
280 }
281 }
282
283 parserStats.SetInt(kPOVAttrib_FiniteObjects, sceneData->numberOfFiniteObjects);
284 parserStats.SetInt(kPOVAttrib_InfiniteObjects, sceneData->numberOfInfiniteObjects);
285 parserStats.SetInt(kPOVAttrib_LightSources, POVMSInt(sceneData->lightSources.size()));
286 parserStats.SetInt(kPOVAttrib_Cameras, POVMSInt(sceneData->cameras.size()));
287
288 if(sceneData->boundingMethod == 2)
289 {
290 parserStats.SetInt(kPOVAttrib_BSPNodes, sceneData->nodes);
291 parserStats.SetInt(kPOVAttrib_BSPSplitNodes, sceneData->splitNodes);
292 parserStats.SetInt(kPOVAttrib_BSPObjectNodes, sceneData->objectNodes);
293 parserStats.SetInt(kPOVAttrib_BSPEmptyNodes, sceneData->emptyNodes);
294 parserStats.SetInt(kPOVAttrib_BSPMaxObjects, sceneData->maxObjects);
295 parserStats.SetFloat(kPOVAttrib_BSPAverageObjects, sceneData->averageObjects);
296 parserStats.SetInt(kPOVAttrib_BSPMaxDepth, sceneData->maxDepth);
297 parserStats.SetFloat(kPOVAttrib_BSPAverageDepth, sceneData->averageDepth);
298 parserStats.SetInt(kPOVAttrib_BSPAborts, sceneData->aborts);
299 parserStats.SetFloat(kPOVAttrib_BSPAverageAborts, sceneData->averageAborts);
300 parserStats.SetFloat(kPOVAttrib_BSPAverageAbortObjects, sceneData->averageAbortObjects);
301 }
302 }
303
SendStatistics(TaskQueue &)304 void Scene::SendStatistics(TaskQueue&)
305 {
306 POVMS_Message parserStats(kPOVObjectClass_ParserStatistics, kPOVMsgClass_SceneOutput, kPOVMsgIdent_ParserStatistics);
307
308 GetStatistics(parserStats);
309
310 parserStats.SetInt(kPOVAttrib_SceneId, sceneData->sceneId);
311 parserStats.SetSourceAddress(sceneData->backendAddress);
312 parserStats.SetDestinationAddress(sceneData->frontendAddress);
313
314 POVMS_SendMessage(parserStats);
315
316 for(vector<TraceThreadData *>::iterator i(sceneThreadData.begin()); i != sceneThreadData.end(); i++)
317 delete (*i);
318 sceneThreadData.clear();
319 }
320
SendDoneMessage(TaskQueue &)321 void Scene::SendDoneMessage(TaskQueue&)
322 {
323 POVMS_Message doneMessage(kPOVObjectClass_ResultData, kPOVMsgClass_SceneOutput, kPOVMsgIdent_Done);
324 doneMessage.SetInt(kPOVAttrib_SceneId, sceneData->sceneId);
325 doneMessage.SetSourceAddress(sceneData->backendAddress);
326 doneMessage.SetDestinationAddress(sceneData->frontendAddress);
327 doneMessage.SetInt(kPOVAttrib_LegacyGammaMode, sceneData->gammaMode);
328 if (sceneData->workingGamma)
329 {
330 doneMessage.SetInt(kPOVAttrib_WorkingGammaType, sceneData->workingGamma->GetTypeId());
331 doneMessage.SetFloat(kPOVAttrib_WorkingGamma, sceneData->workingGamma->GetParam());
332 }
333 POVMS_SendMessage(doneMessage);
334 }
335
ParserControlThread()336 void Scene::ParserControlThread()
337 {
338 bool sentFailedResult = false;
339
340 while(stopRequsted == false)
341 {
342 while((parserTasks.Process() == true) && (stopRequsted == false)) { }
343
344 if((parserTasks.IsDone() == true) && (parserTasks.Failed() == true) && (sentFailedResult == false))
345 {
346 RenderBackend::SendSceneFailedResult(sceneData->sceneId, parserTasks.FailureCode(kUncategorizedError), sceneData->frontendAddress);
347 sentFailedResult = true;
348 }
349
350 if(stopRequsted == false)
351 {
352 boost::thread::yield();
353 Delay(10);
354 }
355 }
356 }
357
358 } // end of namespace
359