nbody.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * Copyright 2014 Stanlo Slasinski. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
  4. */
  5. #include "common.h"
  6. #include "bgfx_utils.h"
  7. #include "imgui/imgui.h"
  8. #include "camera.h"
  9. #include <bgfx/bgfx.h>
  10. namespace
  11. {
  12. static const char* s_shapeNames[] =
  13. {
  14. "Point",
  15. "Sphere",
  16. "Box",
  17. "Donut"
  18. };
  19. struct ParamsData
  20. {
  21. float timeStep;
  22. int32_t dispatchSize;
  23. float gravity;
  24. float damping;
  25. float particleIntensity;
  26. float particleSize;
  27. int32_t baseSeed;
  28. float particlePower;
  29. float initialSpeed;
  30. int32_t initialShape;
  31. float maxAccel;
  32. };
  33. void initializeParams(int32_t _mode, ParamsData* _params)
  34. {
  35. switch(_mode)
  36. {
  37. case 0:
  38. _params->timeStep = 0.0067f;
  39. _params->dispatchSize = 32;
  40. _params->gravity = 0.069f;
  41. _params->damping = 0.0f;
  42. _params->particleIntensity = 0.35f;
  43. _params->particleSize = 0.925f;
  44. _params->baseSeed = 0;
  45. _params->particlePower = 5.0f;
  46. _params->initialSpeed = 122.6f;
  47. _params->initialShape = 0;
  48. _params->maxAccel = 30.0;
  49. break;
  50. case 1:
  51. _params->timeStep = 0.0157f;
  52. _params->dispatchSize = 32;
  53. _params->gravity = 0.109f;
  54. _params->damping = 0.25f;
  55. _params->particleIntensity = 0.64f;
  56. _params->particleSize = 0.279f;
  57. _params->baseSeed = 57;
  58. _params->particlePower = 3.5f;
  59. _params->initialSpeed = 3.2f;
  60. _params->initialShape = 1;
  61. _params->maxAccel = 100.0;
  62. break;
  63. case 2:
  64. _params->timeStep = 0.02f;
  65. _params->dispatchSize = 32;
  66. _params->gravity = 0.24f;
  67. _params->damping = 0.12f;
  68. _params->particleIntensity = 1.0f;
  69. _params->particleSize = 1.0f;
  70. _params->baseSeed = 23;
  71. _params->particlePower = 4.0f;
  72. _params->initialSpeed = 31.1f;
  73. _params->initialShape = 2;
  74. _params->maxAccel = 39.29f;
  75. break;
  76. case 3:
  77. _params->timeStep = 0.0118f;
  78. _params->dispatchSize = 32;
  79. _params->gravity = 0.141f;
  80. _params->damping = 1.0f;
  81. _params->particleIntensity = 0.64f;
  82. _params->particleSize = 0.28f;
  83. _params->baseSeed = 60;
  84. _params->particlePower = 1.97f;
  85. _params->initialSpeed = 69.7f;
  86. _params->initialShape = 3;
  87. _params->maxAccel = 3.21f;
  88. break;
  89. }
  90. }
  91. static const float s_quadVertices[] =
  92. {
  93. 1.0f, 1.0f,
  94. -1.0f, 1.0f,
  95. -1.0f, -1.0f,
  96. 1.0f, -1.0f,
  97. };
  98. static const uint16_t s_quadIndices[] = { 0, 1, 2, 2, 3, 0, };
  99. const uint32_t kThreadGroupUpdateSize = 512;
  100. const uint32_t kMaxParticleCount = 32 * 1024;
  101. class ExampleNbody : public entry::AppI
  102. {
  103. public:
  104. ExampleNbody(const char* _name, const char* _description)
  105. : entry::AppI(_name, _description)
  106. {
  107. }
  108. void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
  109. {
  110. Args args(_argc, _argv);
  111. m_width = _width;
  112. m_height = _height;
  113. m_debug = BGFX_DEBUG_NONE;
  114. m_reset = BGFX_RESET_VSYNC;
  115. bgfx::init(args.m_type, args.m_pciId);
  116. bgfx::reset(m_width, m_height, m_reset);
  117. // Enable debug text.
  118. bgfx::setDebug(m_debug);
  119. // Set view 0 clear state.
  120. bgfx::setViewClear(0
  121. , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
  122. , 0x303030ff
  123. , 1.0f
  124. , 0
  125. );
  126. const bgfx::Caps* caps = bgfx::getCaps();
  127. m_computeSupported = !!(caps->supported & BGFX_CAPS_COMPUTE);
  128. m_indirectSupported = !!(caps->supported & BGFX_CAPS_DRAW_INDIRECT);
  129. imguiCreate();
  130. if (m_computeSupported)
  131. {
  132. bgfx::VertexDecl quadVertexDecl;
  133. quadVertexDecl.begin()
  134. .add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
  135. .end();
  136. // Create static vertex buffer.
  137. m_vbh = bgfx::createVertexBuffer(
  138. // Static data can be passed with bgfx::makeRef
  139. bgfx::makeRef(s_quadVertices, sizeof(s_quadVertices) )
  140. , quadVertexDecl
  141. );
  142. // Create static index buffer.
  143. m_ibh = bgfx::createIndexBuffer(
  144. // Static data can be passed with bgfx::makeRef
  145. bgfx::makeRef(s_quadIndices, sizeof(s_quadIndices) )
  146. );
  147. // Create particle program from shaders.
  148. m_particleProgram = loadProgram("vs_particle", "fs_particle");
  149. // Setup compute buffers
  150. bgfx::VertexDecl computeVertexDecl;
  151. computeVertexDecl.begin()
  152. .add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Float)
  153. .end();
  154. m_currPositionBuffer0 = bgfx::createDynamicVertexBuffer(1 << 15, computeVertexDecl, BGFX_BUFFER_COMPUTE_READ_WRITE);
  155. m_currPositionBuffer1 = bgfx::createDynamicVertexBuffer(1 << 15, computeVertexDecl, BGFX_BUFFER_COMPUTE_READ_WRITE);
  156. m_prevPositionBuffer0 = bgfx::createDynamicVertexBuffer(1 << 15, computeVertexDecl, BGFX_BUFFER_COMPUTE_READ_WRITE);
  157. m_prevPositionBuffer1 = bgfx::createDynamicVertexBuffer(1 << 15, computeVertexDecl, BGFX_BUFFER_COMPUTE_READ_WRITE);
  158. u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4, 3);
  159. m_initInstancesProgram = bgfx::createProgram(loadShader("cs_init_instances"), true);
  160. m_updateInstancesProgram = bgfx::createProgram(loadShader("cs_update_instances"), true);
  161. m_indirectProgram = BGFX_INVALID_HANDLE;
  162. m_indirectBuffer = BGFX_INVALID_HANDLE;
  163. if (m_indirectSupported)
  164. {
  165. m_indirectProgram = bgfx::createProgram(loadShader("cs_indirect"), true);
  166. m_indirectBuffer = bgfx::createIndirectBuffer(2);
  167. }
  168. initializeParams(0, &m_paramsData);
  169. bgfx::setUniform(u_params, &m_paramsData, 3);
  170. bgfx::setBuffer(0, m_prevPositionBuffer0, bgfx::Access::Write);
  171. bgfx::setBuffer(1, m_currPositionBuffer0, bgfx::Access::Write);
  172. bgfx::dispatch(0, m_initInstancesProgram, kMaxParticleCount / kThreadGroupUpdateSize, 1, 1);
  173. float initialPos[3] = { 0.0f, 0.0f, -45.0f };
  174. cameraCreate();
  175. cameraSetPosition(initialPos);
  176. cameraSetVerticalAngle(0.0f);
  177. m_useIndirect = false;
  178. m_timeOffset = bx::getHPCounter();
  179. }
  180. }
  181. virtual int shutdown() override
  182. {
  183. // Cleanup.
  184. cameraDestroy();
  185. imguiDestroy();
  186. if (m_computeSupported)
  187. {
  188. if (m_indirectSupported)
  189. {
  190. bgfx::destroy(m_indirectProgram);
  191. bgfx::destroy(m_indirectBuffer);
  192. }
  193. bgfx::destroy(u_params);
  194. bgfx::destroy(m_currPositionBuffer0);
  195. bgfx::destroy(m_currPositionBuffer1);
  196. bgfx::destroy(m_prevPositionBuffer0);
  197. bgfx::destroy(m_prevPositionBuffer1);
  198. bgfx::destroy(m_updateInstancesProgram);
  199. bgfx::destroy(m_initInstancesProgram);
  200. bgfx::destroy(m_ibh);
  201. bgfx::destroy(m_vbh);
  202. bgfx::destroy(m_particleProgram);
  203. }
  204. // Shutdown bgfx.
  205. bgfx::shutdown();
  206. return 0;
  207. }
  208. bool update() override
  209. {
  210. if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
  211. {
  212. imguiBeginFrame(
  213. m_mouseState.m_mx
  214. , m_mouseState.m_my
  215. , (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0)
  216. | (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0)
  217. | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
  218. , m_mouseState.m_mz
  219. , uint16_t(m_width)
  220. , uint16_t(m_height)
  221. );
  222. showExampleDialog(this
  223. , !m_computeSupported
  224. ? "Compute is not supported."
  225. : NULL
  226. );
  227. int64_t now = bx::getHPCounter();
  228. static int64_t last = now;
  229. const int64_t frameTime = now - last;
  230. last = now;
  231. const double freq = double(bx::getHPFrequency() );
  232. const float deltaTime = float(frameTime/freq);
  233. // Set view 0 default viewport.
  234. bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
  235. if (m_computeSupported)
  236. {
  237. ImGui::SetNextWindowPos(
  238. ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f)
  239. , ImGuiCond_FirstUseEver
  240. );
  241. ImGui::SetNextWindowSize(
  242. ImVec2(m_width / 5.0f, m_height / 1.5f)
  243. , ImGuiCond_FirstUseEver
  244. );
  245. ImGui::Begin("Settings"
  246. , NULL
  247. , 0
  248. );
  249. bool reset = false;
  250. int32_t shape = m_paramsData.initialShape;
  251. if (ImGui::Combo("Initial shape", &shape, s_shapeNames, BX_COUNTOF(s_shapeNames) ) )
  252. {
  253. // Modify parameters and reset if shape is changed
  254. initializeParams(shape, &m_paramsData);
  255. reset = true;
  256. }
  257. ImGui::SliderInt("Random seed", &m_paramsData.baseSeed, 0, 100);
  258. if (ImGui::Button("Reset") )
  259. {
  260. reset = true;
  261. }
  262. ImGui::Separator();
  263. ImGui::SliderInt("Particle count (x512)", &m_paramsData.dispatchSize, 1, 64);
  264. ImGui::SliderFloat("Gravity", &m_paramsData.gravity, 0.0f, 0.3f);
  265. ImGui::SliderFloat("Damping", &m_paramsData.damping, 0.0f, 1.0f);
  266. ImGui::SliderFloat("Max acceleration", &m_paramsData.maxAccel, 0.0f, 100.0f);
  267. ImGui::SliderFloat("Time step", &m_paramsData.timeStep, 0.0f, 0.02f);
  268. ImGui::Separator();
  269. ImGui::SliderFloat("Particle intensity", &m_paramsData.particleIntensity, 0.0f, 1.0f);
  270. ImGui::SliderFloat("Particle size", &m_paramsData.particleSize, 0.0f, 1.0f);
  271. ImGui::SliderFloat("Particle power", &m_paramsData.particlePower, 0.001f, 16.0f);
  272. ImGui::Separator();
  273. if (m_indirectSupported)
  274. {
  275. ImGui::Checkbox("Use draw/dispatch indirect", &m_useIndirect);
  276. }
  277. ImGui::End();
  278. if (reset)
  279. {
  280. bgfx::setBuffer(0, m_prevPositionBuffer0, bgfx::Access::Write);
  281. bgfx::setBuffer(1, m_currPositionBuffer0, bgfx::Access::Write);
  282. bgfx::setUniform(u_params, &m_paramsData, 3);
  283. bgfx::dispatch(0, m_initInstancesProgram, kMaxParticleCount / kThreadGroupUpdateSize, 1, 1);
  284. }
  285. if (m_useIndirect)
  286. {
  287. bgfx::setUniform(u_params, &m_paramsData, 3);
  288. bgfx::setBuffer(0, m_indirectBuffer, bgfx::Access::Write);
  289. bgfx::dispatch(0, m_indirectProgram);
  290. }
  291. bgfx::setBuffer(0, m_prevPositionBuffer0, bgfx::Access::Read);
  292. bgfx::setBuffer(1, m_currPositionBuffer0, bgfx::Access::Read);
  293. bgfx::setBuffer(2, m_prevPositionBuffer1, bgfx::Access::Write);
  294. bgfx::setBuffer(3, m_currPositionBuffer1, bgfx::Access::Write);
  295. bgfx::setUniform(u_params, &m_paramsData, 3);
  296. if (m_useIndirect)
  297. {
  298. bgfx::dispatch(0, m_updateInstancesProgram, m_indirectBuffer, 1);
  299. }
  300. else
  301. {
  302. bgfx::dispatch(0, m_updateInstancesProgram, uint16_t(m_paramsData.dispatchSize), 1, 1);
  303. }
  304. bx::xchg(m_currPositionBuffer0, m_currPositionBuffer1);
  305. bx::xchg(m_prevPositionBuffer0, m_prevPositionBuffer1);
  306. // Update camera.
  307. cameraUpdate(deltaTime, m_mouseState);
  308. float view[16];
  309. cameraGetViewMtx(view);
  310. // Set view and projection matrix for view 0.
  311. const bgfx::HMD* hmd = bgfx::getHMD();
  312. if (NULL != hmd && 0 != (hmd->flags & BGFX_HMD_RENDERING) )
  313. {
  314. float viewHead[16];
  315. float eye[3] = {};
  316. bx::mtxQuatTranslationHMD(viewHead, hmd->eye[0].rotation, eye);
  317. float tmp[16];
  318. bx::mtxMul(tmp, view, viewHead);
  319. bgfx::setViewTransform(
  320. 0
  321. , tmp
  322. , hmd->eye[0].projection
  323. , BGFX_VIEW_STEREO
  324. , hmd->eye[1].projection
  325. );
  326. // Set view 0 default viewport.
  327. //
  328. // Use HMD's width/height since HMD's internal frame buffer size
  329. // might be much larger than window size.
  330. bgfx::setViewRect(0, 0, 0, hmd->width, hmd->height);
  331. }
  332. else
  333. {
  334. float proj[16];
  335. bx::mtxProj(
  336. proj
  337. , 90.0f
  338. , float(m_width)/float(m_height)
  339. , 0.1f
  340. , 10000.0f
  341. , bgfx::getCaps()->homogeneousDepth
  342. );
  343. bgfx::setViewTransform(0, view, proj);
  344. // Set view 0 default viewport.
  345. bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
  346. }
  347. // Set vertex and index buffer.
  348. bgfx::setVertexBuffer(0, m_vbh);
  349. bgfx::setIndexBuffer(m_ibh);
  350. bgfx::setInstanceDataBuffer(m_currPositionBuffer0
  351. , 0
  352. , m_paramsData.dispatchSize * kThreadGroupUpdateSize
  353. );
  354. // Set render states.
  355. bgfx::setState(0
  356. | BGFX_STATE_RGB_WRITE
  357. | BGFX_STATE_BLEND_ADD
  358. | BGFX_STATE_DEPTH_TEST_ALWAYS
  359. );
  360. // Submit primitive for rendering to view 0.
  361. if (m_useIndirect)
  362. {
  363. bgfx::submit(0, m_particleProgram, m_indirectBuffer, 0);
  364. }
  365. else
  366. {
  367. bgfx::submit(0, m_particleProgram);
  368. }
  369. }
  370. imguiEndFrame();
  371. // Advance to next frame. Rendering thread will be kicked to
  372. // process submitted rendering primitives.
  373. bgfx::frame();
  374. return true;
  375. }
  376. return false;
  377. }
  378. entry::MouseState m_mouseState;
  379. uint32_t m_width;
  380. uint32_t m_height;
  381. uint32_t m_debug;
  382. uint32_t m_reset;
  383. bool m_useIndirect;
  384. bool m_computeSupported;
  385. bool m_indirectSupported;
  386. ParamsData m_paramsData;
  387. bgfx::VertexBufferHandle m_vbh;
  388. bgfx::IndexBufferHandle m_ibh;
  389. bgfx::ProgramHandle m_particleProgram;
  390. bgfx::ProgramHandle m_indirectProgram;
  391. bgfx::ProgramHandle m_initInstancesProgram;
  392. bgfx::ProgramHandle m_updateInstancesProgram;
  393. bgfx::IndirectBufferHandle m_indirectBuffer;
  394. bgfx::DynamicVertexBufferHandle m_currPositionBuffer0;
  395. bgfx::DynamicVertexBufferHandle m_currPositionBuffer1;
  396. bgfx::DynamicVertexBufferHandle m_prevPositionBuffer0;
  397. bgfx::DynamicVertexBufferHandle m_prevPositionBuffer1;
  398. bgfx::UniformHandle u_params;
  399. int64_t m_timeOffset;
  400. };
  401. } // namespace
  402. ENTRY_IMPLEMENT_MAIN(ExampleNbody, "24-nbody", "N-body simulation with compute shaders using buffers.");