1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkCachedStreamingDemandDrivenPipeline.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkCachedStreamingDemandDrivenPipeline.h"
16 
17 #include "vtkInformationIntegerKey.h"
18 #include "vtkInformationIntegerVectorKey.h"
19 #include "vtkObjectFactory.h"
20 
21 #include "vtkAlgorithm.h"
22 #include "vtkAlgorithmOutput.h"
23 #include "vtkImageData.h"
24 #include "vtkInformation.h"
25 #include "vtkInformationVector.h"
26 #include "vtkPointData.h"
27 
28 vtkStandardNewMacro(vtkCachedStreamingDemandDrivenPipeline);
29 
30 //------------------------------------------------------------------------------
vtkCachedStreamingDemandDrivenPipeline()31 vtkCachedStreamingDemandDrivenPipeline ::vtkCachedStreamingDemandDrivenPipeline()
32 {
33   this->CacheSize = 0;
34   this->Data = nullptr;
35   this->Times = nullptr;
36 
37   this->SetCacheSize(10);
38 }
39 
40 //------------------------------------------------------------------------------
~vtkCachedStreamingDemandDrivenPipeline()41 vtkCachedStreamingDemandDrivenPipeline ::~vtkCachedStreamingDemandDrivenPipeline()
42 {
43   this->SetCacheSize(0);
44 }
45 
46 //------------------------------------------------------------------------------
SetCacheSize(int size)47 void vtkCachedStreamingDemandDrivenPipeline::SetCacheSize(int size)
48 {
49   int idx;
50 
51   if (size == this->CacheSize)
52   {
53     return;
54   }
55 
56   this->Modified();
57 
58   // free the old data
59   for (idx = 0; idx < this->CacheSize; ++idx)
60   {
61     if (this->Data[idx])
62     {
63       this->Data[idx]->Delete();
64       this->Data[idx] = nullptr;
65     }
66   }
67   delete[] this->Data;
68   this->Data = nullptr;
69   delete[] this->Times;
70   this->Times = nullptr;
71 
72   this->CacheSize = size;
73   if (size == 0)
74   {
75     return;
76   }
77 
78   this->Data = new vtkDataObject*[size];
79   this->Times = new vtkMTimeType[size];
80 
81   for (idx = 0; idx < size; ++idx)
82   {
83     this->Data[idx] = nullptr;
84     this->Times[idx] = 0;
85   }
86 }
87 
88 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)89 void vtkCachedStreamingDemandDrivenPipeline ::PrintSelf(ostream& os, vtkIndent indent)
90 {
91   this->Superclass::PrintSelf(os, indent);
92   os << indent << "CacheSize: " << this->CacheSize << "\n";
93 }
94 
95 //------------------------------------------------------------------------------
NeedToExecuteData(int outputPort,vtkInformationVector ** inInfoVec,vtkInformationVector * outInfoVec)96 int vtkCachedStreamingDemandDrivenPipeline ::NeedToExecuteData(
97   int outputPort, vtkInformationVector** inInfoVec, vtkInformationVector* outInfoVec)
98 {
99   // If no port is specified, check all ports.  This behavior is
100   // implemented by the superclass.
101   if (outputPort < 0)
102   {
103     return this->Superclass::NeedToExecuteData(outputPort, inInfoVec, outInfoVec);
104   }
105 
106   // Does the superclass want to execute? We must skip our direct superclass
107   // because it looks at update extents but does not know about the cache
108   // NOLINTNEXTLINE(bugprone-parent-virtual-call)
109   if (this->vtkDemandDrivenPipeline::NeedToExecuteData(outputPort, inInfoVec, outInfoVec))
110   {
111     return 1;
112   }
113 
114   // Has the algorithm asked to be executed again?
115   if (this->ContinueExecuting)
116   {
117     return 1;
118   }
119 
120   // First look through the cached data to see if it is still valid.
121   int i;
122   vtkMTimeType pmt = this->GetPipelineMTime();
123   for (i = 0; i < this->CacheSize; ++i)
124   {
125     if (this->Data[i] && this->Times[i] < pmt)
126     {
127       this->Data[i]->Delete();
128       this->Data[i] = nullptr;
129       this->Times[i] = 0;
130     }
131   }
132 
133   // We need to check the requested update extent.  Get the output
134   // port information and data information.  We do not need to check
135   // existence of values because it has already been verified by
136   // VerifyOutputInformation.
137   vtkInformation* outInfo = outInfoVec->GetInformationObject(outputPort);
138   vtkDataObject* dataObject = outInfo->Get(vtkDataObject::DATA_OBJECT());
139   vtkInformation* dataInfo = dataObject->GetInformation();
140   if (dataInfo->Get(vtkDataObject::DATA_EXTENT_TYPE()) == VTK_PIECES_EXTENT)
141   {
142     int updatePiece = outInfo->Get(UPDATE_PIECE_NUMBER());
143     int updateNumberOfPieces = outInfo->Get(UPDATE_NUMBER_OF_PIECES());
144     int updateGhostLevel = outInfo->Get(UPDATE_NUMBER_OF_GHOST_LEVELS());
145 
146     // check to see if any data in the cache fits this request
147     for (i = 0; i < this->CacheSize; ++i)
148     {
149       if (this->Data[i])
150       {
151         dataInfo = this->Data[i]->GetInformation();
152 
153         // Check the unstructured extent.  If we do not have the requested
154         // piece, we need to execute.
155         int dataPiece = dataInfo->Get(vtkDataObject::DATA_PIECE_NUMBER());
156         int dataNumberOfPieces = dataInfo->Get(vtkDataObject::DATA_NUMBER_OF_PIECES());
157         int dataGhostLevel = dataInfo->Get(vtkDataObject::DATA_NUMBER_OF_GHOST_LEVELS());
158         if (dataInfo->Get(vtkDataObject::DATA_EXTENT_TYPE()) == VTK_PIECES_EXTENT &&
159           dataPiece == updatePiece && dataNumberOfPieces == updateNumberOfPieces &&
160           dataGhostLevel == updateGhostLevel)
161         {
162           // we have a matching data we must copy it to our output, but for
163           // now we don't support polydata
164           return 1;
165         }
166       }
167     }
168   }
169   else if (dataInfo->Get(vtkDataObject::DATA_EXTENT_TYPE()) == VTK_3D_EXTENT)
170   {
171     // Check the structured extent.  If the update extent is outside
172     // of the extent and not empty, we need to execute.
173     int dataExtent[6];
174     int updateExtent[6];
175     outInfo->Get(UPDATE_EXTENT(), updateExtent);
176 
177     // check to see if any data in the cache fits this request
178     for (i = 0; i < this->CacheSize; ++i)
179     {
180       if (this->Data[i])
181       {
182         dataInfo = this->Data[i]->GetInformation();
183         dataInfo->Get(vtkDataObject::DATA_EXTENT(), dataExtent);
184         if (dataInfo->Get(vtkDataObject::DATA_EXTENT_TYPE()) == VTK_3D_EXTENT &&
185           !(updateExtent[0] < dataExtent[0] || updateExtent[1] > dataExtent[1] ||
186             updateExtent[2] < dataExtent[2] || updateExtent[3] > dataExtent[3] ||
187             updateExtent[4] < dataExtent[4] || updateExtent[5] > dataExtent[5]) &&
188           (updateExtent[0] <= updateExtent[1] && updateExtent[2] <= updateExtent[3] &&
189             updateExtent[4] <= updateExtent[5]))
190         {
191           // we have a match
192           // Pass this data to output.
193           vtkImageData* id = vtkImageData::SafeDownCast(dataObject);
194           vtkImageData* id2 = vtkImageData::SafeDownCast(this->Data[i]);
195           if (id && id2)
196           {
197             id->SetExtent(dataExtent);
198             id->GetPointData()->PassData(id2->GetPointData());
199             // not sure if we need this
200             dataObject->DataHasBeenGenerated();
201             return 0;
202           }
203         }
204       }
205     }
206   }
207 
208   // We do need to execute
209   return 1;
210 }
211 
212 //------------------------------------------------------------------------------
ExecuteData(vtkInformation * request,vtkInformationVector ** inInfoVec,vtkInformationVector * outInfoVec)213 int vtkCachedStreamingDemandDrivenPipeline ::ExecuteData(
214   vtkInformation* request, vtkInformationVector** inInfoVec, vtkInformationVector* outInfoVec)
215 {
216   // only works for one in one out algorithms
217   if (request->Get(FROM_OUTPUT_PORT()) != 0)
218   {
219     vtkErrorMacro("vtkCachedStreamingDemandDrivenPipeline can only be used for algorithms with one "
220                   "output and one input");
221     return 0;
222   }
223 
224   // first do the usual thing
225   int result = this->Superclass::ExecuteData(request, inInfoVec, outInfoVec);
226 
227   // then save the newly generated data
228   vtkMTimeType bestTime = VTK_INT_MAX;
229   int bestIdx = 0;
230 
231   // Save the image in cache.
232   // Find a spot to put the data.
233   for (int i = 0; i < this->CacheSize; ++i)
234   {
235     if (this->Data[i] == nullptr)
236     {
237       bestIdx = i;
238       break;
239     }
240     if (this->Times[i] < bestTime)
241     {
242       bestIdx = i;
243       bestTime = this->Times[i];
244     }
245   }
246 
247   vtkInformation* outInfo = outInfoVec->GetInformationObject(0);
248   vtkDataObject* dataObject = outInfo->Get(vtkDataObject::DATA_OBJECT());
249   if (this->Data[bestIdx] == nullptr)
250   {
251     this->Data[bestIdx] = dataObject->NewInstance();
252   }
253   this->Data[bestIdx]->ReleaseData();
254 
255   vtkImageData* id = vtkImageData::SafeDownCast(dataObject);
256   if (id)
257   {
258     vtkInformation* inInfo = inInfoVec[0]->GetInformationObject(0);
259     vtkImageData* input = vtkImageData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
260     id->SetExtent(input->GetExtent());
261     id->GetPointData()->PassData(input->GetPointData());
262     id->DataHasBeenGenerated();
263   }
264 
265   vtkImageData* id2 = vtkImageData::SafeDownCast(this->Data[bestIdx]);
266   if (id && id2)
267   {
268     id2->SetExtent(id->GetExtent());
269     id2->GetPointData()->SetScalars(id->GetPointData()->GetScalars());
270   }
271 
272   this->Times[bestIdx] = dataObject->GetUpdateTime();
273 
274   return result;
275 }
276