diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp index f202c9950d1c..84589207923e 100644 --- a/dom/canvas/WebGLBuffer.cpp +++ b/dom/canvas/WebGLBuffer.cpp @@ -8,7 +8,6 @@ #include "GLContext.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" -#include "WebGLElementArrayCache.h" namespace mozilla { @@ -38,9 +37,6 @@ WebGLBuffer::SetContentAfterBind(GLenum target) switch (target) { case LOCAL_GL_ELEMENT_ARRAY_BUFFER: mContent = Kind::ElementArray; - if (!mCache) { - mCache.reset(new WebGLElementArrayCache); - } break; case LOCAL_GL_ARRAY_BUFFER: @@ -64,7 +60,8 @@ WebGLBuffer::Delete() mContext->MakeContextCurrent(); mContext->gl->fDeleteBuffers(1, &mGLName); mByteLength = 0; - mCache = nullptr; + mIndexCache = nullptr; + mIndexRanges.clear(); LinkedListElement::remove(); // remove from mContext->mBuffers } @@ -110,14 +107,9 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa if (!ValidateBufferUsageEnum(mContext, funcName, usage)) return; - const auto& gl = mContext->gl; - gl->MakeCurrent(); - const ScopedLazyBind lazyBind(gl, target, this); - mContext->InvalidateBufferFetching(); - #ifdef XP_MACOSX // bug 790879 - if (gl->WorkAroundDriverBugs() && + if (mContext->gl->WorkAroundDriverBugs() && size > INT32_MAX) { mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName); @@ -125,10 +117,31 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa } #endif + const void* uploadData = data; + + UniqueBuffer newIndexCache; + if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER && + mContext->mNeedsIndexValidation) + { + newIndexCache = malloc(size); + if (!newIndexCache) { + mContext->ErrorOutOfMemory("%s: Failed to alloc index cache.", funcName); + return; + } + memcpy(newIndexCache.get(), data, size); + uploadData = newIndexCache.get(); + } + + const auto& gl = mContext->gl; + gl->MakeCurrent(); + const ScopedLazyBind lazyBind(gl, target, this); + const bool sizeChanges = (size != ByteLength()); if (sizeChanges) { + mContext->InvalidateBufferFetching(); + gl::GLContext::LocalErrorScope errorScope(*gl); - gl->fBufferData(target, size, data, usage); + gl->fBufferData(target, size, uploadData, usage); const auto error = errorScope.GetError(); if (error) { @@ -137,19 +150,54 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa return; } } else { - gl->fBufferData(target, size, data, usage); + gl->fBufferData(target, size, uploadData, usage); } mUsage = usage; mByteLength = size; + mIndexCache = Move(newIndexCache); - // Warning: Possibly shared memory. See bug 1225033. - if (!ElementArrayCacheBufferData(data, size)) { - mByteLength = 0; - mContext->ErrorOutOfMemory("%s: Failed update index buffer cache.", funcName); + if (mIndexCache) { + if (mIndexRanges.size()) { + mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this, + uint32_t(mIndexRanges.size())); + mIndexRanges.clear(); + } } } +void +WebGLBuffer::BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen, + const void* data) const +{ + const char funcName[] = "bufferSubData"; + + if (!ValidateRange(funcName, dstByteOffset, dataLen)) + return; + + if (!CheckedInt(dataLen).isValid()) + return mContext->ErrorOutOfMemory("%s: Size too large.", funcName); + + //// + + const void* uploadData = data; + if (mIndexCache) { + const auto cachedDataBegin = (uint8_t*)mIndexCache.get() + dstByteOffset; + memcpy(cachedDataBegin, data, dataLen); + uploadData = cachedDataBegin; + + InvalidateCacheRange(dstByteOffset, dataLen); + } + + //// + + const auto& gl = mContext->gl; + gl->MakeCurrent(); + const ScopedLazyBind lazyBind(gl, target, this); + + gl->fBufferSubData(target, dstByteOffset, dataLen, uploadData); +} + bool WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const { @@ -171,54 +219,131 @@ WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteL //////////////////////////////////////// -bool -WebGLBuffer::ElementArrayCacheBufferData(const void* ptr, - size_t bufferSizeInBytes) +static uint8_t +IndexByteSizeByType(GLenum type) { - if (mContext->IsWebGL2()) - return true; - - if (mContent == Kind::ElementArray) - return mCache->BufferData(ptr, bufferSizeInBytes); - - return true; + switch (type) { + case LOCAL_GL_UNSIGNED_BYTE: return 1; + case LOCAL_GL_UNSIGNED_SHORT: return 2; + case LOCAL_GL_UNSIGNED_INT: return 4; + default: + MOZ_CRASH(); + } } void -WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr, - size_t updateSizeInBytes) +WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const { - if (mContext->IsWebGL2()) - return; + MOZ_ASSERT(mIndexCache); - if (mContent == Kind::ElementArray) - mCache->BufferSubData(pos, ptr, updateSizeInBytes); + std::vector invalids; + const size_t updateBegin = byteOffset; + const size_t updateEnd = updateBegin + byteLength; + for (const auto& cur : mIndexRanges) { + const auto& range = cur.first; + const auto& indexByteSize = IndexByteSizeByType(range.type); + const size_t rangeBegin = range.first * indexByteSize; + const size_t rangeEnd = rangeBegin + range.count*indexByteSize; + if (rangeBegin >= updateEnd || rangeEnd <= updateBegin) + continue; + invalids.push_back(range); + } + + if (invalids.size()) { + mContext->GeneratePerfWarning("[%p] Invalidating %u/%u ranges.", this, + uint32_t(invalids.size()), + uint32_t(mIndexRanges.size())); + + for (const auto& cur : invalids) { + mIndexRanges.erase(cur); + } + } } size_t WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { - size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(mallocSizeOf) - : 0; - return mallocSizeOf(this) + sizeOfCache; + size_t size = mallocSizeOf(this); + if (mIndexCache) { + size += mByteLength; + } + return size; } -bool -WebGLBuffer::Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count) const +template +static size_t +MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal) { - if (mContext->IsWebGL2()) + const T ignoredTVal(ignoredVal); + T ret = 0; + + auto itr = (const T*)data + first; + const auto end = itr + count; + + for (; itr != end; ++itr) { + const auto& val = *itr; + if (val <= ret) + continue; + + if (val == ignoredTVal) + continue; + + ret = val; + } + + return size_t(ret); +} + +const uint32_t kMaxIndexRanges = 256; + +bool +WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t first, + size_t count) const +{ + if (!mIndexCache) return true; - return mCache->Validate(type, maxAllowed, first, count); -} + if (!count) + return true; -bool -WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const -{ - if (mContext->IsWebGL2()) - return false; + const IndexRange range = { type, first, count }; + auto res = mIndexRanges.insert({ range, size_t(0) }); + if (mIndexRanges.size() > kMaxIndexRanges) { + mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.", + this, kMaxIndexRanges); + mIndexRanges.clear(); + res = mIndexRanges.insert({ range, size_t(0) }); + } - return mCache->BeenUsedWithMultipleTypes(); + const auto& itr = res.first; + const auto& didInsert = res.second; + + auto& maxFetchIndex = itr->second; + if (didInsert) { + const auto& data = mIndexCache.get(); + const uint32_t ignoreVal = (mContext->IsWebGL2() ? UINT32_MAX : 0); + + switch (type) { + case LOCAL_GL_UNSIGNED_BYTE: + maxFetchIndex = MaxForRange(data, first, count, ignoreVal); + break; + case LOCAL_GL_UNSIGNED_SHORT: + maxFetchIndex = MaxForRange(data, first, count, ignoreVal); + break; + case LOCAL_GL_UNSIGNED_INT: + maxFetchIndex = MaxForRange(data, first, count, ignoreVal); + break; + default: + MOZ_CRASH(); + } + + mContext->GeneratePerfWarning("[%p] New range #%u: (0x%04x, %u, %u): %u", this, + uint32_t(mIndexRanges.size()), type, + uint32_t(first), uint32_t(count), + uint32_t(maxFetchIndex)); + } + + return maxFetchIndex < numFetchable; } //// diff --git a/dom/canvas/WebGLBuffer.h b/dom/canvas/WebGLBuffer.h index 883712aad451..025ad78dc645 100644 --- a/dom/canvas/WebGLBuffer.h +++ b/dom/canvas/WebGLBuffer.h @@ -6,18 +6,16 @@ #ifndef WEBGL_BUFFER_H_ #define WEBGL_BUFFER_H_ +#include + #include "GLDefs.h" #include "mozilla/LinkedList.h" -#include "mozilla/UniquePtr.h" #include "nsWrapperCache.h" - #include "WebGLObjectModel.h" #include "WebGLTypes.h" namespace mozilla { -class WebGLElementArrayCache; - class WebGLBuffer final : public nsWrapperCache , public WebGLRefCountedObject @@ -46,16 +44,9 @@ public: GLenum Usage() const { return mUsage; } size_t ByteLength() const { return mByteLength; } - bool ElementArrayCacheBufferData(const void* ptr, size_t bufferSizeInBytes); - - void ElementArrayCacheBufferSubData(size_t pos, const void* ptr, - size_t updateSizeInBytes); - - bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count) const; + bool ValidateIndexedFetch(GLenum type, uint32_t max_allowed, size_t first, size_t count) const; bool ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const; - bool IsElementArrayUsedWithMultipleTypes() const; - WebGLContext* GetParentObject() const { return mContext; } @@ -64,6 +55,8 @@ public: bool ValidateCanBindToTarget(const char* funcName, GLenum target); void BufferData(GLenum target, size_t size, const void* data, GLenum usage); + void BufferSubData(GLenum target, size_t dstByteOffset, size_t dataLen, + const void* data) const; //// @@ -102,12 +95,32 @@ public: protected: ~WebGLBuffer(); + void InvalidateCacheRange(size_t offset, size_t length) const; + Kind mContent; GLenum mUsage; size_t mByteLength; - UniquePtr mCache; size_t mTFBindCount; size_t mNonTFBindCount; + + struct IndexRange final { + GLenum type; + size_t first; + size_t count; + + bool operator<(const IndexRange& x) const { + if (type != x.type) + return type < x.type; + + if (first != x.first) + return first < x.first; + + return count < x.count; + } + }; + + UniqueBuffer mIndexCache; + mutable std::map mIndexRanges; }; } // namespace mozilla diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 8147a32ff821..ce0e5e544618 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -2114,50 +2114,6 @@ ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDi GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget, WebGLTexture** const out_tex); -class UniqueBuffer -{ - // Like UniquePtr<>, but for void* and malloc/calloc/free. - void* mBuffer; - -public: - UniqueBuffer() - : mBuffer(nullptr) - { } - - MOZ_IMPLICIT UniqueBuffer(void* buffer) - : mBuffer(buffer) - { } - - ~UniqueBuffer() { - free(mBuffer); - } - - UniqueBuffer(UniqueBuffer&& other) { - this->mBuffer = other.mBuffer; - other.mBuffer = nullptr; - } - - UniqueBuffer& operator =(UniqueBuffer&& other) { - free(this->mBuffer); - this->mBuffer = other.mBuffer; - other.mBuffer = nullptr; - return *this; - } - - UniqueBuffer& operator =(void* newBuffer) { - free(this->mBuffer); - this->mBuffer = newBuffer; - return *this; - } - - explicit operator bool() const { return bool(mBuffer); } - - void* get() const { return mBuffer; } - - UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()! - void operator =(const UniqueBuffer& other) = delete; // assign using Move()! -}; - class ScopedUnpackReset final : public gl::ScopedGLWrapper { diff --git a/dom/canvas/WebGLContextBuffers.cpp b/dom/canvas/WebGLContextBuffers.cpp index 68cceb009742..776b6c883de2 100644 --- a/dom/canvas/WebGLContextBuffers.cpp +++ b/dom/canvas/WebGLContextBuffers.cpp @@ -402,27 +402,11 @@ WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset, if (!buffer) return; - if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen)) - return; - - if (!CheckedInt(dataLen).isValid()) { - ErrorOutOfMemory("%s: Size too large.", funcName); - return; - } - const GLintptr glDataLen(dataLen); - - //// - - MakeContextCurrent(); - const ScopedLazyBind lazyBind(gl, target, buffer); - - // Warning: Possibly shared memory. See bug 1225033. - gl->fBufferSubData(target, dstByteOffset, glDataLen, data); - - // Warning: Possibly shared memory. See bug 1225033. - buffer->ElementArrayCacheBufferSubData(dstByteOffset, data, size_t(glDataLen)); + buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data); } +//// + void WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset, const dom::ArrayBuffer& src) diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index 867e47cbdc69..92ca2ea89aa6 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -738,7 +738,7 @@ WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vert return false; if (!mMaxFetchedVertices || - !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, vertCount)) + !elemArrayBuffer.ValidateIndexedFetch(type, mMaxFetchedVertices, first, vertCount)) { ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient " "size for given indices from the bound element array", @@ -746,15 +746,6 @@ WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vert return false; } - // Bug 1008310 - Check if buffer has been used with a different previous type - if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) { - nsCString typeName; - WebGLContext::EnumName(type, &typeName); - GenerateWarning("%s: bound element array buffer previously used with a type other than " - "%s, this will affect performance.", - funcName, typeName.BeginReading()); - } - return true; } @@ -994,8 +985,8 @@ WebGLContext::ValidateBufferFetching(const char* info) maxInstances = 0; break; } - availBytes -= vd.BytesPerVertex(); - const size_t vertCapacity = 1 + availBytes / vd.ExplicitStride(); + availBytes -= vd.BytesPerVertex(); // Snip off the tail. + const size_t vertCapacity = availBytes / vd.ExplicitStride() + 1; // Add +1 for the snipped tail. if (vd.mDivisor == 0) { if (vertCapacity < maxVertices) { diff --git a/dom/canvas/WebGLElementArrayCache.cpp b/dom/canvas/WebGLElementArrayCache.cpp deleted file mode 100644 index a0591445a06e..000000000000 --- a/dom/canvas/WebGLElementArrayCache.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "WebGLElementArrayCache.h" - -#include -#include -#include -#include -#include "mozilla/Assertions.h" -#include "mozilla/MathAlgorithms.h" -#include "mozilla/MemoryReporting.h" - -namespace mozilla { - -/* WebGLElementArrayCacheTree contains most of the implementation of - * WebGLElementArrayCache, which performs WebGL element array buffer validation - * for drawElements. - * - * Attention: Here lie nontrivial data structures, bug-prone algorithms, and - * non-canonical tweaks! Whence the explanatory comments, and compiled unit - * test. - * - * *** What problem are we solving here? *** - * - * WebGL::DrawElements has to validate that the elements are in range wrt the - * current vertex attribs. This boils down to the problem, given an array of - * integers, of computing the maximum in an arbitrary sub-array. The naive - * algorithm has linear complexity; this has been a major performance problem, - * see bug 569431. In that bug, we took the approach of caching the max for the - * whole array, which does cover most cases (DrawElements typically consumes the - * whole element array buffer) but doesn't help in other use cases: - * - when doing "partial DrawElements" i.e. consuming only part of the element - * array buffer - * - when doing frequent "partial buffer updates" i.e. bufferSubData calls - * updating parts of the element array buffer - * - * *** The solution: A binary tree *** - * - * The solution implemented here is to use a binary tree as the cache data - * structure. Each tree node contains the max of its two children nodes. In this - * way, finding the maximum in any contiguous sub-array has log complexity - * instead of linear complexity. - * - * Simplistically, if the element array is: - * - * [1 4 3 2] - * - * then the corresponding tree is: - * - * 4 - * _/ \_ - * 4 3 - * / \ / \ - * 1 4 3 2 - * - * In practice, the bottom-most levels of the tree are both the largest to store - * (because they have more nodes), and the least useful performance-wise - * (because each node in the bottom levels concerns only few entries in the - * elements array buffer, it is cheap to compute). - * - * For this reason, we stop the tree a few levels above, so that each tree leaf - * actually corresponds to more than one element array entry. - * - * The number of levels that we "drop" is |kSkippedBottomTreeLevels| and the - * number of element array entries that each leaf corresponds to, is - * |kElementsPerLeaf|. This being a binary tree, we have: - * - * kElementsPerLeaf = 2 ^ kSkippedBottomTreeLevels. - * - * *** Storage layout of the binary tree *** - * - * We take advantage of the specifics of the situation to avoid generalist tree - * storage and instead store the tree entries in a vector, mTreeData. - * - * TreeData is always a vector of length: - * - * 2 * (number of leaves). - * - * Its data layout is as follows: mTreeData[0] is unused, mTreeData[1] is the - * root node, then at offsets 2..3 is the tree level immediately below the root - * node, then at offsets 4..7 is the tree level below that, etc. - * - * The figure below illustrates this by writing at each tree node the offset - * into mTreeData at which it is stored: - * - * 1 - * _/ \_ - * 2 3 - * / \ / \ - * 4 5 6 7 - * ... - * - * Thus, under the convention that the root level is level 0, we see that level - * N is stored at offsets: - * - * [ 2^n .. 2^(n+1) - 1 ] - * - * in mTreeData. Likewise, all the usual tree operations have simple - * mathematical expressions in terms of mTreeData offsets, see all the methods - * such as ParentNode, LeftChildNode, etc. - * - * *** Design constraint: Element types aren't known at buffer-update time *** - * - * Note that a key constraint that we're operating under, is that we don't know - * the types of the elements by the time WebGL bufferData/bufferSubData methods - * are called. The type of elements is only specified in the drawElements call. - * This means that we may potentially have to store caches for multiple element - * types, for the same element array buffer. Since we don't know yet how many - * element types we'll eventually support (extensions add more), the concern - * about memory usage is serious. This is addressed by kSkippedBottomTreeLevels - * as explained above. Of course, in the typical case where each element array - * buffer is only ever used with one type, this is also addressed by having - * WebGLElementArrayCache lazily create trees for each type only upon first use. - * - * Another consequence of this constraint is that when updating the trees, we - * have to update all existing trees. So if trees for types uint8_t, uint16_t - * and uint32_t have ever been constructed for this buffer, every subsequent - * update will have to update all trees even if one of the types is never used - * again. That's inefficient, but content should not put indices of different - * types in the same element array buffer anyways. Different index types can - * only be consumed in separate drawElements calls, so nothing particular is - * to be achieved by lumping them in the same buffer object. - */ -template -struct WebGLElementArrayCacheTree -{ - /* A too-high kSkippedBottomTreeLevels would harm the performance of small - * drawElements calls. A too-low kSkippedBottomTreeLevels would cause undue - * memory usage. The current value has been validated by some benchmarking. - * See bug 732660. - */ - static const size_t kSkippedBottomTreeLevels = 3; - static const size_t kElementsPerLeaf = 1 << kSkippedBottomTreeLevels; - // Since kElementsPerLeaf is POT: - static const size_t kElementsPerLeafMask = kElementsPerLeaf - 1; - -private: - // The WebGLElementArrayCache that owns this tree: - WebGLElementArrayCache& mParent; - - // The tree's internal data storage. Its length is 2 * (number of leaves) - // because of its data layout explained in the above class comment. - FallibleTArray mTreeData; - -public: - // Constructor. Takes a reference to the WebGLElementArrayCache that is to be - // the parent. Does not initialize the tree. Should be followed by a call - // to Update() to attempt initializing the tree. - explicit WebGLElementArrayCacheTree(WebGLElementArrayCache& value) - : mParent(value) - { - } - - T GlobalMaximum() const { - return mTreeData[1]; - } - - // returns the index of the parent node; if treeIndex=1 (the root node), - // the return value is 0. - static size_t ParentNode(size_t treeIndex) { - MOZ_ASSERT(treeIndex > 1); - return treeIndex >> 1; - } - - static bool IsRightNode(size_t treeIndex) { - MOZ_ASSERT(treeIndex > 1); - return treeIndex & 1; - } - - static bool IsLeftNode(size_t treeIndex) { - MOZ_ASSERT(treeIndex > 1); - return !IsRightNode(treeIndex); - } - - static size_t SiblingNode(size_t treeIndex) { - MOZ_ASSERT(treeIndex > 1); - return treeIndex ^ 1; - } - - static size_t LeftChildNode(size_t treeIndex) { - MOZ_ASSERT(treeIndex); - return treeIndex << 1; - } - - static size_t RightChildNode(size_t treeIndex) { - MOZ_ASSERT(treeIndex); - return SiblingNode(LeftChildNode(treeIndex)); - } - - static size_t LeftNeighborNode(size_t treeIndex, size_t distance = 1) { - MOZ_ASSERT(treeIndex > 1); - return treeIndex - distance; - } - - static size_t RightNeighborNode(size_t treeIndex, size_t distance = 1) { - MOZ_ASSERT(treeIndex > 1); - return treeIndex + distance; - } - - size_t NumLeaves() const { - // See class comment for why we the tree storage size is 2 * numLeaves. - return mTreeData.Length() >> 1; - } - - size_t LeafForElement(size_t element) const { - size_t leaf = element / kElementsPerLeaf; - MOZ_ASSERT(leaf < NumLeaves()); - return leaf; - } - - size_t LeafForByte(size_t byte) const { - return LeafForElement(byte / sizeof(T)); - } - - // Returns the index, into the tree storage, where a given leaf is stored. - size_t TreeIndexForLeaf(size_t leaf) const { - // See above class comment. The tree storage is an array of length - // 2 * numLeaves. The leaves are stored in its second half. - return leaf + NumLeaves(); - } - - static size_t LastElementUnderSameLeaf(size_t element) { - return element | kElementsPerLeafMask; - } - - static size_t FirstElementUnderSameLeaf(size_t element) { - return element & ~kElementsPerLeafMask; - } - - static size_t NextMultipleOfElementsPerLeaf(size_t numElements) { - MOZ_ASSERT(numElements >= 1); - return ((numElements - 1) | kElementsPerLeafMask) + 1; - } - - bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf) - { - size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf); - size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf); - - while (true) { - // Given that we tweak these values in nontrivial ways, it doesn't - // hurt to do this sanity check. - MOZ_ASSERT(firstTreeIndex <= lastTreeIndex); - - // Final case where there is only one node to validate at the - // current tree level: - if (lastTreeIndex == firstTreeIndex) { - const T& curData = mTreeData[firstTreeIndex]; - return curData <= maxAllowed; - } - - // If the first node at current tree level is a right node, handle - // it individually and replace it with its right neighbor, which is - // a left node. - if (IsRightNode(firstTreeIndex)) { - const T& curData = mTreeData[firstTreeIndex]; - if (curData > maxAllowed) - return false; - - firstTreeIndex = RightNeighborNode(firstTreeIndex); - } - - // If the last node at current tree level is a left node, handle it - // individually and replace it with its left neighbor, which is a - // right node. - if (IsLeftNode(lastTreeIndex)) { - const T& curData = mTreeData[lastTreeIndex]; - if (curData > maxAllowed) - return false; - - lastTreeIndex = LeftNeighborNode(lastTreeIndex); - } - - /* At this point it can happen that firstTreeIndex and lastTreeIndex - * "crossed" eachother. That happens if firstTreeIndex was a right - * node and lastTreeIndex was its right neighor: In that case, both - * above tweaks happened and as a result, they ended up being - * swapped: LastTreeIndex is now the _left_ neighbor of - * firstTreeIndex. When that happens, there is nothing left to - * validate. - */ - if (lastTreeIndex == LeftNeighborNode(firstTreeIndex)) - return true; - - // Walk up one level. - firstTreeIndex = ParentNode(firstTreeIndex); - lastTreeIndex = ParentNode(lastTreeIndex); - } - } - - // Updates the tree from the parent's buffer contents. Fallible, as it - // may have to resize the tree storage. - bool Update(size_t firstByte, size_t lastByte); - - size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const - { - return mallocSizeOf(this) + - mTreeData.ShallowSizeOfExcludingThis(mallocSizeOf); - } -}; - -// TreeForType: just a template helper to select the right tree object for a given -// element type. -template -struct TreeForType {}; - -template<> -struct TreeForType -{ - static UniquePtr>& - Value(WebGLElementArrayCache* b) { - return b->mUint8Tree; - } -}; - -template<> -struct TreeForType -{ - static UniquePtr>& - Value(WebGLElementArrayCache* b) { - return b->mUint16Tree; - } -}; - -template<> -struct TreeForType -{ - static UniquePtr>& - Value(WebGLElementArrayCache* b) { - return b->mUint32Tree; - } -}; - -// Calling this method will 1) update the leaves in this interval -// from the raw buffer data, and 2) propagate this update up the tree. -template -bool -WebGLElementArrayCacheTree::Update(size_t firstByte, size_t lastByte) -{ - MOZ_ASSERT(firstByte <= lastByte); - MOZ_ASSERT(lastByte < mParent.mBytes.Length()); - - size_t numberOfElements = mParent.mBytes.Length() / sizeof(T); - size_t requiredNumLeaves = 0; - if (numberOfElements > 0) { - /* If we didn't require the number of leaves to be a power of two, then - * it would just be equal to - * - * ceil(numberOfElements / kElementsPerLeaf) - * - * The way we implement this (division+ceil) operation in integer - * arithmetic - * is as follows: - */ - size_t numLeavesNonPOT = (numberOfElements + kElementsPerLeaf - 1) / kElementsPerLeaf; - // It only remains to round that up to the next power of two: - requiredNumLeaves = RoundUpPow2(numLeavesNonPOT); - } - - // Step #0: If needed, resize our tree data storage. - if (requiredNumLeaves != NumLeaves()) { - // See class comment for why we the tree storage size is 2 * numLeaves. - if (!mTreeData.SetLength(2 * requiredNumLeaves, fallible)) { - mTreeData.Clear(); - return false; - } - MOZ_ASSERT(NumLeaves() == requiredNumLeaves); - - if (NumLeaves()) { - // When resizing, update the whole tree, not just the subset - // corresponding to the part of the buffer being updated. - memset(mTreeData.Elements(), 0, mTreeData.Length() * sizeof(T)); - firstByte = 0; - lastByte = mParent.mBytes.Length() - 1; - } - } - - if (NumLeaves() == 0) - return true; - - lastByte = std::min(lastByte, NumLeaves() * kElementsPerLeaf * sizeof(T) - 1); - if (firstByte > lastByte) - return true; - - size_t firstLeaf = LeafForByte(firstByte); - size_t lastLeaf = LeafForByte(lastByte); - - MOZ_ASSERT(firstLeaf <= lastLeaf && lastLeaf < NumLeaves()); - - size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf); - size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf); - - // Step #1: Initialize the tree leaves from plain buffer data. - // That is, each tree leaf must be set to the max of the |kElementsPerLeaf| - // corresponding buffer entries. - - // Condition-less scope to prevent leaking this scope's variables into the - // code below: - { - // TreeIndex is the index of the tree leaf we're writing, i.e. the - // destination index. - size_t treeIndex = firstTreeIndex; - // srcIndex is the index in the source buffer. - size_t srcIndex = firstLeaf * kElementsPerLeaf; - while (treeIndex <= lastTreeIndex) { - T m = 0; - size_t a = srcIndex; - size_t srcIndexNextLeaf = std::min(a + kElementsPerLeaf, numberOfElements); - for (; srcIndex < srcIndexNextLeaf; srcIndex++) { - m = std::max(m, mParent.Element(srcIndex)); - } - mTreeData[treeIndex] = m; - treeIndex++; - } - } - - // Step #2: Propagate the values up the tree. This is simply a matter of - // walking up the tree and setting each node to the max of its two children. - while (firstTreeIndex > 1) { - // Move up one level. - firstTreeIndex = ParentNode(firstTreeIndex); - lastTreeIndex = ParentNode(lastTreeIndex); - - // Fast-exit case where only one node is updated at the current level. - if (firstTreeIndex == lastTreeIndex) { - mTreeData[firstTreeIndex] = std::max(mTreeData[LeftChildNode(firstTreeIndex)], mTreeData[RightChildNode(firstTreeIndex)]); - continue; - } - - size_t child = LeftChildNode(firstTreeIndex); - size_t parent = firstTreeIndex; - while (parent <= lastTreeIndex) { - T a = mTreeData[child]; - child = RightNeighborNode(child); - T b = mTreeData[child]; - child = RightNeighborNode(child); - mTreeData[parent] = std::max(a, b); - parent = RightNeighborNode(parent); - } - } - - return true; -} - -WebGLElementArrayCache::WebGLElementArrayCache() -{ -} - -WebGLElementArrayCache::~WebGLElementArrayCache() -{ -} - -bool -WebGLElementArrayCache::BufferData(const void* ptr, size_t byteLength) -{ - if (mBytes.Length() != byteLength) { - if (!mBytes.SetLength(byteLength, fallible)) { - mBytes.Clear(); - return false; - } - } - MOZ_ASSERT(mBytes.Length() == byteLength); - return BufferSubData(0, ptr, byteLength); -} - -bool -WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, - size_t updateByteLength) -{ - MOZ_ASSERT(pos + updateByteLength <= mBytes.Length()); - if (!updateByteLength) - return true; - - // Note, using memcpy on shared racy data is not well-defined, this - // will need to use safe-for-races operations when those become available. - // See bug 1225033. - if (ptr) - memcpy(mBytes.Elements() + pos, ptr, updateByteLength); - else - memset(mBytes.Elements() + pos, 0, updateByteLength); - return UpdateTrees(pos, pos + updateByteLength - 1); -} - -bool -WebGLElementArrayCache::UpdateTrees(size_t firstByte, size_t lastByte) -{ - bool result = true; - if (mUint8Tree) - result &= mUint8Tree->Update(firstByte, lastByte); - if (mUint16Tree) - result &= mUint16Tree->Update(firstByte, lastByte); - if (mUint32Tree) - result &= mUint32Tree->Update(firstByte, lastByte); - return result; -} - -template -bool -WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, - size_t countElements) -{ - // If maxAllowed is >= the max T value, then there is no way that a T index - // could be invalid. - uint32_t maxTSize = std::numeric_limits::max(); - if (maxAllowed >= maxTSize) - return true; - - T maxAllowedT(maxAllowed); - - // Integer overflow must have been handled earlier, so we assert that - // maxAllowedT is exactly the max allowed value. - MOZ_ASSERT(uint32_t(maxAllowedT) == maxAllowed); - - if (!mBytes.Length() || !countElements) - return true; - - UniquePtr>& tree = TreeForType::Value(this); - if (!tree) { - tree = MakeUnique>(*this); - if (mBytes.Length()) { - bool valid = tree->Update(0, mBytes.Length() - 1); - if (!valid) { - // Do not assert here. This case would happen if an allocation - // failed. We've already settled on fallible allocations around - // here. - tree = nullptr; - return false; - } - } - } - - size_t lastElement = firstElement + countElements - 1; - - // Fast-exit path when the global maximum for the whole element array buffer - // falls in the allowed range: - T globalMax = tree->GlobalMaximum(); - if (globalMax <= maxAllowedT) - return true; - - const T* elements = Elements(); - - // Before calling tree->Validate, we have to validate ourselves the - // boundaries of the elements span, to round them to the nearest multiple of - // kElementsPerLeaf. - size_t firstElementAdjustmentEnd = std::min(lastElement, - tree->LastElementUnderSameLeaf(firstElement)); - while (firstElement <= firstElementAdjustmentEnd) { - const T& curData = elements[firstElement]; - if (curData > maxAllowedT) - return false; - - firstElement++; - } - size_t lastElementAdjustmentEnd = std::max(firstElement, - tree->FirstElementUnderSameLeaf(lastElement)); - while (lastElement >= lastElementAdjustmentEnd) { - const T& curData = elements[lastElement]; - if (curData > maxAllowedT) - return false; - - lastElement--; - } - - // at this point, for many tiny validations, we're already done. - if (firstElement > lastElement) - return true; - - // general case - return tree->Validate(maxAllowedT, tree->LeafForElement(firstElement), - tree->LeafForElement(lastElement)); -} - -bool -WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed, - size_t firstElement, size_t countElements) -{ - if (type == LOCAL_GL_UNSIGNED_BYTE) - return Validate(maxAllowed, firstElement, countElements); - if (type == LOCAL_GL_UNSIGNED_SHORT) - return Validate(maxAllowed, firstElement, countElements); - if (type == LOCAL_GL_UNSIGNED_INT) - return Validate(maxAllowed, firstElement, countElements); - - MOZ_ASSERT(false, "Invalid type."); - return false; -} - -template -static size_t -SizeOfNullable(mozilla::MallocSizeOf mallocSizeOf, const T& obj) -{ - if (!obj) - return 0; - return obj->SizeOfIncludingThis(mallocSizeOf); -} - -size_t -WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const -{ - return mallocSizeOf(this) + - mBytes.ShallowSizeOfExcludingThis(mallocSizeOf) + - SizeOfNullable(mallocSizeOf, mUint8Tree) + - SizeOfNullable(mallocSizeOf, mUint16Tree) + - SizeOfNullable(mallocSizeOf, mUint32Tree); -} - -bool -WebGLElementArrayCache::BeenUsedWithMultipleTypes() const -{ - // C++ Standard ($4.7) - // "If the source type is bool, the value false is converted to zero and - // the value true is converted to one." - const int num_types_used = (mUint8Tree != nullptr) + - (mUint16Tree != nullptr) + - (mUint32Tree != nullptr); - return num_types_used > 1; -} - -} // end namespace mozilla diff --git a/dom/canvas/WebGLElementArrayCache.h b/dom/canvas/WebGLElementArrayCache.h deleted file mode 100644 index eba0535a237e..000000000000 --- a/dom/canvas/WebGLElementArrayCache.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef WEBGL_ELEMENT_ARRAY_CACHE_H -#define WEBGL_ELEMENT_ARRAY_CACHE_H - -#include "GLDefs.h" -#include "mozilla/MemoryReporting.h" -#include "mozilla/UniquePtr.h" -#include "nscore.h" -#include "nsTArray.h" -#include - -namespace mozilla { - -template -struct WebGLElementArrayCacheTree; - -/* WebGLElementArrayCache implements WebGL element array buffer validation for - * drawElements. - * - * Its exposes methods meant to be called by WebGL method implementations: - * - * - Validate, to be called by WebGLContext::DrawElements, is where we use the - * cache. - * - * - BufferData and BufferSubData, to be called by eponymous WebGL methods, are - * how data is fed into the cache. - * - * Most of the implementation is hidden in the auxilary class template, - * WebGLElementArrayCacheTree. Refer to its code for design comments. - */ -class WebGLElementArrayCache { -public: - bool BufferData(const void* ptr, size_t byteLength); - bool BufferSubData(size_t pos, const void* ptr, size_t updateByteSize); - - bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count); - - template - T Element(size_t i) const { return Elements()[i]; } - - WebGLElementArrayCache(); - ~WebGLElementArrayCache(); - - size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; - bool BeenUsedWithMultipleTypes() const; - -private: - /* Returns true if a drawElements call with the given parameters should - * succeed, false otherwise. - * - * In other words, this returns true if all entries in the element array at - * positions: - * - * first .. first+count-1 - * - * are less than or equal to maxAllowed. - * - * Input parameters: - * maxAllowed: Maximum value to be allowed in the specificied portion of - * the element array. - * first: Start of the portion of the element array to consume. - * count: Number of entries from the element array to consume. - * - * Output parameter: - * out_upperBound: Upon success, is set to the actual maximum value in the - * specified range, which is then guaranteed to be less - * than or equal to maxAllowed. upon failure, is set to - * the first value in the specified range, that is greater - * than maxAllowed. - */ - template - bool Validate(uint32_t maxAllowed, size_t first, size_t count); - - template - const T* Elements() const { - return reinterpret_cast(mBytes.Elements()); - } - - template - T* Elements() { return reinterpret_cast(mBytes.Elements()); } - - bool UpdateTrees(size_t firstByte, size_t lastByte); - - template - friend struct WebGLElementArrayCacheTree; - template - friend struct TreeForType; - - FallibleTArray mBytes; - UniquePtr> mUint8Tree; - UniquePtr> mUint16Tree; - UniquePtr> mUint32Tree; -}; - -} // end namespace mozilla - -#endif // WEBGL_ELEMENT_ARRAY_CACHE_H diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index 448e232d4826..ac6145bf6f4b 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -173,6 +173,50 @@ enum class WebGLExtensionID : uint8_t { Unknown }; +class UniqueBuffer +{ + // Like UniquePtr<>, but for void* and malloc/calloc/free. + void* mBuffer; + +public: + UniqueBuffer() + : mBuffer(nullptr) + { } + + MOZ_IMPLICIT UniqueBuffer(void* buffer) + : mBuffer(buffer) + { } + + ~UniqueBuffer() { + free(mBuffer); + } + + UniqueBuffer(UniqueBuffer&& other) { + this->mBuffer = other.mBuffer; + other.mBuffer = nullptr; + } + + UniqueBuffer& operator =(UniqueBuffer&& other) { + free(this->mBuffer); + this->mBuffer = other.mBuffer; + other.mBuffer = nullptr; + return *this; + } + + UniqueBuffer& operator =(void* newBuffer) { + free(this->mBuffer); + this->mBuffer = newBuffer; + return *this; + } + + explicit operator bool() const { return bool(mBuffer); } + + void* get() const { return mBuffer; } + + UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()! + void operator =(const UniqueBuffer& other) = delete; // assign using Move()! +}; + } // namespace mozilla #endif diff --git a/dom/canvas/gtest/TestWebGLElementArrayCache.cpp b/dom/canvas/gtest/TestWebGLElementArrayCache.cpp deleted file mode 100644 index c8ffc8701fdd..000000000000 --- a/dom/canvas/gtest/TestWebGLElementArrayCache.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/Assertions.h" - -#include "WebGLElementArrayCache.h" - -#include -#include -#include "nscore.h" -#include "nsTArray.h" - -void -MakeRandomVector(nsTArray& a, size_t size) -{ - a.SetLength(size); - // only the most-significant bits of rand() are reasonably random. - // RAND_MAX can be as low as 0x7fff, and we need 8 bits for the result, so we can only - // ignore the 7 least significant bits. - for (size_t i = 0; i < size; i++) - a[i] = static_cast((unsigned int)(rand()) >> 7); -} - -template -T -RandomInteger(T a, T b) -{ - T result(a + rand() % (b - a + 1)); - return result; -} - -template -GLenum -GLType() -{ - switch (sizeof(T)) { - case 4: return LOCAL_GL_UNSIGNED_INT; - case 2: return LOCAL_GL_UNSIGNED_SHORT; - case 1: return LOCAL_GL_UNSIGNED_BYTE; - default: - MOZ_RELEASE_ASSERT(false); - return 0; - } -} - -void -CheckValidate(bool expectSuccess, mozilla::WebGLElementArrayCache& c, GLenum type, - uint32_t maxAllowed, size_t first, size_t count) -{ - const bool success = c.Validate(type, maxAllowed, first, count); - ASSERT_TRUE(success == expectSuccess); -} - -template -void -CheckValidateOneTypeVariousBounds(mozilla::WebGLElementArrayCache& c, size_t firstByte, - size_t countBytes) -{ - size_t first = firstByte / sizeof(T); - size_t count = countBytes / sizeof(T); - - GLenum type = GLType(); - - T max = 0; - for (size_t i = 0; i < count; i++) - if (c.Element(first + i) > max) - max = c.Element(first + i); - - CheckValidate(true, c, type, max, first, count); - CheckValidate(true, c, type, T(-1), first, count); - if (T(max + 1)) CheckValidate(true, c, type, T(max + 1), first, count); - if (max > 0) { - CheckValidate(false, c, type, max - 1, first, count); - CheckValidate(false, c, type, 0, first, count); - } -} - -void CheckValidateAllTypes(mozilla::WebGLElementArrayCache& c, size_t firstByte, - size_t countBytes) -{ - CheckValidateOneTypeVariousBounds(c, firstByte, countBytes); - CheckValidateOneTypeVariousBounds(c, firstByte, countBytes); - CheckValidateOneTypeVariousBounds(c, firstByte, countBytes); -} - -template -void -CheckSanity() -{ - const size_t numElems = 64; // should be significantly larger than tree leaf size to - // ensure we exercise some nontrivial tree-walking - T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now - size_t numBytes = numElems * sizeof(T); - ASSERT_TRUE(numBytes == sizeof(data)); - - GLenum type = GLType(); - - mozilla::WebGLElementArrayCache c; - c.BufferData(data, numBytes); - CheckValidate(true, c, type, 6, 0, 8); - CheckValidate(false, c, type, 5, 0, 8); - CheckValidate(true, c, type, 3, 0, 3); - CheckValidate(false, c, type, 2, 0, 3); - CheckValidate(true, c, type, 6, 2, 4); - CheckValidate(false, c, type, 5, 2, 4); - - c.BufferSubData(5*sizeof(T), data, sizeof(T)); - CheckValidate(true, c, type, 5, 0, 8); - CheckValidate(false, c, type, 4, 0, 8); - - // now test a somewhat larger size to ensure we exceed the size of a tree leaf - for(size_t i = 0; i < numElems; i++) - data[i] = numElems - i; - c.BufferData(data, numBytes); - CheckValidate(true, c, type, numElems, 0, numElems); - CheckValidate(false, c, type, numElems - 1, 0, numElems); - - ASSERT_TRUE(numElems > 10); - CheckValidate(true, c, type, numElems - 10, 10, numElems - 10); - CheckValidate(false, c, type, numElems - 11, 10, numElems - 10); -} - -template -void -CheckUintOverflow() -{ - // This test is only for integer types smaller than uint32_t - static_assert(sizeof(T) < sizeof(uint32_t), "This test is only for integer types \ - smaller than uint32_t"); - - const size_t numElems = 64; // should be significantly larger than tree leaf size to - // ensure we exercise some nontrivial tree-walking - T data[numElems]; - size_t numBytes = numElems * sizeof(T); - ASSERT_TRUE(numBytes == sizeof(data)); - - GLenum type = GLType(); - - mozilla::WebGLElementArrayCache c; - - for(size_t i = 0; i < numElems; i++) - data[i] = numElems - i; - c.BufferData(data, numBytes); - - // bug 825205 - uint32_t bigValWrappingToZero = uint32_t(T(-1)) + 1; - CheckValidate(true, c, type, bigValWrappingToZero, 0, numElems); - CheckValidate(true, c, type, bigValWrappingToZero - 1, 0, numElems); - CheckValidate(false, c, type, 0, 0, numElems); -} - -TEST(WebGLElementArrayCache, Test) -{ - srand(0); // do not want a random seed here. - - CheckSanity(); - CheckSanity(); - CheckSanity(); - - CheckUintOverflow(); - CheckUintOverflow(); - - nsTArray v, vsub; - mozilla::WebGLElementArrayCache b; - - for (int maxBufferSize = 1; maxBufferSize <= 4096; maxBufferSize *= 2) { - // See bug 800612. We originally had | repeat = min(maxBufferSize, 20) | - // and a real bug was only caught on Windows and not on Linux due to rand() - // producing different values. In that case, the minimum value by which to replace - // this 20 to reproduce the bug on Linux, was 25. Replacing it with 64 should give - // us some comfort margin. - int repeat = std::min(maxBufferSize, 64); - for (int i = 0; i < repeat; i++) { - size_t size = RandomInteger(0, maxBufferSize); - MakeRandomVector(v, size); - b.BufferData(v.Elements(), size); - CheckValidateAllTypes(b, 0, size); - - for (int j = 0; j < 16; j++) { - for (int bufferSubDataCalls = 1; bufferSubDataCalls <= 8; bufferSubDataCalls *= 2) { - for (int validateCalls = 1; validateCalls <= 8; validateCalls *= 2) { - - size_t offset = 0, subsize = 0; - - for (int k = 0; k < bufferSubDataCalls; k++) { - offset = RandomInteger(0, size); - subsize = RandomInteger(0, size - offset); - MakeRandomVector(vsub, subsize); - b.BufferSubData(offset, vsub.Elements(), subsize); - } - - for (int k = 0; k < validateCalls; k++) { - offset = RandomInteger(0, size); - subsize = RandomInteger(0, size - offset); - CheckValidateAllTypes(b, offset, subsize); - } - } // validateCalls - } // bufferSubDataCalls - } // j - } // i - } // maxBufferSize -} - diff --git a/dom/canvas/gtest/moz.build b/dom/canvas/gtest/moz.build index dcebc4a5e4ef..db5da92baf9e 100644 --- a/dom/canvas/gtest/moz.build +++ b/dom/canvas/gtest/moz.build @@ -7,12 +7,8 @@ with Files('**'): BUG_COMPONENT = ('Core', 'Canvas: 2D') -with Files('*WebGL*'): - BUG_COMPONENT = ('Core', 'Canvas: WebGL') - UNIFIED_SOURCES += [ 'TestImageBitmapColorUtils.cpp', - 'TestWebGLElementArrayCache.cpp', ] LOCAL_INCLUDES += [ diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index 112a906a4724..fa2315356c8e 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -125,7 +125,6 @@ UNIFIED_SOURCES += [ 'WebGLContextValidate.cpp', 'WebGLContextVertexArray.cpp', 'WebGLContextVertices.cpp', - 'WebGLElementArrayCache.cpp', 'WebGLExtensionBase.cpp', 'WebGLExtensionBlendMinMax.cpp', 'WebGLExtensionColorBufferFloat.cpp', diff --git a/dom/canvas/test/webgl-conf/generated-mochitest.ini b/dom/canvas/test/webgl-conf/generated-mochitest.ini index c26475abcde8..1dc4d5aeb596 100644 --- a/dom/canvas/test/webgl-conf/generated-mochitest.ini +++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini @@ -4958,7 +4958,6 @@ skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance2__rendering__draw-buffers.html] skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance2__rendering__element-index-uint.html] -fail-if = (os != 'win') skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance2__rendering__framebuffer-completeness-unaffected.html] skip-if = (os == 'android' || os == 'linux') @@ -6125,19 +6124,16 @@ skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__element-array-buffer-delete-recreate.html] skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__index-validation-copies-indices.html] -fail-if = (os != 'win') skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__index-validation-crash-with-buffer-sub-data.html] skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__index-validation-large-buffer.html] skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__index-validation-verifies-too-many-indices.html] -fail-if = (os != 'win') skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__index-validation-with-resized-buffer.html] skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__buffers__index-validation.html] -fail-if = (os != 'win') skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__canvas__buffer-offscreen-test.html] skip-if = (os == 'android' || os == 'linux') @@ -7259,7 +7255,6 @@ skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__rendering__draw-arrays-out-of-bounds.html] skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__rendering__draw-elements-out-of-bounds.html] -fail-if = (os != 'win') skip-if = (os == 'android' || os == 'linux') [generated/test_2_conformance__rendering__draw-with-changing-start-vertex-bug.html] skip-if = (os == 'android' || os == 'linux') diff --git a/dom/canvas/test/webgl-conf/mochitest-errata.ini b/dom/canvas/test/webgl-conf/mochitest-errata.ini index 632b7cada02e..8bba34bc62e3 100644 --- a/dom/canvas/test/webgl-conf/mochitest-errata.ini +++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini @@ -71,20 +71,6 @@ skip-if = (os == 'android') || (os == 'linux') || (os == 'win') # Timeout on D3D11 skip-if = (os == 'win') -#################### -# Tests expect conservative index validation, which we skip on WebGL 2. -# ANGLE still provides it though, so they pass on windows. -[generated/test_2_conformance__rendering__draw-elements-out-of-bounds.html] -fail-if = (os != 'win') -[generated/test_2_conformance__buffers__index-validation-copies-indices.html] -fail-if = (os != 'win') -[generated/test_2_conformance__buffers__index-validation.html] -fail-if = (os != 'win') -[generated/test_2_conformance__buffers__index-validation-verifies-too-many-indices.html] -fail-if = (os != 'win') -[generated/test_2_conformance2__rendering__element-index-uint.html] -fail-if = (os != 'win') - ######################################################################## # Complicated