diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/Android.common.mk | 1 | ||||
| -rw-r--r-- | libs/hwui/DeferredDisplayList.h | 2 | ||||
| -rw-r--r-- | libs/hwui/tests/Android.mk | 13 | ||||
| -rw-r--r-- | libs/hwui/unit_tests/Android.mk | 34 | ||||
| -rw-r--r-- | libs/hwui/unit_tests/ClipAreaTests.cpp (renamed from libs/hwui/tests/ClipAreaTests.cpp) | 0 | ||||
| -rw-r--r-- | libs/hwui/unit_tests/LinearAllocatorTests.cpp | 108 | ||||
| -rwxr-xr-x | libs/hwui/unit_tests/how_to_run.txt | 4 | ||||
| -rw-r--r-- | libs/hwui/unit_tests/main.cpp | 22 | ||||
| -rw-r--r-- | libs/hwui/utils/LinearAllocator.cpp | 272 | ||||
| -rw-r--r-- | libs/hwui/utils/LinearAllocator.h | 142 |
10 files changed, 584 insertions, 14 deletions
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk index 5fca8ec9a358..836f86875dcd 100644 --- a/libs/hwui/Android.common.mk +++ b/libs/hwui/Android.common.mk @@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \ thread/TaskManager.cpp \ utils/Blur.cpp \ utils/GLUtils.cpp \ + utils/LinearAllocator.cpp \ utils/SortedListImpl.cpp \ AmbientShadow.cpp \ AnimationContext.cpp \ diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index c92ab91d21bf..f535afb2d61a 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -127,7 +127,7 @@ private: } void tryRecycleState(DeferredDisplayState* state) { - mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState)); + mAllocator.rewindIfLastAlloc(state); } /** diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk index 51898d2a1d22..b6f0baf4bf3e 100644 --- a/libs/hwui/tests/Android.mk +++ b/libs/hwui/tests/Android.mk @@ -34,16 +34,3 @@ LOCAL_SRC_FILES += \ tests/main.cpp include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) - -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk -LOCAL_MODULE := hwui_unit_tests -LOCAL_MODULE_TAGS := tests - -include $(LOCAL_PATH)/Android.common.mk - -LOCAL_SRC_FILES += \ - tests/ClipAreaTests.cpp \ - -include $(BUILD_NATIVE_TEST) diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk new file mode 100644 index 000000000000..51601b072405 --- /dev/null +++ b/libs/hwui/unit_tests/Android.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2014 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. +# + +local_target_dir := $(TARGET_OUT_DATA)/local/tmp +LOCAL_PATH:= $(call my-dir)/.. + +include $(CLEAR_VARS) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk +LOCAL_MODULE := hwui_unit_tests +LOCAL_MODULE_TAGS := tests + +include $(LOCAL_PATH)/Android.common.mk + +LOCAL_SRC_FILES += \ + unit_tests/ClipAreaTests.cpp \ + unit_tests/LinearAllocatorTests.cpp \ + unit_tests/main.cpp + + +include $(BUILD_NATIVE_TEST) diff --git a/libs/hwui/tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp index 166d5b6e92f6..166d5b6e92f6 100644 --- a/libs/hwui/tests/ClipAreaTests.cpp +++ b/libs/hwui/unit_tests/ClipAreaTests.cpp diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp new file mode 100644 index 000000000000..b3959d169e1d --- /dev/null +++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp @@ -0,0 +1,108 @@ +/* + * 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 <gtest/gtest.h> +#include <utils/LinearAllocator.h> + +using namespace android; +using namespace android::uirenderer; + +struct SimplePair { + int one = 1; + int two = 2; +}; + +class SignalingDtor { +public: + SignalingDtor() { + mDestroyed = nullptr; + } + SignalingDtor(bool* destroyedSignal) { + mDestroyed = destroyedSignal; + *mDestroyed = false; + } + virtual ~SignalingDtor() { + if (mDestroyed) { + *mDestroyed = true; + } + } + void setSignal(bool* destroyedSignal) { + mDestroyed = destroyedSignal; + } +private: + bool* mDestroyed; +}; + +TEST(LinearAllocator, alloc) { + LinearAllocator la; + EXPECT_EQ(0u, la.usedSize()); + la.alloc(64); + // There's some internal tracking as well as padding + // so the usedSize isn't strictly defined + EXPECT_LE(64u, la.usedSize()); + EXPECT_GT(80u, la.usedSize()); + auto pair = la.alloc<SimplePair>(); + EXPECT_LE(64u + sizeof(SimplePair), la.usedSize()); + EXPECT_GT(80u + sizeof(SimplePair), la.usedSize()); + EXPECT_EQ(1, pair->one); + EXPECT_EQ(2, pair->two); +} + +TEST(LinearAllocator, dtor) { + bool destroyed[10]; + { + LinearAllocator la; + for (int i = 0; i < 5; i++) { + la.alloc<SignalingDtor>()->setSignal(destroyed + i); + la.alloc<SimplePair>(); + } + la.alloc(100); + for (int i = 0; i < 5; i++) { + auto sd = new (la) SignalingDtor(destroyed + 5 + i); + la.autoDestroy(sd); + new (la) SimplePair(); + } + la.alloc(100); + for (int i = 0; i < 10; i++) { + EXPECT_FALSE(destroyed[i]); + } + } + for (int i = 0; i < 10; i++) { + EXPECT_TRUE(destroyed[i]); + } +} + +TEST(LinearAllocator, rewind) { + bool destroyed; + { + LinearAllocator la; + auto addr = la.alloc(100); + EXPECT_LE(100u, la.usedSize()); + la.rewindIfLastAlloc(addr, 100); + EXPECT_GT(16u, la.usedSize()); + size_t emptySize = la.usedSize(); + auto sigdtor = la.alloc<SignalingDtor>(); + sigdtor->setSignal(&destroyed); + EXPECT_FALSE(destroyed); + EXPECT_LE(emptySize, la.usedSize()); + la.rewindIfLastAlloc(sigdtor); + EXPECT_TRUE(destroyed); + EXPECT_EQ(emptySize, la.usedSize()); + destroyed = false; + } + // Checking for a double-destroy case + EXPECT_EQ(destroyed, false); +} diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt new file mode 100755 index 000000000000..a2d6a34726df --- /dev/null +++ b/libs/hwui/unit_tests/how_to_run.txt @@ -0,0 +1,4 @@ +mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests && +adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \ + /data/nativetest/hwui_unit_tests/hwui_unit_tests && +adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests diff --git a/libs/hwui/unit_tests/main.cpp b/libs/hwui/unit_tests/main.cpp new file mode 100644 index 000000000000..c9b96360b36b --- /dev/null +++ b/libs/hwui/unit_tests/main.cpp @@ -0,0 +1,22 @@ +/* + * 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 <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp new file mode 100644 index 000000000000..59b12cf66a89 --- /dev/null +++ b/libs/hwui/utils/LinearAllocator.cpp @@ -0,0 +1,272 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_NDEBUG 1 + +#include "utils/LinearAllocator.h" + +#include <stdlib.h> +#include <utils/Log.h> + + +// The ideal size of a page allocation (these need to be multiples of 8) +#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb +#define MAX_PAGE_SIZE ((size_t)131072) // 128kb + +// The maximum amount of wasted space we can have per page +// Allocations exceeding this will have their own dedicated page +// If this is too low, we will malloc too much +// Too high, and we may waste too much space +// Must be smaller than INITIAL_PAGE_SIZE +#define MAX_WASTE_SIZE ((size_t)1024) + +#if ALIGN_DOUBLE +#define ALIGN_SZ (sizeof(double)) +#else +#define ALIGN_SZ (sizeof(int)) +#endif + +#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) +#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) + +#if LOG_NDEBUG +#define ADD_ALLOCATION(size) +#define RM_ALLOCATION(size) +#else +#include <utils/Thread.h> +#include <utils/Timers.h> +static size_t s_totalAllocations = 0; +static nsecs_t s_nextLog = 0; +static android::Mutex s_mutex; + +static void _logUsageLocked() { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now > s_nextLog) { + s_nextLog = now + milliseconds_to_nanoseconds(10); + ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); + } +} + +static void _addAllocation(size_t size) { + android::AutoMutex lock(s_mutex); + s_totalAllocations += size; + _logUsageLocked(); +} + +#define ADD_ALLOCATION(size) _addAllocation(size); +#define RM_ALLOCATION(size) _addAllocation(-size); +#endif + +#define min(x,y) (((x) < (y)) ? (x) : (y)) + +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la) { + return la.alloc(size); +} + +namespace android { +namespace uirenderer { + +class LinearAllocator::Page { +public: + Page* next() { return mNextPage; } + void setNext(Page* next) { mNextPage = next; } + + Page() + : mNextPage(0) + {} + + void* operator new(size_t /*size*/, void* buf) { return buf; } + + void* start() { + return (void*) (((size_t)this) + sizeof(Page)); + } + + void* end(int pageSize) { + return (void*) (((size_t)start()) + pageSize); + } + +private: + Page(const Page& /*other*/) {} + Page* mNextPage; +}; + +LinearAllocator::LinearAllocator() + : mPageSize(INITIAL_PAGE_SIZE) + , mMaxAllocSize(MAX_WASTE_SIZE) + , mNext(0) + , mCurrentPage(0) + , mPages(0) + , mTotalAllocated(0) + , mWastedSpace(0) + , mPageCount(0) + , mDedicatedPageCount(0) {} + +LinearAllocator::~LinearAllocator(void) { + while (mDtorList) { + auto node = mDtorList; + mDtorList = node->next; + node->dtor(node->addr); + } + Page* p = mPages; + while (p) { + Page* next = p->next(); + p->~Page(); + free(p); + RM_ALLOCATION(mPageSize); + p = next; + } +} + +void* LinearAllocator::start(Page* p) { + return ALIGN_PTR(((size_t*)p) + sizeof(Page)); +} + +void* LinearAllocator::end(Page* p) { + return ((char*)p) + mPageSize; +} + +bool LinearAllocator::fitsInCurrentPage(size_t size) { + return mNext && ((char*)mNext + size) <= end(mCurrentPage); +} + +void LinearAllocator::ensureNext(size_t size) { + if (fitsInCurrentPage(size)) return; + + if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { + mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); + mPageSize = ALIGN(mPageSize); + } + mWastedSpace += mPageSize; + Page* p = newPage(mPageSize); + if (mCurrentPage) { + mCurrentPage->setNext(p); + } + mCurrentPage = p; + if (!mPages) { + mPages = mCurrentPage; + } + mNext = start(mCurrentPage); +} + +void* LinearAllocator::alloc(size_t size) { + size = ALIGN(size); + if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { + ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); + // Allocation is too large, create a dedicated page for the allocation + Page* page = newPage(size); + mDedicatedPageCount++; + page->setNext(mPages); + mPages = page; + if (!mCurrentPage) + mCurrentPage = mPages; + return start(page); + } + ensureNext(size); + void* ptr = mNext; + mNext = ((char*)mNext) + size; + mWastedSpace -= size; + return ptr; +} + +void LinearAllocator::addToDestructionList(Destructor dtor, void* addr) { + static_assert(std::is_standard_layout<DestructorNode>::value, + "DestructorNode must have standard layout"); + static_assert(std::is_trivially_destructible<DestructorNode>::value, + "DestructorNode must be trivially destructable"); + auto node = new (*this) DestructorNode(); + node->dtor = dtor; + node->addr = addr; + node->next = mDtorList; + mDtorList = node; +} + +void LinearAllocator::runDestructorFor(void* addr) { + auto node = mDtorList; + DestructorNode* previous = nullptr; + while (node) { + if (node->addr == addr) { + if (previous) { + previous->next = node->next; + } else { + mDtorList = node->next; + } + node->dtor(node->addr); + rewindIfLastAlloc(node, sizeof(DestructorNode)); + break; + } + previous = node; + node = node->next; + } +} + +void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { + // First run the destructor as running the destructor will + // also rewind for the DestructorNode allocation which will + // have been allocated after this void* if it has a destructor + runDestructorFor(ptr); + // Don't bother rewinding across pages + allocSize = ALIGN(allocSize); + if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) + && ptr == ((char*)mNext - allocSize)) { + mWastedSpace += allocSize; + mNext = ptr; + } +} + +LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { + pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); + ADD_ALLOCATION(pageSize); + mTotalAllocated += pageSize; + mPageCount++; + void* buf = malloc(pageSize); + return new (buf) Page(); +} + +static const char* toSize(size_t value, float& result) { + if (value < 2000) { + result = value; + return "B"; + } + if (value < 2000000) { + result = value / 1024.0f; + return "KB"; + } + result = value / 1048576.0f; + return "MB"; +} + +void LinearAllocator::dumpMemoryStats(const char* prefix) { + float prettySize; + const char* prettySuffix; + prettySuffix = toSize(mTotalAllocated, prettySize); + ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); + prettySuffix = toSize(mWastedSpace, prettySize); + ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, + (float) mWastedSpace / (float) mTotalAllocated * 100.0f); + ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h new file mode 100644 index 000000000000..d90dd825ea1d --- /dev/null +++ b/libs/hwui/utils/LinearAllocator.h @@ -0,0 +1,142 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANDROID_LINEARALLOCATOR_H +#define ANDROID_LINEARALLOCATOR_H + +#include <stddef.h> +#include <type_traits> + +namespace android { +namespace uirenderer { + +/** + * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids + * the overhead of malloc when many objects are allocated. It is most useful when creating many + * small objects with a similar lifetime, and doesn't add significant overhead for large + * allocations. + */ +class LinearAllocator { +public: + LinearAllocator(); + ~LinearAllocator(); + + /** + * Reserves and returns a region of memory of at least size 'size', aligning as needed. + * Typically this is used in an object's overridden new() method or as a replacement for malloc. + * + * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling + * delete() on an object stored in a buffer is needed, it should be overridden to use + * rewindIfLastAlloc() + */ + void* alloc(size_t size); + + /** + * Allocates an instance of the template type with the default constructor + * and adds it to the automatic destruction list. + */ + template<class T> + T* alloc() { + T* ret = new (*this) T; + autoDestroy(ret); + return ret; + } + + /** + * Adds the pointer to the tracking list to have its destructor called + * when the LinearAllocator is destroyed. + */ + template<class T> + void autoDestroy(T* addr) { + if (!std::is_trivially_destructible<T>::value) { + auto dtor = [](void* addr) { ((T*)addr)->~T(); }; + addToDestructionList(dtor, addr); + } + } + + /** + * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its + * state if possible. + */ + void rewindIfLastAlloc(void* ptr, size_t allocSize); + + /** + * Same as rewindIfLastAlloc(void*, size_t) + */ + template<class T> + void rewindIfLastAlloc(T* ptr) { + rewindIfLastAlloc((void*)ptr, sizeof(T)); + } + + /** + * Dump memory usage statistics to the log (allocated and wasted space) + */ + void dumpMemoryStats(const char* prefix = ""); + + /** + * The number of bytes used for buffers allocated in the LinearAllocator (does not count space + * wasted) + */ + size_t usedSize() const { return mTotalAllocated - mWastedSpace; } + +private: + LinearAllocator(const LinearAllocator& other); + + class Page; + typedef void (*Destructor)(void* addr); + struct DestructorNode { + Destructor dtor; + void* addr; + DestructorNode* next = nullptr; + }; + + void addToDestructionList(Destructor, void* addr); + void runDestructorFor(void* addr); + Page* newPage(size_t pageSize); + bool fitsInCurrentPage(size_t size); + void ensureNext(size_t size); + void* start(Page *p); + void* end(Page* p); + + size_t mPageSize; + size_t mMaxAllocSize; + void* mNext; + Page* mCurrentPage; + Page* mPages; + DestructorNode* mDtorList = nullptr; + + // Memory usage tracking + size_t mTotalAllocated; + size_t mWastedSpace; + size_t mPageCount; + size_t mDedicatedPageCount; +}; + +}; // namespace uirenderer +}; // namespace android + +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la); + +#endif // ANDROID_LINEARALLOCATOR_H |