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