Commit 06b8d29e authored by danakj's avatar danakj Committed by Commit Bot

cc: Remove cc::ContiguousContainer.

Now that we have no DisplayItems and DisplayItemList is holding a
PaintOpBuffer, there are no consumers of cc::ContiguousContainer. The
PaintOpBuffer does a similar job for PaintOps. So we can remove this
class from the codebase.

R=weiliangc@chromium.org

Bug: 671433
Change-Id: Id11741b42bb0a75ab4a6c6db11779773741df2fd
Reviewed-on: https://chromium-review.googlesource.com/531669Reviewed-by: default avatarWeiliang Chen <weiliangc@chromium.org>
Commit-Queue: danakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#478784}
parent 7978ea6f
......@@ -708,7 +708,6 @@ cc_static_library("test_support") {
cc_test("cc_unittests") {
sources = [
"base/contiguous_container_unittest.cc",
"base/delayed_unique_notifier_unittest.cc",
"base/filter_operations_unittest.cc",
"base/float_quad_unittest.cc",
......
......@@ -10,8 +10,6 @@ component("base") {
"base_export.h",
"completion_event.h",
"container_util.h",
"contiguous_container.cc",
"contiguous_container.h",
"delayed_unique_notifier.cc",
"delayed_unique_notifier.h",
"devtools_instrumentation.cc",
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/base/contiguous_container.h"
#include <stddef.h>
#include <algorithm>
#include <utility>
namespace cc {
// Default number of max-sized elements to allocate space for, if there is no
// initial buffer.
static const unsigned kDefaultInitialBufferSize = 32;
class ContiguousContainerBase::Buffer {
public:
explicit Buffer(size_t buffer_size) : capacity_(buffer_size) {}
~Buffer() = default;
Buffer(Buffer&&) = default;
Buffer& operator=(Buffer&&) = default;
size_t Capacity() const { return capacity_; }
size_t UsedCapacity() const { return end_ - data_.get(); }
size_t UnusedCapacity() const { return Capacity() - UsedCapacity(); }
size_t MemoryUsage() const { return data_ ? capacity_ : 0; }
bool empty() const { return UsedCapacity() == 0; }
void* Allocate(size_t object_size) {
DCHECK_GE(UnusedCapacity(), object_size);
if (!data_) {
data_.reset(new char[capacity_]);
end_ = data_.get();
}
void* result = end_;
end_ += object_size;
return result;
}
private:
size_t capacity_;
std::unique_ptr<char[]> data_;
// begin() <= end_ <= begin() + capacity_
char* end_ = nullptr;
};
ContiguousContainerBase::ContiguousContainerBase(size_t max_object_size)
: max_object_size_(max_object_size) {}
ContiguousContainerBase::ContiguousContainerBase(size_t max_object_size,
size_t initial_size_bytes)
: max_object_size_(max_object_size) {
buffers_.emplace_back(std::max(max_object_size_, initial_size_bytes));
}
ContiguousContainerBase::~ContiguousContainerBase() {}
size_t ContiguousContainerBase::GetCapacityInBytes() const {
size_t capacity = 0;
for (const auto& buffer : buffers_)
capacity += buffer.Capacity();
return capacity;
}
size_t ContiguousContainerBase::UsedCapacityInBytes() const {
size_t used_capacity = 0;
for (const auto& buffer : buffers_)
used_capacity += buffer.UsedCapacity();
return used_capacity;
}
size_t ContiguousContainerBase::MemoryUsageInBytes() const {
size_t memory_usage = 0;
for (const auto& buffer : buffers_)
memory_usage += buffer.MemoryUsage();
return sizeof(*this) + memory_usage +
elements_.capacity() * sizeof(elements_[0]);
}
void* ContiguousContainerBase::Allocate(size_t object_size) {
DCHECK_LE(object_size, max_object_size_);
if (buffers_.empty())
buffers_.emplace_back(kDefaultInitialBufferSize * max_object_size_);
else if (buffers_.back().UnusedCapacity() < object_size)
buffers_.emplace_back(2 * buffers_.back().Capacity());
void* element = buffers_.back().Allocate(object_size);
elements_.push_back(element);
return element;
}
} // namespace cc
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CC_BASE_CONTIGUOUS_CONTAINER_H_
#define CC_BASE_CONTIGUOUS_CONTAINER_H_
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
#include "cc/base/base_export.h"
namespace cc {
// ContiguousContainer is a container which stores a list of heterogeneous
// objects (in particular, of varying sizes), packed next to one another in
// memory. Objects are never relocated, so it is safe to store pointers to them
// for the lifetime of the container (unless the object is removed).
//
// Memory is allocated in a series of buffers (with exponential growth). When an
// object is allocated, it is given only the space it requires (possibly with
// enough padding to preserve alignment), rather than the maximum possible size.
// This allows small and large objects to coexist without wasting much space.
//
// Since it stores pointers to all of the objects it allocates in a vector, it
// supports efficient iteration and indexing. However, for mutation the
// supported operations are limited to appending to, and removing from, the end
// of the list.
//
// Clients should instantiate ContiguousContainer; ContiguousContainerBase is an
// artifact of the implementation.
class CC_BASE_EXPORT ContiguousContainerBase {
protected:
explicit ContiguousContainerBase(size_t max_object_size);
ContiguousContainerBase(size_t max_object_size, size_t initial_size_bytes);
~ContiguousContainerBase();
size_t size() const { return elements_.size(); }
bool empty() const { return !size(); }
size_t GetCapacityInBytes() const;
size_t UsedCapacityInBytes() const;
size_t MemoryUsageInBytes() const;
// These do not invoke constructors or destructors.
void* Allocate(size_t object_size);
std::vector<void*> elements_;
private:
const size_t max_object_size_;
class Buffer;
std::vector<Buffer> buffers_;
DISALLOW_COPY_AND_ASSIGN(ContiguousContainerBase);
};
// For most cases, no alignment stricter than pointer alignment is required. If
// one of the derived classes has stronger alignment requirements (and the
// static_assert fires), set alignment to the least common multiple of the
// derived class alignments. For small structs without pointers, it may be
// possible to reduce alignment for tighter packing.
template <class BaseElementType, unsigned alignment = sizeof(void*)>
class ContiguousContainer : public ContiguousContainerBase {
private:
// Declares itself as a forward iterator, but also supports a few more
// things. The whole random access iterator interface is a bit much.
template <typename BaseIterator, typename ValueType>
class IteratorWrapper
: public std::iterator<std::forward_iterator_tag, ValueType> {
public:
IteratorWrapper() {}
bool operator==(const IteratorWrapper& other) const {
return it_ == other.it_;
}
bool operator!=(const IteratorWrapper& other) const {
return it_ != other.it_;
}
ValueType& operator*() const { return *static_cast<ValueType*>(*it_); }
ValueType* operator->() const { return &operator*(); }
IteratorWrapper operator+(std::ptrdiff_t n) const {
return IteratorWrapper(it_ + n);
}
IteratorWrapper operator++(int) {
IteratorWrapper tmp = *this;
++it_;
return tmp;
}
std::ptrdiff_t operator-(const IteratorWrapper& other) const {
return it_ - other.it_;
}
IteratorWrapper& operator++() {
++it_;
return *this;
}
private:
explicit IteratorWrapper(const BaseIterator& it) : it_(it) {}
BaseIterator it_;
friend class ContiguousContainer;
};
public:
using iterator =
IteratorWrapper<std::vector<void*>::iterator, BaseElementType>;
using const_iterator = IteratorWrapper<std::vector<void*>::const_iterator,
const BaseElementType>;
using reverse_iterator =
IteratorWrapper<std::vector<void*>::reverse_iterator, BaseElementType>;
using const_reverse_iterator =
IteratorWrapper<std::vector<void*>::const_reverse_iterator,
const BaseElementType>;
explicit ContiguousContainer(size_t max_object_size)
: ContiguousContainerBase(Align(max_object_size)) {}
ContiguousContainer(size_t max_object_size, size_t initial_size_bytes)
: ContiguousContainerBase(Align(max_object_size), initial_size_bytes) {}
DISABLE_CFI_PERF
~ContiguousContainer() {
for (auto& element : *this) {
// MSVC incorrectly reports this variable as unused.
(void)element;
element.~BaseElementType();
}
}
using ContiguousContainerBase::size;
using ContiguousContainerBase::empty;
using ContiguousContainerBase::GetCapacityInBytes;
using ContiguousContainerBase::UsedCapacityInBytes;
using ContiguousContainerBase::MemoryUsageInBytes;
iterator begin() { return iterator(elements_.begin()); }
iterator end() { return iterator(elements_.end()); }
const_iterator begin() const { return const_iterator(elements_.begin()); }
const_iterator end() const { return const_iterator(elements_.end()); }
reverse_iterator rbegin() { return reverse_iterator(elements_.rbegin()); }
reverse_iterator rend() { return reverse_iterator(elements_.rend()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(elements_.rbegin());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(elements_.rend());
}
BaseElementType& first() { return *begin(); }
const BaseElementType& first() const { return *begin(); }
BaseElementType& last() { return *rbegin(); }
const BaseElementType& last() const { return *rbegin(); }
BaseElementType& operator[](size_t index) { return *(begin() + index); }
const BaseElementType& operator[](size_t index) const {
return *(begin() + index);
}
template <class DerivedElementType, typename... Args>
DerivedElementType& AllocateAndConstruct(Args&&... args) {
static_assert(alignment % ALIGNOF(DerivedElementType) == 0,
"Derived type requires stronger alignment.");
return *new (AlignedAllocate(sizeof(DerivedElementType)))
DerivedElementType(std::forward<Args>(args)...);
}
private:
void* AlignedAllocate(size_t size) {
void* result = ContiguousContainerBase::Allocate(Align(size));
DCHECK_EQ(reinterpret_cast<intptr_t>(result) & (alignment - 1), 0u);
return result;
}
size_t Align(size_t size) {
size_t aligned_size = alignment * ((size + alignment - 1) / alignment);
DCHECK_EQ(aligned_size % alignment, 0u);
DCHECK_GE(aligned_size, size);
DCHECK_LT(aligned_size, size + alignment);
return aligned_size;
}
};
} // namespace cc
#endif // CC_BASE_CONTIGUOUS_CONTAINER_H_
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <iterator>
#include "cc/base/contiguous_container.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
struct Point2D {
Point2D() : Point2D(0, 0) {}
Point2D(int x, int y) : x(x), y(y) {}
int x, y;
};
struct Point3D : public Point2D {
Point3D() : Point3D(0, 0, 0) {}
Point3D(int x, int y, int z) : Point2D(x, y), z(z) {}
int z;
};
// Maximum size of a subclass of Point2D.
static const size_t kMaxPointSize = sizeof(Point3D);
// Alignment for Point2D and its subclasses.
static const size_t kPointAlignment = sizeof(int);
// How many elements to use for tests with "plenty" of elements.
static const size_t kNumElements = 150;
TEST(ContiguousContainerTest, SimpleStructs) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
list.AllocateAndConstruct<Point2D>(1, 2);
list.AllocateAndConstruct<Point3D>(3, 4, 5);
list.AllocateAndConstruct<Point2D>(6, 7);
ASSERT_EQ(3u, list.size());
EXPECT_EQ(1, list[0].x);
EXPECT_EQ(2, list[0].y);
EXPECT_EQ(3, list[1].x);
EXPECT_EQ(4, list[1].y);
EXPECT_EQ(5, static_cast<Point3D&>(list[1]).z);
EXPECT_EQ(6, list[2].x);
EXPECT_EQ(7, list[2].y);
}
TEST(ContiguousContainerTest, AllocateLots) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
for (int i = 0; i < (int)kNumElements; i++)
list.AllocateAndConstruct<Point2D>(i, i);
ASSERT_EQ(kNumElements, list.size());
for (int i = 0; i < (int)kNumElements; i++) {
ASSERT_EQ(i, list[i].x);
ASSERT_EQ(i, list[i].y);
}
}
class MockDestructible {
public:
~MockDestructible() { Destruct(); }
MOCK_METHOD0(Destruct, void());
};
TEST(ContiguousContainerTest, DestructorCalled) {
ContiguousContainer<MockDestructible> list(sizeof(MockDestructible));
auto& destructible = list.AllocateAndConstruct<MockDestructible>();
EXPECT_EQ(&destructible, &list.first());
EXPECT_CALL(destructible, Destruct());
}
TEST(ContiguousContainerTest, InsertionAndIndexedAccess) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
auto& point1 = list.AllocateAndConstruct<Point2D>();
auto& point2 = list.AllocateAndConstruct<Point2D>();
auto& point3 = list.AllocateAndConstruct<Point2D>();
EXPECT_EQ(3u, list.size());
EXPECT_EQ(&point1, &list.first());
EXPECT_EQ(&point3, &list.last());
EXPECT_EQ(&point1, &list[0]);
EXPECT_EQ(&point2, &list[1]);
EXPECT_EQ(&point3, &list[2]);
}
TEST(ContiguousContainerTest, ElementAddressesAreStable) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
std::vector<Point2D*> pointers;
for (int i = 0; i < (int)kNumElements; i++)
pointers.push_back(&list.AllocateAndConstruct<Point2D>());
EXPECT_EQ(kNumElements, list.size());
EXPECT_EQ(kNumElements, pointers.size());
auto listIt = list.begin();
auto vectorIt = pointers.begin();
for (; listIt != list.end(); ++listIt, ++vectorIt)
EXPECT_EQ(&*listIt, *vectorIt);
}
TEST(ContiguousContainerTest, ForwardIteration) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
for (int i = 0; i < (int)kNumElements; i++)
list.AllocateAndConstruct<Point2D>(i, i);
unsigned count = 0;
for (Point2D& point : list) {
EXPECT_EQ((int)count, point.x);
count++;
}
EXPECT_EQ(kNumElements, count);
static_assert(std::is_same<decltype(*list.begin()), Point2D&>::value,
"Non-const iteration should produce non-const references.");
}
TEST(ContiguousContainerTest, ForwardIterationEmpty) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
// ContiguousContainer allocates memory for elements lazily at first append
// operation, so at this point memory is not allocated. Check that iteration
// doesn't crash and produces zero elements.
EXPECT_EQ(0, std::distance(list.begin(), list.end()));
}
TEST(ContiguousContainerTest, ConstForwardIteration) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
for (int i = 0; i < (int)kNumElements; i++)
list.AllocateAndConstruct<Point2D>(i, i);
const auto& const_list = list;
unsigned count = 0;
for (const Point2D& point : const_list) {
EXPECT_EQ((int)count, point.x);
count++;
}
EXPECT_EQ(kNumElements, count);
static_assert(
std::is_same<decltype(*const_list.begin()), const Point2D&>::value,
"Const iteration should produce const references.");
}
TEST(ContiguousContainerTest, ReverseIteration) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
for (int i = 0; i < (int)kNumElements; i++)
list.AllocateAndConstruct<Point2D>(i, i);
unsigned count = 0;
for (auto it = list.rbegin(); it != list.rend(); ++it) {
EXPECT_EQ((int)(kNumElements - 1 - count), it->x);
count++;
}
EXPECT_EQ(kNumElements, count);
static_assert(std::is_same<decltype(*list.rbegin()), Point2D&>::value,
"Non-const iteration should produce non-const references.");
}
TEST(ContiguousContainerTest, CapacityInBytes) {
const int iterations = 500;
const size_t initial_capacity = 10 * kMaxPointSize;
const size_t upper_bound_on_min_capacity = initial_capacity;
// At time of writing, removing elements from the end can cause up to 7x the
// memory required to be consumed, in the worst case, since we can have up to
// two trailing inner lists that are empty (for 2*size + 4*size in unused
// memory, due to the exponential growth strategy).
// Unfortunately, this captures behaviour of the underlying allocator as
// well as this container, so we're pretty loose here. This constant may
// need to be adjusted.
const size_t max_waste_factor = 8;
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize,
initial_capacity);
// The capacity should grow with the list.
for (int i = 0; i < iterations; i++) {
size_t capacity = list.GetCapacityInBytes();
ASSERT_GE(capacity, list.size() * sizeof(Point2D));
ASSERT_LE(capacity, std::max(list.size() * sizeof(Point2D),
upper_bound_on_min_capacity) *
max_waste_factor);
list.AllocateAndConstruct<Point2D>();
}
}
TEST(ContiguousContainerTest, MemoryUsageInBytes) {
constexpr size_t initial_size1 = 10 * kMaxPointSize;
ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize,
initial_size1);
constexpr size_t initial_size2 = 10000 * kMaxPointSize;
ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize,
initial_size2);
// Memory is allocated lazily, so even though lists were created with
// different initial_size values, they'll have the same memory usage here.
size_t memory_usage1 = list1.MemoryUsageInBytes();
size_t memory_usage2 = list2.MemoryUsageInBytes();
EXPECT_EQ(memory_usage1, memory_usage2);
// Trigger memory allocation.
list1.AllocateAndConstruct<Point2D>();
list2.AllocateAndConstruct<Point2D>();
// Same object was created in both lists, but their memory usages grew
// differently, based on initial_size values lists were created with.
EXPECT_NE(list1.MemoryUsageInBytes(), list2.MemoryUsageInBytes());
EXPECT_GE(list1.MemoryUsageInBytes(), memory_usage1 + initial_size1);
EXPECT_GE(list2.MemoryUsageInBytes(), memory_usage2 + initial_size2);
}
TEST(ContiguousContainerTest, Alignment) {
const size_t max_align = ALIGNOF(long double);
ContiguousContainer<Point2D, max_align> list(kMaxPointSize);
list.AllocateAndConstruct<Point2D>();
EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.last()) & (max_align - 1));
list.AllocateAndConstruct<Point2D>();
EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.last()) & (max_align - 1));
list.AllocateAndConstruct<Point3D>();
EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.last()) & (max_align - 1));
list.AllocateAndConstruct<Point3D>();
EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.last()) & (max_align - 1));
list.AllocateAndConstruct<Point2D>();
EXPECT_EQ(0u, reinterpret_cast<intptr_t>(&list.last()) & (max_align - 1));
}
} // namespace
} // namespace cc
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment