Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
host_buffer_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <limits>
6#include <utility>
7
9#include "gmock/gmock.h"
15
16namespace impeller {
17namespace testing {
18
19class MockIdleWaiter : public IdleWaiter {
20 public:
21 MOCK_METHOD(void, WaitIdle, (), (const, override));
22};
23
26
28 auto mock_idle_waiter = std::make_shared<MockIdleWaiter>();
29 {
30 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
31 mock_idle_waiter, 256);
32 EXPECT_CALL(*mock_idle_waiter, WaitIdle());
33 }
34}
35
36TEST_P(HostBufferTest, CanEmplace) {
37 struct Length2 {
38 uint8_t pad[2];
39 };
40 static_assert(sizeof(Length2) == 2u);
41
42 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
43 GetContext()->GetIdleWaiter(), 256);
44
45 for (size_t i = 0; i < 12500; i++) {
46 auto view = buffer->Emplace(Length2{});
47 ASSERT_TRUE(view);
48 ASSERT_EQ(view.GetRange(), Range(i * sizeof(Length2), 2u));
49 }
50}
51
52TEST_P(HostBufferTest, CanEmplaceWithAlignment) {
53 struct Length2 {
54 uint8_t pad[2];
55 };
56 static_assert(sizeof(Length2) == 2);
57 struct alignas(16) Align16 {
58 uint8_t pad[2];
59 };
60 static_assert(alignof(Align16) == 16);
61 static_assert(sizeof(Align16) == 16);
62
63 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
64 GetContext()->GetIdleWaiter(), 256);
65 ASSERT_TRUE(buffer);
66
67 {
68 auto view = buffer->Emplace(Length2{});
69 ASSERT_TRUE(view);
70 ASSERT_EQ(view.GetRange(), Range(0u, 2u));
71 }
72
73 {
74 auto view = buffer->Emplace(Align16{});
75 ASSERT_TRUE(view);
76 ASSERT_EQ(view.GetRange().offset, 16u);
77 ASSERT_EQ(view.GetRange().length, 16u);
78 }
79 {
80 auto view = buffer->Emplace(Length2{});
81 ASSERT_TRUE(view);
82 ASSERT_EQ(view.GetRange(), Range(32u, 2u));
83 }
84
85 {
86 auto view = buffer->Emplace(Align16{});
87 ASSERT_TRUE(view);
88 ASSERT_EQ(view.GetRange().offset, 48u);
89 ASSERT_EQ(view.GetRange().length, 16u);
90 }
91}
92
93TEST_P(HostBufferTest, HostBufferInitialState) {
94 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
95 GetContext()->GetIdleWaiter(), 256);
96
97 EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
98 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
99 EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
100}
101
102TEST_P(HostBufferTest, ResetIncrementsFrameCounter) {
103 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
104 GetContext()->GetIdleWaiter(), 256);
105
106 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
107
108 buffer->Reset();
109 EXPECT_EQ(buffer->GetStateForTest().current_frame, 1u);
110
111 buffer->Reset();
112 EXPECT_EQ(buffer->GetStateForTest().current_frame, 2u);
113
114 buffer->Reset();
115 EXPECT_EQ(buffer->GetStateForTest().current_frame, 3u);
116
117 buffer->Reset();
118 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
119}
120
122 EmplacingLargerThanBlockSizeCreatesOneOffBufferCallback) {
123 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
124 GetContext()->GetIdleWaiter(), 256);
125
126 // Emplace an amount larger than the block size, to verify that the host
127 // buffer does not create a buffer.
128 auto buffer_view = buffer->Emplace(1024000 + 10, 0, [](uint8_t* data) {});
129
130 EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
131 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
132 EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
133}
134
135TEST_P(HostBufferTest, EmplacingLargerThanBlockSizeCreatesOneOffBuffer) {
136 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
137 GetContext()->GetIdleWaiter(), 256);
138
139 // Emplace an amount larger than the block size, to verify that the host
140 // buffer does not create a buffer.
141 auto buffer_view = buffer->Emplace(nullptr, 1024000 + 10, 0);
142
143 EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
144 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
145 EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
146}
147
148TEST_P(HostBufferTest, UnusedBuffersAreDiscardedWhenResetting) {
149 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
150 GetContext()->GetIdleWaiter(), 256);
151
152 // Emplace two large allocations to force the allocation of a second buffer.
153 auto buffer_view_a = buffer->Emplace(1020000, 0, [](uint8_t* data) {});
154 auto buffer_view_b = buffer->Emplace(1020000, 0, [](uint8_t* data) {});
155
156 EXPECT_EQ(buffer->GetStateForTest().current_buffer, 1u);
157 EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 2u);
158 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
159
160 // Reset until we get back to this frame.
161 for (auto i = 0; i < 4; i++) {
162 buffer->Reset();
163 }
164
165 EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
166 EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 2u);
167 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
168
169 // Now when we reset, the buffer should get dropped.
170 // Reset until we get back to this frame.
171 for (auto i = 0; i < 4; i++) {
172 buffer->Reset();
173 }
174
175 EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
176 EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
177 EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
178}
179
180TEST_P(HostBufferTest, EmplaceWithProcIsAligned) {
181 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
182 GetContext()->GetIdleWaiter(), 256);
183
184 BufferView view = buffer->Emplace(std::array<char, 21>());
185 EXPECT_EQ(view.GetRange(), Range(0, 21));
186
187 view = buffer->Emplace(64, 16, [](uint8_t*) {});
188 EXPECT_EQ(view.GetRange(), Range(32, 64));
189}
190
191static constexpr const size_t kMagicFailingAllocation = 1024000 * 2;
192
194 public:
195 explicit FailingAllocator(std::shared_ptr<Allocator> delegate)
196 : Allocator(), delegate_(std::move(delegate)) {}
197
198 ~FailingAllocator() = default;
199
200 std::shared_ptr<DeviceBuffer> OnCreateBuffer(
201 const DeviceBufferDescriptor& desc) {
202 // Magic number used in test below to trigger failure.
203 if (desc.size == kMagicFailingAllocation) {
204 return nullptr;
205 }
206 return delegate_->CreateBuffer(desc);
207 }
208
209 std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc,
210 bool threadsafe) {
211 return delegate_->CreateTexture(desc);
212 }
213
215 return delegate_->GetMaxTextureSizeSupported();
216 }
217
218 private:
219 std::shared_ptr<Allocator> delegate_;
220};
221
222TEST(GpuSubmissionTrackerTest, WatermarkHandlesOutOfOrderCompletion) {
223 GpuSubmissionTracker tracker;
224 EXPECT_EQ(tracker.CompletedThrough(), 0u);
225 uint64_t a = tracker.RecordSubmission();
226 uint64_t b = tracker.RecordSubmission();
227 uint64_t c = tracker.RecordSubmission();
228 EXPECT_EQ(tracker.LatestSubmission(), c);
229
230 tracker.RecordCompletion(b);
231 EXPECT_EQ(tracker.CompletedThrough(), 0u);
232 tracker.RecordCompletion(a);
233 EXPECT_EQ(tracker.CompletedThrough(), b);
234 tracker.RecordCompletion(c);
235 EXPECT_EQ(tracker.CompletedThrough(), c);
236}
237
238TEST_P(HostBufferTest, PendingEntriesAreReplacedNotReused) {
239 auto tracker = std::make_shared<GpuSubmissionTracker>();
240 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
241 GetContext()->GetIdleWaiter(), 256, tracker);
242
243 // Write to the first arena entry and pretend its GPU work never completes.
244 const DeviceBuffer* first_entry = buffer->EmplaceUniform(0.0f).GetBuffer();
245 uint64_t pending = tracker->RecordSubmission();
246 for (size_t i = 0; i < kHostBufferArenaSize; i++) {
247 buffer->Reset();
248 }
249
250 // The entry must be replaced with a fresh allocation.
251 EXPECT_NE(buffer->EmplaceUniform(0.0f).GetBuffer(), first_entry);
252
253 // Once completed, entries are reused in place.
254 tracker->RecordCompletion(pending);
255 const DeviceBuffer* second_entry = buffer->EmplaceUniform(0.0f).GetBuffer();
256 for (size_t i = 0; i < kHostBufferArenaSize; i++) {
257 buffer->Reset();
258 }
259 EXPECT_EQ(buffer->EmplaceUniform(0.0f).GetBuffer(), second_entry);
260}
261
262TEST_P(HostBufferTest, CompletedEntriesAreReusedWithoutTrackerChurn) {
263 auto tracker = std::make_shared<GpuSubmissionTracker>();
264 auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator(),
265 GetContext()->GetIdleWaiter(), 256, tracker);
266
267 // Simulate frames whose GPU work completes promptly. Entries must be
268 // reused, not replaced.
269 const DeviceBuffer* entry = buffer->EmplaceUniform(0.0f).GetBuffer();
270 for (size_t i = 0; i < kHostBufferArenaSize * 3; i++) {
271 tracker->RecordCompletion(tracker->RecordSubmission());
272 buffer->Reset();
273 }
274 EXPECT_EQ(buffer->EmplaceUniform(0.0f).GetBuffer(), entry);
275}
276
277TEST_P(HostBufferTest, EmplaceWithFailingAllocationDoesntCrash) {
279 std::shared_ptr<FailingAllocator> allocator =
280 std::make_shared<FailingAllocator>(GetContext()->GetResourceAllocator());
281 auto buffer =
282 HostBuffer::Create(allocator, GetContext()->GetIdleWaiter(), 256);
283
284 auto view = buffer->Emplace(nullptr, kMagicFailingAllocation, 0);
285
286 EXPECT_EQ(view.GetBuffer(), nullptr);
287 EXPECT_EQ(view.GetRange().offset, 0u);
288 EXPECT_EQ(view.GetRange().length, 0u);
289}
290
291} // namespace testing
292} // namespace impeller
An object that allocates device memory.
Definition allocator.h:24
Tracks GPU completion of submitted command buffers as a monotonic watermark.
uint64_t LatestSubmission() const
Returns the id of the most recent submission.
void RecordCompletion(uint64_t id)
Marks a previously recorded submission as completed by the GPU.
uint64_t RecordSubmission()
Records a command buffer submission and returns its id.
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator, const std::shared_ptr< const IdleWaiter > &idle_waiter, size_t minimum_uniform_alignment, std::shared_ptr< const GpuSubmissionTracker > submission_tracker=nullptr)
virtual void WaitIdle() const =0
std::shared_ptr< Texture > OnCreateTexture(const TextureDescriptor &desc, bool threadsafe)
FailingAllocator(std::shared_ptr< Allocator > delegate)
std::shared_ptr< DeviceBuffer > OnCreateBuffer(const DeviceBufferDescriptor &desc)
MOCK_METHOD(void, WaitIdle,(),(const, override))
FlView * view
std::shared_ptr< ImpellerAllocator > allocator
TEST(FrameTimingsRecorderTest, RecordVsync)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
static constexpr const size_t kMagicFailingAllocation
TEST_P(AiksTest, DrawAtlasNoColor)
static const constexpr size_t kHostBufferArenaSize
Approximately the same size as the max frames in flight.
Definition host_buffer.h:23
Definition ref_ptr.h:261
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...