Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion impeller/base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ impeller_component("base") {
sources = [
"allocation.cc",
"allocation.h",
"allocation_size.cc",
"allocation_size.h",
"backend_cast.h",
"comparable.cc",
"comparable.h",
Expand All @@ -33,7 +35,10 @@ impeller_component("base") {

impeller_component("base_unittests") {
testonly = true
sources = [ "base_unittests.cc" ]
sources = [
"allocation_size_unittests.cc",
"base_unittests.cc",
]
deps = [
":base",
"//flutter/testing",
Expand Down
1 change: 1 addition & 0 deletions impeller/base/allocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <memory>

#include "flutter/fml/mapping.h"
#include "impeller/base/allocation_size.h"

namespace impeller {

Expand Down
11 changes: 11 additions & 0 deletions impeller/base/allocation_size.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2013 The Flutter 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 "impeller/base/allocation_size.h"

namespace impeller {

//

} // namespace impeller
186 changes: 186 additions & 0 deletions impeller/base/allocation_size.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright 2013 The Flutter 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 FLUTTER_IMPELLER_BASE_ALLOCATION_SIZE_H_
#define FLUTTER_IMPELLER_BASE_ALLOCATION_SIZE_H_

#include <cstddef>
#include <cstdint>

namespace impeller {

enum class FromBytesTag { kFromBytes };

//------------------------------------------------------------------------------
/// @brief Represents the size of an allocation in different units.
///
/// Refer to the typedefs for Bytes, KiloBytes, MegaBytes,
/// Gigabytes, KibiBytes, MebiBytes, and GigiBytes below when using.
///
/// Storage and all operations are always on unsigned units of
/// bytes.
///
/// @tparam Period The number of bytes in 1 unit of the allocation size.
///
template <size_t Period>
class AllocationSize {
public:
//----------------------------------------------------------------------------
/// @brief Create an allocation size with the amount in the `Period`
/// number of bytes.
///
/// @param[in] size The size in `Period` number of bytes.
///
explicit constexpr AllocationSize(double size) : bytes_(size * Period) {}

//----------------------------------------------------------------------------
/// @brief Create an allocation size from another instance with a
/// different period.
///
/// @param[in] other The other allocation size.
///
/// @tparam OtherPeriod The period of the other allocation.
///
template <size_t OtherPeriod>
explicit constexpr AllocationSize(const AllocationSize<OtherPeriod>& other)
: bytes_(other.GetByteSize()) {}

//----------------------------------------------------------------------------
/// @brief Create an allocation size with the amount directly specified
/// in bytes.
///
/// @param[in] byte_size The byte size.
/// @param[in] tag A tag for this constructor.
///
constexpr AllocationSize(uint64_t byte_size, FromBytesTag)
: bytes_(byte_size) {}

//----------------------------------------------------------------------------
/// @return The byte size.
///
constexpr uint64_t GetByteSize() const { return bytes_; }

//----------------------------------------------------------------------------
/// @return The number of `Periods` of bytes.
///
constexpr double GetSize() const {
return GetByteSize() / static_cast<double>(Period);
}

//----------------------------------------------------------------------------
/// @brief Convert the allocation size from one unit to another.
///
/// Conversions are non-truncating.
///
/// @tparam AllocationSize The allocation size to convert to.
///
/// @return The new allocation size.
///
template <class AllocationSize>
constexpr AllocationSize ConvertTo() {
return AllocationSize{GetByteSize(), FromBytesTag::kFromBytes};
}

// The following relational operators can be replaced with a defaulted
// spaceship operator post C++20.

constexpr bool operator<(const AllocationSize& other) const {
return bytes_ < other.bytes_;
}

constexpr bool operator>(const AllocationSize& other) const {
return bytes_ > other.bytes_;
}

constexpr bool operator>=(const AllocationSize& other) const {
return bytes_ >= other.bytes_;
}

constexpr bool operator<=(const AllocationSize& other) const {
return bytes_ <= other.bytes_;
}

constexpr bool operator==(const AllocationSize& other) const {
return bytes_ == other.bytes_;
}

constexpr bool operator!=(const AllocationSize& other) const {
return bytes_ != other.bytes_;
}

// Explicit casts.

explicit constexpr operator bool() const { return bytes_ != 0u; }

// Arithmetic operators (overflows are caller error).

constexpr AllocationSize operator+(const AllocationSize& other) const {
return AllocationSize(bytes_ + other.GetByteSize(),
FromBytesTag::kFromBytes);
}

constexpr AllocationSize operator-(const AllocationSize& other) const {
return AllocationSize(bytes_ - other.GetByteSize(),
FromBytesTag::kFromBytes);
}

constexpr AllocationSize& operator+=(const AllocationSize& other) {
bytes_ += other.GetByteSize();
return *this;
}

constexpr AllocationSize& operator-=(const AllocationSize& other) {
bytes_ -= other.GetByteSize();
return *this;
}

private:
uint64_t bytes_ = {};
};

using Bytes = AllocationSize<1u>;

using KiloBytes = AllocationSize<1'000u>;
using MegaBytes = AllocationSize<1'000u * 1'000u>;
using GigaBytes = AllocationSize<1'000u * 1'000u * 1'000u>;

using KibiBytes = AllocationSize<1'024u>;
using MebiBytes = AllocationSize<1'024u * 1'024u>;
using GigiBytes = AllocationSize<1'024u * 1'024u * 1'024u>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not gigibytes, but gibibytes :) You're probably going to need to change it across the PR, since the typo came up in a few places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

derp. Good catch. Fixed everywhere.


inline namespace allocation_size_literals {

constexpr Bytes operator"" _bytes(unsigned long long size) {
return Bytes{static_cast<double>(size)};
}

constexpr KiloBytes operator"" _kb(unsigned long long size) {
return KiloBytes{static_cast<double>(size)};
}

constexpr MegaBytes operator"" _mb(unsigned long long size) {
return MegaBytes{static_cast<double>(size)};
}

constexpr GigaBytes operator"" _gb(unsigned long long size) {
return GigaBytes{static_cast<double>(size)};
}

constexpr KibiBytes operator"" _kib(unsigned long long size) {
return KibiBytes{static_cast<double>(size)};
}

constexpr MebiBytes operator"" _mib(unsigned long long size) {
return MebiBytes{static_cast<double>(size)};
}

constexpr GigiBytes operator"" _gib(unsigned long long size) {
return GigiBytes{static_cast<double>(size)};
}

} // namespace allocation_size_literals

} // namespace impeller

#endif // FLUTTER_IMPELLER_BASE_ALLOCATION_SIZE_H_
110 changes: 110 additions & 0 deletions impeller/base/allocation_size_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2013 The Flutter 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 "flutter/testing/testing.h"
#include "impeller/base/allocation_size.h"

namespace impeller::testing {

TEST(AllocationSizeTest, CanCreateTypedAllocations) {
auto bytes = Bytes{1024};
ASSERT_EQ(bytes.GetByteSize(), 1024u);

auto kilobytes = KiloBytes{5};
ASSERT_EQ(kilobytes.GetByteSize(), 5u * 1e3);

auto megabytes = MegaBytes{5};
ASSERT_EQ(megabytes.GetByteSize(), 5u * 1e6);

auto gigabytes = GigaBytes{5};
ASSERT_EQ(gigabytes.GetByteSize(), 5u * 1e9);

auto kibibytes = KibiBytes{1};
ASSERT_EQ(kibibytes.GetByteSize(), 1024u);

auto mebibytes = MebiBytes{1};
ASSERT_EQ(mebibytes.GetByteSize(), 1048576u);

auto gigibytes = GigiBytes{1};
ASSERT_EQ(gigibytes.GetByteSize(), 1073741824u);
}

TEST(AllocationSizeTest, CanCreateTypedAllocationsWithLiterals) {
using namespace allocation_size_literals;
ASSERT_EQ((1024_bytes).GetByteSize(), 1024u);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooo!

ASSERT_EQ((5_kb).GetByteSize(), 5u * 1e3);
ASSERT_EQ((5_mb).GetByteSize(), 5u * 1e6);
ASSERT_EQ((5_gb).GetByteSize(), 5u * 1e9);
ASSERT_EQ((1_kib).GetByteSize(), 1024u);
ASSERT_EQ((1_mib).GetByteSize(), 1048576u);
ASSERT_EQ((1_gib).GetByteSize(), 1073741824u);
}

TEST(AllocationSizeTest, CanConvert) {
using namespace allocation_size_literals;
ASSERT_EQ((5_gb).ConvertTo<MegaBytes>().GetSize(), 5000u);
}

TEST(AllocationSizeTest, ConversionsAreNonTruncating) {
using namespace allocation_size_literals;
ASSERT_DOUBLE_EQ((1500_bytes).ConvertTo<KiloBytes>().GetSize(), 1.5);
ASSERT_EQ((1500_bytes).ConvertTo<KiloBytes>().GetByteSize(), 1500u);
}

TEST(AllocationSizeTest, CanGetFloatValues) {
using namespace allocation_size_literals;
ASSERT_DOUBLE_EQ((1500_bytes).ConvertTo<KiloBytes>().GetSize(), 1.5);
}

TEST(AllocationSizeTest, RelationalOperatorsAreFunctional) {
using namespace allocation_size_literals;

auto a = 1500_bytes;
auto b = 2500_bytes;
auto c = 0_bytes;

ASSERT_TRUE(a != b);
ASSERT_FALSE(a == b);
ASSERT_TRUE(b > a);
ASSERT_TRUE(b >= a);
ASSERT_TRUE(a < b);
ASSERT_TRUE(a <= b);
ASSERT_TRUE(a);
ASSERT_FALSE(c);
}

TEST(AllocationSizeTest, CanCast) {
using namespace allocation_size_literals;
{
auto a = KiloBytes{1500_bytes};
ASSERT_DOUBLE_EQ(a.GetSize(), 1.5);
}
{
auto a = KiloBytes{Bytes{1500}};
ASSERT_DOUBLE_EQ(a.GetSize(), 1.5);
}

ASSERT_DOUBLE_EQ(MebiBytes{Bytes{4194304}}.GetSize(), 4);
}

TEST(AllocationSizeTest, CanPerformSimpleArithmetic) {
using namespace allocation_size_literals;
{
auto a = 100_bytes;
auto b = 200_bytes;
ASSERT_EQ((a + b).GetByteSize(), 300u);
}
{
auto a = 100_bytes;
a += 200_bytes;
ASSERT_EQ(a.GetByteSize(), 300u);
}
{
auto a = 100_bytes;
a -= 50_bytes;
ASSERT_EQ(a.GetByteSize(), 50u);
}
}

} // namespace impeller::testing
3 changes: 2 additions & 1 deletion impeller/core/allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_IMPELLER_CORE_ALLOCATOR_H_

#include "flutter/fml/mapping.h"
#include "impeller/base/allocation_size.h"
#include "impeller/core/device_buffer_descriptor.h"
#include "impeller/core/texture.h"
#include "impeller/core/texture_descriptor.h"
Expand Down Expand Up @@ -51,7 +52,7 @@ class Allocator {
virtual void DebugTraceMemoryStatistics() const {};

// Visible for testing.
virtual size_t DebugGetHeapUsage() const { return 0; }
virtual Bytes DebugGetHeapUsage() const { return Bytes{0}; }

protected:
Allocator();
Expand Down
6 changes: 3 additions & 3 deletions impeller/renderer/backend/metal/allocator_mtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ class DebugAllocatorStats {
/// Decrement the tracked allocation size in bytes.
void Decrement(size_t size);

/// Get the current tracked allocation size in MB.
size_t GetAllocationSizeMB();
/// Get the current tracked allocation size.
Bytes GetAllocationSize();

private:
std::atomic<size_t> size_ = 0;
Expand All @@ -40,7 +40,7 @@ class AllocatorMTL final : public Allocator {
~AllocatorMTL() override;

// |Allocator|
size_t DebugGetHeapUsage() const override;
Bytes DebugGetHeapUsage() const override;

private:
friend class ContextMTL;
Expand Down
Loading