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