diff options
-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 | 45 | ||||
-rw-r--r-- | libs/hwui/utils/LinearAllocator.h | 45 |
9 files changed, 257 insertions, 16 deletions
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index c92ab91..f535afb 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 51898d2..b6f0baf 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 0000000..51601b0 --- /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 166d5b6..166d5b6 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 0000000..b3959d1 --- /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 0000000..a2d6a34 --- /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 0000000..c9b9636 --- /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 index 31e439f..59b12cf 100644 --- a/libs/hwui/utils/LinearAllocator.cpp +++ b/libs/hwui/utils/LinearAllocator.cpp @@ -81,6 +81,10 @@ static void _addAllocation(size_t size) { #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 { @@ -120,6 +124,11 @@ LinearAllocator::LinearAllocator() , 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(); @@ -181,12 +190,46 @@ void* LinearAllocator::alloc(size_t 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)) { - mTotalAllocated -= allocSize; mWastedSpace += allocSize; mNext = ptr; } diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h index 6ca9f8d..d90dd82 100644 --- a/libs/hwui/utils/LinearAllocator.h +++ b/libs/hwui/utils/LinearAllocator.h @@ -27,6 +27,7 @@ #define ANDROID_LINEARALLOCATOR_H #include <stddef.h> +#include <type_traits> namespace android { namespace uirenderer { @@ -53,12 +54,43 @@ public: 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. No destructors are called. + * 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 = ""); @@ -73,7 +105,15 @@ 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); @@ -85,6 +125,7 @@ private: void* mNext; Page* mCurrentPage; Page* mPages; + DestructorNode* mDtorList = nullptr; // Memory usage tracking size_t mTotalAllocated; @@ -96,4 +137,6 @@ private: }; // namespace uirenderer }; // namespace android +void* operator new(std::size_t size, android::uirenderer::LinearAllocator& la); + #endif // ANDROID_LINEARALLOCATOR_H |