зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1008717 - Additional WebGLElementArrayCache fixes and improvements - r=jgilbert
This commit is contained in:
Родитель
97221e50db
Коммит
4cc86bff8f
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "WebGLElementArrayCache.h"
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
|
@ -88,9 +87,9 @@ UpdateUpperBound(uint32_t* out_upperBound, uint32_t newBound)
|
|||
* We take advantage of the specifics of the situation to avoid generalist tree storage and instead
|
||||
* store the tree entries in a vector, mTreeData.
|
||||
*
|
||||
* The number of leaves is given by mNumLeaves, and mTreeData is always a vector of length
|
||||
* TreeData is always a vector of length
|
||||
*
|
||||
* 2 * mNumLeaves.
|
||||
* 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
|
||||
|
@ -143,20 +142,21 @@ struct WebGLElementArrayCacheTree
|
|||
static const size_t sElementsPerLeafMask = sElementsPerLeaf - 1; // sElementsPerLeaf is POT
|
||||
|
||||
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;
|
||||
size_t mNumLeaves;
|
||||
size_t mParentByteSize;
|
||||
|
||||
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.
|
||||
WebGLElementArrayCacheTree(WebGLElementArrayCache& p)
|
||||
: mParent(p)
|
||||
, mNumLeaves(0)
|
||||
, mParentByteSize(0)
|
||||
{
|
||||
if (mParent.ByteSize()) {
|
||||
Update(0, mParent.ByteSize() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
T GlobalMaximum() const {
|
||||
|
@ -205,21 +205,26 @@ public:
|
|||
return treeIndex + distance;
|
||||
}
|
||||
|
||||
size_t LeafForElement(size_t element) {
|
||||
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 / sElementsPerLeaf;
|
||||
MOZ_ASSERT(leaf < mNumLeaves);
|
||||
MOZ_ASSERT(leaf < NumLeaves());
|
||||
return leaf;
|
||||
}
|
||||
|
||||
size_t LeafForByte(size_t byte) {
|
||||
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) {
|
||||
// See above class comment. The tree storage is an array of length 2*mNumLeaves.
|
||||
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 + mNumLeaves;
|
||||
return leaf + NumLeaves();
|
||||
}
|
||||
|
||||
static size_t LastElementUnderSameLeaf(size_t element) {
|
||||
|
@ -298,6 +303,8 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
// 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 aMallocSizeOf) const
|
||||
|
@ -314,19 +321,28 @@ struct TreeForType {};
|
|||
template<>
|
||||
struct TreeForType<uint8_t>
|
||||
{
|
||||
static WebGLElementArrayCacheTree<uint8_t>*& Run(WebGLElementArrayCache *b) { return b->mUint8Tree; }
|
||||
static ScopedDeletePtr<WebGLElementArrayCacheTree<uint8_t>>&
|
||||
Value(WebGLElementArrayCache *b) {
|
||||
return b->mUint8Tree;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TreeForType<uint16_t>
|
||||
{
|
||||
static WebGLElementArrayCacheTree<uint16_t>*& Run(WebGLElementArrayCache *b) { return b->mUint16Tree; }
|
||||
static ScopedDeletePtr<WebGLElementArrayCacheTree<uint16_t>>&
|
||||
Value(WebGLElementArrayCache *b) {
|
||||
return b->mUint16Tree;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TreeForType<uint32_t>
|
||||
{
|
||||
static WebGLElementArrayCacheTree<uint32_t>*& Run(WebGLElementArrayCache *b) { return b->mUint32Tree; }
|
||||
static ScopedDeletePtr<WebGLElementArrayCacheTree<uint32_t>>&
|
||||
Value(WebGLElementArrayCache *b) {
|
||||
return b->mUint32Tree;
|
||||
}
|
||||
};
|
||||
|
||||
// Calling this method will 1) update the leaves in this interval
|
||||
|
@ -335,36 +351,46 @@ template<typename T>
|
|||
bool WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
||||
{
|
||||
MOZ_ASSERT(firstByte <= lastByte);
|
||||
MOZ_ASSERT(lastByte < mParent.ByteSize());
|
||||
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 / sElementsPerLeaf)
|
||||
//
|
||||
// The way we implement this (division+ceil) operation in integer arithmetic
|
||||
// is as follows:
|
||||
size_t numLeavesNonPOT = (numberOfElements + sElementsPerLeaf - 1) / sElementsPerLeaf;
|
||||
// It only remains to round that up to the next power of two:
|
||||
requiredNumLeaves = NextPowerOfTwo(numLeavesNonPOT);
|
||||
}
|
||||
|
||||
// Step #0: if needed, resize our tree data storage.
|
||||
if (mParentByteSize != mParent.ByteSize())
|
||||
{
|
||||
size_t numberOfElements = mParent.ByteSize() / sizeof(T);
|
||||
if (numberOfElements == 0) {
|
||||
mParentByteSize = mParent.ByteSize();
|
||||
return true;
|
||||
if (requiredNumLeaves != NumLeaves()) {
|
||||
// see class comment for why we the tree storage size is 2 * numLeaves
|
||||
if (!mTreeData.SetLength(2 * requiredNumLeaves)) {
|
||||
mTreeData.SetLength(0);
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(NumLeaves() == requiredNumLeaves);
|
||||
|
||||
size_t requiredNumLeaves = (numberOfElements + sElementsPerLeaf - 1) / sElementsPerLeaf;
|
||||
size_t oldNumLeaves = mNumLeaves;
|
||||
mNumLeaves = NextPowerOfTwo(requiredNumLeaves);
|
||||
if (mNumLeaves != oldNumLeaves) {
|
||||
// see class comment for why we the tree storage size is 2 * mNumLeaves
|
||||
if (!mTreeData.SetLength(2 * mNumLeaves)) {
|
||||
return false;
|
||||
}
|
||||
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.ByteSize() - 1;
|
||||
lastByte = mParent.mBytes.Length() - 1;
|
||||
}
|
||||
|
||||
mParentByteSize = mParent.ByteSize();
|
||||
}
|
||||
|
||||
lastByte = std::min(lastByte, mNumLeaves * sElementsPerLeaf * sizeof(T) - 1);
|
||||
if (NumLeaves() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
lastByte = std::min(lastByte, NumLeaves() * sElementsPerLeaf * sizeof(T) - 1);
|
||||
if (firstByte > lastByte) {
|
||||
return true;
|
||||
}
|
||||
|
@ -372,7 +398,7 @@ bool WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
|||
size_t firstLeaf = LeafForByte(firstByte);
|
||||
size_t lastLeaf = LeafForByte(lastByte);
|
||||
|
||||
MOZ_ASSERT(firstLeaf <= lastLeaf && lastLeaf < mNumLeaves);
|
||||
MOZ_ASSERT(firstLeaf <= lastLeaf && lastLeaf < NumLeaves());
|
||||
|
||||
size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
|
||||
size_t lastTreeIndex = TreeIndexForLeaf(lastLeaf);
|
||||
|
@ -386,7 +412,6 @@ bool WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
|||
size_t treeIndex = firstTreeIndex;
|
||||
// srcIndex is the index in the source buffer
|
||||
size_t srcIndex = firstLeaf * sElementsPerLeaf;
|
||||
size_t numberOfElements = mParentByteSize / sizeof(T);
|
||||
while (treeIndex <= lastTreeIndex) {
|
||||
T m = 0;
|
||||
size_t a = srcIndex;
|
||||
|
@ -412,26 +437,8 @@ bool WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
|||
continue;
|
||||
}
|
||||
|
||||
// initialize local iteration variables: child and parent.
|
||||
size_t child = LeftChildNode(firstTreeIndex);
|
||||
size_t parent = firstTreeIndex;
|
||||
|
||||
// the unrolling makes this look more complicated than it is; the plain non-unrolled
|
||||
// version is in the second while loop below
|
||||
const int unrollSize = 8;
|
||||
while (RightNeighborNode(parent, unrollSize - 1) <= lastTreeIndex)
|
||||
{
|
||||
for (int unroll = 0; unroll < unrollSize; unroll++)
|
||||
{
|
||||
T a = mTreeData[child];
|
||||
child = RightNeighborNode(child);
|
||||
T b = mTreeData[child];
|
||||
child = RightNeighborNode(child);
|
||||
mTreeData[parent] = std::max(a, b);
|
||||
parent = RightNeighborNode(parent);
|
||||
}
|
||||
}
|
||||
// plain non-unrolled version, used to terminate the job after the last unrolled iteration
|
||||
while (parent <= lastTreeIndex)
|
||||
{
|
||||
T a = mTreeData[child];
|
||||
|
@ -446,39 +453,32 @@ bool WebGLElementArrayCacheTree<T>::Update(size_t firstByte, size_t lastByte)
|
|||
return true;
|
||||
}
|
||||
|
||||
WebGLElementArrayCache::WebGLElementArrayCache() {
|
||||
}
|
||||
|
||||
WebGLElementArrayCache::~WebGLElementArrayCache() {
|
||||
delete mUint8Tree;
|
||||
delete mUint16Tree;
|
||||
delete mUint32Tree;
|
||||
free(mUntypedData);
|
||||
}
|
||||
|
||||
bool WebGLElementArrayCache::BufferData(const void* ptr, size_t byteSize) {
|
||||
if (byteSize == 0) {
|
||||
mByteSize = 0;
|
||||
free(mUntypedData);
|
||||
mUntypedData = nullptr;
|
||||
return true;
|
||||
}
|
||||
if (byteSize != mByteSize) {
|
||||
void* newUntypedData = realloc(mUntypedData, byteSize);
|
||||
if (!newUntypedData)
|
||||
bool WebGLElementArrayCache::BufferData(const void* ptr, size_t byteLength) {
|
||||
if (mBytes.Length() != byteLength) {
|
||||
if (!mBytes.SetLength(byteLength)) {
|
||||
mBytes.SetLength(0);
|
||||
return false;
|
||||
mByteSize = byteSize;
|
||||
mUntypedData = newUntypedData;
|
||||
}
|
||||
}
|
||||
|
||||
BufferSubData(0, ptr, byteSize);
|
||||
return true;
|
||||
MOZ_ASSERT(mBytes.Length() == byteLength);
|
||||
return BufferSubData(0, ptr, byteLength);
|
||||
}
|
||||
|
||||
bool WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteSize) {
|
||||
if (!updateByteSize) return true;
|
||||
bool WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteLength) {
|
||||
MOZ_ASSERT(pos + updateByteLength <= mBytes.Length());
|
||||
if (!updateByteLength)
|
||||
return true;
|
||||
if (ptr)
|
||||
memcpy(static_cast<uint8_t*>(mUntypedData) + pos, ptr, updateByteSize);
|
||||
memcpy(mBytes.Elements() + pos, ptr, updateByteLength);
|
||||
else
|
||||
memset(static_cast<uint8_t*>(mUntypedData) + pos, 0, updateByteSize);
|
||||
return UpdateTrees(pos, pos + updateByteSize - 1);
|
||||
memset(mBytes.Elements() + pos, 0, updateByteLength);
|
||||
return UpdateTrees(pos, pos + updateByteLength - 1);
|
||||
}
|
||||
|
||||
bool WebGLElementArrayCache::UpdateTrees(size_t firstByte, size_t lastByte)
|
||||
|
@ -513,12 +513,21 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
// is exactly the max allowed value.
|
||||
MOZ_ASSERT(uint32_t(maxAllowedT) == maxAllowed);
|
||||
|
||||
if (!mByteSize || !countElements)
|
||||
if (!mBytes.Length() || !countElements)
|
||||
return true;
|
||||
|
||||
WebGLElementArrayCacheTree<T>*& tree = TreeForType<T>::Run(this);
|
||||
ScopedDeletePtr<WebGLElementArrayCacheTree<T>>& tree = TreeForType<T>::Value(this);
|
||||
if (!tree) {
|
||||
tree = new 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;
|
||||
|
@ -537,7 +546,7 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
// before calling tree->Validate, we have to validate ourselves the boundaries of the elements span,
|
||||
// to round them to the nearest multiple of sElementsPerLeaf.
|
||||
size_t firstElementAdjustmentEnd = std::min(lastElement,
|
||||
tree->LastElementUnderSameLeaf(firstElement));
|
||||
tree->LastElementUnderSameLeaf(firstElement));
|
||||
while (firstElement <= firstElementAdjustmentEnd) {
|
||||
const T& curData = elements[firstElement];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
|
@ -546,7 +555,7 @@ WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
|
|||
firstElement++;
|
||||
}
|
||||
size_t lastElementAdjustmentEnd = std::max(firstElement,
|
||||
tree->FirstElementUnderSameLeaf(lastElement));
|
||||
tree->FirstElementUnderSameLeaf(lastElement));
|
||||
while (lastElement >= lastElementAdjustmentEnd) {
|
||||
const T& curData = elements[lastElement];
|
||||
UpdateUpperBound(out_upperBound, curData);
|
||||
|
@ -589,7 +598,7 @@ WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|||
size_t uint16TreeSize = mUint16Tree ? mUint16Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
|
||||
size_t uint32TreeSize = mUint32Tree ? mUint32Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
|
||||
return aMallocSizeOf(this) +
|
||||
mByteSize +
|
||||
mBytes.SizeOfExcludingThis(aMallocSizeOf) +
|
||||
uint8TreeSize +
|
||||
uint16TreeSize +
|
||||
uint32TreeSize;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#define WEBGLELEMENTARRAYCACHE_H
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Scoped.h"
|
||||
#include "nsTArray.h"
|
||||
#include <stdint.h>
|
||||
#include "nscore.h"
|
||||
#include "GLDefs.h"
|
||||
|
@ -30,7 +32,7 @@ struct WebGLElementArrayCacheTree;
|
|||
class WebGLElementArrayCache {
|
||||
|
||||
public:
|
||||
bool BufferData(const void* ptr, size_t byteSize);
|
||||
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,
|
||||
|
@ -39,13 +41,7 @@ public:
|
|||
template<typename T>
|
||||
T Element(size_t i) const { return Elements<T>()[i]; }
|
||||
|
||||
WebGLElementArrayCache()
|
||||
: mUntypedData(nullptr)
|
||||
, mByteSize(0)
|
||||
, mUint8Tree(nullptr)
|
||||
, mUint16Tree(nullptr)
|
||||
, mUint32Tree(nullptr)
|
||||
{}
|
||||
WebGLElementArrayCache();
|
||||
|
||||
~WebGLElementArrayCache();
|
||||
|
||||
|
@ -57,14 +53,10 @@ private:
|
|||
bool Validate(uint32_t maxAllowed, size_t first, size_t count,
|
||||
uint32_t* out_upperBound);
|
||||
|
||||
size_t ByteSize() const {
|
||||
return mByteSize;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Elements() const { return static_cast<const T*>(mUntypedData); }
|
||||
const T* Elements() const { return reinterpret_cast<const T*>(mBytes.Elements()); }
|
||||
template<typename T>
|
||||
T* Elements() { return static_cast<T*>(mUntypedData); }
|
||||
T* Elements() { return reinterpret_cast<T*>(mBytes.Elements()); }
|
||||
|
||||
bool UpdateTrees(size_t firstByte, size_t lastByte);
|
||||
|
||||
|
@ -73,11 +65,10 @@ private:
|
|||
template<typename T>
|
||||
friend struct TreeForType;
|
||||
|
||||
void* mUntypedData;
|
||||
size_t mByteSize;
|
||||
WebGLElementArrayCacheTree<uint8_t>* mUint8Tree;
|
||||
WebGLElementArrayCacheTree<uint16_t>* mUint16Tree;
|
||||
WebGLElementArrayCacheTree<uint32_t>* mUint32Tree;
|
||||
FallibleTArray<uint8_t> mBytes;
|
||||
ScopedDeletePtr<WebGLElementArrayCacheTree<uint8_t>> mUint8Tree;
|
||||
ScopedDeletePtr<WebGLElementArrayCacheTree<uint16_t>> mUint16Tree;
|
||||
ScopedDeletePtr<WebGLElementArrayCacheTree<uint32_t>> mUint32Tree;
|
||||
};
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче