content/canvas/src/WebGLElementArrayCache.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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/. */
     6 #include "WebGLElementArrayCache.h"
     8 #include "nsTArray.h"
     9 #include "mozilla/Assertions.h"
    10 #include "mozilla/MemoryReporting.h"
    12 #include <cstdlib>
    13 #include <cstring>
    14 #include <limits>
    15 #include <algorithm>
    17 namespace mozilla {
    19 static void
    20 SetUpperBound(uint32_t* out_upperBound, uint32_t newBound)
    21 {
    22   if (!out_upperBound)
    23       return;
    25   *out_upperBound = newBound;
    26 }
    28 static void
    29 UpdateUpperBound(uint32_t* out_upperBound, uint32_t newBound)
    30 {
    31   if (!out_upperBound)
    32       return;
    34   *out_upperBound = std::max(*out_upperBound, newBound);
    35 }
    37 /*
    38  * WebGLElementArrayCacheTree contains most of the implementation of WebGLElementArrayCache,
    39  * which performs WebGL element array buffer validation for drawElements.
    40  *
    41  * Attention: Here lie nontrivial data structures, bug-prone algorithms, and non-canonical tweaks!
    42  * Whence the explanatory comments, and compiled unit test.
    43  *
    44  * *** What problem are we solving here? ***
    45  *
    46  * WebGL::DrawElements has to validate that the elements are in range wrt the current vertex attribs.
    47  * This boils down to the problem, given an array of integers, of computing the maximum in an arbitrary
    48  * sub-array. The naive algorithm has linear complexity; this has been a major performance problem,
    49  * see bug 569431. In that bug, we took the approach of caching the max for the whole array, which
    50  * does cover most cases (DrawElements typically consumes the whole element array buffer) but doesn't
    51  * help in other use cases:
    52  *  - when doing "partial DrawElements" i.e. consuming only part of the element array buffer
    53  *  - when doing frequent "partial buffer updates" i.e. bufferSubData calls updating parts of the
    54  *    element array buffer
    55  *
    56  * *** The solution: a binary tree ***
    57  *
    58  * The solution implemented here is to use a binary tree as the cache data structure. Each tree node
    59  * contains the max of its two children nodes. In this way, finding the maximum in any contiguous sub-array
    60  * has log complexity instead of linear complexity.
    61  *
    62  * Simplistically, if the element array is
    63  *
    64  *     1   4   3   2
    65  *
    66  * then the corresponding tree is
    67  *
    68  *           4
    69  *         _/ \_
    70  *       4       3
    71  *      / \     / \
    72  *     1   4   3   2
    73  *
    74  * In practice, the bottom-most levels of the tree are both the largest to store (because they
    75  * have more nodes), and the least useful performance-wise (because each node in the bottom
    76  * levels concerns only few entries in the elements array buffer, it is cheap to compute).
    77  *
    78  * For this reason, we stop the tree a few levels above, so that each tree leaf actually corresponds
    79  * to more than one element array entry.
    80  *
    81  * The number of levels that we "drop" is |sSkippedBottomTreeLevels| and the number of element array entries
    82  * that each leaf corresponds to, is |sElementsPerLeaf|. This being a binary tree, we have
    83  *
    84  *   sElementsPerLeaf = 2 ^ sSkippedBottomTreeLevels.
    85  *
    86  * *** Storage layout of the binary tree ***
    87  *
    88  * We take advantage of the specifics of the situation to avoid generalist tree storage and instead
    89  * store the tree entries in a vector, mTreeData.
    90  *
    91  * The number of leaves is given by mNumLeaves, and mTreeData is always a vector of length
    92  *
    93  *    2 * mNumLeaves.
    94  *
    95  * Its data layout is as follows: mTreeData[0] is unused, mTreeData[1] is the root node,
    96  * then at offsets 2..3 is the tree level immediately below the root node, then at offsets 4..7
    97  * is the tree level below that, etc.
    98  *
    99  * The figure below illustrates this by writing at each tree node the offset into mTreeData at
   100  * which it is stored:
   101  *
   102  *           1
   103  *         _/ \_
   104  *       2       3
   105  *      / \     / \
   106  *     4   5   6   7
   107  *    ...
   108  *
   109  * Thus, under the convention that the root level is level 0, we see that level N is stored at offsets
   110  *
   111  *    [ 2^n .. 2^(n+1) - 1 ]
   112  *
   113  * in mTreeData. Likewise, all the usual tree operations have simple mathematical expressions in
   114  * terms of mTreeData offsets, see all the methods such as ParentNode, LeftChildNode, etc.
   115  *
   116  * *** Design constraint: element types aren't known at buffer-update time ***
   117  *
   118  * Note that a key constraint that we're operating under, is that we don't know the types of the elements
   119  * by the time WebGL bufferData/bufferSubData methods are called. The type of elements is only
   120  * specified in the drawElements call. This means that we may potentially have to store caches for
   121  * multiple element types, for the same element array buffer. Since we don't know yet how many
   122  * element types we'll eventually support (extensions add more), the concern about memory usage is serious.
   123  * This is addressed by sSkippedBottomTreeLevels as explained above. Of course, in the typical
   124  * case where each element array buffer is only ever used with one type, this is also addressed
   125  * by having WebGLElementArrayCache lazily create trees for each type only upon first use.
   126  *
   127  * Another consequence of this constraint is that when invalidating the trees, we have to invalidate
   128  * all existing trees. So if trees for types uint8_t, uint16_t and uint32_t have ever been constructed for this buffer,
   129  * every subsequent invalidation will have to invalidate all trees even if one of the types is never
   130  * used again. This implies that it is important to minimize the cost of invalidation i.e.
   131  * do lazy updates upon use as opposed to immediately updating invalidated trees. This poses a problem:
   132  * it is nontrivial to keep track of the part of the tree that's invalidated. The current solution
   133  * can only keep track of an invalidated interval, from |mFirstInvalidatedLeaf| to |mLastInvalidatedLeaf|.
   134  * The problem is that if one does two small, far-apart partial buffer updates, the resulting invalidated
   135  * area is very large even though only a small part of the array really needed to be invalidated.
   136  * The real solution to this problem would be to use a smarter data structure to keep track of the
   137  * invalidated area, probably an interval tree. Meanwhile, we can probably live with the current situation
   138  * as the unfavorable case seems to be a small corner case: in order to run into performance issues,
   139  * the number of bufferSubData in between two consecutive draws must be small but greater than 1, and
   140  * the partial buffer updates must be small and far apart. Anything else than this corner case
   141  * should run fast in the current setting.
   142  */
   143 template<typename T>
   144 struct WebGLElementArrayCacheTree
   145 {
   146   // A too-high sSkippedBottomTreeLevels would harm the performance of small drawElements calls
   147   // A too-low sSkippedBottomTreeLevels would cause undue memory usage.
   148   // The current value has been validated by some benchmarking. See bug 732660.
   149   static const size_t sSkippedBottomTreeLevels = 3;
   150   static const size_t sElementsPerLeaf = 1 << sSkippedBottomTreeLevels;
   151   static const size_t sElementsPerLeafMask = sElementsPerLeaf - 1; // sElementsPerLeaf is POT
   153 private:
   154   WebGLElementArrayCache& mParent;
   155   FallibleTArray<T> mTreeData;
   156   size_t mNumLeaves;
   157   bool mInvalidated;
   158   size_t mFirstInvalidatedLeaf;
   159   size_t mLastInvalidatedLeaf;
   161 public:
   162   WebGLElementArrayCacheTree(WebGLElementArrayCache& p)
   163     : mParent(p)
   164     , mNumLeaves(0)
   165     , mInvalidated(false)
   166     , mFirstInvalidatedLeaf(0)
   167     , mLastInvalidatedLeaf(0)
   168   {
   169     ResizeToParentSize();
   170   }
   172   T GlobalMaximum() const {
   173     MOZ_ASSERT(!mInvalidated);
   174     return mTreeData[1];
   175   }
   177   // returns the index of the parent node; if treeIndex=1 (the root node),
   178   // the return value is 0.
   179   static size_t ParentNode(size_t treeIndex) {
   180     MOZ_ASSERT(treeIndex > 1);
   181     return treeIndex >> 1;
   182   }
   184   static bool IsRightNode(size_t treeIndex) {
   185     MOZ_ASSERT(treeIndex > 1);
   186     return treeIndex & 1;
   187   }
   189   static bool IsLeftNode(size_t treeIndex) {
   190     MOZ_ASSERT(treeIndex > 1);
   191     return !IsRightNode(treeIndex);
   192   }
   194   static size_t SiblingNode(size_t treeIndex) {
   195     MOZ_ASSERT(treeIndex > 1);
   196     return treeIndex ^ 1;
   197   }
   199   static size_t LeftChildNode(size_t treeIndex) {
   200     MOZ_ASSERT(treeIndex);
   201     return treeIndex << 1;
   202   }
   204   static size_t RightChildNode(size_t treeIndex) {
   205     MOZ_ASSERT(treeIndex);
   206     return SiblingNode(LeftChildNode(treeIndex));
   207   }
   209   static size_t LeftNeighborNode(size_t treeIndex, size_t distance = 1) {
   210     MOZ_ASSERT(treeIndex > 1);
   211     return treeIndex - distance;
   212   }
   214   static size_t RightNeighborNode(size_t treeIndex, size_t distance = 1) {
   215     MOZ_ASSERT(treeIndex > 1);
   216     return treeIndex + distance;
   217   }
   219   size_t LeafForElement(size_t element) {
   220     size_t leaf = element / sElementsPerLeaf;
   221     MOZ_ASSERT(leaf < mNumLeaves);
   222     return leaf;
   223   }
   225   size_t LeafForByte(size_t byte) {
   226     return LeafForElement(byte / sizeof(T));
   227   }
   229   // Returns the index, into the tree storage, where a given leaf is stored
   230   size_t TreeIndexForLeaf(size_t leaf) {
   231     // See above class comment. The tree storage is an array of length 2*mNumLeaves.
   232     // The leaves are stored in its second half.
   233     return leaf + mNumLeaves;
   234   }
   236   static size_t LastElementUnderSameLeaf(size_t element) {
   237     return element | sElementsPerLeafMask;
   238   }
   240   static size_t FirstElementUnderSameLeaf(size_t element) {
   241     return element & ~sElementsPerLeafMask;
   242   }
   244   static size_t NextMultipleOfElementsPerLeaf(size_t numElements) {
   245     return ((numElements - 1) | sElementsPerLeafMask) + 1;
   246   }
   248   bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf,
   249                 uint32_t* out_upperBound)
   250   {
   251     MOZ_ASSERT(!mInvalidated);
   253     size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
   254     size_t lastTreeIndex  = TreeIndexForLeaf(lastLeaf);
   256     while (true) {
   257       // given that we tweak these values in nontrivial ways, it doesn't hurt to do
   258       // this sanity check
   259       MOZ_ASSERT(firstTreeIndex <= lastTreeIndex);
   261       // final case where there is only 1 node to validate at the current tree level
   262       if (lastTreeIndex == firstTreeIndex) {
   263         const T& curData = mTreeData[firstTreeIndex];
   264         UpdateUpperBound(out_upperBound, curData);
   265         return curData <= maxAllowed;
   266       }
   268       // if the first node at current tree level is a right node, handle it individually
   269       // and replace it with its right neighbor, which is a left node
   270       if (IsRightNode(firstTreeIndex)) {
   271         const T& curData = mTreeData[firstTreeIndex];
   272         UpdateUpperBound(out_upperBound, curData);
   273         if (curData > maxAllowed)
   274           return false;
   275         firstTreeIndex = RightNeighborNode(firstTreeIndex);
   276       }
   278       // if the last node at current tree level is a left node, handle it individually
   279       // and replace it with its left neighbor, which is a right node
   280       if (IsLeftNode(lastTreeIndex)) {
   281         const T& curData = mTreeData[lastTreeIndex];
   282         UpdateUpperBound(out_upperBound, curData);
   283         if (curData > maxAllowed)
   284           return false;
   285         lastTreeIndex = LeftNeighborNode(lastTreeIndex);
   286       }
   288       // at this point it can happen that firstTreeIndex and lastTreeIndex "crossed" each
   289       // other. That happens if firstTreeIndex was a right node and lastTreeIndex was its
   290       // right neighor: in that case, both above tweaks happened and as a result, they ended
   291       // up being swapped: lastTreeIndex is now the _left_ neighbor of firstTreeIndex.
   292       // When that happens, there is nothing left to validate.
   293       if (lastTreeIndex == LeftNeighborNode(firstTreeIndex)) {
   294         return true;
   295       }
   297       // walk up 1 level
   298       firstTreeIndex = ParentNode(firstTreeIndex);
   299       lastTreeIndex = ParentNode(lastTreeIndex);
   300     }
   301   }
   303   template<typename U>
   304   static U NextPowerOfTwo(U x) {
   305     U result = 1;
   306     while (result < x)
   307       result <<= 1;
   308     MOZ_ASSERT(result >= x);
   309     MOZ_ASSERT((result & (result - 1)) == 0);
   310     return result;
   311   }
   313   bool ResizeToParentSize()
   314   {
   315     size_t numberOfElements = mParent.ByteSize() / sizeof(T);
   316     size_t requiredNumLeaves = (numberOfElements + sElementsPerLeaf - 1) / sElementsPerLeaf;
   318     size_t oldNumLeaves = mNumLeaves;
   319     mNumLeaves = NextPowerOfTwo(requiredNumLeaves);
   320     Invalidate(0, mParent.ByteSize() - 1);
   322     // see class comment for why we the tree storage size is 2 * mNumLeaves
   323     if (!mTreeData.SetLength(2 * mNumLeaves)) {
   324       return false;
   325     }
   326     if (mNumLeaves != oldNumLeaves) {
   327       memset(mTreeData.Elements(), 0, mTreeData.Length() * sizeof(mTreeData[0]));
   328     }
   329     return true;
   330   }
   332   void Invalidate(size_t firstByte, size_t lastByte);
   334   void Update();
   336   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   337   {
   338     return aMallocSizeOf(this) + mTreeData.SizeOfExcludingThis(aMallocSizeOf);
   339   }
   340 };
   342 // TreeForType: just a template helper to select the right tree object for a given
   343 // element type.
   344 template<typename T>
   345 struct TreeForType {};
   347 template<>
   348 struct TreeForType<uint8_t>
   349 {
   350   static WebGLElementArrayCacheTree<uint8_t>*& Run(WebGLElementArrayCache *b) { return b->mUint8Tree; }
   351 };
   353 template<>
   354 struct TreeForType<uint16_t>
   355 {
   356   static WebGLElementArrayCacheTree<uint16_t>*& Run(WebGLElementArrayCache *b) { return b->mUint16Tree; }
   357 };
   359 template<>
   360 struct TreeForType<uint32_t>
   361 {
   362   static WebGLElementArrayCacheTree<uint32_t>*& Run(WebGLElementArrayCache *b) { return b->mUint32Tree; }
   363 };
   365 // When the buffer gets updated from firstByte to lastByte,
   366 // calling this method will notify the tree accordingly
   367 template<typename T>
   368 void WebGLElementArrayCacheTree<T>::Invalidate(size_t firstByte, size_t lastByte)
   369 {
   370   lastByte = std::min(lastByte, mNumLeaves * sElementsPerLeaf * sizeof(T) - 1);
   371   if (firstByte > lastByte) {
   372     return;
   373   }
   375   size_t firstLeaf = LeafForByte(firstByte);
   376   size_t lastLeaf = LeafForByte(lastByte);
   378   if (mInvalidated) {
   379     mFirstInvalidatedLeaf = std::min(firstLeaf, mFirstInvalidatedLeaf);
   380     mLastInvalidatedLeaf = std::max(lastLeaf, mLastInvalidatedLeaf);
   381   } else {
   382     mInvalidated = true;
   383     mFirstInvalidatedLeaf = firstLeaf;
   384     mLastInvalidatedLeaf = lastLeaf;
   385   }
   386 }
   389 // When tree has been partially invalidated, from mFirstInvalidatedLeaf to
   390 // mLastInvalidatedLeaf, calling this method will 1) update the leaves in this interval
   391 // from the raw buffer data, and 2) propagate this update up the tree
   392 template<typename T>
   393 void WebGLElementArrayCacheTree<T>::Update()
   394 {
   395   if (!mInvalidated) {
   396     return;
   397   }
   399   MOZ_ASSERT(mLastInvalidatedLeaf < mNumLeaves);
   401   size_t firstTreeIndex = TreeIndexForLeaf(mFirstInvalidatedLeaf);
   402   size_t lastTreeIndex = TreeIndexForLeaf(mLastInvalidatedLeaf);
   404   // Step #1: initialize the tree leaves from plain buffer data.
   405   // That is, each tree leaf must be set to the max of the |sElementsPerLeaf| corresponding
   406   // buffer entries.
   407   // condition-less scope to prevent leaking this scope's variables into the code below
   408   {
   409     // treeIndex is the index of the tree leaf we're writing, i.e. the destination index
   410     size_t treeIndex = firstTreeIndex;
   411     // srcIndex is the index in the source buffer
   412     size_t srcIndex = mFirstInvalidatedLeaf * sElementsPerLeaf;
   413     size_t numberOfElements = mParent.ByteSize() / sizeof(T);
   414     while (treeIndex <= lastTreeIndex) {
   415       T m = 0;
   416       size_t a = srcIndex;
   417       size_t srcIndexNextLeaf = std::min(a + sElementsPerLeaf, numberOfElements);
   418       for (; srcIndex < srcIndexNextLeaf; srcIndex++) {
   419         m = std::max(m, mParent.Element<T>(srcIndex));
   420       }
   421       mTreeData[treeIndex] = m;
   422       treeIndex++;
   423     }
   424   }
   426   // Step #2: propagate the values up the tree. This is simply a matter of walking up
   427   // the tree and setting each node to the max of its two children.
   428   while (firstTreeIndex > 1) {
   430     // move up 1 level
   431     firstTreeIndex = ParentNode(firstTreeIndex);
   432     lastTreeIndex = ParentNode(lastTreeIndex);
   434     // fast-exit case where only one node is invalidated at the current level
   435     if (firstTreeIndex == lastTreeIndex) {
   436       mTreeData[firstTreeIndex] = std::max(mTreeData[LeftChildNode(firstTreeIndex)], mTreeData[RightChildNode(firstTreeIndex)]);
   437       continue;
   438     }
   440     // initialize local iteration variables: child and parent.
   441     size_t child = LeftChildNode(firstTreeIndex);
   442     size_t parent = firstTreeIndex;
   444     // the unrolling makes this look more complicated than it is; the plain non-unrolled
   445     // version is in the second while loop below
   446     const int unrollSize = 8;
   447     while (RightNeighborNode(parent, unrollSize - 1) <= lastTreeIndex)
   448     {
   449       for (int unroll = 0; unroll < unrollSize; unroll++)
   450       {
   451         T a = mTreeData[child];
   452         child = RightNeighborNode(child);
   453         T b = mTreeData[child];
   454         child = RightNeighborNode(child);
   455         mTreeData[parent] = std::max(a, b);
   456         parent = RightNeighborNode(parent);
   457       }
   458     }
   459     // plain non-unrolled version, used to terminate the job after the last unrolled iteration
   460     while (parent <= lastTreeIndex)
   461     {
   462       T a = mTreeData[child];
   463       child = RightNeighborNode(child);
   464       T b = mTreeData[child];
   465       child = RightNeighborNode(child);
   466       mTreeData[parent] = std::max(a, b);
   467       parent = RightNeighborNode(parent);
   468     }
   469   }
   471   mInvalidated = false;
   472 }
   474 WebGLElementArrayCache::~WebGLElementArrayCache() {
   475   delete mUint8Tree;
   476   delete mUint16Tree;
   477   delete mUint32Tree;
   478   free(mUntypedData);
   479 }
   481 bool WebGLElementArrayCache::BufferData(const void* ptr, size_t byteSize) {
   482   mByteSize = byteSize;
   483   if (mUint8Tree)
   484     if (!mUint8Tree->ResizeToParentSize())
   485       return false;
   486   if (mUint16Tree)
   487     if (!mUint16Tree->ResizeToParentSize())
   488       return false;
   489   if (mUint32Tree)
   490     if (!mUint32Tree->ResizeToParentSize())
   491       return false;
   492   mUntypedData = realloc(mUntypedData, byteSize);
   493   if (!mUntypedData)
   494     return false;
   495   BufferSubData(0, ptr, byteSize);
   496   return true;
   497 }
   499 void WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteSize) {
   500   if (!updateByteSize) return;
   501   if (ptr)
   502       memcpy(static_cast<uint8_t*>(mUntypedData) + pos, ptr, updateByteSize);
   503   else
   504       memset(static_cast<uint8_t*>(mUntypedData) + pos, 0, updateByteSize);
   505   InvalidateTrees(pos, pos + updateByteSize - 1);
   506 }
   508 void WebGLElementArrayCache::InvalidateTrees(size_t firstByte, size_t lastByte)
   509 {
   510   if (mUint8Tree)
   511     mUint8Tree->Invalidate(firstByte, lastByte);
   512   if (mUint16Tree)
   513     mUint16Tree->Invalidate(firstByte, lastByte);
   514   if (mUint32Tree)
   515     mUint32Tree->Invalidate(firstByte, lastByte);
   516 }
   518 template<typename T>
   519 bool
   520 WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
   521                                  size_t countElements, uint32_t* out_upperBound)
   522 {
   523   SetUpperBound(out_upperBound, 0);
   525   // if maxAllowed is >= the max T value, then there is no way that a T index could be invalid
   526   uint32_t maxTSize = std::numeric_limits<T>::max();
   527   if (maxAllowed >= maxTSize) {
   528     SetUpperBound(out_upperBound, maxTSize);
   529     return true;
   530   }
   532   T maxAllowedT(maxAllowed);
   534   // integer overflow must have been handled earlier, so we assert that maxAllowedT
   535   // is exactly the max allowed value.
   536   MOZ_ASSERT(uint32_t(maxAllowedT) == maxAllowed);
   538   if (!mByteSize || !countElements)
   539     return true;
   541   WebGLElementArrayCacheTree<T>*& tree = TreeForType<T>::Run(this);
   542   if (!tree) {
   543     tree = new WebGLElementArrayCacheTree<T>(*this);
   544   }
   546   size_t lastElement = firstElement + countElements - 1;
   548   tree->Update();
   550   // fast exit path when the global maximum for the whole element array buffer
   551   // falls in the allowed range
   552   T globalMax = tree->GlobalMaximum();
   553   if (globalMax <= maxAllowedT)
   554   {
   555     SetUpperBound(out_upperBound, globalMax);
   556     return true;
   557   }
   559   const T* elements = Elements<T>();
   561   // before calling tree->Validate, we have to validate ourselves the boundaries of the elements span,
   562   // to round them to the nearest multiple of sElementsPerLeaf.
   563   size_t firstElementAdjustmentEnd = std::min(lastElement,
   564                                             tree->LastElementUnderSameLeaf(firstElement));
   565   while (firstElement <= firstElementAdjustmentEnd) {
   566     const T& curData = elements[firstElement];
   567     UpdateUpperBound(out_upperBound, curData);
   568     if (curData > maxAllowedT)
   569       return false;
   570     firstElement++;
   571   }
   572   size_t lastElementAdjustmentEnd = std::max(firstElement,
   573                                            tree->FirstElementUnderSameLeaf(lastElement));
   574   while (lastElement >= lastElementAdjustmentEnd) {
   575     const T& curData = elements[lastElement];
   576     UpdateUpperBound(out_upperBound, curData);
   577     if (curData > maxAllowedT)
   578       return false;
   579     lastElement--;
   580   }
   582   // at this point, for many tiny validations, we're already done.
   583   if (firstElement > lastElement)
   584     return true;
   586   // general case
   587   return tree->Validate(maxAllowedT,
   588                         tree->LeafForElement(firstElement),
   589                         tree->LeafForElement(lastElement),
   590                         out_upperBound);
   591 }
   593 bool
   594 WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed,
   595                                  size_t firstElement, size_t countElements,
   596                                  uint32_t* out_upperBound)
   597 {
   598   if (type == LOCAL_GL_UNSIGNED_BYTE)
   599     return Validate<uint8_t>(maxAllowed, firstElement, countElements, out_upperBound);
   600   if (type == LOCAL_GL_UNSIGNED_SHORT)
   601     return Validate<uint16_t>(maxAllowed, firstElement, countElements, out_upperBound);
   602   if (type == LOCAL_GL_UNSIGNED_INT)
   603     return Validate<uint32_t>(maxAllowed, firstElement, countElements, out_upperBound);
   605   MOZ_ASSERT(false, "Invalid type.");
   606   return false;
   607 }
   609 size_t
   610 WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   611 {
   612   size_t uint8TreeSize  = mUint8Tree  ? mUint8Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
   613   size_t uint16TreeSize = mUint16Tree ? mUint16Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
   614   size_t uint32TreeSize = mUint32Tree ? mUint32Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
   615   return aMallocSizeOf(this) +
   616           mByteSize +
   617           uint8TreeSize +
   618           uint16TreeSize +
   619           uint32TreeSize;
   620 }
   622 } // end namespace mozilla

mercurial