* Automatically flush CommandStream When generating commands, we now automatically flush the CommandStream, so that we're guaranteed to not overrun the circular buffer. * clenaup CircularBuffer implementation and API Also fix a bug in DEBUG mode that could corrupt the CircularBuffer, it was due to a wrong debugging code attempting to clear the unused area of the buffer (this was wrong because in "ashmem" mode, there are no guaranteed unused areas). * Fix a couple threading vs. allocations - prepareVisibleLights was run on a dedicated thread (via JobSystem), but was using its own local ArenaScope. This is wrong because it could reset the root arena at any later point. This is fixed by just not using a local ArenaScope. - related to the above, the root Arena (LinearAllocatorArena) didn't use a locked policy, which cause also cause problems since some allocations are done off the main thread. We now pre-allocate the one buffer we need. This PR also renames some variable and types to improve readability. * Rework RenderPass to improve allocations and API RenderPass now is a fully immutable object that gets constructed with a RenderPassBuilder. RenderPassBuilder can be passed around and doesn't do any (major) allocations. All RenderPass allocations and heavy lifting is done in RenderPassBuilder::Build(). Additionally, RenderPass cannot be copied anymore. Where allocations happen is now much clearer. * new LinearAllocatorWithFallback LinearAllocatorWithFallback is a linear allocator that can fall back to the heap allocator. We use it for the high level command buffer to avoid crashing when running out of memory. FIXES=[277115740] * Update filament/src/RenderPass.h Co-authored-by: Powei Feng <powei@google.com> * Update libs/utils/include/utils/Allocator.h Co-authored-by: Powei Feng <powei@google.com> --------- Co-authored-by: Powei Feng <powei@google.com>
227 lines
7.4 KiB
C++
227 lines
7.4 KiB
C++
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <utils/Allocator.h>
|
|
|
|
#include <utils/compiler.h>
|
|
#include <utils/debug.h>
|
|
#include <utils/Log.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
namespace utils {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// LinearAllocator
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
LinearAllocator::LinearAllocator(void* begin, void* end) noexcept
|
|
: mBegin(begin), mSize(uintptr_t(end) - uintptr_t(begin)) {
|
|
}
|
|
|
|
LinearAllocator::LinearAllocator(LinearAllocator&& rhs) noexcept {
|
|
// class attributes have been initialized to default values
|
|
this->swap(rhs);
|
|
}
|
|
|
|
LinearAllocator& LinearAllocator::operator=(LinearAllocator&& rhs) noexcept {
|
|
if (this != &rhs) {
|
|
this->swap(rhs);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void LinearAllocator::swap(LinearAllocator& rhs) noexcept {
|
|
std::swap(mBegin, rhs.mBegin);
|
|
std::swap(mSize, rhs.mSize);
|
|
std::swap(mCur, rhs.mCur);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// LinearAllocatorWithFallback
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
void* LinearAllocatorWithFallback::alloc(size_t size, size_t alignment) {
|
|
void* p = LinearAllocator::alloc(size, alignment);
|
|
if (UTILS_UNLIKELY(!p)) {
|
|
p = HeapAllocator::alloc(size, alignment);
|
|
mHeapAllocations.push_back(p);
|
|
}
|
|
assert_invariant(p);
|
|
return p;
|
|
}
|
|
|
|
void LinearAllocatorWithFallback::reset() noexcept {
|
|
LinearAllocator::reset();
|
|
for (auto* p : mHeapAllocations) {
|
|
HeapAllocator::free(p);
|
|
}
|
|
mHeapAllocations.clear();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// FreeList
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
FreeList::Node* FreeList::init(void* begin, void* end,
|
|
size_t elementSize, size_t alignment, size_t extra) noexcept
|
|
{
|
|
void* const p = pointermath::align(begin, alignment, extra);
|
|
void* const n = pointermath::align(pointermath::add(p, elementSize), alignment, extra);
|
|
assert_invariant(p >= begin && p < end);
|
|
assert_invariant(n >= begin && n < end && n > p);
|
|
|
|
const size_t d = uintptr_t(n) - uintptr_t(p);
|
|
const size_t num = (uintptr_t(end) - uintptr_t(p)) / d;
|
|
|
|
// set first entry
|
|
Node* head = static_cast<Node*>(p);
|
|
|
|
// next entry
|
|
Node* cur = head;
|
|
for (size_t i = 1; i < num; ++i) {
|
|
Node* next = pointermath::add(cur, d);
|
|
cur->next = next;
|
|
cur = next;
|
|
}
|
|
assert_invariant(cur < end);
|
|
assert_invariant(pointermath::add(cur, d) <= end);
|
|
cur->next = nullptr;
|
|
return head;
|
|
}
|
|
|
|
FreeList::FreeList(void* begin, void* end,
|
|
size_t elementSize, size_t alignment, size_t extra) noexcept
|
|
: mHead(init(begin, end, elementSize, alignment, extra))
|
|
#ifndef NDEBUG
|
|
, mBegin(begin), mEnd(end)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
AtomicFreeList::AtomicFreeList(void* begin, void* end,
|
|
size_t elementSize, size_t alignment, size_t extra) noexcept
|
|
{
|
|
#ifdef __ANDROID__
|
|
// on some platform (e.g. web) this returns false. we really only care about mobile though.
|
|
assert_invariant(mHead.is_lock_free());
|
|
#endif
|
|
|
|
void* const p = pointermath::align(begin, alignment, extra);
|
|
void* const n = pointermath::align(pointermath::add(p, elementSize), alignment, extra);
|
|
assert_invariant(p >= begin && p < end);
|
|
assert_invariant(n >= begin && n < end && n > p);
|
|
|
|
const size_t d = uintptr_t(n) - uintptr_t(p);
|
|
const size_t num = (uintptr_t(end) - uintptr_t(p)) / d;
|
|
|
|
// set first entry
|
|
Node* head = static_cast<Node*>(p);
|
|
mStorage = head;
|
|
|
|
// next entry
|
|
Node* cur = head;
|
|
for (size_t i = 1; i < num; ++i) {
|
|
Node* next = pointermath::add(cur, d);
|
|
cur->next = next;
|
|
cur = next;
|
|
}
|
|
assert_invariant(cur < end);
|
|
assert_invariant(pointermath::add(cur, d) <= end);
|
|
cur->next = nullptr;
|
|
|
|
mHead.store({ int32_t(head - mStorage), 0 });
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
void TrackingPolicy::HighWatermark::onAlloc(
|
|
void* p, size_t size, size_t alignment, size_t extra) noexcept {
|
|
mCurrent += uint32_t(size);
|
|
mHighWaterMark = mCurrent > mHighWaterMark ? mCurrent : mHighWaterMark;
|
|
}
|
|
|
|
TrackingPolicy::HighWatermark::~HighWatermark() noexcept {
|
|
const size_t wm = mHighWaterMark;
|
|
if (mSize > 0) {
|
|
size_t wmpct = wm / (mSize / 100);
|
|
if (wmpct > 80) {
|
|
slog.d << mName << " arena: High watermark "
|
|
<< wm / 1024 << " KiB (" << wmpct << "%)" << io::endl;
|
|
}
|
|
} else {
|
|
slog.d << mName << " arena: High watermark " << wm / 1024 << " KiB" << io::endl;
|
|
}
|
|
}
|
|
|
|
void TrackingPolicy::HighWatermark::onFree(void* p, size_t size) noexcept {
|
|
// FIXME: this code is incorrect with LinearAllocators because free() is a no-op for them
|
|
assert_invariant(mCurrent >= size);
|
|
mCurrent -= uint32_t(size);
|
|
}
|
|
void TrackingPolicy::HighWatermark::onReset() noexcept {
|
|
// we should never be here if mBase is nullptr because compilation would have failed when
|
|
// Arena::onReset() tries to call the underlying allocator's onReset()
|
|
assert_invariant(mBase);
|
|
mCurrent = 0;
|
|
}
|
|
|
|
void TrackingPolicy::HighWatermark::onRewind(void const* addr) noexcept {
|
|
// we should never be here if mBase is nullptr because compilation would have failed when
|
|
// Arena::onRewind() tries to call the underlying allocator's onReset()
|
|
assert_invariant(mBase);
|
|
// for LinearAllocatorWithFallback we could get pointers outside the range
|
|
if (addr >= mBase && addr < pointermath::add(mBase, mSize)) {
|
|
mCurrent = uint32_t(uintptr_t(addr) - uintptr_t(mBase));
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
void TrackingPolicy::Debug::onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept {
|
|
if (p) {
|
|
memset(p, 0xeb, size);
|
|
}
|
|
}
|
|
|
|
void TrackingPolicy::Debug::onFree(void* p, size_t size) noexcept {
|
|
if (p) {
|
|
memset(p, 0xef, size);
|
|
}
|
|
}
|
|
|
|
void TrackingPolicy::Debug::onReset() noexcept {
|
|
// we should never be here if mBase is nullptr because compilation would have failed when
|
|
// Arena::onReset() tries to call the underlying allocator's onReset()
|
|
assert_invariant(mBase);
|
|
memset(mBase, 0xec, mSize);
|
|
}
|
|
|
|
void TrackingPolicy::Debug::onRewind(void* addr) noexcept {
|
|
// we should never be here if mBase is nullptr because compilation would have failed when
|
|
// Arena::onRewind() tries to call the underlying allocator's onReset()
|
|
assert(mBase);
|
|
assert(addr >= mBase);
|
|
memset(addr, 0x55, uintptr_t(mBase) + mSize - uintptr_t(addr));
|
|
}
|
|
|
|
} // namespace utils
|