main.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #include <cstdio>
  2. #include <climits>
  3. #include <chrono>
  4. #include <thread>
  5. #include <tuple>
  6. #include <bitset>
  7. #include <random>
  8. #include <vector>
  9. #include <map>
  10. #include <iostream>
  11. #include "simple/graphical.hpp"
  12. #include "simple/interactive.h"
  13. #include "simple/support.hpp"
  14. #include "simple/geom/algorithm.hpp"
  15. #include "recorder.h"
  16. #include "factors.hpp"
  17. #include "utils.hpp"
  18. using namespace simple;
  19. using namespace simple::graphical;
  20. using namespace color_literals;
  21. using namespace std::chrono;
  22. using cell_type = surface::byte;
  23. using cell_writer = pixel_writer<cell_type>;
  24. using cell_bits = std::bitset<CHAR_BIT * sizeof(cell_type)>;
  25. static_assert(std::is_same_v
  26. <cell_writer::raw_type, cell_writer::pixel_type> );
  27. using palette = std::array<rgba_pixel, 256>;
  28. constexpr palette make_palette(rgb_pixel base_color)
  29. {
  30. using simple::support::add_overflow;
  31. using simple::support::count_trailing_zeros;
  32. palette colors{};
  33. for(cell_type i = 0; !add_overflow(i,i,cell_type(1));) // this skips 0 intentionally
  34. {
  35. auto rot = count_trailing_zeros(i); // because ctz is undefined for 0
  36. colors[i] = base_color / cell_bits().size() * (cell_bits().size() - rot);
  37. }
  38. // colors[0] will be 0_rgb by default construction anyways.
  39. return colors;
  40. }
  41. constexpr auto colors = make_palette(0x009dff_rgb);
  42. constexpr auto frametime = duration<std::uintmax_t, std::ratio<1, 60>>{1};
  43. void gol_advance(const cell_writer&, range<int2>);
  44. using supported_concurrencies = std::integer_sequence<size_t, 1,2,4,6,8,10,12,14,16,32>;
  45. constexpr auto split_range = prepare_splits(supported_concurrencies{});
  46. int main(int argc, char** argv) try
  47. {
  48. const char* recording_file = argc > 1 ? argv[1] : nullptr;
  49. bool recording_started = false;
  50. size_t recorded_frame_count = 0;
  51. size_t current_recording_frame = 0;
  52. std::map<size_t,std::vector<interactive::event>> event_record;
  53. auto current_event_record = event_record.begin();
  54. std::optional<recorder> recorder;
  55. auto selected_concurrency = upper_bound(supported_concurrencies{}, std::thread::hardware_concurrency());
  56. std::cout << "Hardware concurrency: " << std::thread::hardware_concurrency() << '\n';
  57. std::cout << "Selected concurrency: " << selected_concurrency.value << '(' << selected_concurrency.index << ')'<< '\n';
  58. graphical::initializer graphics{};
  59. interactive::initializer input{};
  60. const auto screen_size = (*graphics.displays().begin()).current_mode().size;
  61. std::vector<int> cell_sizes;
  62. auto factorization = partial_prime_factorize(std::gcd(screen_size[0], screen_size[1]));
  63. for(auto&& p : factorization.powers) ++p;
  64. all_factors(
  65. factorization.primes,
  66. factorization.powers,
  67. std::vector<int>(factorization.primes.size()),
  68. std::back_inserter(cell_sizes)
  69. );
  70. sort(cell_sizes.begin(), cell_sizes.end());
  71. software_window win("neogol", screen_size, window::flags::borderless);
  72. surface world (win.size(), pixel_format(pixel_format::type::index8));
  73. if(auto palette = world.format().palette())
  74. {
  75. palette->set_colors(colors);
  76. }
  77. else
  78. {
  79. std::fputs("Wow! index8 surface has no palette???", stderr);
  80. return -1;
  81. }
  82. surface world_snapshot(world);
  83. auto pixels = world.pixels();
  84. if(!std::holds_alternative<cell_writer>(pixels))
  85. {
  86. std::fputs("Wow! Can't access index8 surface pixels as bytes???", stderr);
  87. return -2;
  88. }
  89. auto cells = std::get<cell_writer>(pixels);
  90. surface intermediate(win.size(), win.surface().format());
  91. std::seed_seq seeds{std::random_device{}(), std::random_device{}()};
  92. std::mt19937_64 twister{seeds};
  93. std::uniform_int_distribution<cell_type> dist{0,255};
  94. frametime_logger frametime_logger;
  95. bool done = false;
  96. auto cell_size = cell_sizes.begin();
  97. bool live = false;
  98. bool live_once = false;
  99. size_t history = 0;
  100. auto cell_size_snapshot = cell_size;
  101. bool live_snapshot = live;
  102. size_t history_snapshot = history;
  103. using namespace simple::interactive;
  104. auto quit_handler = [&done](quit_request)
  105. { done = true; return false; };
  106. auto event_handler = support::overload
  107. {
  108. quit_handler,
  109. [&cells, &cell_size](mouse_down e)
  110. {
  111. if(e.data.button == mouse_button::left)
  112. cells[e.data.position / *cell_size] ^= 1;
  113. return true;
  114. },
  115. [&cells, &cell_size](mouse_motion e)
  116. {
  117. if(bool(e.data.button_state & mouse_button_mask::left))
  118. {
  119. auto start = e.data.position / *cell_size;
  120. auto end = start - e.data.motion / *cell_size;
  121. bresenham_line<int2>({start, end}, [&cells](int2 p)
  122. {
  123. cells[p] |= 1;
  124. });
  125. return true;
  126. }
  127. return false;
  128. },
  129. [&](key_pressed e)
  130. {
  131. switch(e.data.scancode)
  132. {
  133. case scancode::enter:
  134. live = !live;
  135. break;
  136. case scancode::right:
  137. live_once = true;
  138. live = false;
  139. break;
  140. case scancode::left:
  141. if(history > 1)
  142. {
  143. for(auto&& cell : cells.raw_range()) cell >>= 1;
  144. --history;
  145. }
  146. live = false;
  147. break;
  148. case scancode::equals:
  149. case scancode::kp_plus:
  150. if(cell_size < cell_sizes.end()-1)
  151. ++cell_size;
  152. break;
  153. case scancode::minus:
  154. case scancode::kp_minus:
  155. if(cell_size > cell_sizes.begin())
  156. --cell_size;
  157. break;
  158. case scancode::grave:
  159. std::generate(cells.raw_range().begin(), cells.raw_range().end(), [&]() { return dist(twister); });
  160. history = 8;
  161. break;
  162. case scancode::backspace:
  163. std::fill(cells.raw_range().begin(), cells.raw_range().end(), 0);
  164. history = 0;
  165. break;
  166. case scancode::r:
  167. if(pressed(scancode::rshift) || pressed(scancode::lshift))
  168. {
  169. if(recording_file)
  170. {
  171. recording_started = !recording_started;
  172. if(recording_started)
  173. {
  174. std::cout << "Recording" << '\n';
  175. blit(world, world_snapshot);
  176. live_snapshot = live;
  177. history_snapshot = history;
  178. cell_size_snapshot = cell_size;
  179. recorded_frame_count = 0;
  180. }
  181. else
  182. {
  183. std::cout << "Rendering" << '\n';
  184. recorder.emplace(recording_file, win.size());
  185. current_recording_frame = 0;
  186. current_event_record = event_record.begin();
  187. blit(world_snapshot, world);
  188. live = live_snapshot;
  189. history = history_snapshot;
  190. cell_size = cell_size_snapshot;
  191. }
  192. }
  193. return false;
  194. }
  195. break;
  196. default: return false;
  197. }
  198. return true;
  199. },
  200. [](auto&&){ return false; }
  201. };
  202. auto recording_event_handler = support::overload
  203. {
  204. quit_handler,
  205. [&](key_pressed e)
  206. {
  207. switch(e.data.scancode)
  208. {
  209. case scancode::escape: if(recorder)
  210. {
  211. std::cout << "Rendering interrupted" << '\n';
  212. recorder->record(world, *cell_size, true);
  213. recorder.reset();
  214. event_record.clear();
  215. return true;
  216. }
  217. break;
  218. default: break;
  219. }
  220. return false;
  221. },
  222. [](auto&&){ return false; }
  223. };
  224. auto current_frame = steady_clock::now();
  225. while(!done)
  226. {
  227. current_frame = steady_clock::now();
  228. if(recorder)
  229. {
  230. bool escaped = false;
  231. while(auto event = next_event())
  232. escaped |= std::visit(recording_event_handler, *event);
  233. if(!escaped)
  234. {
  235. if(current_event_record != event_record.end())
  236. if(current_event_record->first < current_recording_frame)
  237. ++current_event_record;
  238. // have to check this again?? come ooon...!
  239. if(current_event_record != event_record.end())
  240. if(current_event_record->first == current_recording_frame)
  241. for(auto&& event : current_event_record->second)
  242. std::visit(event_handler, event);
  243. }
  244. }
  245. else
  246. {
  247. while(auto event = next_event())
  248. {
  249. auto record = std::visit(event_handler, *event);
  250. if(recording_started && record)
  251. event_record[recorded_frame_count-1].push_back(*event);
  252. }
  253. }
  254. const int2 active_size = world.size() / *cell_size;
  255. if(live || live_once)
  256. {
  257. // so fast it does not benefit from multi-threading? o.o
  258. for(auto&& cell : cells.raw_range()) cell <<= 1;
  259. // technically UB? but should work fine for any sane arch
  260. // not too hard to un-UB with small performance hit
  261. // also TODO: if the world is small(er than nproc * cacheline) don't split
  262. support::apply_to(selected_concurrency.index,
  263. [&](auto splitter)
  264. {
  265. const auto whole = range<int2>{int2::one(),active_size-1};
  266. auto pieces = splitter(whole, bool2::j());
  267. std::array<std::thread, pieces.size()-1> threads;
  268. for(size_t i = 0; i < threads.size(); ++i)
  269. {
  270. threads[i] = std::thread(
  271. [&cells, piece=pieces[i]]()
  272. { gol_advance(cells, piece); }
  273. );
  274. }
  275. gol_advance(cells, pieces.back());
  276. for(auto&& t : threads) t.join();
  277. },
  278. split_range);
  279. live_once = false;
  280. if(history < cell_bits().size())
  281. ++history;
  282. }
  283. if(!blit(world, rect{active_size}, win.surface(), rect{win.size()}))
  284. {
  285. if(!blit(world, rect{active_size}, intermediate))
  286. {
  287. std::fputs("Wow! Can't blit from index8 to intermediate surface!", stderr);
  288. return -3;
  289. }
  290. if(!blit(intermediate, rect{active_size}, win.surface(), rect{active_size * *cell_size}))
  291. {
  292. std::fputs("Wow! Can't blit from intermediate surface to window surface?!", stderr);
  293. return -4;
  294. }
  295. }
  296. win.update();
  297. frametime_logger.log(steady_clock::now() - current_frame);
  298. if(recording_started)
  299. {
  300. ++recorded_frame_count;
  301. }
  302. else if(recorder)
  303. {
  304. if(current_recording_frame < recorded_frame_count)
  305. {
  306. if(current_recording_frame == recorded_frame_count - 1 || done)
  307. {
  308. recorder->record(world, *cell_size, true);
  309. recorder.reset();
  310. event_record.clear();
  311. }
  312. else
  313. recorder->record(world, *cell_size, false);
  314. }
  315. ++current_recording_frame;
  316. std::cout << "rendered: "
  317. << current_recording_frame << '/'
  318. << recorded_frame_count << '\n';
  319. }
  320. // this is not very precise, maybe busy wait for last millisecond(or less)
  321. std::this_thread::sleep_until(current_frame + frametime);
  322. }
  323. return 0;
  324. }
  325. catch(...)
  326. {
  327. if(errno)
  328. std::perror("ERROR");
  329. const char* sdl_error = SDL_GetError();
  330. if(*sdl_error)
  331. {
  332. std::fputs(sdl_error, stderr);
  333. std::fputs("\n", stderr);
  334. }
  335. throw;
  336. }
  337. void gol_advance(const cell_writer& cells, range<int2> range)
  338. {
  339. loop(range.lower(), range.upper(), int2::one(), [&](auto& i)
  340. {
  341. int living_neighbors = 0;
  342. loop(i-1, i+2, int2::one(), [&cells, &living_neighbors](auto& j)
  343. {
  344. cell_bits cell = cells[j];
  345. if(cell[1])
  346. living_neighbors += 1;
  347. });
  348. cell_bits cell = cells[i];
  349. living_neighbors -= cell[1]; // *2
  350. if(living_neighbors < 2 || living_neighbors > 3)
  351. {
  352. cell[0] = false;
  353. }
  354. else if(living_neighbors == 3 ||
  355. (living_neighbors == 2 && cell[1]))
  356. {
  357. cell[0] = true;
  358. }
  359. cells[i] = cell.to_ulong();
  360. });
  361. }