1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //  This software is distributed WITHOUT ANY WARRANTY; without even
6 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
7 //  PURPOSE.  See the above copyright notice for more information.
8 //
9 //  Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
10 //  Copyright 2014 UT-Battelle, LLC.
11 //  Copyright 2014 Los Alamos National Security.
12 //
13 //  Under the terms of Contract DE-NA0003525 with NTESS,
14 //  the U.S. Government retains certain rights in this software.
15 //
16 //  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
17 //  Laboratory (LANL), the U.S. Government retains certain rights in
18 //  this software.
19 //============================================================================
20 // Must be included before any other GL includes:
21 #include <GL/glew.h>
22 
23 // Must be included before any other GL includes:
24 #include <GL/glew.h>
25 
26 #include <algorithm>
27 #include <iostream>
28 #include <random>
29 
30 #include <vtkm/Math.h>
31 #include <vtkm/cont/ArrayHandle.h>
32 #include <vtkm/cont/ArrayHandleCounting.h>
33 #include <vtkm/cont/DataSetBuilderUniform.h>
34 #include <vtkm/cont/Timer.h>
35 
36 #include <vtkm/interop/TransferToOpenGL.h>
37 
38 #include <vtkm/filter/FilterDataSet.h>
39 #include <vtkm/worklet/DispatcherPointNeighborhood.h>
40 #include <vtkm/worklet/WorkletPointNeighborhood.h>
41 
42 #include <vtkm/cont/TryExecute.h>
43 #include <vtkm/cont/cuda/DeviceAdapterCuda.h>
44 #include <vtkm/cont/serial/DeviceAdapterSerial.h>
45 #include <vtkm/cont/tbb/DeviceAdapterTBB.h>
46 
47 //Suppress warnings about glut being deprecated on OSX
48 #if (defined(VTKM_GCC) || defined(VTKM_CLANG))
49 #pragma GCC diagnostic push
50 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
51 #endif
52 
53 //OpenGL Graphics includes
54 //glew needs to go before glut
55 //that is why this is after the TransferToOpenGL include
56 #if defined(__APPLE__)
57 #include <GLUT/glut.h>
58 #else
59 #include <GL/glut.h>
60 #endif
61 
62 #include "LoadShaders.h"
63 
64 //This is the list of devices to compile in support for. The order of the
65 //devices determines the runtime preference.
66 struct DevicesToTry : vtkm::ListTagBase<vtkm::cont::DeviceAdapterTagCuda,
67                                         vtkm::cont::DeviceAdapterTagTBB,
68                                         vtkm::cont::DeviceAdapterTagSerial>
69 {
70 };
71 
72 struct GameOfLifePolicy : public vtkm::filter::PolicyBase<GameOfLifePolicy>
73 {
74   using DeviceAdapterList = DevicesToTry;
75 };
76 
77 struct UpdateLifeState : public vtkm::worklet::WorkletPointNeighborhood3x3x3
78 {
79   using CountingHandle = vtkm::cont::ArrayHandleCounting<vtkm::Id>;
80 
81   using ControlSignature = void(CellSetIn,
82                                 FieldInNeighborhood<> prevstate,
83                                 FieldOut<> state,
84                                 FieldOut<> color);
85 
86   using ExecutionSignature = void(_2, _3, _4);
87 
88   template <typename NeighIn>
operator ()UpdateLifeState89   VTKM_EXEC void operator()(const NeighIn& prevstate,
90                             vtkm::UInt8& state,
91                             vtkm::Vec<vtkm::UInt8, 4>& color) const
92   {
93     // Any live cell with fewer than two live neighbors dies, as if caused by under-population.
94     // Any live cell with two or three live neighbors lives on to the next generation.
95     // Any live cell with more than three live neighbors dies, as if by overcrowding.
96     // Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
97     auto current = prevstate.Get(0, 0, 0);
98     auto count = prevstate.Get(-1, -1, 0) + prevstate.Get(-1, 0, 0) + prevstate.Get(-1, 1, 0) +
99       prevstate.Get(0, -1, 0) + prevstate.Get(0, 1, 0) + prevstate.Get(1, -1, 0) +
100       prevstate.Get(1, 0, 0) + prevstate.Get(1, 1, 0);
101 
102     if (current == 1 && (count == 2 || count == 3))
103     {
104       state = 1;
105     }
106     else if (current == 0 && count == 3)
107     {
108       state = 1;
109     }
110     else
111     {
112       state = 0;
113     }
114 
115     color[0] = 0;
116     color[1] = static_cast<vtkm::UInt8>(state * (100 + (count * 32)));
117     color[2] = (state && !current) ? static_cast<vtkm::UInt8>(100 + (count * 32)) : 0;
118     color[3] = 255; //alpha channel
119   }
120 };
121 
122 
123 class GameOfLife : public vtkm::filter::FilterDataSet<GameOfLife>
124 {
125   bool PrintedDeviceMsg = false;
126 
127 public:
128   template <typename Policy, typename Device>
DoExecute(const vtkm::cont::DataSet & input,vtkm::filter::PolicyBase<Policy> policy,Device)129   VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input,
130                                           vtkm::filter::PolicyBase<Policy> policy,
131                                           Device)
132 
133   {
134     if (!this->PrintedDeviceMsg)
135     {
136       using DeviceAdapterTraits = vtkm::cont::DeviceAdapterTraits<Device>;
137       std::cout << "Running GameOfLife filter on device adapter: " << DeviceAdapterTraits::GetName()
138                 << std::endl;
139       this->PrintedDeviceMsg = true;
140     }
141 
142     using DispatcherType = vtkm::worklet::DispatcherPointNeighborhood<UpdateLifeState>;
143 
144 
145     vtkm::cont::ArrayHandle<vtkm::UInt8> state;
146     vtkm::cont::ArrayHandle<vtkm::UInt8> prevstate;
147     vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>> colors;
148 
149     //get the coordinate system we are using for the 2D area
150     const vtkm::cont::DynamicCellSet& cells = input.GetCellSet(this->GetActiveCellSetIndex());
151 
152     //get the previous state of the game
153     input.GetField("state", vtkm::cont::Field::Association::POINTS).GetData().CopyTo(prevstate);
154 
155     //Update the game state
156     DispatcherType dispatcher;
157     dispatcher.SetDevice(Device());
158     dispatcher.Invoke(vtkm::filter::ApplyPolicy(cells, policy), prevstate, state, colors);
159 
160     //save the results
161     vtkm::cont::DataSet output;
162     output.AddCellSet(input.GetCellSet(this->GetActiveCellSetIndex()));
163     output.AddCoordinateSystem(input.GetCoordinateSystem(this->GetActiveCoordinateSystemIndex()));
164 
165     vtkm::cont::Field colorField("colors", vtkm::cont::Field::Association::POINTS, colors);
166     output.AddField(colorField);
167 
168     vtkm::cont::Field stateField("state", vtkm::cont::Field::Association::POINTS, state);
169     output.AddField(stateField);
170 
171     return output;
172   }
173 
174   template <typename T, typename StorageType, typename DerivedPolicy, typename DeviceAdapter>
DoMapField(vtkm::cont::DataSet &,const vtkm::cont::ArrayHandle<T,StorageType> &,const vtkm::filter::FieldMetadata &,const vtkm::filter::PolicyBase<DerivedPolicy> &,DeviceAdapter)175   VTKM_CONT bool DoMapField(vtkm::cont::DataSet&,
176                             const vtkm::cont::ArrayHandle<T, StorageType>&,
177                             const vtkm::filter::FieldMetadata&,
178                             const vtkm::filter::PolicyBase<DerivedPolicy>&,
179                             DeviceAdapter)
180   {
181     return false;
182   }
183 };
184 
185 struct UploadData
186 {
187   vtkm::interop::BufferState* ColorState;
188   vtkm::cont::Field Colors;
189 
UploadDataUploadData190   UploadData(vtkm::interop::BufferState* cs, vtkm::cont::Field colors)
191     : ColorState(cs)
192     , Colors(colors)
193   {
194   }
195   template <typename DeviceAdapterTag>
operator ()UploadData196   bool operator()(DeviceAdapterTag device)
197   {
198     vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>> colors;
199     this->Colors.GetData().CopyTo(colors);
200     vtkm::interop::TransferToOpenGL(colors, *this->ColorState, device);
201     return true;
202   }
203 };
204 
205 struct RenderGameOfLife
206 {
207   vtkm::Int32 ScreenWidth;
208   vtkm::Int32 ScreenHeight;
209   GLuint ShaderProgramId;
210   GLuint VAOId;
211   vtkm::interop::BufferState VBOState;
212   vtkm::interop::BufferState ColorState;
213 
RenderGameOfLifeRenderGameOfLife214   RenderGameOfLife(vtkm::Int32 width, vtkm::Int32 height, vtkm::Int32 x, vtkm::Int32 y)
215     : ScreenWidth(width)
216     , ScreenHeight(height)
217     , ShaderProgramId()
218     , VAOId()
219     , ColorState()
220   {
221     this->ShaderProgramId = LoadShaders();
222     glUseProgram(this->ShaderProgramId);
223 
224     glGenVertexArrays(1, &this->VAOId);
225     glBindVertexArray(this->VAOId);
226 
227     glClearColor(.0f, .0f, .0f, 0.f);
228     glPointSize(1);
229     glViewport(0, 0, this->ScreenWidth, this->ScreenHeight);
230 
231     //generate coords and render them
232     vtkm::Id3 dimensions(x, y, 1);
233     vtkm::Vec<float, 3> origin(-4.f, -4.f, 0.0f);
234     vtkm::Vec<float, 3> spacing(0.0075f, 0.0075f, 0.0f);
235 
236     vtkm::cont::ArrayHandleUniformPointCoordinates coords(dimensions, origin, spacing);
237     vtkm::interop::TransferToOpenGL(coords, this->VBOState, vtkm::cont::DeviceAdapterTagSerial());
238   }
239 
renderRenderGameOfLife240   void render(vtkm::cont::DataSet& data)
241   {
242     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
243     vtkm::Int32 arraySize = (vtkm::Int32)data.GetCoordinateSystem().GetData().GetNumberOfValues();
244 
245     UploadData task(&this->ColorState,
246                     data.GetField("colors", vtkm::cont::Field::Association::POINTS));
247     vtkm::cont::TryExecute(task, DevicesToTry());
248 
249     vtkm::Float32 mvp[16] = { 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f,
250                               0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 3.5f };
251 
252     GLint unifLoc = glGetUniformLocation(this->ShaderProgramId, "MVP");
253     glUniformMatrix4fv(unifLoc, 1, GL_FALSE, mvp);
254 
255     glEnableVertexAttribArray(0);
256     glBindBuffer(GL_ARRAY_BUFFER, *this->VBOState.GetHandle());
257     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
258 
259     glEnableClientState(GL_COLOR_ARRAY);
260     glBindBuffer(GL_ARRAY_BUFFER, *this->ColorState.GetHandle());
261     glColorPointer(4, GL_UNSIGNED_BYTE, 0, 0);
262 
263     glDrawArrays(GL_POINTS, 0, arraySize);
264 
265     glDisableClientState(GL_COLOR_ARRAY);
266     glDisableVertexAttribArray(0);
267 
268     glMatrixMode(GL_PROJECTION);
269     glPopMatrix();
270   }
271 };
272 
273 vtkm::cont::Timer<vtkm::cont::DeviceAdapterTagSerial> gTimer;
274 vtkm::cont::DataSet* gData = nullptr;
275 GameOfLife* gFilter = nullptr;
276 RenderGameOfLife* gRenderer = nullptr;
277 
278 
stamp_acorn(std::vector<vtkm::UInt8> & input_state,vtkm::UInt32 i,vtkm::UInt32 j,vtkm::UInt32 width,vtkm::UInt32 height)279 vtkm::UInt32 stamp_acorn(std::vector<vtkm::UInt8>& input_state,
280                          vtkm::UInt32 i,
281                          vtkm::UInt32 j,
282                          vtkm::UInt32 width,
283                          vtkm::UInt32 height)
284 {
285   (void)width;
286   static vtkm::UInt8 acorn[5][9] = {
287     { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0, 0, 0 },
288     { 0, 1, 1, 0, 0, 1, 1, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
289   };
290 
291   vtkm::UInt32 uindex = (i * height) + j;
292   std::ptrdiff_t index = static_cast<std::ptrdiff_t>(uindex);
293   for (vtkm::UInt32 x = 0; x < 5; ++x)
294   {
295     auto iter = input_state.begin() + index + static_cast<std::ptrdiff_t>((x * height));
296     for (vtkm::UInt32 y = 0; y < 9; ++y, ++iter)
297     {
298       *iter = acorn[x][y];
299     }
300   }
301   return j + 64;
302 }
303 
populate(std::vector<vtkm::UInt8> & input_state,vtkm::UInt32 width,vtkm::UInt32 height,vtkm::Float32 rate)304 void populate(std::vector<vtkm::UInt8>& input_state,
305               vtkm::UInt32 width,
306               vtkm::UInt32 height,
307               vtkm::Float32 rate)
308 {
309   std::random_device rd;
310   std::mt19937 gen(rd());
311   std::bernoulli_distribution d(rate);
312 
313   // Initially fill with random values
314   {
315     std::size_t index = 0;
316     for (vtkm::UInt32 i = 0; i < width; ++i)
317     {
318       for (vtkm::UInt32 j = 0; j < height; ++j, ++index)
319       {
320         vtkm::UInt8 v = d(gen);
321         input_state[index] = v;
322       }
323     }
324   }
325 
326   //stamp out areas for acorns
327   for (vtkm::UInt32 i = 2; i < (width - 64); i += 64)
328   {
329     for (vtkm::UInt32 j = 2; j < (height - 64);)
330     {
331       j = stamp_acorn(input_state, i, j, width, height);
332     }
333   }
334 }
335 
main(int argc,char ** argv)336 int main(int argc, char** argv)
337 {
338   glewExperimental = GL_TRUE;
339   glutInit(&argc, argv);
340 
341   const vtkm::UInt32 width = 1024;
342   const vtkm::UInt32 height = 768;
343 
344   const vtkm::UInt32 x = 1024;
345   const vtkm::UInt32 y = 1024;
346 
347   vtkm::Float32 rate = 0.275f; //gives 1 27.5% of the time
348   if (argc > 1)
349   {
350     rate = static_cast<vtkm::Float32>(std::atof(argv[1]));
351     rate = std::max(0.0001f, rate);
352     rate = std::min(0.9f, rate);
353   }
354 
355   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
356   glutInitWindowSize(width, height);
357   glutCreateWindow("VTK-m Game Of Life");
358 
359   GLenum err = glewInit();
360   if (GLEW_OK != err)
361   {
362     std::cout << "glewInit failed\n";
363   }
364 
365   std::vector<vtkm::UInt8> input_state;
366   input_state.resize(static_cast<std::size_t>(x * y), 0);
367   populate(input_state, x, y, rate);
368 
369 
370   vtkm::cont::DataSetBuilderUniform builder;
371   vtkm::cont::DataSet data = builder.Create(vtkm::Id2(x, y));
372 
373   auto stateField =
374     vtkm::cont::make_Field("state", vtkm::cont::Field::Association::POINTS, input_state);
375   data.AddField(stateField);
376 
377   GameOfLife filter;
378   RenderGameOfLife renderer(width, height, x, y);
379 
380   gData = &data;
381   gFilter = &filter;
382   gRenderer = &renderer;
383 
384   glutDisplayFunc([]() {
385     const vtkm::Float32 c = static_cast<vtkm::Float32>(gTimer.GetElapsedTime());
386 
387     vtkm::cont::DataSet oData = gFilter->Execute(*gData, GameOfLifePolicy());
388     gRenderer->render(oData);
389     glutSwapBuffers();
390 
391     *gData = oData;
392 
393     if (c > 120)
394     {
395       //after 1 minute quit the demo
396       exit(0);
397     }
398   });
399 
400   glutIdleFunc([]() { glutPostRedisplay(); });
401 
402   glutMainLoop();
403 
404   return 0;
405 }
406 
407 #if (defined(VTKM_GCC) || defined(VTKM_CLANG))
408 #pragma GCC diagnostic pop
409 #endif
410