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 
6 
7 #include <cstring>
8 #include <tuple>
9 
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) {
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 
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 
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()) {
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  std::shared_ptr<DeviceBuffer> device_buffer =
103  allocator_->CreateBuffer(desc);
104  if (!device_buffer) {
105  return {};
106  }
107  if (cb) {
108  cb(device_buffer->OnGetContents());
109  device_buffer->Flush(Range{0, length});
110  }
111  return std::make_tuple(Range{0, length}, std::move(device_buffer));
112  }
113 
114  size_t padding = 0;
115  if (align > 0 && offset_ % align) {
116  padding = align - (offset_ % align);
117  }
118  if (offset_ + padding + length > kAllocatorBlockSize) {
119  MaybeCreateNewBuffer();
120  } else {
121  offset_ += padding;
122  }
123 
124  const std::shared_ptr<DeviceBuffer>& current_buffer = GetCurrentBuffer();
125  auto contents = current_buffer->OnGetContents();
126  cb(contents + offset_);
127  Range output_range(offset_, length);
128  current_buffer->Flush(output_range);
129 
130  offset_ += length;
131  return std::make_tuple(output_range, current_buffer);
132 }
133 
134 std::tuple<Range, std::shared_ptr<DeviceBuffer>> HostBuffer::EmplaceInternal(
135  const void* buffer,
136  size_t length) {
137  // If the requested allocation is bigger than the block size, create a one-off
138  // device buffer and write to that.
139  if (length > kAllocatorBlockSize) {
140  DeviceBufferDescriptor desc;
141  desc.size = length;
142  desc.storage_mode = StorageMode::kHostVisible;
143  std::shared_ptr<DeviceBuffer> device_buffer =
144  allocator_->CreateBuffer(desc);
145  if (!device_buffer) {
146  return {};
147  }
148  if (buffer) {
149  if (!device_buffer->CopyHostBuffer(static_cast<const uint8_t*>(buffer),
150  Range{0, length})) {
151  return {};
152  }
153  }
154  return std::make_tuple(Range{0, length}, std::move(device_buffer));
155  }
156 
157  auto old_length = GetLength();
158  if (old_length + length > kAllocatorBlockSize) {
159  MaybeCreateNewBuffer();
160  }
161  old_length = GetLength();
162 
163  const std::shared_ptr<DeviceBuffer>& current_buffer = GetCurrentBuffer();
164  auto contents = current_buffer->OnGetContents();
165  if (buffer) {
166  ::memmove(contents + old_length, buffer, length);
167  current_buffer->Flush(Range{old_length, length});
168  }
169  offset_ += length;
170  return std::make_tuple(Range{old_length, length}, current_buffer);
171 }
172 
173 std::tuple<Range, std::shared_ptr<DeviceBuffer>>
174 HostBuffer::EmplaceInternal(const void* buffer, size_t length, size_t align) {
175  if (align == 0 || (GetLength() % align) == 0) {
176  return EmplaceInternal(buffer, length);
177  }
178 
179  {
180  auto padding = align - (GetLength() % align);
181  if (offset_ + padding < kAllocatorBlockSize) {
182  offset_ += padding;
183  } else {
184  MaybeCreateNewBuffer();
185  }
186  }
187 
188  return EmplaceInternal(buffer, length);
189 }
190 
191 const std::shared_ptr<DeviceBuffer>& HostBuffer::GetCurrentBuffer() const {
192  return device_buffers_[frame_index_][current_buffer_];
193 }
194 
196  // When resetting the host buffer state at the end of the frame, check if
197  // there are any unused buffers and remove them.
198  while (device_buffers_[frame_index_].size() > current_buffer_ + 1) {
199  device_buffers_[frame_index_].pop_back();
200  }
201 
202  offset_ = 0u;
203  current_buffer_ = 0u;
204  frame_index_ = (frame_index_ + 1) % kHostBufferArenaSize;
205 }
206 
207 } // 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
formats.h
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
padding
Vector2 padding
The halo padding in source space.
Definition: gaussian_blur_filter_contents.cc:91
impeller::StorageMode::kHostVisible
@ kHostVisible
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: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:195
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_blend_unittests.cc:18