--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -3,17 +3,16 @@
* 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 "WebGLBuffer.h"
#include "GLContext.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "WebGLContext.h"
-#include "WebGLElementArrayCache.h"
namespace mozilla {
WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
: WebGLRefCountedObject(webgl)
, mGLName(buf)
, mContent(Kind::Undefined)
, mUsage(LOCAL_GL_STATIC_DRAW)
@@ -33,19 +32,16 @@ void
WebGLBuffer::SetContentAfterBind(GLenum target)
{
if (mContent != Kind::Undefined)
return;
switch (target) {
case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
mContent = Kind::ElementArray;
- if (!mCache) {
- mCache.reset(new WebGLElementArrayCache);
- }
break;
case LOCAL_GL_ARRAY_BUFFER:
case LOCAL_GL_PIXEL_PACK_BUFFER:
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
case LOCAL_GL_UNIFORM_BUFFER:
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
case LOCAL_GL_COPY_READ_BUFFER:
@@ -59,17 +55,18 @@ WebGLBuffer::SetContentAfterBind(GLenum
}
void
WebGLBuffer::Delete()
{
mContext->MakeContextCurrent();
mContext->gl->fDeleteBuffers(1, &mGLName);
mByteLength = 0;
- mCache = nullptr;
+ mIndexCache = nullptr;
+ mIndexRanges.clear();
LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
}
////////////////////////////////////////
static bool
ValidateBufferUsageEnum(WebGLContext* webgl, const char* funcName, GLenum usage)
{
@@ -105,56 +102,107 @@ WebGLBuffer::BufferData(GLenum target, s
// Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
// is like intptr_t.
if (!CheckedInt<GLsizeiptr>(size).isValid())
return mContext->ErrorOutOfMemory("%s: bad size", funcName);
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);
return;
}
#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) {
MOZ_ASSERT(error == LOCAL_GL_OUT_OF_MEMORY);
mContext->ErrorOutOfMemory("%s: Error from driver: 0x%04x", funcName, error);
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<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
WebGLBuffer::ValidateRange(const char* funcName, size_t byteOffset, size_t byteLen) const
{
auto availLength = mByteLength;
if (byteOffset > availLength) {
mContext->ErrorInvalidValue("%s: Offset passes the end of the buffer.", funcName);
return false;
}
@@ -166,64 +214,141 @@ WebGLBuffer::ValidateRange(const char* f
return false;
}
return true;
}
////////////////////////////////////////
-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<IndexRange> 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;
}
+template<typename T>
+static size_t
+MaxForRange(const void* data, size_t first, size_t count, const uint32_t ignoredVal)
+{
+ 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::Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count) const
+WebGLBuffer::ValidateIndexedFetch(GLenum type, uint32_t numFetchable, size_t first,
+ size_t count) const
{
- if (mContext->IsWebGL2())
+ if (!mIndexCache)
return true;
- return mCache->Validate(type, maxAllowed, first, count);
-}
+ if (!count)
+ return true;
+
+ 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) });
+ }
+
+ 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);
-bool
-WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const
-{
- if (mContext->IsWebGL2())
- return false;
+ 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();
+ }
- return mCache->BeenUsedWithMultipleTypes();
+ 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;
}
////
bool
WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
{
/* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -1,28 +1,26 @@
/* -*- 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_BUFFER_H_
#define WEBGL_BUFFER_H_
+#include <map>
+
#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<WebGLBuffer>
, public LinkedListElement<WebGLBuffer>
{
friend class WebGLContext;
friend class WebGL2Context;
friend class WebGLTexture;
@@ -41,34 +39,29 @@ public:
void Delete();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
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;
}
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
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;
////
static void AddBindCount(GLenum target, WebGLBuffer* buffer, int8_t addVal) {
if (!buffer)
return;
if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
@@ -97,19 +90,39 @@ public:
const GLenum mGLName;
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
protected:
~WebGLBuffer();
+ void InvalidateCacheRange(size_t offset, size_t length) const;
+
Kind mContent;
GLenum mUsage;
size_t mByteLength;
- UniquePtr<WebGLElementArrayCache> 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<IndexRange, size_t> mIndexRanges;
};
} // namespace mozilla
#endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -2109,60 +2109,16 @@ bool
ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
GLenum rawTexTarget, TexTarget* const out_texTarget,
WebGLTexture** const out_tex);
bool
ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims,
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<ScopedUnpackReset>
{
friend struct gl::ScopedGLWrapper<ScopedUnpackReset>;
private:
WebGLContext* const mWebGL;
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -397,36 +397,20 @@ WebGLContext::BufferSubDataImpl(GLenum t
if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
return;
const auto& buffer = ValidateBufferSelection(funcName, target);
if (!buffer)
return;
- if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen))
- return;
-
- if (!CheckedInt<GLintptr>(dataLen).isValid()) {
- ErrorOutOfMemory("%s: Size too large.", funcName);
- return;
- }
- const GLintptr glDataLen(dataLen);
+ buffer->BufferSubData(target, size_t(dstByteOffset), dataLen, data);
+}
- ////
-
- 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
WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
const dom::ArrayBuffer& src)
{
if (IsContextLost())
return;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -733,33 +733,24 @@ WebGLContext::DrawElements_check(const c
funcName);
return false;
}
if (!ValidateBufferFetching(funcName))
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",
funcName);
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;
}
static void
HandleDrawElementsErrors(WebGLContext* webgl, const char* funcName,
gl::GLContext::LocalErrorScope& errorScope)
{
const auto err = errorScope.GetError();
@@ -989,18 +980,18 @@ WebGLContext::ValidateBufferFetching(con
}
size_t availBytes = bufByteLen - vd.ByteOffset();
if (vd.BytesPerVertex() > availBytes) {
maxVertices = 0;
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) {
maxVertices = vertCapacity;
}
hasPerVertex = true;
} else {
const auto curMaxInstances = CheckedInt<size_t>(vertCapacity) * vd.mDivisor;
deleted file mode 100644
--- 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 <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
deleted file mode 100644
--- 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 <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
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -168,11 +168,55 @@ enum class WebGLExtensionID : uint8_t {
WEBGL_debug_shaders,
WEBGL_depth_texture,
WEBGL_draw_buffers,
WEBGL_lose_context,
Max,
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
deleted file mode 100644
--- 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 <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
-}
-
--- a/dom/canvas/gtest/moz.build
+++ b/dom/canvas/gtest/moz.build
@@ -2,22 +2,18 @@
# vim: set filetype=python:
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'Canvas: 2D')
-with Files('*WebGL*'):
- BUG_COMPONENT = ('Core', 'Canvas: WebGL')
-
UNIFIED_SOURCES += [
'TestImageBitmapColorUtils.cpp',
- 'TestWebGLElementArrayCache.cpp',
]
LOCAL_INCLUDES += [
'/dom/canvas',
'/media/libyuv/libyuv/include'
]
FINAL_LIBRARY = 'xul-gtest'
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -120,17 +120,16 @@ UNIFIED_SOURCES += [
'WebGLContextLossHandler.cpp',
'WebGLContextState.cpp',
'WebGLContextTextures.cpp',
'WebGLContextUnchecked.cpp',
'WebGLContextUtils.cpp',
'WebGLContextValidate.cpp',
'WebGLContextVertexArray.cpp',
'WebGLContextVertices.cpp',
- 'WebGLElementArrayCache.cpp',
'WebGLExtensionBase.cpp',
'WebGLExtensionBlendMinMax.cpp',
'WebGLExtensionColorBufferFloat.cpp',
'WebGLExtensionColorBufferHalfFloat.cpp',
'WebGLExtensionCompressedTextureASTC.cpp',
'WebGLExtensionCompressedTextureATC.cpp',
'WebGLExtensionCompressedTextureES3.cpp',
'WebGLExtensionCompressedTextureETC1.cpp',
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4953,17 +4953,16 @@ fail-if = (os == 'mac') || (os == 'win')
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance2__rendering__clear-srgb-color-buffer.html]
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance2__rendering__clipping-wide-points.html]
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')
[generated/test_2_conformance2__rendering__framebuffer-unsupported.html]
fail-if = (os == 'mac')
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance2__rendering__fs-color-type-mismatch-color-buffer-type.html]
fail-if = (os == 'mac') || (os == 'win')
@@ -6120,29 +6119,26 @@ skip-if = (os == 'android' || os == 'lin
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__buffer-data-array-buffer-delete.html]
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__buffers__buffer-uninitialized.html]
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')
[generated/test_2_conformance__canvas__buffer-preserve-test.html]
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__canvas__canvas-test.html]
skip-if = (os == 'win') || (os == 'android' || os == 'linux')
[generated/test_2_conformance__canvas__canvas-zero-size.html]
@@ -7254,17 +7250,16 @@ skip-if = (os == 'android' || os == 'lin
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__rendering__culling.html]
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__rendering__default-texture-draw-bug.html]
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')
[generated/test_2_conformance__rendering__framebuffer-switch.html]
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__rendering__framebuffer-texture-switch.html]
skip-if = (os == 'android' || os == 'linux')
[generated/test_2_conformance__rendering__gl-clear.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -66,30 +66,16 @@ skip-if = 1
# Timing out
[generated/test_conformance__uniforms__uniform-default-values.html]
# Timeout on Windows, crash on Android/Linux.
skip-if = (os == 'android') || (os == 'linux') || (os == 'win')
[generated/test_conformance__ogles__GL__mat3__mat3_001_to_006.html]
# 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
[generated/test_conformance__context__context-attributes-alpha-depth-stencil-antialias.html]
fail-if = (os == 'mac' && os_version == '10.6')
# Asserts on 'B2G ICS Emulator Debug' and linux debug. Crashes on Android.
skip-if = (os == 'b2g') || (os == 'linux') || (os == 'android')