Bug 1783717 - Faster allocation of TexturePacker children. r=aosmond,gfx-reviewers

Rather than allocating TexturePacker children individually, allocate an array
of all the children at once to cut the number of memory allocations in half.

Depends on D154118

Differential Revision: https://phabricator.services.mozilla.com/D154182
This commit is contained in:
Lee Salzman 2022-08-10 02:57:26 +00:00
Родитель 119e3df4ef
Коммит 681687a77b
2 изменённых файлов: 41 добавлений и 42 удалений

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

@ -24,7 +24,7 @@
namespace mozilla::gfx { namespace mozilla::gfx {
// Inserts (allocated) a rectangle of the requested size into the tree. // Inserts (allocates) a rectangle of the requested size into the tree.
Maybe<IntPoint> TexturePacker::Insert(const IntSize& aSize) { Maybe<IntPoint> TexturePacker::Insert(const IntSize& aSize) {
// Check if the available space could possibly fit the requested size. If // Check if the available space could possibly fit the requested size. If
// not, there is no reason to continue searching within this sub-tree. // not, there is no reason to continue searching within this sub-tree.
@ -32,17 +32,17 @@ Maybe<IntPoint> TexturePacker::Insert(const IntSize& aSize) {
mBounds.width < aSize.width || mBounds.height < aSize.height) { mBounds.width < aSize.width || mBounds.height < aSize.height) {
return Nothing(); return Nothing();
} }
if (mChildren[0]) { if (mChildren) {
// If this node has children, then try to insert into each of the children // If this node has children, then try to insert into each of the children
// in turn. // in turn.
Maybe<IntPoint> inserted = mChildren[0]->Insert(aSize); Maybe<IntPoint> inserted = mChildren[0].Insert(aSize);
if (!inserted) { if (!inserted) {
inserted = mChildren[1]->Insert(aSize); inserted = mChildren[1].Insert(aSize);
} }
// If the insertion succeeded, adjust the available state to reflect the // If the insertion succeeded, adjust the available state to reflect the
// remaining space in the children. // remaining space in the children.
if (inserted) { if (inserted) {
mAvailable = std::max(mChildren[0]->mAvailable, mChildren[1]->mAvailable); mAvailable = std::max(mChildren[0].mAvailable, mChildren[1].mAvailable);
if (!mAvailable) { if (!mAvailable) {
DiscardChildren(); DiscardChildren();
} }
@ -60,29 +60,29 @@ Maybe<IntPoint> TexturePacker::Insert(const IntSize& aSize) {
// most excess space beyond the requested size and split it so that at least // most excess space beyond the requested size and split it so that at least
// one of the children matches the requested size for that axis. // one of the children matches the requested size for that axis.
if (mBounds.width - aSize.width > mBounds.height - aSize.height) { if (mBounds.width - aSize.width > mBounds.height - aSize.height) {
mChildren[0].reset(new TexturePacker( mChildren.reset(new TexturePacker[2]{
IntRect(mBounds.x, mBounds.y, aSize.width, mBounds.height))); TexturePacker(
mChildren[1].reset(new TexturePacker( IntRect(mBounds.x, mBounds.y, aSize.width, mBounds.height)),
IntRect(mBounds.x + aSize.width, mBounds.y, mBounds.width - aSize.width, TexturePacker(IntRect(mBounds.x + aSize.width, mBounds.y,
mBounds.height))); mBounds.width - aSize.width, mBounds.height))});
} else { } else {
mChildren[0].reset(new TexturePacker( mChildren.reset(new TexturePacker[2]{
IntRect(mBounds.x, mBounds.y, mBounds.width, aSize.height))); TexturePacker(
mChildren[1].reset(new TexturePacker( IntRect(mBounds.x, mBounds.y, mBounds.width, aSize.height)),
IntRect(mBounds.x, mBounds.y + aSize.height, mBounds.width, TexturePacker(IntRect(mBounds.x, mBounds.y + aSize.height,
mBounds.height - aSize.height))); mBounds.width, mBounds.height - aSize.height))});
} }
// After splitting, try to insert into the first child, which should usually // After splitting, try to insert into the first child, which should usually
// be big enough to accomodate the request. Adjust the available state to the // be big enough to accomodate the request. Adjust the available state to the
// remaining space. // remaining space.
Maybe<IntPoint> inserted = mChildren[0]->Insert(aSize); Maybe<IntPoint> inserted = mChildren[0].Insert(aSize);
mAvailable = std::max(mChildren[0]->mAvailable, mChildren[1]->mAvailable); mAvailable = std::max(mChildren[0].mAvailable, mChildren[1].mAvailable);
return inserted; return inserted;
} }
// Removes (frees) a rectangle with the given bounds from the tree. // Removes (frees) a rectangle with the given bounds from the tree.
bool TexturePacker::Remove(const IntRect& aBounds) { bool TexturePacker::Remove(const IntRect& aBounds) {
if (!mChildren[0]) { if (!mChildren) {
// If there are no children, we encountered a leaf node. Non-zero available // If there are no children, we encountered a leaf node. Non-zero available
// state means that this node was already removed previously. Also, if the // state means that this node was already removed previously. Also, if the
// bounds don't contain the request, and assuming the tree was previously // bounds don't contain the request, and assuming the tree was previously
@ -107,22 +107,24 @@ bool TexturePacker::Remove(const IntRect& aBounds) {
int split = aBounds.x - mBounds.x > mBounds.XMost() - aBounds.XMost() int split = aBounds.x - mBounds.x > mBounds.XMost() - aBounds.XMost()
? aBounds.x ? aBounds.x
: aBounds.XMost(); : aBounds.XMost();
mChildren[0].reset(new TexturePacker( mChildren.reset(new TexturePacker[2]{
IntRect(mBounds.x, mBounds.y, split - mBounds.x, mBounds.height), TexturePacker(
false)); IntRect(mBounds.x, mBounds.y, split - mBounds.x, mBounds.height),
mChildren[1].reset(new TexturePacker( false),
IntRect(split, mBounds.y, mBounds.XMost() - split, mBounds.height), TexturePacker(IntRect(split, mBounds.y, mBounds.XMost() - split,
false)); mBounds.height),
false)});
} else { } else {
int split = aBounds.y - mBounds.y > mBounds.YMost() - aBounds.YMost() int split = aBounds.y - mBounds.y > mBounds.YMost() - aBounds.YMost()
? aBounds.y ? aBounds.y
: aBounds.YMost(); : aBounds.YMost();
mChildren[0].reset(new TexturePacker( mChildren.reset(new TexturePacker[2]{
IntRect(mBounds.x, mBounds.y, mBounds.width, split - mBounds.y), TexturePacker(
false)); IntRect(mBounds.x, mBounds.y, mBounds.width, split - mBounds.y),
mChildren[1].reset(new TexturePacker( false),
IntRect(mBounds.x, split, mBounds.width, mBounds.YMost() - split), TexturePacker(
false)); IntRect(mBounds.x, split, mBounds.width, mBounds.YMost() - split),
false)});
} }
} }
// We've encountered a branch node. Determine which of the two child nodes // We've encountered a branch node. Determine which of the two child nodes
@ -130,16 +132,16 @@ bool TexturePacker::Remove(const IntRect& aBounds) {
// children were split on and then whether the removed bounds on that axis // children were split on and then whether the removed bounds on that axis
// are past the start of the second child. Proceed to recurse into that // are past the start of the second child. Proceed to recurse into that
// child node for removal. // child node for removal.
bool next = mChildren[0]->mBounds.x < mChildren[1]->mBounds.x bool next = mChildren[0].mBounds.x < mChildren[1].mBounds.x
? aBounds.x >= mChildren[1]->mBounds.x ? aBounds.x >= mChildren[1].mBounds.x
: aBounds.y >= mChildren[1]->mBounds.y; : aBounds.y >= mChildren[1].mBounds.y;
bool removed = mChildren[next ? 1 : 0]->Remove(aBounds); bool removed = mChildren[next ? 1 : 0].Remove(aBounds);
if (removed) { if (removed) {
if (mChildren[0]->IsFullyAvailable() && mChildren[1]->IsFullyAvailable()) { if (mChildren[0].IsFullyAvailable() && mChildren[1].IsFullyAvailable()) {
DiscardChildren(); DiscardChildren();
mAvailable = std::min(mBounds.width, mBounds.height); mAvailable = std::min(mBounds.width, mBounds.height);
} else { } else {
mAvailable = std::max(mChildren[0]->mAvailable, mChildren[1]->mAvailable); mAvailable = std::max(mChildren[0].mAvailable, mChildren[1].mAvailable);
} }
} }
return removed; return removed;

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

@ -31,17 +31,14 @@ class TexturePacker {
const IntRect& GetBounds() const { return mBounds; } const IntRect& GetBounds() const { return mBounds; }
private: private:
bool IsLeaf() const { return !mChildren[0]; } bool IsLeaf() const { return !mChildren; }
bool IsFullyAvailable() const { return IsLeaf() && mAvailable > 0; } bool IsFullyAvailable() const { return IsLeaf() && mAvailable > 0; }
void DiscardChildren() { void DiscardChildren() { mChildren.reset(); }
mChildren[0] = nullptr;
mChildren[1] = nullptr;
}
// If applicable, the two children produced by picking a single axis split // If applicable, the two children produced by picking a single axis split
// within the node's bounds and subdividing the bounds there. // within the node's bounds and subdividing the bounds there.
UniquePtr<TexturePacker> mChildren[2]; UniquePtr<TexturePacker[]> mChildren;
// The bounds enclosing this node and any children within it. // The bounds enclosing this node and any children within it.
IntRect mBounds; IntRect mBounds;
// For a leaf node, specifies the size of the smallest dimension available to // For a leaf node, specifies the size of the smallest dimension available to