Bug 1339256 - Simplify index validation. - r=kvark

MozReview-Commit-ID: 4FALdIyhBP8
This commit is contained in:
Jeff Gilbert 2017-02-09 20:32:58 -08:00
Родитель 7baee17628
Коммит bd43a20c46
13 изменённых файлов: 248 добавлений и 1088 удалений

Просмотреть файл

@ -8,7 +8,6 @@
#include "GLContext.h" #include "GLContext.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h" #include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "WebGLContext.h" #include "WebGLContext.h"
#include "WebGLElementArrayCache.h"
namespace mozilla { namespace mozilla {
@ -38,9 +37,6 @@ WebGLBuffer::SetContentAfterBind(GLenum target)
switch (target) { switch (target) {
case LOCAL_GL_ELEMENT_ARRAY_BUFFER: case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
mContent = Kind::ElementArray; mContent = Kind::ElementArray;
if (!mCache) {
mCache.reset(new WebGLElementArrayCache);
}
break; break;
case LOCAL_GL_ARRAY_BUFFER: case LOCAL_GL_ARRAY_BUFFER:
@ -64,7 +60,8 @@ WebGLBuffer::Delete()
mContext->MakeContextCurrent(); mContext->MakeContextCurrent();
mContext->gl->fDeleteBuffers(1, &mGLName); mContext->gl->fDeleteBuffers(1, &mGLName);
mByteLength = 0; mByteLength = 0;
mCache = nullptr; mIndexCache = nullptr;
mIndexRanges.clear();
LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers LinkedListElement<WebGLBuffer>::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)) if (!ValidateBufferUsageEnum(mContext, funcName, usage))
return; return;
const auto& gl = mContext->gl;
gl->MakeCurrent();
const ScopedLazyBind lazyBind(gl, target, this);
mContext->InvalidateBufferFetching();
#ifdef XP_MACOSX #ifdef XP_MACOSX
// bug 790879 // bug 790879
if (gl->WorkAroundDriverBugs() && if (mContext->gl->WorkAroundDriverBugs() &&
size > INT32_MAX) size > INT32_MAX)
{ {
mContext->ErrorOutOfMemory("%s: Allocation size too large.", funcName); 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 #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()); const bool sizeChanges = (size != ByteLength());
if (sizeChanges) { if (sizeChanges) {
mContext->InvalidateBufferFetching();
gl::GLContext::LocalErrorScope errorScope(*gl); gl::GLContext::LocalErrorScope errorScope(*gl);
gl->fBufferData(target, size, data, usage); gl->fBufferData(target, size, uploadData, usage);
const auto error = errorScope.GetError(); const auto error = errorScope.GetError();
if (error) { if (error) {
@ -137,19 +150,54 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa
return; return;
} }
} else { } else {
gl->fBufferData(target, size, data, usage); gl->fBufferData(target, size, uploadData, usage);
} }
mUsage = usage; mUsage = usage;
mByteLength = size; mByteLength = size;
mIndexCache = Move(newIndexCache);
// Warning: Possibly shared memory. See bug 1225033. if (mIndexCache) {
if (!ElementArrayCacheBufferData(data, size)) { if (mIndexRanges.size()) {
mByteLength = 0; mContext->GeneratePerfWarning("[%p] Invalidating %u ranges.", this,
mContext->ErrorOutOfMemory("%s: Failed update index buffer cache.", funcName); 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<GLintptr>(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 bool
WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const 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 static uint8_t
WebGLBuffer::ElementArrayCacheBufferData(const void* ptr, IndexByteSizeByType(GLenum type)
size_t bufferSizeInBytes)
{ {
if (mContext->IsWebGL2()) switch (type) {
return true; case LOCAL_GL_UNSIGNED_BYTE: return 1;
case LOCAL_GL_UNSIGNED_SHORT: return 2;
if (mContent == Kind::ElementArray) case LOCAL_GL_UNSIGNED_INT: return 4;
return mCache->BufferData(ptr, bufferSizeInBytes); default:
MOZ_CRASH();
return true; }
} }
void void
WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr, WebGLBuffer::InvalidateCacheRange(size_t byteOffset, size_t byteLength) const
size_t updateSizeInBytes)
{ {
if (mContext->IsWebGL2()) MOZ_ASSERT(mIndexCache);
return;
if (mContent == Kind::ElementArray) std::vector<IndexRange> invalids;
mCache->BufferSubData(pos, ptr, updateSizeInBytes); 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 size_t
WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{ {
size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(mallocSizeOf) size_t size = mallocSizeOf(this);
: 0; if (mIndexCache) {
return mallocSizeOf(this) + sizeOfCache; size += mByteLength;
}
return size;
} }
bool template<typename T>
WebGLBuffer::Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count) const 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 true;
return mCache->Validate(type, maxAllowed, first, count); if (!count)
} return true;
bool const IndexRange range = { type, first, count };
WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const auto res = mIndexRanges.insert({ range, size_t(0) });
{ if (mIndexRanges.size() > kMaxIndexRanges) {
if (mContext->IsWebGL2()) mContext->GeneratePerfWarning("[%p] Clearing mIndexRanges after exceeding %u.",
return false; 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<uint8_t>(data, first, count, ignoreVal);
break;
case LOCAL_GL_UNSIGNED_SHORT:
maxFetchIndex = MaxForRange<uint16_t>(data, first, count, ignoreVal);
break;
case LOCAL_GL_UNSIGNED_INT:
maxFetchIndex = MaxForRange<uint32_t>(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;
} }
//// ////

Просмотреть файл

@ -6,18 +6,16 @@
#ifndef WEBGL_BUFFER_H_ #ifndef WEBGL_BUFFER_H_
#define WEBGL_BUFFER_H_ #define WEBGL_BUFFER_H_
#include <map>
#include "GLDefs.h" #include "GLDefs.h"
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/UniquePtr.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "WebGLObjectModel.h" #include "WebGLObjectModel.h"
#include "WebGLTypes.h" #include "WebGLTypes.h"
namespace mozilla { namespace mozilla {
class WebGLElementArrayCache;
class WebGLBuffer final class WebGLBuffer final
: public nsWrapperCache : public nsWrapperCache
, public WebGLRefCountedObject<WebGLBuffer> , public WebGLRefCountedObject<WebGLBuffer>
@ -46,16 +44,9 @@ public:
GLenum Usage() const { return mUsage; } GLenum Usage() const { return mUsage; }
size_t ByteLength() const { return mByteLength; } size_t ByteLength() const { return mByteLength; }
bool ElementArrayCacheBufferData(const void* ptr, size_t bufferSizeInBytes); bool ValidateIndexedFetch(GLenum type, uint32_t max_allowed, size_t first, size_t count) const;
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 ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const; bool ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const;
bool IsElementArrayUsedWithMultipleTypes() const;
WebGLContext* GetParentObject() const { WebGLContext* GetParentObject() const {
return mContext; return mContext;
} }
@ -64,6 +55,8 @@ public:
bool ValidateCanBindToTarget(const char* funcName, GLenum target); bool ValidateCanBindToTarget(const char* funcName, GLenum target);
void BufferData(GLenum target, size_t size, const void* data, GLenum usage); 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: protected:
~WebGLBuffer(); ~WebGLBuffer();
void InvalidateCacheRange(size_t offset, size_t length) const;
Kind mContent; Kind mContent;
GLenum mUsage; GLenum mUsage;
size_t mByteLength; size_t mByteLength;
UniquePtr<WebGLElementArrayCache> mCache;
size_t mTFBindCount; size_t mTFBindCount;
size_t mNonTFBindCount; 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<IndexRange, size_t> mIndexRanges;
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -2114,50 +2114,6 @@ ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDi
GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget, GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget,
WebGLTexture** const out_tex); 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 class ScopedUnpackReset final
: public gl::ScopedGLWrapper<ScopedUnpackReset> : public gl::ScopedGLWrapper<ScopedUnpackReset>
{ {

Просмотреть файл

@ -402,27 +402,11 @@ WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
if (!buffer) if (!buffer)
return; return;
if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen)) buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data);
return;
if (!CheckedInt<GLintptr>(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));
} }
////
void void
WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset, WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
const dom::ArrayBuffer& src) const dom::ArrayBuffer& src)

Просмотреть файл

@ -738,7 +738,7 @@ WebGLContext::DrawElements_check(const char* funcName, GLenum mode, GLsizei vert
return false; return false;
if (!mMaxFetchedVertices || 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 " ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient "
"size for given indices from the bound element array", "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; 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; return true;
} }
@ -994,8 +985,8 @@ WebGLContext::ValidateBufferFetching(const char* info)
maxInstances = 0; maxInstances = 0;
break; break;
} }
availBytes -= vd.BytesPerVertex(); availBytes -= vd.BytesPerVertex(); // Snip off the tail.
const size_t vertCapacity = 1 + availBytes / vd.ExplicitStride(); const size_t vertCapacity = availBytes / vd.ExplicitStride() + 1; // Add +1 for the snipped tail.
if (vd.mDivisor == 0) { if (vd.mDivisor == 0) {
if (vertCapacity < maxVertices) { if (vertCapacity < maxVertices) {

Просмотреть файл

@ -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 <algorithm>
#include <cstdlib>
#include <cstring>
#include <limits>
#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<typename T>
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<T> 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<typename T>
struct TreeForType {};
template<>
struct TreeForType<uint8_t>
{
static UniquePtr<WebGLElementArrayCacheTree<uint8_t>>&
Value(WebGLElementArrayCache* b) {
return b->mUint8Tree;
}
};
template<>
struct TreeForType<uint16_t>
{
static UniquePtr<WebGLElementArrayCacheTree<uint16_t>>&
Value(WebGLElementArrayCache* b) {
return b->mUint16Tree;
}
};
template<>
struct TreeForType<uint32_t>
{
static UniquePtr<WebGLElementArrayCacheTree<uint32_t>>&
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<typename T>
bool
WebGLElementArrayCacheTree<T>::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<T>(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<typename T>
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<T>::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<WebGLElementArrayCacheTree<T>>& tree = TreeForType<T>::Value(this);
if (!tree) {
tree = MakeUnique<WebGLElementArrayCacheTree<T>>(*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<T>();
// 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<uint8_t>(maxAllowed, firstElement, countElements);
if (type == LOCAL_GL_UNSIGNED_SHORT)
return Validate<uint16_t>(maxAllowed, firstElement, countElements);
if (type == LOCAL_GL_UNSIGNED_INT)
return Validate<uint32_t>(maxAllowed, firstElement, countElements);
MOZ_ASSERT(false, "Invalid type.");
return false;
}
template<typename T>
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

Просмотреть файл

@ -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 <stdint.h>
namespace mozilla {
template<typename T>
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<typename T>
T Element(size_t i) const { return Elements<T>()[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<typename T>
bool Validate(uint32_t maxAllowed, size_t first, size_t count);
template<typename T>
const T* Elements() const {
return reinterpret_cast<const T*>(mBytes.Elements());
}
template<typename T>
T* Elements() { return reinterpret_cast<T*>(mBytes.Elements()); }
bool UpdateTrees(size_t firstByte, size_t lastByte);
template<typename T>
friend struct WebGLElementArrayCacheTree;
template<typename T>
friend struct TreeForType;
FallibleTArray<uint8_t> mBytes;
UniquePtr<WebGLElementArrayCacheTree<uint8_t>> mUint8Tree;
UniquePtr<WebGLElementArrayCacheTree<uint16_t>> mUint16Tree;
UniquePtr<WebGLElementArrayCacheTree<uint32_t>> mUint32Tree;
};
} // end namespace mozilla
#endif // WEBGL_ELEMENT_ARRAY_CACHE_H

Просмотреть файл

@ -173,6 +173,50 @@ enum class WebGLExtensionID : uint8_t {
Unknown 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 } // namespace mozilla
#endif #endif

Просмотреть файл

@ -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 <cstdio>
#include <cstdlib>
#include "nscore.h"
#include "nsTArray.h"
void
MakeRandomVector(nsTArray<uint8_t>& 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<uint8_t>((unsigned int)(rand()) >> 7);
}
template<typename T>
T
RandomInteger(T a, T b)
{
T result(a + rand() % (b - a + 1));
return result;
}
template<typename T>
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<typename T>
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>();
T max = 0;
for (size_t i = 0; i < count; i++)
if (c.Element<T>(first + i) > max)
max = c.Element<T>(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<uint8_t>(c, firstByte, countBytes);
CheckValidateOneTypeVariousBounds<uint16_t>(c, firstByte, countBytes);
CheckValidateOneTypeVariousBounds<uint32_t>(c, firstByte, countBytes);
}
template<typename T>
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<T>();
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<typename T>
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<T>();
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<uint8_t>();
CheckSanity<uint16_t>();
CheckSanity<uint32_t>();
CheckUintOverflow<uint8_t>();
CheckUintOverflow<uint16_t>();
nsTArray<uint8_t> 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<size_t>(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<size_t>(0, size);
subsize = RandomInteger<size_t>(0, size - offset);
MakeRandomVector(vsub, subsize);
b.BufferSubData(offset, vsub.Elements(), subsize);
}
for (int k = 0; k < validateCalls; k++) {
offset = RandomInteger<size_t>(0, size);
subsize = RandomInteger<size_t>(0, size - offset);
CheckValidateAllTypes(b, offset, subsize);
}
} // validateCalls
} // bufferSubDataCalls
} // j
} // i
} // maxBufferSize
}

Просмотреть файл

@ -7,12 +7,8 @@
with Files('**'): with Files('**'):
BUG_COMPONENT = ('Core', 'Canvas: 2D') BUG_COMPONENT = ('Core', 'Canvas: 2D')
with Files('*WebGL*'):
BUG_COMPONENT = ('Core', 'Canvas: WebGL')
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'TestImageBitmapColorUtils.cpp', 'TestImageBitmapColorUtils.cpp',
'TestWebGLElementArrayCache.cpp',
] ]
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [

Просмотреть файл

@ -125,7 +125,6 @@ UNIFIED_SOURCES += [
'WebGLContextValidate.cpp', 'WebGLContextValidate.cpp',
'WebGLContextVertexArray.cpp', 'WebGLContextVertexArray.cpp',
'WebGLContextVertices.cpp', 'WebGLContextVertices.cpp',
'WebGLElementArrayCache.cpp',
'WebGLExtensionBase.cpp', 'WebGLExtensionBase.cpp',
'WebGLExtensionBlendMinMax.cpp', 'WebGLExtensionBlendMinMax.cpp',
'WebGLExtensionColorBufferFloat.cpp', 'WebGLExtensionColorBufferFloat.cpp',

Просмотреть файл

@ -4958,7 +4958,6 @@ skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance2__rendering__draw-buffers.html] [generated/test_2_conformance2__rendering__draw-buffers.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance2__rendering__element-index-uint.html] [generated/test_2_conformance2__rendering__element-index-uint.html]
fail-if = (os != 'win')
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance2__rendering__framebuffer-completeness-unaffected.html] [generated/test_2_conformance2__rendering__framebuffer-completeness-unaffected.html]
skip-if = (os == 'android' || os == 'linux') 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] [generated/test_2_conformance__buffers__element-array-buffer-delete-recreate.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__index-validation-copies-indices.html] [generated/test_2_conformance__buffers__index-validation-copies-indices.html]
fail-if = (os != 'win')
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__index-validation-crash-with-buffer-sub-data.html] [generated/test_2_conformance__buffers__index-validation-crash-with-buffer-sub-data.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__index-validation-large-buffer.html] [generated/test_2_conformance__buffers__index-validation-large-buffer.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__index-validation-verifies-too-many-indices.html] [generated/test_2_conformance__buffers__index-validation-verifies-too-many-indices.html]
fail-if = (os != 'win')
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__index-validation-with-resized-buffer.html] [generated/test_2_conformance__buffers__index-validation-with-resized-buffer.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__index-validation.html] [generated/test_2_conformance__buffers__index-validation.html]
fail-if = (os != 'win')
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__canvas__buffer-offscreen-test.html] [generated/test_2_conformance__canvas__buffer-offscreen-test.html]
skip-if = (os == 'android' || os == 'linux') 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] [generated/test_2_conformance__rendering__draw-arrays-out-of-bounds.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__rendering__draw-elements-out-of-bounds.html] [generated/test_2_conformance__rendering__draw-elements-out-of-bounds.html]
fail-if = (os != 'win')
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__rendering__draw-with-changing-start-vertex-bug.html] [generated/test_2_conformance__rendering__draw-with-changing-start-vertex-bug.html]
skip-if = (os == 'android' || os == 'linux') skip-if = (os == 'android' || os == 'linux')

Просмотреть файл

@ -71,20 +71,6 @@ skip-if = (os == 'android') || (os == 'linux') || (os == 'win')
# Timeout on D3D11 # Timeout on D3D11
skip-if = (os == 'win') 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 # Complicated