| Flutter Impeller |

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
5 #include "impeller/core/host_buffer.h"
6
7 #include <cstring>
8 #include <tuple>
9
10 #include "impeller/base/validation.h"
11 #include "impeller/core/allocator.h"
12 #include "impeller/core/buffer_view.h"
13 #include "impeller/core/device_buffer.h"
14 #include "impeller/core/device_buffer_descriptor.h"
15 #include "impeller/core/formats.h"
16
17 namespace impeller {
18
19 constexpr size_t kAllocatorBlockSize = 1024000; // 1024 Kb.
20
21 std::shared_ptr<HostBuffer> HostBuffer::Create(
22 const std::shared_ptr<Allocator>& allocator) {
23 return std::shared_ptr<HostBuffer>(new HostBuffer(allocator));
24 }
25
26 HostBuffer::HostBuffer(const std::shared_ptr<Allocator>& allocator)
27 : allocator_(allocator) {
28 DeviceBufferDescriptor desc;
29 desc.size = kAllocatorBlockSize;
30 desc.storage_mode = StorageMode::kHostVisible;
31 for (auto i = 0u; i < kHostBufferArenaSize; i++) {
32 std::shared_ptr<DeviceBuffer> device_buffer = allocator->CreateBuffer(desc);
33 FML_CHECK(device_buffer) << "Failed to allocate device buffer.";
34 device_buffers_[i].push_back(device_buffer);
35 }
36 }
37
38 HostBuffer::~HostBuffer() = default;
39
40 void HostBuffer::SetLabel(std::string label) {
41 label_ = std::move(label);
42 }
43
44 BufferView HostBuffer::Emplace(const void* buffer,
45 size_t length,
46 size_t align) {
47 auto [range, device_buffer] = EmplaceInternal(buffer, length, align);
48 if (!device_buffer) {
49 return {};
50 }
51 return BufferView{std::move(device_buffer), range};
52 }
53
54 BufferView HostBuffer::Emplace(const void* buffer, size_t length) {
55 auto [range, device_buffer] = EmplaceInternal(buffer, length);
56 if (!device_buffer) {
57 return {};
58 }
59 return BufferView{std::move(device_buffer), range};
60 }
61
62 BufferView HostBuffer::Emplace(size_t length,
63 size_t align,
64 const EmplaceProc& cb) {
65 auto [range, device_buffer] = EmplaceInternal(length, align, cb);
66 if (!device_buffer) {
67 return {};
68 }
69 return BufferView{std::move(device_buffer), range};
70 }
71
72 HostBuffer::TestStateQuery HostBuffer::GetStateForTest() {
73 return HostBuffer::TestStateQuery{
74 .current_frame = frame_index_,
75 .current_buffer = current_buffer_,
76 .total_buffer_count = device_buffers_[frame_index_].size(),
77 };
78 }
79
80 bool HostBuffer::MaybeCreateNewBuffer() {
81 current_buffer_++;
82 if (current_buffer_ >= device_buffers_[frame_index_].size()) {
83 DeviceBufferDescriptor desc;
84 desc.size = kAllocatorBlockSize;
85 desc.storage_mode = StorageMode::kHostVisible;
86 std::shared_ptr<DeviceBuffer> buffer = allocator_->CreateBuffer(desc);
87 if (!buffer) {
88 VALIDATION_LOG << "Failed to allocate host buffer of size " << desc.size;
89 return false;
90 }
91 device_buffers_[frame_index_].push_back(std::move(buffer));
92 }
93 offset_ = 0;
94 return true;
95 }
96
97 std::tuple<Range, std::shared_ptr<DeviceBuffer>> HostBuffer::EmplaceInternal(
98 size_t length,
99 size_t align,
100 const EmplaceProc& cb) {
101 if (!cb) {
102 return {};
103 }
104
105 // If the requested allocation is bigger than the block size, create a one-off
106 // device buffer and write to that.
107 if (length > kAllocatorBlockSize) {
108 DeviceBufferDescriptor desc;
109 desc.size = length;
110 desc.storage_mode = StorageMode::kHostVisible;
111 std::shared_ptr<DeviceBuffer> device_buffer =
112 allocator_->CreateBuffer(desc);
113 if (!device_buffer) {
114 return {};
115 }
116 if (cb) {
117 cb(device_buffer->OnGetContents());
118 device_buffer->Flush(Range{0, length});
119 }
120 return std::make_tuple(Range{0, length}, std::move(device_buffer));
121 }
122
123 size_t padding = 0;
124 if (align > 0 && offset_ % align) {
125 padding = align - (offset_ % align);
126 }
127 if (offset_ + padding + length > kAllocatorBlockSize) {
128 if (!MaybeCreateNewBuffer()) {
129 return {};
130 }
131 } else {
132 offset_ += padding;
133 }
134
135 const std::shared_ptr<DeviceBuffer>& current_buffer = GetCurrentBuffer();
136 auto contents = current_buffer->OnGetContents();
137 cb(contents + offset_);
138 Range output_range(offset_, length);
139 current_buffer->Flush(output_range);
140
141 offset_ += length;
142 return std::make_tuple(output_range, current_buffer);
143 }
144
145 std::tuple<Range, std::shared_ptr<DeviceBuffer>> HostBuffer::EmplaceInternal(
146 const void* buffer,
147 size_t length) {
148 // If the requested allocation is bigger than the block size, create a one-off
149 // device buffer and write to that.
150 if (length > kAllocatorBlockSize) {
151 DeviceBufferDescriptor desc;
152 desc.size = length;
153 desc.storage_mode = StorageMode::kHostVisible;
154 std::shared_ptr<DeviceBuffer> device_buffer =
155 allocator_->CreateBuffer(desc);
156 if (!device_buffer) {
157 return {};
158 }
159 if (buffer) {
160 if (!device_buffer->CopyHostBuffer(static_cast<const uint8_t*>(buffer),
161 Range{0, length})) {
162 return {};
163 }
164 }
165 return std::make_tuple(Range{0, length}, std::move(device_buffer));
166 }
167
168 auto old_length = GetLength();
169 if (old_length + length > kAllocatorBlockSize) {
170 if (!MaybeCreateNewBuffer()) {
171 return {};
172 }
173 }
174 old_length = GetLength();
175
176 const std::shared_ptr<DeviceBuffer>& current_buffer = GetCurrentBuffer();
177 auto contents = current_buffer->OnGetContents();
178 if (buffer) {
179 ::memmove(contents + old_length, buffer, length);
180 current_buffer->Flush(Range{old_length, length});
181 }
182 offset_ += length;
183 return std::make_tuple(Range{old_length, length}, current_buffer);
184 }
185
186 std::tuple<Range, std::shared_ptr<DeviceBuffer>>
187 HostBuffer::EmplaceInternal(const void* buffer, size_t length, size_t align) {
188 if (align == 0 || (GetLength() % align) == 0) {
189 return EmplaceInternal(buffer, length);
190 }
191
192 {
193 auto padding = align - (GetLength() % align);
194 if (offset_ + padding < kAllocatorBlockSize) {
195 offset_ += padding;
196 } else if (!MaybeCreateNewBuffer()) {
197 return {};
198 }
199 }
200
201 return EmplaceInternal(buffer, length);
202 }
203
204 const std::shared_ptr<DeviceBuffer>& HostBuffer::GetCurrentBuffer() const {
205 return device_buffers_[frame_index_][current_buffer_];
206 }
207
208 void HostBuffer::Reset() {
209 // When resetting the host buffer state at the end of the frame, check if
210 // there are any unused buffers and remove them.
211 while (device_buffers_[frame_index_].size() > current_buffer_ + 1) {
212 device_buffers_[frame_index_].pop_back();
213 }
214
215 offset_ = 0u;
216 current_buffer_ = 0u;
217 frame_index_ = (frame_index_ + 1) % kHostBufferArenaSize;
218 }
219
220 } // namespace impeller
impeller::HostBuffer::EmplaceProc
std::function< void(uint8_t *buffer)> EmplaceProc
Definition: host_buffer.h:107
host_buffer.h
impeller::HostBuffer::Emplace
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:95
impeller::DeviceBufferDescriptor
Definition: device_buffer_descriptor.h:14
device_buffer.h
impeller::HostBuffer
Definition: host_buffer.h:28
impeller::HostBuffer::Create
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator)
Definition: host_buffer.cc:21
formats.h
impeller::DeviceBufferDescriptor::size
size_t size
Definition: device_buffer_descriptor.h:16
padding
Vector2 padding
The halo padding in source space.
Definition: gaussian_blur_filter_contents.cc:85
impeller::StorageMode::kHostVisible
@ kHostVisible
validation.h
impeller::HostBuffer::TestStateQuery
Test only internal state.
Definition: host_buffer.h:129
impeller::HostBuffer::TestStateQuery::current_frame
size_t current_frame
Definition: host_buffer.h:130
impeller::HostBuffer::~HostBuffer
virtual ~HostBuffer()
impeller::DeviceBufferDescriptor::storage_mode
StorageMode storage_mode
Definition: device_buffer_descriptor.h:15
impeller::kHostBufferArenaSize
static const constexpr size_t kHostBufferArenaSize
Approximately the same size as the max frames in flight.
Definition: host_buffer.h:22
impeller::kAllocatorBlockSize
constexpr size_t kAllocatorBlockSize
Definition: host_buffer.cc:19
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
allocator.h
impeller::BufferView
Definition: buffer_view.h:15
buffer_view.h
impeller::HostBuffer::Reset
void Reset()
Resets the contents of the HostBuffer to nothing so it can be reused.
Definition: host_buffer.cc:208
impeller::HostBuffer::GetStateForTest
TestStateQuery GetStateForTest()
Retrieve internal buffer state for test expectations.
Definition: host_buffer.cc:72
device_buffer_descriptor.h
impeller::HostBuffer::SetLabel
void SetLabel(std::string label)
Definition: host_buffer.cc:40
impeller
Definition: allocation.cc:12