GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
79 / 83
95.2%
Branches:
39 / 74
52.7%
Functions:
11 / 11
100.0%

device/arena.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file arena.cpp
3 /// @brief Sub-allocating arenas with VMA's virtual blocks.
4 #include "ernie/device/arena.h"
5
6 #include <algorithm>
7 #include <optional>
8
9 namespace ernie {
10
11 103 Arenas::Arena::~Arena() {
12
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
103 if (block != VK_NULL_HANDLE) {
13 103 vmaClearVirtualBlock(block);
14 103 vmaDestroyVirtualBlock(block);
15 }
16 103 }
17
18 109 Arenas::Arenas(DeviceContext& context, MemoryUse use, std::string_view name,
19 109 std::uint64_t arenaSize)
20
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 109 times.
109 : context_(context), use_(use), name_(name),
21
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 arenaSize_(std::min(arenaSize, context.MaxBufferSize())) {}
22
23 109 Arenas::~Arenas() {
24
1/2
✓ Branch 1 taken 109 times.
✗ Branch 2 not taken.
218 Quietly([this] { context_.WaitIdle(); });
25 109 }
26
27 782 BufferRange Arenas::Allocate(std::uint64_t size, std::uint64_t alignment) {
28
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 781 times.
782 if (size > context_.MaxBufferSize()) {
29 1 throw DeviceError(DeviceErrorKind::Unsupported,
30
3/6
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
4 std::to_string(size) + " bytes of " + name_ +
31 3 " exceed the largest buffer the device can address");
32 }
33
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 781 times.
781 size = std::max<std::uint64_t>(size, 1);
34 781 const VmaVirtualAllocationCreateInfo info{.size = size, .alignment = alignment};
35 1568 const auto take = [&](std::uint32_t index) -> std::optional<BufferRange> {
36 787 Arena& arena = *arenas_[index];
37 787 VmaVirtualAllocation allocation = VK_NULL_HANDLE;
38 787 VkDeviceSize offset = 0;
39
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 781 times.
787 if (vmaVirtualAllocate(arena.block, &info, &allocation, &offset) != VK_SUCCESS) {
40 6 return std::nullopt;
41 }
42 781 arena.allocations.emplace(offset, allocation);
43 781 arena.used += size;
44 781 return BufferRange{index, offset, size};
45 781 };
46 1665 const auto takeAny = [&]() -> std::optional<BufferRange> {
47
2/2
✓ Branch 0 taken 684 times.
✓ Branch 1 taken 206 times.
890 for (std::uint32_t index = 0; index < arenas_.size(); ++index) {
48
2/2
✓ Branch 1 taken 678 times.
✓ Branch 2 taken 6 times.
684 if (const std::optional<BufferRange> range = take(index)) {
49 678 return range;
50 }
51 }
52 206 return std::nullopt;
53 781 };
54 781 {
55 781 const std::lock_guard lock(mutex_);
56
3/4
✓ Branch 1 taken 781 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 678 times.
✓ Branch 4 taken 103 times.
781 if (const std::optional<BufferRange> range = takeAny()) {
57 678 return *range;
58 }
59 678 }
60 // Ranges freed earlier come back once their work is done; collected without the lock, as
61 // giving them back takes it.
62 103 context_.Collect();
63 103 const std::lock_guard lock(mutex_);
64
2/4
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 103 times.
103 if (const std::optional<BufferRange> range = takeAny()) {
65 ✗ return *range;
66 }
67 // A range larger than the arena size gets an arena of its own.
68
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 3 times.
103 const std::uint64_t own = std::max(size, arenaSize_);
69
3/6
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 103 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 103 times.
✗ Branch 7 not taken.
103 if (const std::optional<BufferRange> range = take(AddArena(own))) {
70 103 return *range;
71 }
72
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ throw DeviceError(DeviceErrorKind::Failed, "Allocating " + std::to_string(size) + " bytes of " +
73
0/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ name_ + " at alignment " +
74
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ std::to_string(alignment));
75 103 }
76
77 2015 const Buffer& Arenas::Storage(const BufferRange& range) const {
78 2015 const std::lock_guard lock(mutex_);
79 2015 return arenas_[range.arena]->buffer;
80 2015 }
81
82 735 void Arenas::Free(const BufferRange& range) {
83
1/2
✓ Branch 3 taken 735 times.
✗ Branch 4 not taken.
1470 context_.Retire([this, range] { Release(range); });
84 735 }
85
86 3 std::uint64_t Arenas::Used() const {
87 3 const std::lock_guard lock(mutex_);
88 3 std::uint64_t used = 0;
89
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 for (const auto& arena : arenas_) {
90 3 used += arena->used;
91 }
92 3 return used;
93 3 }
94
95 103 std::uint32_t Arenas::AddArena(std::uint64_t size) {
96 103 auto arena = std::make_unique<Arena>();
97 206 arena->buffer =
98
4/8
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 103 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 103 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 103 times.
✗ Branch 11 not taken.
103 Buffer(context_, size, use_, name_ + " arena " + std::to_string(arenas_.size()));
99 103 const VmaVirtualBlockCreateInfo block{.size = size};
100
3/6
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 103 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 103 times.
✗ Branch 8 not taken.
309 CheckVk(vmaCreateVirtualBlock(&block, &arena->block), "Creating " + name_ + "'s arena");
101
1/2
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
103 arenas_.push_back(std::move(arena));
102 103 return static_cast<std::uint32_t>(arenas_.size() - 1);
103 103 }
104
105 735 void Arenas::Release(const BufferRange& range) {
106 735 const std::lock_guard lock(mutex_);
107 735 Arena& arena = *arenas_[range.arena];
108 735 const auto found = arena.allocations.find(range.offset);
109
1/2
✓ Branch 0 taken 735 times.
✗ Branch 1 not taken.
735 if (found != arena.allocations.end()) {
110
1/2
✓ Branch 2 taken 735 times.
✗ Branch 3 not taken.
735 vmaVirtualFree(arena.block, found->second);
111 735 arena.allocations.erase(found);
112 735 arena.used -= range.size;
113 }
114 735 }
115
116 } // namespace ernie
117