/src/library/gl_object_unit_tests.cc

https://github.com/yeahren/RSXGL · C++ · 607 lines · 452 code · 111 blank · 44 comment · 49 complexity · f1450b3f1704cceb13ed01b2882a8ba8 MD5 · raw file

  1. // "Unit testing" for the implementation of gl_object.
  2. //
  3. // Test different object types:
  4. // - normal object (there are none in OpenGL)
  5. // - normal objects with a valid default object (VAO, textures)
  6. // - reference counted objects (shader objects, programs, buffers)
  7. // - bindable or not
  8. // - test preallocating too
  9. #include <iostream>
  10. #include <string>
  11. #include <stdexcept>
  12. #include <cstdlib>
  13. #include <vector>
  14. struct assertion : public std::runtime_error {
  15. assertion(const std::string & info)
  16. : std::runtime_error(info) {
  17. }
  18. };
  19. #define cxx_assert(__e) ((__e) ? (void)0 : throw assertion(std::string(#__e)));
  20. #define assert cxx_assert
  21. #include "gl_object.h"
  22. //
  23. struct normal_object {
  24. static const size_t max_buffers = (1 << 16);
  25. static const size_t max_targets = 16;
  26. typedef bindable_gl_object< normal_object, max_buffers, max_targets > gl_object_type;
  27. typedef gl_object_type::name_type name_type;
  28. typedef gl_object_type::storage_type storage_type;
  29. typedef gl_object_type::binding_bitfield_type binding_bitfield_type;
  30. typedef gl_object_type::binding_type binding_type;
  31. static storage_type & storage();
  32. binding_bitfield_type binding_bitfield;
  33. uint32_t deleted;
  34. uint32_t ref_count;
  35. normal_object() : deleted(0), ref_count(0), contents(current_contents++) {
  36. std::cout << __PRETTY_FUNCTION__ << ":" << contents << std::endl;
  37. }
  38. ~normal_object() {
  39. std::cout << __PRETTY_FUNCTION__ << ":" << contents << std::endl;
  40. }
  41. void access(std::ostream & s) const {
  42. s << contents;
  43. }
  44. static uint32_t current_contents;
  45. uint32_t contents;
  46. };
  47. uint32_t normal_object::current_contents = 42;
  48. normal_object::storage_type &
  49. normal_object::storage()
  50. {
  51. static normal_object::storage_type _storage(16);
  52. return _storage;
  53. }
  54. //
  55. struct default_object {
  56. static const size_t max_buffers = (1 << 16);
  57. static const size_t max_targets = 16;
  58. typedef bindable_gl_object< default_object, max_buffers, max_targets, 1 > gl_object_type;
  59. typedef gl_object_type::name_type name_type;
  60. typedef gl_object_type::storage_type storage_type;
  61. typedef gl_object_type::binding_bitfield_type binding_bitfield_type;
  62. typedef gl_object_type::binding_type binding_type;
  63. static storage_type & storage();
  64. binding_bitfield_type binding_bitfield;
  65. default_object() : contents(current_contents++) {
  66. std::cout << __PRETTY_FUNCTION__ << ":" << contents << std::endl;
  67. }
  68. ~default_object() {
  69. std::cout << __PRETTY_FUNCTION__ << ":" << contents << std::endl;
  70. }
  71. void access(std::ostream & s) const {
  72. s << contents;
  73. }
  74. static uint32_t current_contents;
  75. uint32_t contents;
  76. };
  77. uint32_t default_object::current_contents = 42;
  78. default_object::storage_type &
  79. default_object::storage()
  80. {
  81. static default_object::storage_type _storage;
  82. return _storage;
  83. }
  84. template< typename Object, size_t Size >
  85. struct container_object : public object_container_type< Object, Size > {
  86. static const size_t max_objects = (1 << 16);
  87. static const size_t max_targets = 1;
  88. typedef container_object< Object, Size > this_type;
  89. typedef bindable_gl_object< this_type, max_objects, max_targets, 1 > gl_object_type;
  90. typedef typename gl_object_type::name_type name_type;
  91. typedef typename gl_object_type::storage_type storage_type;
  92. typedef typename gl_object_type::binding_bitfield_type binding_bitfield_type;
  93. typedef typename gl_object_type::binding_type binding_type;
  94. typedef object_container_type< Object, Size > container_type;
  95. static storage_type & storage();
  96. binding_bitfield_type binding_bitfield;
  97. uint32_t deleted;
  98. uint32_t ref_count;
  99. #if 0
  100. private:
  101. typename Object::name_type names[Size];
  102. #endif
  103. public:
  104. container_object() : deleted(0), ref_count(0), contents(current_contents++) {
  105. std::cout << __PRETTY_FUNCTION__ << ":" << contents << std::endl;
  106. #if 0
  107. for(size_t i = 0;i < Size;++i) {
  108. names[i] = 0;
  109. }
  110. #endif
  111. }
  112. ~container_object() {
  113. std::cout << __PRETTY_FUNCTION__ << ":" << contents << std::endl;
  114. #if 0
  115. for(size_t i = 0;i < Size;++i) {
  116. if(names[i] == 0) continue;
  117. Object::gl_object_type::unref_and_maybe_delete(names[i]);
  118. }
  119. #endif
  120. }
  121. void access(std::ostream & s) const {
  122. s << contents << " contains the following objects: ";
  123. for(size_t i = 0;i < Size;++i) {
  124. if(container_type::names[i] != 0) s << container_type::names[i] << " ";
  125. }
  126. }
  127. static uint32_t current_contents;
  128. uint32_t contents;
  129. #if 0
  130. void bind(size_t i,const typename Object::name_type name) {
  131. assert(i < Size);
  132. const typename Object::name_type prev_name = names[i];
  133. names[i] = name;
  134. if(name != 0) Object::gl_object_type::ref(name);
  135. if(prev_name != 0) Object::gl_object_type::unref_and_maybe_delete(prev_name);
  136. }
  137. #endif
  138. };
  139. template< typename Object, size_t Size >
  140. uint32_t container_object< Object, Size >::current_contents = 13;
  141. template< typename Object, size_t Size >
  142. typename container_object< Object, Size >::storage_type &
  143. container_object< Object, Size >::storage()
  144. {
  145. static typename container_object< Object, Size >::storage_type _storage;
  146. return _storage;
  147. }
  148. //
  149. template< typename Object >
  150. void summarize()
  151. {
  152. #if !defined(NDEBUG)
  153. std::cout << Object::storage().current_potential_size() << " potential names (" << Object::storage().m_num_names << " actual) "
  154. << Object::storage().contents_size() << " potential objects (" << Object::storage().m_num_objects << " actual) "
  155. << Object::storage().orphans_size() << " potential orphans (" << Object::storage().m_num_orphans << " actual) "
  156. << std::endl;
  157. #else
  158. std::cout << Object::storage().current_potential_size() << " potential names "
  159. << Object::storage().contents_size() << " potential objects "
  160. << Object::storage().orphans_size() << " potential orphans (" << Object::storage().m_num_orphans << " actual) "
  161. << std::endl;
  162. #endif
  163. for(size_t i = 0;i < Object::storage().current_potential_size();++i) {
  164. std::cout << " name i: " << i
  165. << " is_name: " << (int)Object::storage().is_name(i)
  166. << " is_constructed: " << (int)Object::storage().is_constructed(i)
  167. << " is_object: " << (int)Object::storage().is_object(i)
  168. << std::endl;
  169. }
  170. }
  171. // Try accessing all the potentially available objects; some will fail:
  172. template< typename Object >
  173. void access()
  174. {
  175. for(size_t i = 0;i < Object::storage().current_potential_size();++i) {
  176. std::cout << i << ": ";
  177. try {
  178. if(Object::storage().is_constructed(i) && !Object::storage().is_name(i)) std::cout << " ! ";
  179. Object::storage().at(i).access(std::cout);
  180. }
  181. catch (const assertion a) {
  182. std::cout << "possibly expected assertion: " << a.what();
  183. }
  184. std::cout << std::endl;
  185. }
  186. }
  187. template< typename Object >
  188. void basic_tests()
  189. {
  190. try {
  191. // Create some names:
  192. // - Should return 1 through 10
  193. // - Should not see any constructors get called in the process
  194. // - For each name, is_name(), is_constructed(), is_object() will return 1, 0, 0
  195. size_t num_created_objects = 0;
  196. // Summarize an empty set of objects:
  197. summarize< Object >();
  198. access< Object >();
  199. // Access object 0; should fail:
  200. {
  201. try {
  202. Object & object = Object::storage().at(0);
  203. assert(0);
  204. }
  205. catch (const assertion a) {
  206. std::cout << "expected assertion: " << a.what() << std::endl;
  207. }
  208. }
  209. // Detach object 0; should also fail:
  210. {
  211. try {
  212. Object::storage().detach(0);
  213. assert(0);
  214. }
  215. catch (const assertion a) {
  216. std::cout << "expected assertion: " << a.what() << std::endl;
  217. }
  218. }
  219. // Summarize an empty set of objects:
  220. summarize< Object >();
  221. access< Object >();
  222. const size_t n = 10;
  223. typename Object::name_type names[n];
  224. for(size_t i = 0;i < n;++i) {
  225. names[i] = Object::storage().create_name();
  226. }
  227. summarize< Object >();
  228. access< Object >();
  229. {
  230. // turn a sampling of names into actual objects:
  231. const size_t m = 5;
  232. typename Object::name_type objects[m];
  233. for(size_t i = 0;i < m;++i) {
  234. typename Object::name_type name = names[rand() % n];
  235. if(!Object::storage().is_object(name)) {
  236. objects[m] = name;
  237. Object::storage().create_object(objects[m]);
  238. ++num_created_objects;
  239. }
  240. }
  241. }
  242. summarize< Object >();
  243. access< Object >();
  244. // detach a sampling of the objects:
  245. // this should not fail, but, afterward,
  246. {
  247. size_t count = 0;
  248. for(size_t i = 0;i < n;++i) {
  249. if(rand() % 2) {
  250. if(Object::storage().is_object(names[i])) {
  251. Object::storage().detach(names[i]);
  252. ++count;
  253. }
  254. }
  255. }
  256. std::cout << "detached " << count << " objects" << std::endl;
  257. }
  258. summarize< Object >();
  259. access< Object >();
  260. // get more names - these should all be unique names, despite some having been detached:
  261. {
  262. std::cout << "new names" << std::endl;
  263. const size_t m = 30;
  264. typename Object::name_type new_names[m];
  265. Object::storage().create_names(m,new_names);
  266. for(size_t i = 0;i < m;++i) {
  267. std::cout << new_names[i] << std::endl;
  268. for(size_t j = 0;j < n;++j) {
  269. if(new_names[i] == names[j]) assert(0);
  270. }
  271. }
  272. }
  273. summarize< Object >();
  274. access< Object >();
  275. // create yet more objects, via create_name_and_object; this should cause the contents list to grow
  276. {
  277. const size_t m = 64;
  278. typename Object::name_type new_objects[m];
  279. for(size_t i = 0;i < m;++i) {
  280. new_objects[i] = Object::storage().create_name_and_object();
  281. ++num_created_objects;
  282. std::cout << i << ":" << new_objects[i] << std::endl;
  283. }
  284. }
  285. summarize< Object >();
  286. access< Object >();
  287. std::cout << "tried to create " << num_created_objects << " objects" << std::endl;
  288. assert(num_created_objects != Object::current_contents);
  289. //
  290. // detach more objects:
  291. {
  292. size_t count = 0;
  293. for(size_t i = 0;i < num_created_objects;++i) {
  294. if(rand() % 2) {
  295. if(i != 0 && Object::storage().is_object(i)) {
  296. Object::storage().detach(i);
  297. ++count;
  298. }
  299. }
  300. }
  301. std::cout << "detached " << count << " objects" << std::endl;
  302. }
  303. summarize< Object >();
  304. access< Object >();
  305. // simulate object deletion
  306. //
  307. {
  308. // Just delete a bunch of objects:
  309. const size_t m = (Object::storage().m_num_objects < 20) ? Object::storage().m_num_objects : 20;
  310. std::cout << "try to delete the first " << m << " objects" << std::endl;
  311. for(size_t i = 0;i < (num_created_objects < m ? num_created_objects : m);++i) {
  312. std::cout << "want to destroy " << i << ": "
  313. << " is_name: " << (int)Object::storage().is_name(i)
  314. << " is_constructed: " << (int)Object::storage().is_constructed(i)
  315. << " is_object: " << (int)Object::storage().is_object(i)
  316. << std::endl;
  317. try {
  318. Object::storage().destroy(i);
  319. }
  320. catch (const assertion a) {
  321. std::cout << "possibly expected assertion: " << a.what() << std::endl;
  322. }
  323. }
  324. }
  325. summarize< Object >();
  326. access< Object >();
  327. {
  328. // Delete the first actually created objects:
  329. std::cout << "Delete the first actually created objects:" << std::endl;
  330. const size_t m = (Object::storage().m_num_objects < 20) ? Object::storage().m_num_objects : 20;
  331. std::cout << "try to delete the first " << m << " actual objects" << std::endl;
  332. for(size_t i = 0,count = 0;i < Object::storage().current_potential_size() && count < m;++i) {
  333. if(!Object::storage().is_object(i)) continue;
  334. std::cout << "want to destroy " << i << ": "
  335. << " is_name: " << (int)Object::storage().is_name(i)
  336. << " is_constructed: " << (int)Object::storage().is_constructed(i)
  337. << " is_object: " << (int)Object::storage().is_object(i);
  338. try {
  339. Object::storage().destroy(i);
  340. std::cout << std::endl;
  341. }
  342. catch (const assertion a) {
  343. std::cout << "possibly expected assertion: " << a.what() << std::endl;
  344. }
  345. ++count;
  346. }
  347. }
  348. summarize< Object >();
  349. access< Object >();
  350. // create yet more objects, via create_name_and_object - this should re-use some names:
  351. {
  352. std::cout << "create yet more objects, via create_name_and_object - this should re-use some names:" << std::endl;
  353. const size_t m = 64;
  354. typename Object::name_type new_objects[m];
  355. for(size_t i = 0;i < m;++i) {
  356. new_objects[i] = Object::storage().create_name_and_object();
  357. ++num_created_objects;
  358. std::cout << i << ":" << new_objects[i] << std::endl;
  359. }
  360. }
  361. summarize< Object >();
  362. access< Object >();
  363. // try orphaning some objects, indiscrimantly:
  364. {
  365. const size_t m = 30;
  366. size_t num_orphans = 0;
  367. for(size_t i = 0;i < m;++i) {
  368. //typename Object::name_type name = (rand() % Object::storage().current_potential_size() - 1) + 1;
  369. typename Object::name_type name = i + 1;
  370. typename Object::storage_type::orphan_size_type j = Object::storage().orphan(name);
  371. if(j != (typename Object::storage_type::orphan_size_type)~0U) {
  372. std::cout << "orphaned: " << name << " ";
  373. Object::storage().orphan_at(j).access(std::cout);
  374. std::cout << std::endl;
  375. ++num_orphans;
  376. }
  377. }
  378. std::cout << "orphaned " << num_orphans << " objects" << std::endl;
  379. }
  380. // create more objects - should see some re-used names:
  381. {
  382. std::cout << "create yet more objects, via create_name_and_object - this should re-use some names:" << std::endl;
  383. const size_t m = 64;
  384. typename Object::name_type new_objects[m];
  385. for(size_t i = 0;i < m;++i) {
  386. new_objects[i] = Object::storage().create_name_and_object();
  387. ++num_created_objects;
  388. std::cout << i << ":" << new_objects[i] << std::endl;
  389. }
  390. }
  391. // delete all possible object objects - we should see that destructors are called, but storage arrays are not free'd
  392. {
  393. std::cout << "try to delete everything" << std::endl;
  394. for(size_t i = 0;i < Object::storage().current_potential_size();++i) {
  395. std::cout << "want to destroy " << i << ": "
  396. << " is_name: " << (int)Object::storage().is_name(i)
  397. << " is_constructed: " << (int)Object::storage().is_constructed(i)
  398. << " is_object: " << (int)Object::storage().is_object(i);
  399. try {
  400. Object::storage().destroy(i);
  401. std::cout << std::endl;
  402. }
  403. catch (const assertion a) {
  404. std::cout << "possibly expected assertion: " << a.what() << std::endl;
  405. }
  406. }
  407. }
  408. summarize< Object >();
  409. access< Object >();
  410. {
  411. std::cout << "Destroy orphans" << std::endl;
  412. Object::storage().destroy_orphans();
  413. }
  414. {
  415. // Start over:
  416. typename Object::name_type name = Object::storage().create_name_and_object();
  417. std::cout << "started over with: " << name << std::endl;
  418. }
  419. summarize< Object >();
  420. access< Object >();
  421. }
  422. catch(const assertion a) {
  423. std::cout << "unexpected assertion: " << a.what() << std::endl;
  424. }
  425. }
  426. template< typename Object >
  427. void refcount_tests()
  428. {
  429. typedef container_object< Object, 16 > container_type;
  430. typedef typename container_type::name_type container_name_type;
  431. typedef typename Object::name_type object_name_type;
  432. // create the container itself:
  433. container_name_type c = container_type::storage().create_name_and_object();
  434. std::cout << "c: " << c << std::endl;
  435. // create some objects:
  436. const size_t n = 10;
  437. object_name_type names[n];
  438. for(size_t i = 0;i < n;++i) {
  439. names[i] = Object::storage().create_name_and_object();
  440. }
  441. std::cout << "object names: " << std::endl;
  442. for(size_t i = 0;i < n;++i) {
  443. std::cout << "\t" << names[i] << std::endl;
  444. }
  445. // associate some of those objects with the container:
  446. for(size_t i = 0;i < n;++i) {
  447. if(rand() % 2) {
  448. container_type::storage().at(c).bind(rand() % 16,names[i]);
  449. }
  450. }
  451. summarize< container_type >();
  452. access< container_type >();
  453. // /try/ to delete all of the objects; the ones that are contained will not be deleted:
  454. for(size_t i = 0;i < n;++i) {
  455. Object::gl_object_type::maybe_delete(names[i]);
  456. }
  457. summarize< Object >();
  458. access< Object >();
  459. // construct a bunch of new objects - this should result in completely new set of names
  460. {
  461. object_name_type names[n];
  462. for(size_t i = 0;i < n;++i) {
  463. names[i] = Object::storage().create_name_and_object();
  464. }
  465. std::cout << "object names: " << std::endl;
  466. for(size_t i = 0;i < n;++i) {
  467. std::cout << "\t" << names[i] << std::endl;
  468. }
  469. container_type::storage().at(c).access(std::cout);
  470. std::cout << std::endl;
  471. }
  472. // delete c - we should see destructors called for Object
  473. container_type::storage().destroy(c);
  474. }
  475. int
  476. main(int argc, char ** argv)
  477. {
  478. {
  479. // Create initial storage. Even if a certain number of objects are pre-allocated, you should not see any constructors get called:
  480. normal_object::storage();
  481. basic_tests< normal_object >();
  482. std::cout << "normal_object done" << std::endl;
  483. }
  484. #if 0
  485. {
  486. default_object::storage();
  487. basic_tests< default_object >();
  488. std::cout << "default_object done" << std::endl;
  489. }
  490. {
  491. refcount_tests< normal_object >();
  492. std::cout << "refcount tests done" << std::endl;
  493. }
  494. #endif
  495. return 0;
  496. }