Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
host_buffer.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
6
7#include <cstring>
8#include <tuple>
9
16
17namespace impeller {
18
19constexpr size_t kAllocatorBlockSize = 1024000; // 1024 Kb.
20
21std::shared_ptr<HostBuffer> HostBuffer::Create(
22 const std::shared_ptr<Allocator>& allocator,
23 const std::shared_ptr<const IdleWaiter>& idle_waiter,
24 size_t minimum_uniform_alignment,
25 std::shared_ptr<const GpuSubmissionTracker> submission_tracker) {
26 return std::shared_ptr<HostBuffer>(
27 new HostBuffer(allocator, idle_waiter, minimum_uniform_alignment,
28 std::move(submission_tracker)));
29}
30
31HostBuffer::HostBuffer(
32 const std::shared_ptr<Allocator>& allocator,
33 const std::shared_ptr<const IdleWaiter>& idle_waiter,
34 size_t minimum_uniform_alignment,
35 std::shared_ptr<const GpuSubmissionTracker> submission_tracker)
36 : allocator_(allocator),
37 idle_waiter_(idle_waiter),
38 submission_tracker_(std::move(submission_tracker)),
39 minimum_uniform_alignment_(minimum_uniform_alignment) {
43 for (auto i = 0u; i < kHostBufferArenaSize; i++) {
44 std::shared_ptr<DeviceBuffer> device_buffer = allocator->CreateBuffer(desc);
45 FML_CHECK(device_buffer) << "Failed to allocate device buffer.";
46 device_buffers_[i].push_back(device_buffer);
47 }
48}
49
51 if (idle_waiter_) {
52 // Since we hold on to DeviceBuffers we should make sure they aren't being
53 // used while we are deleting the HostBuffer.
54 idle_waiter_->WaitIdle();
55 }
56};
57
59 size_t length,
60 size_t align) {
61 auto [range, device_buffer, raw_device_buffer] =
62 EmplaceInternal(buffer, length, align);
63 if (device_buffer) {
64 return BufferView(std::move(device_buffer), range);
65 } else if (raw_device_buffer) {
66 return BufferView(raw_device_buffer, range);
67 } else {
68 return {};
69 }
70}
71
72BufferView HostBuffer::Emplace(const void* buffer, size_t length) {
73 auto [range, device_buffer, raw_device_buffer] =
74 EmplaceInternal(buffer, length);
75 if (device_buffer) {
76 return BufferView(std::move(device_buffer), range);
77 } else if (raw_device_buffer) {
78 return BufferView(raw_device_buffer, range);
79 } else {
80 return {};
81 }
82}
83
85 size_t align,
86 const EmplaceProc& cb) {
87 auto [range, device_buffer, raw_device_buffer] =
88 EmplaceInternal(length, align, cb);
89 if (device_buffer) {
90 return BufferView(std::move(device_buffer), range);
91 } else if (raw_device_buffer) {
92 return BufferView(raw_device_buffer, range);
93 } else {
94 return {};
95 }
96}
97
100 .current_frame = frame_index_,
101 .current_buffer = current_buffer_,
102 .total_buffer_count = device_buffers_[frame_index_].size(),
103 };
104}
105
106bool HostBuffer::MaybeCreateNewBuffer() {
107 current_buffer_++;
108 if (current_buffer_ >= device_buffers_[frame_index_].size()) {
112 std::shared_ptr<DeviceBuffer> buffer = allocator_->CreateBuffer(desc);
113 if (!buffer) {
114 VALIDATION_LOG << "Failed to allocate host buffer of size " << desc.size;
115 return false;
116 }
117 device_buffers_[frame_index_].push_back(std::move(buffer));
118 }
119 offset_ = 0;
120 return true;
121}
122
123std::tuple<Range, std::shared_ptr<DeviceBuffer>, DeviceBuffer*>
124HostBuffer::EmplaceInternal(size_t length,
125 size_t align,
126 const EmplaceProc& cb) {
127 if (!cb) {
128 return {};
129 }
130
131 // If the requested allocation is bigger than the block size, create a one-off
132 // device buffer and write to that.
134 DeviceBufferDescriptor desc;
135 desc.size = length;
136 desc.storage_mode = StorageMode::kHostVisible;
137 std::shared_ptr<DeviceBuffer> device_buffer =
138 allocator_->CreateBuffer(desc);
139 if (!device_buffer) {
140 return {};
141 }
142 if (cb) {
143 cb(device_buffer->OnGetContents());
144 device_buffer->Flush(Range{0, length});
145 }
146 return std::make_tuple(Range{0, length}, std::move(device_buffer), nullptr);
147 }
148
149 size_t padding = 0;
150 if (align > 0 && offset_ % align) {
151 padding = align - (offset_ % align);
152 }
153 if (offset_ + padding + length > kAllocatorBlockSize) {
154 if (!MaybeCreateNewBuffer()) {
155 return {};
156 }
157 } else {
158 offset_ += padding;
159 }
160
161 const std::shared_ptr<DeviceBuffer>& current_buffer = GetCurrentBuffer();
162 auto contents = current_buffer->OnGetContents();
163 cb(contents + offset_);
164 Range output_range(offset_, length);
165 current_buffer->Flush(output_range);
166
167 offset_ += length;
168 return std::make_tuple(output_range, nullptr, current_buffer.get());
169}
170
171std::tuple<Range, std::shared_ptr<DeviceBuffer>, DeviceBuffer*>
172HostBuffer::EmplaceInternal(const void* buffer, size_t length) {
173 // If the requested allocation is bigger than the block size, create a one-off
174 // device buffer and write to that.
176 DeviceBufferDescriptor desc;
177 desc.size = length;
178 desc.storage_mode = StorageMode::kHostVisible;
179 std::shared_ptr<DeviceBuffer> device_buffer =
180 allocator_->CreateBuffer(desc);
181 if (!device_buffer) {
182 return {};
183 }
184 if (buffer) {
185 if (!device_buffer->CopyHostBuffer(static_cast<const uint8_t*>(buffer),
186 Range{0, length})) {
187 return {};
188 }
189 }
190 return std::make_tuple(Range{0, length}, std::move(device_buffer), nullptr);
191 }
192
193 auto old_length = GetLength();
194 if (old_length + length > kAllocatorBlockSize) {
195 if (!MaybeCreateNewBuffer()) {
196 return {};
197 }
198 }
199 old_length = GetLength();
200
201 const std::shared_ptr<DeviceBuffer>& current_buffer = GetCurrentBuffer();
202 auto contents = current_buffer->OnGetContents();
203 if (buffer) {
204 ::memmove(contents + old_length, buffer, length);
205 current_buffer->Flush(Range{old_length, length});
206 }
207 offset_ += length;
208 return std::make_tuple(Range{old_length, length}, nullptr,
209 current_buffer.get());
210}
211
212std::tuple<Range, std::shared_ptr<DeviceBuffer>, DeviceBuffer*>
213HostBuffer::EmplaceInternal(const void* buffer, size_t length, size_t align) {
214 if (align == 0 || (GetLength() % align) == 0) {
215 return EmplaceInternal(buffer, length);
216 }
217
218 {
219 auto padding = align - (GetLength() % align);
220 if (offset_ + padding < kAllocatorBlockSize) {
221 offset_ += padding;
222 } else if (!MaybeCreateNewBuffer()) {
223 return {};
224 }
225 }
226
227 return EmplaceInternal(buffer, length);
228}
229
230const std::shared_ptr<DeviceBuffer>& HostBuffer::GetCurrentBuffer() const {
231 return device_buffers_[frame_index_][current_buffer_];
232}
233
235 // When resetting the host buffer state at the end of the frame, check if
236 // there are any unused buffers and remove them.
237 while (device_buffers_[frame_index_].size() > current_buffer_ + 1) {
238 device_buffers_[frame_index_].pop_back();
239 }
240
241 if (submission_tracker_) {
242 // Everything submitted so far may reference this entry's buffers.
243 entry_stamps_[frame_index_] = submission_tracker_->LatestSubmission();
244 }
245
246 offset_ = 0u;
247 current_buffer_ = 0u;
248 frame_index_ = (frame_index_ + 1) % kHostBufferArenaSize;
249
250 if (!submission_tracker_) {
251 return;
252 }
253 uint64_t completed = submission_tracker_->CompletedThrough();
254
255 // Release retired buffers the GPU has completed with.
256 std::erase_if(retired_buffers_, [completed](const auto& retired) {
257 return retired.first <= completed;
258 });
259
260 if (entry_stamps_[frame_index_] <= completed) {
261 return;
262 }
263
264 // The GPU may still be reading the next entry's buffers. Retire them and
265 // start the entry over with a fresh allocation, since reusing them would
266 // race the reads of an incomplete earlier frame.
270 std::shared_ptr<DeviceBuffer> buffer = allocator_->CreateBuffer(desc);
271 if (!buffer) {
272 VALIDATION_LOG << "Failed to replace an in-flight host buffer entry.";
273 return;
274 }
275 retired_buffers_.emplace_back(entry_stamps_[frame_index_],
276 std::move(device_buffers_[frame_index_]));
277 device_buffers_[frame_index_].clear();
278 device_buffers_[frame_index_].push_back(std::move(buffer));
279 entry_stamps_[frame_index_] = 0;
280}
281
283 return minimum_uniform_alignment_;
284}
285
286} // namespace impeller
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
size_t GetMinimumUniformAlignment() const
Retrieve the minimum uniform buffer alignment in bytes.
TestStateQuery GetStateForTest()
Retrieve internal buffer state for test expectations.
std::function< void(uint8_t *buffer)> EmplaceProc
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)
void Reset()
Resets the contents of the HostBuffer to nothing so it can be reused.
#define FML_CHECK(condition)
Definition logging.h:104
Vector2 padding
The halo padding in source space.
std::shared_ptr< ImpellerAllocator > allocator
size_t length
constexpr size_t kAllocatorBlockSize
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
Test only internal state.
#define VALIDATION_LOG
Definition validation.h:91