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