1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    TestThreshold.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 
16 // Uncomment this to directly compare serial and TBB versions
17 // #define FORCE_VTKM_DEVICE
18 
19 // TODO: Make a way to force the VTK-m device without actually loading VTK-m
20 // headers (and all subsequent dependent headers).
21 
22 #include "vtkActor.h"
23 #include "vtkCamera.h"
24 #include "vtkCellArray.h"
25 #include "vtkCellData.h"
26 #include "vtkContourFilter.h"
27 #include "vtkDataSetMapper.h"
28 #include "vtkDataSetSurfaceFilter.h"
29 #include "vtkMaskPoints.h"
30 #include "vtkmLevelOfDetail.h"
31 #include "vtkNew.h"
32 #include "vtkPLYReader.h"
33 #include "vtkPointData.h"
34 #include "vtkPoints.h"
35 #include "vtkPolyData.h"
36 #include "vtkPolyDataMapper.h"
37 #include "vtkPNGWriter.h"
38 #include "vtkProperty.h"
39 #include "vtkQuadricClustering.h"
40 #include "vtkRegressionTestImage.h"
41 #include "vtkRenderer.h"
42 #include "vtkRenderWindow.h"
43 #include "vtkRenderWindowInteractor.h"
44 #include "vtkRTAnalyticSource.h"
45 #include "vtkSmartPointer.h"
46 #include "vtkTestUtilities.h"
47 #include "vtkTextActor.h"
48 #include "vtkTextProperty.h"
49 #include "vtkTimerLog.h"
50 #include "vtkTriangleFilter.h"
51 #include "vtkWindowToImageFilter.h"
52 #include "vtkXMLPolyDataReader.h"
53 
54 #ifdef FORCE_VTKM_DEVICE
55 
56 #include <vtkm/cont/RuntimeDeviceTracker.h>
57 
58 #include <vtkm/cont/serial/DeviceAdapterSerial.h>
59 #include <vtkm/cont/tbb/DeviceAdapterTBB.h>
60 
61 #endif // FORCE_VTKM_DEVICE
62 
63 #include <iomanip>
64 #include <sstream>
65 
66 /*
67  * This test has benchmarking code as well as a unit test.
68  *
69  * To run the benchmarks, add a "Benchmark" argument when invoking this test.
70  *
71  * By default, a wavelet is generated and used to time the filter's execution.
72  * By setting the LUCY_PATH define below to the path to lucy.ply (or any other
73  * ply file), other datasets can be used during benchmarking.
74  *
75  * The benchmark will print out timing information comparing vtkmLevelOfDetail
76  * to vtkQuadricClustering, and also generate side-by-side renderings of each
77  * algorithm for various grid dimensions. These images are written to the
78  * working directory can be combined into a summary image by running
79  * imagemagick's convert utility:
80  *
81  * convert LOD_0* -append summary.png
82  */
83 
84 //#define LUCY_PATH "/prm/lucy.ply"
85 
86 namespace
87 {
88 
89 const static int NUM_SAMPLES = 1;
90 const static int FONT_SIZE = 30;
91 
92 struct VTKmFilterGenerator
93 {
94   using FilterType = vtkmLevelOfDetail;
95 
96   int GridSize;
97 
VTKmFilterGenerator__anon114032100111::VTKmFilterGenerator98   VTKmFilterGenerator(int gridSize) : GridSize(gridSize) {}
99 
operator ()__anon114032100111::VTKmFilterGenerator100   FilterType *operator()() const
101   {
102     FilterType *filter = FilterType::New();
103     filter->SetNumberOfDivisions(this->GridSize, this->GridSize, this->GridSize);
104     return filter;
105   }
106 
107   vtkSmartPointer<vtkPolyData> Result;
108 };
109 
110 struct VTKFilterGenerator
111 {
112   using FilterType = vtkQuadricClustering;
113 
114   int GridSize;
115   bool UseInputPoints;
116   vtkSmartPointer<vtkPolyData> Result;
117 
VTKFilterGenerator__anon114032100111::VTKFilterGenerator118   VTKFilterGenerator(int gridSize, bool useInputPoints)
119     : GridSize(gridSize)
120     , UseInputPoints(useInputPoints)
121   {
122   }
123 
operator ()__anon114032100111::VTKFilterGenerator124   FilterType *operator()() const
125   {
126     FilterType *filter = FilterType::New();
127     filter->SetNumberOfDivisions(this->GridSize, this->GridSize, this->GridSize);
128 
129     // Mimic PV's GeometeryRepresentation decimator settings:
130     filter->SetAutoAdjustNumberOfDivisions(0);
131     filter->SetUseInternalTriangles(0);
132     filter->SetCopyCellData(1);
133     filter->SetUseInputPoints(this->UseInputPoints ? 1 : 0);
134 
135     return filter;
136   }
137 };
138 
139 template <typename FilterGenerator>
BenchmarkFilter(FilterGenerator & filterGen,vtkPolyData * input)140 double BenchmarkFilter(FilterGenerator &filterGen, vtkPolyData *input)
141 {
142   using FilterType = typename FilterGenerator::FilterType;
143 
144   vtkNew<vtkTimerLog> timer;
145   double result = 0.f;
146 
147   for (int i = 0; i < NUM_SAMPLES; ++i)
148   {
149     FilterType *filter = filterGen();
150     filter->SetInputData(input);
151 
152     timer->StartTimer();
153     filter->Update();
154     timer->StopTimer();
155 
156     result += timer->GetElapsedTime();
157     filterGen.Result = filter->GetOutput();
158     filter->Delete();
159   }
160 
161   return result / static_cast<double>(NUM_SAMPLES);
162 }
163 
RenderResults(int gridSize,vtkPolyData * input,double vtkmTime,vtkPolyData * vtkmData,double vtkTime,vtkPolyData * vtkData)164 void RenderResults(int gridSize, vtkPolyData *input,
165                    double vtkmTime, vtkPolyData *vtkmData,
166                    double vtkTime, vtkPolyData *vtkData)
167 {
168   double modelColor[3] = { 1., 1., 1. };
169   double bgColor[3] = { .75, .75, .75 };
170   double textColor[3] = { 0., 0., 0. };
171 
172   vtkNew<vtkRenderer> vtkRen;
173   {
174     vtkRen->SetViewport(0., 0., 0.5, 1.);
175     vtkRen->SetBackground(bgColor);
176 
177     vtkNew<vtkPolyDataMapper> mapper;
178     mapper->SetInputData(vtkData);
179     vtkNew<vtkActor> actor;
180     actor->SetMapper(mapper);
181     actor->GetProperty()->SetRepresentationToSurface();
182     actor->GetProperty()->SetColor(modelColor);
183     vtkRen->AddActor(actor);
184 
185     std::ostringstream tmp;
186     tmp << "VTK: " << std::setprecision(3) << vtkTime << "s\n"
187         << "NumPts: " << vtkData->GetNumberOfPoints() << "\n"
188         << "NumTri: " << vtkData->GetNumberOfCells() << "\n";
189 
190     vtkNew<vtkTextActor> timeText;
191     timeText->SetInput(tmp.str().c_str());
192     timeText->GetTextProperty()->SetJustificationToCentered();
193     timeText->GetTextProperty()->SetColor(textColor);
194     timeText->GetTextProperty()->SetFontSize(FONT_SIZE);
195     timeText->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
196     timeText->GetPositionCoordinate()->SetValue(0.5, 0.01);
197     vtkRen->AddActor(timeText);
198   }
199 
200   vtkNew<vtkRenderer> vtkmRen;
201   {
202     vtkmRen->SetViewport(0.5, 0., 1., 1.);
203     vtkmRen->SetBackground(bgColor);
204 
205     vtkNew<vtkPolyDataMapper> mapper;
206     mapper->SetInputData(vtkmData);
207     vtkNew<vtkActor> actor;
208     actor->SetMapper(mapper);
209     actor->GetProperty()->SetRepresentationToSurface();
210     actor->GetProperty()->SetColor(modelColor);
211     vtkmRen->AddActor(actor);
212 
213     std::ostringstream tmp;
214     tmp << "VTK-m: " << std::setprecision(3) << vtkmTime << "s\n"
215         << "NumPts: " << vtkmData->GetNumberOfPoints() << "\n"
216         << "NumTri: " << vtkmData->GetNumberOfCells() << "\n";
217 
218     vtkNew<vtkTextActor> timeText;
219     timeText->SetInput(tmp.str().c_str());
220     timeText->GetTextProperty()->SetJustificationToCentered();
221     timeText->GetTextProperty()->SetColor(textColor);
222     timeText->GetTextProperty()->SetFontSize(FONT_SIZE);
223     timeText->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
224     timeText->GetPositionCoordinate()->SetValue(0.5, 0.01);
225     vtkmRen->AddActor(timeText);
226   }
227 
228   vtkNew<vtkRenderer> metaRen;
229   {
230     metaRen->SetPreserveColorBuffer(1);
231 
232     std::ostringstream tmp;
233     tmp << gridSize << "x" << gridSize << "x" << gridSize << "\n"
234         << "InPts: " << input->GetNumberOfPoints() << "\n"
235         << "InTri: " << input->GetNumberOfCells() << "\n";
236 
237     vtkNew<vtkTextActor> gridText;
238     gridText->SetInput(tmp.str().c_str());
239     gridText->GetTextProperty()->SetJustificationToCentered();
240     gridText->GetTextProperty()->SetVerticalJustificationToTop();
241     gridText->GetTextProperty()->SetColor(textColor);
242     gridText->GetTextProperty()->SetFontSize(FONT_SIZE);
243     gridText->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
244     gridText->GetPositionCoordinate()->SetValue(0.5, 0.95);
245     metaRen->AddActor(gridText);
246   }
247 
248   vtkNew<vtkRenderWindow> renWin;
249   renWin->SetSize(800, 400);
250   renWin->AddRenderer(vtkRen);
251   renWin->AddRenderer(vtkmRen);
252   renWin->AddRenderer(metaRen);
253 
254   renWin->Render();
255 
256 #ifdef LUCY_PATH
257   vtkRen->GetActiveCamera()->SetPosition(0, 1, 0);
258   vtkRen->GetActiveCamera()->SetViewUp(0, 0, 1);
259   vtkRen->GetActiveCamera()->SetFocalPoint(0, 0, 0);
260 #endif
261 
262   vtkRen->ResetCamera();
263   vtkRen->GetActiveCamera()->Zoom(2.0);
264   vtkmRen->SetActiveCamera(vtkRen->GetActiveCamera());
265   renWin->Render();
266 
267   vtkNew<vtkWindowToImageFilter> w2i;
268   w2i->SetInput(renWin);
269 
270   std::ostringstream tmp;
271   tmp << "LOD_"
272       << std::setw(4) << std::setfill('0') << std::right << gridSize
273       << ".png";
274 
275   vtkNew<vtkPNGWriter> png;
276   png->SetInputConnection(w2i->GetOutputPort());
277   png->SetFileName(tmp.str().c_str());
278   png->Write();
279 }
280 
RunBenchmark(int gridSize)281 void RunBenchmark(int gridSize)
282 {
283   // Prepare input dataset:
284   static vtkSmartPointer<vtkPolyData> input;
285   if (!input)
286   {
287 #ifndef LUCY_PATH
288     vtkNew<vtkRTAnalyticSource> wavelet;
289     wavelet->SetXFreq(60);
290     wavelet->SetYFreq(30);
291     wavelet->SetZFreq(40);
292     wavelet->SetXMag(10);
293     wavelet->SetYMag(18);
294     wavelet->SetZMag(5);
295     wavelet->SetWholeExtent(-255, 256, -255, 256, -127, 128);
296     vtkNew<vtkContourFilter> contour;
297     contour->SetInputConnection(wavelet->GetOutputPort());
298     contour->SetNumberOfContours(1);
299     contour->SetValue(0, 157.);
300     contour->Update();
301     input = contour->GetOutput();
302 #else
303     vtkNew<vtkPLYReader> reader;
304     reader->SetFileName(LUCY_PATH);
305     reader->Update();
306     input = reader->GetOutput();
307 #endif
308   }
309 
310 #ifdef FORCE_VTKM_DEVICE
311 
312   vtkm::cont::RuntimeDeviceTracker tracker =
313       vtkm::cont::GetGlobalRuntimeDeviceTracker();
314 
315   // Run VTKm
316   vtkSmartPointer<vtkPolyData> vtkmResultSerial;
317   double vtkmTimeSerial = 0.;
318   {
319     tracker.ForceDevice(vtkm::cont::DeviceAdapterTagSerial());
320     VTKmFilterGenerator generator(gridSize);
321     vtkmTimeSerial = BenchmarkFilter(generator, input);
322     vtkmResultSerial = generator.Result;
323     tracker.Reset();
324   }
325 
326 #ifdef VTKM_ENABLE_TBB
327   vtkSmartPointer<vtkPolyData> vtkmResultTBB;
328   double vtkmTimeTBB = 0.;
329   bool tbbDeviceValid = tracker.CanRunOn(vtkm::cont::DeviceAdapterTagTBB());
330   if (tbbDeviceValid)
331   {
332     tracker.ForceDevice(vtkm::cont::DeviceAdapterTagTBB());
333     VTKmFilterGenerator generator(gridSize);
334     vtkmTimeTBB = BenchmarkFilter(generator, input);
335     vtkmResultTBB = generator.Result;
336     tracker.Reset();
337   }
338 #endif // VTKM_ENABLE_TBB
339 
340 #else // !FORCE_VTKM_DEVICE
341 
342   // Run VTKm
343   vtkSmartPointer<vtkPolyData> vtkmResult;
344   double vtkmTime = 0.;
345   {
346     VTKmFilterGenerator generator(gridSize);
347     vtkmTime = BenchmarkFilter(generator, input);
348     vtkmResult = generator.Result;
349   }
350 
351 #endif
352 
353   // Run VTK -- average clustered points
354   vtkSmartPointer<vtkPolyData> vtkResultAvePts;
355   double vtkTimeAvePts = 0.;
356   {
357     VTKFilterGenerator generator(gridSize, false);
358     vtkTimeAvePts = BenchmarkFilter(generator, input);
359     vtkResultAvePts = generator.Result;
360   }
361 
362   // Run VTK -- reuse input points
363   vtkSmartPointer<vtkPolyData> vtkResult;
364   double vtkTime = 0.;
365   {
366     VTKFilterGenerator generator(gridSize, true);
367     vtkTime = BenchmarkFilter(generator, input);
368     vtkResult = generator.Result;
369   }
370 
371   std::cerr << "Results for a "
372             << gridSize << "x" << gridSize << "x" << gridSize << " grid.\n"
373             << "Input dataset has " << input->GetNumberOfPoints() << " points "
374                "and " << input->GetNumberOfCells() << " cells.\n";
375 
376 #ifdef FORCE_VTKM_DEVICE
377 
378   std::cerr << "vtkmLevelOfDetail (serial, average clustered points): "
379             << vtkmTimeSerial << " seconds, "
380             << vtkmResultSerial->GetNumberOfPoints() << " points, "
381             << vtkmResultSerial->GetNumberOfCells() << " cells.\n";
382 
383 #ifdef VTKM_ENABLE_TBB
384   if (tbbDeviceValid)
385   {
386     std::cerr << "vtkmLevelOfDetail (tbb, average clustered points): "
387               << vtkmTimeTBB << " seconds, "
388               << vtkmResultTBB->GetNumberOfPoints() << " points, "
389               << vtkmResultTBB->GetNumberOfCells() << " cells.\n";
390   }
391 #endif // VTKM_ENABLE_TBB
392 
393 #else // !FORCE_VTKM_DEVICE
394 
395   std::cerr << "vtkmLevelOfDetail (average clustered points): "
396             << vtkmTime << " seconds, "
397             << vtkmResult->GetNumberOfPoints() << " points, "
398             << vtkmResult->GetNumberOfCells() << " cells.\n";
399 
400 #endif // !FORCE_VTKM_DEVICE
401 
402   std::cerr << "vtkQuadricClustering (average clustered points): "
403             << vtkTimeAvePts << " seconds, "
404             << vtkResultAvePts->GetNumberOfPoints() << " points, "
405             << vtkResultAvePts->GetNumberOfCells() << " cells.\n"
406             << "vtkQuadricClustering (reuse input points): "
407             << vtkTime << " seconds, "
408             << vtkResult->GetNumberOfPoints() << " points, "
409             << vtkResult->GetNumberOfCells() << " cells.\n";
410 
411 #ifdef FORCE_VTKM_DEVICE
412 #ifdef VTKM_ENABLE_TBB
413   RenderResults(gridSize, input,
414                 vtkmTimeTBB, vtkmResultTBB,
415                 vtkTime, vtkResult);
416 #endif // VTKM_ENABLE_TBB
417 #else // !FORCE_VTKM_DEVICE
418   RenderResults(gridSize, input,
419                 vtkmTime, vtkmResult,
420                 vtkTime, vtkResult);
421 #endif // !FORCE_VTKM_DEVICE
422 }
423 
RunBenchmarks()424 void RunBenchmarks()
425 {
426   RunBenchmark(32);
427   RunBenchmark(64);
428   RunBenchmark(128);
429   RunBenchmark(256);
430   RunBenchmark(512);
431 }
432 
433 } // end anon namespace
434 
TestVTKMLevelOfDetail(int argc,char * argv[])435 int TestVTKMLevelOfDetail(int argc, char *argv[])
436 {
437   bool doBenchmarks = false;
438 
439   for (int i = 1; i < argc; ++i)
440   {
441     if (std::string("Benchmark") == argv[i])
442     {
443       doBenchmarks = true;
444       break;
445     }
446   }
447 
448   if (doBenchmarks)
449   {
450     RunBenchmarks();
451     return 0;
452   }
453 
454   vtkNew<vtkRenderer> ren;
455   vtkNew<vtkRenderWindow> renWin;
456   vtkNew<vtkRenderWindowInteractor> iren;
457 
458   renWin->AddRenderer(ren);
459   iren->SetRenderWindow(renWin);
460 
461   //---------------------------------------------------
462   // Load file and make only triangles
463   //---------------------------------------------------
464   char* fname =
465     vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/cow.vtp");
466   vtkNew<vtkXMLPolyDataReader> reader;
467   reader->SetFileName(fname);
468   delete[] fname;
469 
470   vtkNew<vtkTriangleFilter> clean;
471   clean->SetInputConnection(reader->GetOutputPort());
472   clean->Update();
473 
474   //---------------------------------------------------
475   // Test LOD filter 4 times
476   // We will setup 4 instances of the filter at different
477   // levels of subdivision to make sure it is working properly
478   //---------------------------------------------------
479 
480   std::vector< vtkNew<vtkmLevelOfDetail> > levelOfDetails(4);
481   std::vector< vtkNew<vtkDataSetSurfaceFilter> > surfaces(4);
482   std::vector< vtkNew<vtkPolyDataMapper> > mappers(4);
483   std::vector< vtkNew<vtkActor> > actors(4);
484 
485   for(int i=0; i < 4; ++i)
486     {
487     levelOfDetails[i]->SetInputConnection(clean->GetOutputPort());
488     //subdivision levels of 16, 32, 48, 64
489     levelOfDetails[i]->SetNumberOfXDivisions( ((i+1) * 16) );
490     levelOfDetails[i]->SetNumberOfYDivisions( ((i+1) * 16) );
491     levelOfDetails[i]->SetNumberOfZDivisions( ((i+1) * 16));
492 
493     surfaces[i]->SetInputConnection(levelOfDetails[i]->GetOutputPort());
494 
495     mappers[i]->SetInputConnection(surfaces[i]->GetOutputPort());
496 
497     actors[i]->SetMapper(mappers[i]);
498     actors[i]->SetPosition( i * 10, 0, 0);
499 
500     ren->AddActor(actors[i]);
501     }
502 
503   ren->SetBackground(0.1, 0.2, 0.4);
504   ren->ResetCamera();
505   ren->GetActiveCamera()->Zoom(6.);
506   renWin->SetSize(1600, 250);
507 
508   renWin->Render();
509 
510   int retVal = vtkRegressionTestImage(renWin);
511   if(retVal == vtkRegressionTester::DO_INTERACTOR)
512   {
513   iren->Start();
514   retVal = vtkRegressionTester::PASSED;
515   }
516   return (!retVal);
517 }
518