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