nbody.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. //---------------------------------------------------------------------------//
  2. // Copyright (c) 2014 Fabian Köhler <fabian2804@googlemail.com>
  3. //
  4. // Distributed under the Boost Software License, Version 1.0
  5. // See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt
  7. //
  8. // See http://boostorg.github.com/compute for more information.
  9. //---------------------------------------------------------------------------//
  10. #include <iostream>
  11. #define GL_GLEXT_PROTOTYPES
  12. #ifdef __APPLE__
  13. #include <OpenGL/gl.h>
  14. #include <OpenGL/glext.h>
  15. #else
  16. #include <GL/gl.h>
  17. #include <GL/glext.h>
  18. #endif
  19. #include <QtGlobal>
  20. #if QT_VERSION >= 0x050000
  21. #include <QtWidgets>
  22. #else
  23. #include <QtGui>
  24. #endif
  25. #include <QtOpenGL>
  26. #include <QTimer>
  27. #include <boost/program_options.hpp>
  28. #include <boost/random/uniform_real_distribution.hpp>
  29. #include <boost/random/mersenne_twister.hpp>
  30. #ifndef Q_MOC_RUN
  31. #include <boost/compute/system.hpp>
  32. #include <boost/compute/algorithm.hpp>
  33. #include <boost/compute/container/vector.hpp>
  34. #include <boost/compute/interop/opengl.hpp>
  35. #include <boost/compute/utility/source.hpp>
  36. #endif // Q_MOC_RUN
  37. namespace compute = boost::compute;
  38. namespace po = boost::program_options;
  39. using compute::uint_;
  40. using compute::float4_;
  41. const char source[] = BOOST_COMPUTE_STRINGIZE_SOURCE(
  42. __kernel void updateVelocity(__global const float4* position, __global float4* velocity, float dt, uint N)
  43. {
  44. uint gid = get_global_id(0);
  45. float4 r = { 0.0f, 0.0f, 0.0f, 0.0f };
  46. float f = 0.0f;
  47. for(uint i = 0; i != gid; i++) {
  48. if(i != gid) {
  49. r = position[i]-position[gid];
  50. f = length(r)+0.001f;
  51. f *= f*f;
  52. f = dt/f;
  53. velocity[gid] += f*r;
  54. }
  55. }
  56. }
  57. __kernel void updatePosition(__global float4* position, __global const float4* velocity, float dt)
  58. {
  59. uint gid = get_global_id(0);
  60. position[gid].xyz += dt*velocity[gid].xyz;
  61. }
  62. );
  63. class NBodyWidget : public QGLWidget
  64. {
  65. Q_OBJECT
  66. public:
  67. NBodyWidget(std::size_t particles, float dt, QWidget* parent = 0);
  68. ~NBodyWidget();
  69. void initializeGL();
  70. void resizeGL(int width, int height);
  71. void paintGL();
  72. void updateParticles();
  73. void keyPressEvent(QKeyEvent* event);
  74. private:
  75. QTimer* timer;
  76. compute::context m_context;
  77. compute::command_queue m_queue;
  78. compute::program m_program;
  79. compute::opengl_buffer m_position;
  80. compute::vector<float4_>* m_velocity;
  81. compute::kernel m_velocity_kernel;
  82. compute::kernel m_position_kernel;
  83. bool m_initial_draw;
  84. const uint_ m_particles;
  85. const float m_dt;
  86. };
  87. NBodyWidget::NBodyWidget(std::size_t particles, float dt, QWidget* parent)
  88. : QGLWidget(parent), m_initial_draw(true), m_particles(particles), m_dt(dt)
  89. {
  90. // create a timer to redraw as fast as possible
  91. timer = new QTimer(this);
  92. connect(timer, SIGNAL(timeout()), this, SLOT(updateGL()));
  93. timer->start(1);
  94. }
  95. NBodyWidget::~NBodyWidget()
  96. {
  97. delete m_velocity;
  98. // delete the opengl buffer
  99. GLuint vbo = m_position.get_opengl_object();
  100. glDeleteBuffers(1, &vbo);
  101. }
  102. void NBodyWidget::initializeGL()
  103. {
  104. // create context, command queue and program
  105. m_context = compute::opengl_create_shared_context();
  106. m_queue = compute::command_queue(m_context, m_context.get_device());
  107. m_program = compute::program::create_with_source(source, m_context);
  108. m_program.build();
  109. // prepare random particle positions that will be transferred to the vbo
  110. float4_* temp = new float4_[m_particles];
  111. boost::random::uniform_real_distribution<float> dist(-0.5f, 0.5f);
  112. boost::random::mt19937_64 gen;
  113. for(size_t i = 0; i < m_particles; i++) {
  114. temp[i][0] = dist(gen);
  115. temp[i][1] = dist(gen);
  116. temp[i][2] = dist(gen);
  117. temp[i][3] = 1.0f;
  118. }
  119. // create an OpenGL vbo
  120. GLuint vbo = 0;
  121. glGenBuffers(1, &vbo);
  122. glBindBuffer(GL_ARRAY_BUFFER, vbo);
  123. glBufferData(GL_ARRAY_BUFFER, m_particles*sizeof(float4_), temp, GL_DYNAMIC_DRAW);
  124. // create a OpenCL buffer from the vbo
  125. m_position = compute::opengl_buffer(m_context, vbo);
  126. delete[] temp;
  127. // create buffer for velocities
  128. m_velocity = new compute::vector<float4_>(m_particles, m_context);
  129. compute::fill(m_velocity->begin(), m_velocity->end(), float4_(0.0f, 0.0f, 0.0f, 0.0f), m_queue);
  130. // create compute kernels
  131. m_velocity_kernel = m_program.create_kernel("updateVelocity");
  132. m_velocity_kernel.set_arg(0, m_position);
  133. m_velocity_kernel.set_arg(1, m_velocity->get_buffer());
  134. m_velocity_kernel.set_arg(2, m_dt);
  135. m_velocity_kernel.set_arg(3, m_particles);
  136. m_position_kernel = m_program.create_kernel("updatePosition");
  137. m_position_kernel.set_arg(0, m_position);
  138. m_position_kernel.set_arg(1, m_velocity->get_buffer());
  139. m_position_kernel.set_arg(2, m_dt);
  140. }
  141. void NBodyWidget::resizeGL(int width, int height)
  142. {
  143. // update viewport
  144. glViewport(0, 0, width, height);
  145. }
  146. void NBodyWidget::paintGL()
  147. {
  148. // clear buffer
  149. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  150. // check if this is the first draw
  151. if(m_initial_draw) {
  152. // do not update particles
  153. m_initial_draw = false;
  154. } else {
  155. // update particles
  156. updateParticles();
  157. }
  158. // draw
  159. glVertexPointer(4, GL_FLOAT, 0, 0);
  160. glEnableClientState(GL_VERTEX_ARRAY);
  161. glDrawArrays(GL_POINTS, 0, m_particles);
  162. glFinish();
  163. }
  164. void NBodyWidget::updateParticles()
  165. {
  166. // enqueue kernels to update particles and make sure that the command queue is finished
  167. compute::opengl_enqueue_acquire_buffer(m_position, m_queue);
  168. m_queue.enqueue_1d_range_kernel(m_velocity_kernel, 0, m_particles, 0).wait();
  169. m_queue.enqueue_1d_range_kernel(m_position_kernel, 0, m_particles, 0).wait();
  170. m_queue.finish();
  171. compute::opengl_enqueue_release_buffer(m_position, m_queue);
  172. }
  173. void NBodyWidget::keyPressEvent(QKeyEvent* event)
  174. {
  175. if(event->key() == Qt::Key_Escape) {
  176. this->close();
  177. }
  178. }
  179. int main(int argc, char** argv)
  180. {
  181. // parse command line arguments
  182. po::options_description options("options");
  183. options.add_options()
  184. ("help", "show usage")
  185. ("particles", po::value<uint_>()->default_value(1000), "number of particles")
  186. ("dt", po::value<float>()->default_value(0.00001f), "width of each integration step");
  187. po::variables_map vm;
  188. po::store(po::parse_command_line(argc, argv, options), vm);
  189. po::notify(vm);
  190. if(vm.count("help") > 0) {
  191. std::cout << options << std::endl;
  192. return 0;
  193. }
  194. const uint_ particles = vm["particles"].as<uint_>();
  195. const float dt = vm["dt"].as<float>();
  196. QApplication app(argc, argv);
  197. NBodyWidget nbody(particles, dt);
  198. nbody.show();
  199. return app.exec();
  200. }
  201. #include "nbody.moc"