WebGLElementArrayCache.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "WebGLElementArrayCache.h"
  6. #include <algorithm>
  7. #include <cstdlib>
  8. #include <cstring>
  9. #include <limits>
  10. #include "mozilla/Assertions.h"
  11. #include "mozilla/MathAlgorithms.h"
  12. #include "mozilla/MemoryReporting.h"
  13. namespace mozilla {
  14. /* WebGLElementArrayCacheTree contains most of the implementation of
  15. * WebGLElementArrayCache, which performs WebGL element array buffer validation
  16. * for drawElements.
  17. *
  18. * Attention: Here lie nontrivial data structures, bug-prone algorithms, and
  19. * non-canonical tweaks! Whence the explanatory comments, and compiled unit
  20. * test.
  21. *
  22. * *** What problem are we solving here? ***
  23. *
  24. * WebGL::DrawElements has to validate that the elements are in range wrt the
  25. * current vertex attribs. This boils down to the problem, given an array of
  26. * integers, of computing the maximum in an arbitrary sub-array. The naive
  27. * algorithm has linear complexity; this has been a major performance problem,
  28. * see bug 569431. In that bug, we took the approach of caching the max for the
  29. * whole array, which does cover most cases (DrawElements typically consumes the
  30. * whole element array buffer) but doesn't help in other use cases:
  31. * - when doing "partial DrawElements" i.e. consuming only part of the element
  32. * array buffer
  33. * - when doing frequent "partial buffer updates" i.e. bufferSubData calls
  34. * updating parts of the element array buffer
  35. *
  36. * *** The solution: A binary tree ***
  37. *
  38. * The solution implemented here is to use a binary tree as the cache data
  39. * structure. Each tree node contains the max of its two children nodes. In this
  40. * way, finding the maximum in any contiguous sub-array has log complexity
  41. * instead of linear complexity.
  42. *
  43. * Simplistically, if the element array is:
  44. *
  45. * [1 4 3 2]
  46. *
  47. * then the corresponding tree is:
  48. *
  49. * 4
  50. * _/ \_
  51. * 4 3
  52. * / \ / \
  53. * 1 4 3 2
  54. *
  55. * In practice, the bottom-most levels of the tree are both the largest to store
  56. * (because they have more nodes), and the least useful performance-wise
  57. * (because each node in the bottom levels concerns only few entries in the
  58. * elements array buffer, it is cheap to compute).
  59. *
  60. * For this reason, we stop the tree a few levels above, so that each tree leaf
  61. * actually corresponds to more than one element array entry.
  62. *
  63. * The number of levels that we "drop" is |kSkippedBottomTreeLevels| and the
  64. * number of element array entries that each leaf corresponds to, is
  65. * |kElementsPerLeaf|. This being a binary tree, we have:
  66. *
  67. * kElementsPerLeaf = 2 ^ kSkippedBottomTreeLevels.
  68. *
  69. * *** Storage layout of the binary tree ***
  70. *
  71. * We take advantage of the specifics of the situation to avoid generalist tree
  72. * storage and instead store the tree entries in a vector, mTreeData.
  73. *
  74. * TreeData is always a vector of length:
  75. *
  76. * 2 * (number of leaves).
  77. *
  78. * Its data layout is as follows: mTreeData[0] is unused, mTreeData[1] is the
  79. * root node, then at offsets 2..3 is the tree level immediately below the root
  80. * node, then at offsets 4..7 is the tree level below that, etc.
  81. *
  82. * The figure below illustrates this by writing at each tree node the offset
  83. * into mTreeData at which it is stored:
  84. *
  85. * 1
  86. * _/ \_
  87. * 2 3
  88. * / \ / \
  89. * 4 5 6 7
  90. * ...
  91. *
  92. * Thus, under the convention that the root level is level 0, we see that level
  93. * N is stored at offsets:
  94. *
  95. * [ 2^n .. 2^(n+1) - 1 ]
  96. *
  97. * in mTreeData. Likewise, all the usual tree operations have simple
  98. * mathematical expressions in terms of mTreeData offsets, see all the methods
  99. * such as ParentNode, LeftChildNode, etc.
  100. *
  101. * *** Design constraint: Element types aren't known at buffer-update time ***
  102. *
  103. * Note that a key constraint that we're operating under, is that we don't know
  104. * the types of the elements by the time WebGL bufferData/bufferSubData methods
  105. * are called. The type of elements is only specified in the drawElements call.
  106. * This means that we may potentially have to store caches for multiple element
  107. * types, for the same element array buffer. Since we don't know yet how many
  108. * element types we'll eventually support (extensions add more), the concern
  109. * about memory usage is serious. This is addressed by kSkippedBottomTreeLevels
  110. * as explained above. Of course, in the typical case where each element array
  111. * buffer is only ever used with one type, this is also addressed by having
  112. * WebGLElementArrayCache lazily create trees for each type only upon first use.
  113. *
  114. * Another consequence of this constraint is that when updating the trees, we
  115. * have to update all existing trees. So if trees for types uint8_t, uint16_t
  116. * and uint32_t have ever been constructed for this buffer, every subsequent
  117. * update will have to update all trees even if one of the types is never used
  118. * again. That's inefficient, but content should not put indices of different
  119. * types in the same element array buffer anyways. Different index types can
  120. * only be consumed in separate drawElements calls, so nothing particular is
  121. * to be achieved by lumping them in the same buffer object.
  122. */
  123. template<typename T>
  124. struct WebGLElementArrayCacheTree
  125. {
  126. /* A too-high kSkippedBottomTreeLevels would harm the performance of small
  127. * drawElements calls. A too-low kSkippedBottomTreeLevels would cause undue
  128. * memory usage. The current value has been validated by some benchmarking.
  129. * See bug 732660.
  130. */
  131. static const size_t kSkippedBottomTreeLevels = 3;
  132. static const size_t kElementsPerLeaf = 1 << kSkippedBottomTreeLevels;
  133. // Since kElementsPerLeaf is POT:
  134. static const size_t kElementsPerLeafMask = kElementsPerLeaf - 1;
  135. private:
  136. // The WebGLElementArrayCache that owns this tree:
  137. WebGLElementArrayCache& mParent;
  138. // The tree's internal data storage. Its length is 2 * (number of leaves)
  139. // because of its data layout explained in the above class comment.
  140. FallibleTArray<T> mTreeData;
  141. public:
  142. // Constructor. Takes a reference to the WebGLElementArrayCache that is to be
  143. // the parent. Does not initialize the tree. Should be followed by a call
  144. // to Update() to attempt initializing the tree.
  145. explicit WebGLElementArrayCacheTree(WebGLElementArrayCache& value)
  146. : mParent(value)
  147. {
  148. }
  149. T GlobalMaximum() const {
  150. return mTreeData[1];
  151. }
  152. // returns the index of the parent node; if treeIndex=1 (the root node),
  153. // the return value is 0.
  154. static size_t ParentNode(size_t treeIndex) {
  155. MOZ_ASSERT(treeIndex > 1);
  156. return treeIndex >> 1;
  157. }
  158. static bool IsRightNode(size_t treeIndex) {
  159. MOZ_ASSERT(treeIndex > 1);
  160. return treeIndex & 1;
  161. }
  162. static bool IsLeftNode(size_t treeIndex) {
  163. MOZ_ASSERT(treeIndex > 1);
  164. return !IsRightNode(treeIndex);
  165. }
  166. static size_t SiblingNode(size_t treeIndex) {
  167. MOZ_ASSERT(treeIndex > 1);
  168. return treeIndex ^ 1;
  169. }
  170. static size_t LeftChildNode(size_t treeIndex) {
  171. MOZ_ASSERT(treeIndex);
  172. return treeIndex << 1;
  173. }
  174. static size_t RightChildNode(size_t treeIndex) {
  175. MOZ_ASSERT(treeIndex);
  176. return SiblingNode(LeftChildNode(treeIndex));
  177. }
  178. static size_t LeftNeighborNode(size_t treeIndex, size_t distance = 1) {
  179. MOZ_ASSERT(treeIndex > 1);
  180. return treeIndex - distance;
  181. }
  182. static size_t RightNeighborNode(size_t treeIndex, size_t distance = 1) {
  183. MOZ_ASSERT(treeIndex > 1);
  184. return treeIndex + distance;
  185. }
  186. size_t NumLeaves() const {
  187. // See class comment for why we the tree storage size is 2 * numLeaves.
  188. return mTreeData.Length() >> 1;
  189. }
  190. size_t LeafForElement(size_t element) const {
  191. size_t leaf = element / kElementsPerLeaf;
  192. MOZ_ASSERT(leaf < NumLeaves());
  193. return leaf;
  194. }
  195. size_t LeafForByte(size_t byte) const {
  196. return LeafForElement(byte / sizeof(T));
  197. }
  198. // Returns the index, into the tree storage, where a given leaf is stored.
  199. size_t TreeIndexForLeaf(size_t leaf) const {
  200. // See above class comment. The tree storage is an array of length
  201. // 2 * numLeaves. The leaves are stored in its second half.
  202. return leaf + NumLeaves();
  203. }
  204. static size_t LastElementUnderSameLeaf(size_t element) {
  205. return element | kElementsPerLeafMask;
  206. }
  207. static size_t FirstElementUnderSameLeaf(size_t element) {
  208. return element & ~kElementsPerLeafMask;
  209. }
  210. static size_t NextMultipleOfElementsPerLeaf(size_t numElements) {
  211. MOZ_ASSERT(numElements >= 1);
  212. return ((numElements - 1) | kElementsPerLeafMask) + 1;
  213. }
  214. bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf)
  215. {
  216. size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
  217. size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
  218. while (true) {
  219. // Given that we tweak these values in nontrivial ways, it doesn't
  220. // hurt to do this sanity check.
  221. MOZ_ASSERT(firstTreeIndex <= lastTreeIndex);
  222. // Final case where there is only one node to validate at the
  223. // current tree level:
  224. if (lastTreeIndex == firstTreeIndex) {
  225. const T& curData = mTreeData[firstTreeIndex];
  226. return curData <= maxAllowed;
  227. }
  228. // If the first node at current tree level is a right node, handle
  229. // it individually and replace it with its right neighbor, which is
  230. // a left node.
  231. if (IsRightNode(firstTreeIndex)) {
  232. const T& curData = mTreeData[firstTreeIndex];
  233. if (curData > maxAllowed)
  234. return false;
  235. firstTreeIndex = RightNeighborNode(firstTreeIndex);
  236. }
  237. // If the last node at current tree level is a left node, handle it
  238. // individually and replace it with its left neighbor, which is a
  239. // right node.
  240. if (IsLeftNode(lastTreeIndex)) {
  241. const T& curData = mTreeData[lastTreeIndex];
  242. if (curData > maxAllowed)
  243. return false;
  244. lastTreeIndex = LeftNeighborNode(lastTreeIndex);
  245. }
  246. /* At this point it can happen that firstTreeIndex and lastTreeIndex
  247. * "crossed" eachother. That happens if firstTreeIndex was a right
  248. * node and lastTreeIndex was its right neighor: In that case, both
  249. * above tweaks happened and as a result, they ended up being
  250. * swapped: LastTreeIndex is now the _left_ neighbor of
  251. * firstTreeIndex. When that happens, there is nothing left to
  252. * validate.
  253. */
  254. if (lastTreeIndex == LeftNeighborNode(firstTreeIndex))
  255. return true;
  256. // Walk up one level.
  257. firstTreeIndex = ParentNode(firstTreeIndex);
  258. lastTreeIndex = ParentNode(lastTreeIndex);
  259. }
  260. }
  261. // Updates the tree from the parent's buffer contents. Fallible, as it
  262. // may have to resize the tree storage.
  263. bool Update(size_t firstByte, size_t lastByte);
  264. size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  265. {
  266. return mallocSizeOf(this) +
  267. mTreeData.ShallowSizeOfExcludingThis(mallocSizeOf);
  268. }
  269. };
  270. // TreeForType: just a template helper to select the right tree object for a given
  271. // element type.
  272. template<typename T>
  273. struct TreeForType {};
  274. template<>
  275. struct TreeForType<uint8_t>
  276. {
  277. static UniquePtr<WebGLElementArrayCacheTree<uint8_t>>&
  278. Value(WebGLElementArrayCache* b) {
  279. return b->mUint8Tree;
  280. }
  281. };
  282. template<>
  283. struct TreeForType<uint16_t>
  284. {
  285. static UniquePtr<WebGLElementArrayCacheTree<uint16_t>>&
  286. Value(WebGLElementArrayCache* b) {
  287. return b->mUint16Tree;
  288. }
  289. };
  290. template<>
  291. struct TreeForType<uint32_t>
  292. {
  293. static UniquePtr<WebGLElementArrayCacheTree<uint32_t>>&
  294. Value(WebGLElementArrayCache* b) {
  295. return b->mUint32Tree;
  296. }
  297. };
  298. // Calling this method will 1) update the leaves in this interval
  299. // from the raw buffer data, and 2) propagate this update up the tree.
  300. template<typename T>
  301. bool
  302. WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
  303. {
  304. MOZ_ASSERT(firstByte <= lastByte);
  305. MOZ_ASSERT(lastByte < mParent.mBytes.Length());
  306. size_t numberOfElements = mParent.mBytes.Length() / sizeof(T);
  307. size_t requiredNumLeaves = 0;
  308. if (numberOfElements > 0) {
  309. /* If we didn't require the number of leaves to be a power of two, then
  310. * it would just be equal to
  311. *
  312. * ceil(numberOfElements / kElementsPerLeaf)
  313. *
  314. * The way we implement this (division+ceil) operation in integer
  315. * arithmetic
  316. * is as follows:
  317. */
  318. size_t numLeavesNonPOT = (numberOfElements + kElementsPerLeaf - 1) / kElementsPerLeaf;
  319. // It only remains to round that up to the next power of two:
  320. requiredNumLeaves = RoundUpPow2(numLeavesNonPOT);
  321. }
  322. // Step #0: If needed, resize our tree data storage.
  323. if (requiredNumLeaves != NumLeaves()) {
  324. // See class comment for why we the tree storage size is 2 * numLeaves.
  325. if (!mTreeData.SetLength(2 * requiredNumLeaves, fallible)) {
  326. mTreeData.Clear();
  327. return false;
  328. }
  329. MOZ_ASSERT(NumLeaves() == requiredNumLeaves);
  330. if (NumLeaves()) {
  331. // When resizing, update the whole tree, not just the subset
  332. // corresponding to the part of the buffer being updated.
  333. memset(mTreeData.Elements(), 0, mTreeData.Length() * sizeof(T));
  334. firstByte = 0;
  335. lastByte = mParent.mBytes.Length() - 1;
  336. }
  337. }
  338. if (NumLeaves() == 0)
  339. return true;
  340. lastByte = std::min(lastByte, NumLeaves() * kElementsPerLeaf * sizeof(T) - 1);
  341. if (firstByte > lastByte)
  342. return true;
  343. size_t firstLeaf = LeafForByte(firstByte);
  344. size_t lastLeaf = LeafForByte(lastByte);
  345. MOZ_ASSERT(firstLeaf <= lastLeaf && lastLeaf < NumLeaves());
  346. size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
  347. size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
  348. // Step #1: Initialize the tree leaves from plain buffer data.
  349. // That is, each tree leaf must be set to the max of the |kElementsPerLeaf|
  350. // corresponding buffer entries.
  351. // Condition-less scope to prevent leaking this scope's variables into the
  352. // code below:
  353. {
  354. // TreeIndex is the index of the tree leaf we're writing, i.e. the
  355. // destination index.
  356. size_t treeIndex = firstTreeIndex;
  357. // srcIndex is the index in the source buffer.
  358. size_t srcIndex = firstLeaf * kElementsPerLeaf;
  359. while (treeIndex <= lastTreeIndex) {
  360. T m = 0;
  361. size_t a = srcIndex;
  362. size_t srcIndexNextLeaf = std::min(a + kElementsPerLeaf, numberOfElements);
  363. for (; srcIndex < srcIndexNextLeaf; srcIndex++) {
  364. m = std::max(m, mParent.Element<T>(srcIndex));
  365. }
  366. mTreeData[treeIndex] = m;
  367. treeIndex++;
  368. }
  369. }
  370. // Step #2: Propagate the values up the tree. This is simply a matter of
  371. // walking up the tree and setting each node to the max of its two children.
  372. while (firstTreeIndex > 1) {
  373. // Move up one level.
  374. firstTreeIndex = ParentNode(firstTreeIndex);
  375. lastTreeIndex = ParentNode(lastTreeIndex);
  376. // Fast-exit case where only one node is updated at the current level.
  377. if (firstTreeIndex == lastTreeIndex) {
  378. mTreeData[firstTreeIndex] = std::max(mTreeData[LeftChildNode(firstTreeIndex)], mTreeData[RightChildNode(firstTreeIndex)]);
  379. continue;
  380. }
  381. size_t child = LeftChildNode(firstTreeIndex);
  382. size_t parent = firstTreeIndex;
  383. while (parent <= lastTreeIndex) {
  384. T a = mTreeData[child];
  385. child = RightNeighborNode(child);
  386. T b = mTreeData[child];
  387. child = RightNeighborNode(child);
  388. mTreeData[parent] = std::max(a, b);
  389. parent = RightNeighborNode(parent);
  390. }
  391. }
  392. return true;
  393. }
  394. WebGLElementArrayCache::WebGLElementArrayCache()
  395. {
  396. }
  397. WebGLElementArrayCache::~WebGLElementArrayCache()
  398. {
  399. }
  400. bool
  401. WebGLElementArrayCache::BufferData(const void* ptr, size_t byteLength)
  402. {
  403. if (mBytes.Length() != byteLength) {
  404. if (!mBytes.SetLength(byteLength, fallible)) {
  405. mBytes.Clear();
  406. return false;
  407. }
  408. }
  409. MOZ_ASSERT(mBytes.Length() == byteLength);
  410. return BufferSubData(0, ptr, byteLength);
  411. }
  412. bool
  413. WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr,
  414. size_t updateByteLength)
  415. {
  416. MOZ_ASSERT(pos + updateByteLength <= mBytes.Length());
  417. if (!updateByteLength)
  418. return true;
  419. // Note, using memcpy on shared racy data is not well-defined, this
  420. // will need to use safe-for-races operations when those become available.
  421. // See bug 1225033.
  422. if (ptr)
  423. memcpy(mBytes.Elements() + pos, ptr, updateByteLength);
  424. else
  425. memset(mBytes.Elements() + pos, 0, updateByteLength);
  426. return UpdateTrees(pos, pos + updateByteLength - 1);
  427. }
  428. bool
  429. WebGLElementArrayCache::UpdateTrees(size_t firstByte, size_t lastByte)
  430. {
  431. bool result = true;
  432. if (mUint8Tree)
  433. result &= mUint8Tree->Update(firstByte, lastByte);
  434. if (mUint16Tree)
  435. result &= mUint16Tree->Update(firstByte, lastByte);
  436. if (mUint32Tree)
  437. result &= mUint32Tree->Update(firstByte, lastByte);
  438. return result;
  439. }
  440. template<typename T>
  441. bool
  442. WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
  443. size_t countElements)
  444. {
  445. // If maxAllowed is >= the max T value, then there is no way that a T index
  446. // could be invalid.
  447. uint32_t maxTSize = std::numeric_limits<T>::max();
  448. if (maxAllowed >= maxTSize)
  449. return true;
  450. T maxAllowedT(maxAllowed);
  451. // Integer overflow must have been handled earlier, so we assert that
  452. // maxAllowedT is exactly the max allowed value.
  453. MOZ_ASSERT(uint32_t(maxAllowedT) == maxAllowed);
  454. if (!mBytes.Length() || !countElements)
  455. return true;
  456. UniquePtr<WebGLElementArrayCacheTree<T>>& tree = TreeForType<T>::Value(this);
  457. if (!tree) {
  458. tree = MakeUnique<WebGLElementArrayCacheTree<T>>(*this);
  459. if (mBytes.Length()) {
  460. bool valid = tree->Update(0, mBytes.Length() - 1);
  461. if (!valid) {
  462. // Do not assert here. This case would happen if an allocation
  463. // failed. We've already settled on fallible allocations around
  464. // here.
  465. tree = nullptr;
  466. return false;
  467. }
  468. }
  469. }
  470. size_t lastElement = firstElement + countElements - 1;
  471. // Fast-exit path when the global maximum for the whole element array buffer
  472. // falls in the allowed range:
  473. T globalMax = tree->GlobalMaximum();
  474. if (globalMax <= maxAllowedT)
  475. return true;
  476. const T* elements = Elements<T>();
  477. // Before calling tree->Validate, we have to validate ourselves the
  478. // boundaries of the elements span, to round them to the nearest multiple of
  479. // kElementsPerLeaf.
  480. size_t firstElementAdjustmentEnd = std::min(lastElement,
  481. tree->LastElementUnderSameLeaf(firstElement));
  482. while (firstElement <= firstElementAdjustmentEnd) {
  483. const T& curData = elements[firstElement];
  484. if (curData > maxAllowedT)
  485. return false;
  486. firstElement++;
  487. }
  488. size_t lastElementAdjustmentEnd = std::max(firstElement,
  489. tree->FirstElementUnderSameLeaf(lastElement));
  490. while (lastElement >= lastElementAdjustmentEnd) {
  491. const T& curData = elements[lastElement];
  492. if (curData > maxAllowedT)
  493. return false;
  494. lastElement--;
  495. }
  496. // at this point, for many tiny validations, we're already done.
  497. if (firstElement > lastElement)
  498. return true;
  499. // general case
  500. return tree->Validate(maxAllowedT, tree->LeafForElement(firstElement),
  501. tree->LeafForElement(lastElement));
  502. }
  503. bool
  504. WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed,
  505. size_t firstElement, size_t countElements)
  506. {
  507. if (type == LOCAL_GL_UNSIGNED_BYTE)
  508. return Validate<uint8_t>(maxAllowed, firstElement, countElements);
  509. if (type == LOCAL_GL_UNSIGNED_SHORT)
  510. return Validate<uint16_t>(maxAllowed, firstElement, countElements);
  511. if (type == LOCAL_GL_UNSIGNED_INT)
  512. return Validate<uint32_t>(maxAllowed, firstElement, countElements);
  513. MOZ_ASSERT(false, "Invalid type.");
  514. return false;
  515. }
  516. template<typename T>
  517. static size_t
  518. SizeOfNullable(mozilla::MallocSizeOf mallocSizeOf, const T& obj)
  519. {
  520. if (!obj)
  521. return 0;
  522. return obj->SizeOfIncludingThis(mallocSizeOf);
  523. }
  524. size_t
  525. WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
  526. {
  527. return mallocSizeOf(this) +
  528. mBytes.ShallowSizeOfExcludingThis(mallocSizeOf) +
  529. SizeOfNullable(mallocSizeOf, mUint8Tree) +
  530. SizeOfNullable(mallocSizeOf, mUint16Tree) +
  531. SizeOfNullable(mallocSizeOf, mUint32Tree);
  532. }
  533. bool
  534. WebGLElementArrayCache::BeenUsedWithMultipleTypes() const
  535. {
  536. // C++ Standard ($4.7)
  537. // "If the source type is bool, the value false is converted to zero and
  538. // the value true is converted to one."
  539. const int num_types_used = (mUint8Tree != nullptr) +
  540. (mUint16Tree != nullptr) +
  541. (mUint32Tree != nullptr);
  542. return num_types_used > 1;
  543. }
  544. } // end namespace mozilla