blob: 0752818256076414317e3076ba840cc4ef3e0bec [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/core/host_buffer.h"
#include <cstring>
#include <tuple>
#include "impeller/core/allocator.h"
#include "impeller/core/buffer_view.h"
#include "impeller/core/device_buffer.h"
#include "impeller/core/device_buffer_descriptor.h"
#include "impeller/core/formats.h"
namespace impeller {
constexpr size_t kAllocatorBlockSize = 1024000; // 1024 Kb.
std::shared_ptr<HostBuffer> HostBuffer::Create(
const std::shared_ptr<Allocator>& allocator) {
return std::shared_ptr<HostBuffer>(new HostBuffer(allocator));
}
HostBuffer::HostBuffer(const std::shared_ptr<Allocator>& allocator)
: allocator_(allocator) {
DeviceBufferDescriptor desc;
desc.size = kAllocatorBlockSize;
desc.storage_mode = StorageMode::kHostVisible;
for (auto i = 0u; i < kHostBufferArenaSize; i++) {
device_buffers_[i].push_back(allocator->CreateBuffer(desc));
}
}
HostBuffer::~HostBuffer() = default;
void HostBuffer::SetLabel(std::string label) {
label_ = std::move(label);
}
BufferView HostBuffer::Emplace(const void* buffer,
size_t length,
size_t align) {
auto [range, device_buffer] = EmplaceInternal(buffer, length, align);
if (!device_buffer) {
return {};
}
return BufferView{std::move(device_buffer), range};
}
BufferView HostBuffer::Emplace(const void* buffer, size_t length) {
auto [range, device_buffer] = EmplaceInternal(buffer, length);
if (!device_buffer) {
return {};
}
return BufferView{std::move(device_buffer), range};
}
BufferView HostBuffer::Emplace(size_t length,
size_t align,
const EmplaceProc& cb) {
auto [range, device_buffer] = EmplaceInternal(length, align, cb);
if (!device_buffer) {
return {};
}
return BufferView{std::move(device_buffer), range};
}
HostBuffer::TestStateQuery HostBuffer::GetStateForTest() {
return HostBuffer::TestStateQuery{
.current_frame = frame_index_,
.current_buffer = current_buffer_,
.total_buffer_count = device_buffers_[frame_index_].size(),
};
}
void HostBuffer::MaybeCreateNewBuffer() {
current_buffer_++;
if (current_buffer_ >= device_buffers_[frame_index_].size()) {
DeviceBufferDescriptor desc;
desc.size = kAllocatorBlockSize;
desc.storage_mode = StorageMode::kHostVisible;
device_buffers_[frame_index_].push_back(allocator_->CreateBuffer(desc));
}
offset_ = 0;
}
std::tuple<Range, std::shared_ptr<DeviceBuffer>> HostBuffer::EmplaceInternal(
size_t length,
size_t align,
const EmplaceProc& cb) {
if (!cb) {
return {};
}
// If the requested allocation is bigger than the block size, create a one-off
// device buffer and write to that.
if (length > kAllocatorBlockSize) {
DeviceBufferDescriptor desc;
desc.size = length;
desc.storage_mode = StorageMode::kHostVisible;
auto device_buffer = allocator_->CreateBuffer(desc);
if (!device_buffer) {
return {};
}
if (cb) {
cb(device_buffer->OnGetContents());
device_buffer->Flush(Range{0, length});
}
return std::make_tuple(Range{0, length}, device_buffer);
}
auto old_length = GetLength();
if (old_length + length > kAllocatorBlockSize) {
MaybeCreateNewBuffer();
}
old_length = GetLength();
auto current_buffer = GetCurrentBuffer();
auto contents = current_buffer->OnGetContents();
cb(contents + old_length);
current_buffer->Flush(Range{old_length, length});
offset_ += length;
return std::make_tuple(Range{old_length, length}, std::move(current_buffer));
}
std::tuple<Range, std::shared_ptr<DeviceBuffer>> HostBuffer::EmplaceInternal(
const void* buffer,
size_t length) {
// If the requested allocation is bigger than the block size, create a one-off
// device buffer and write to that.
if (length > kAllocatorBlockSize) {
DeviceBufferDescriptor desc;
desc.size = length;
desc.storage_mode = StorageMode::kHostVisible;
auto device_buffer = allocator_->CreateBuffer(desc);
if (!device_buffer) {
return {};
}
if (buffer) {
if (!device_buffer->CopyHostBuffer(static_cast<const uint8_t*>(buffer),
Range{0, length})) {
return {};
}
}
return std::make_tuple(Range{0, length}, device_buffer);
}
auto old_length = GetLength();
if (old_length + length > kAllocatorBlockSize) {
MaybeCreateNewBuffer();
}
old_length = GetLength();
auto current_buffer = GetCurrentBuffer();
auto contents = current_buffer->OnGetContents();
if (buffer) {
::memmove(contents + old_length, buffer, length);
current_buffer->Flush(Range{old_length, length});
}
offset_ += length;
return std::make_tuple(Range{old_length, length}, std::move(current_buffer));
}
std::tuple<Range, std::shared_ptr<DeviceBuffer>>
HostBuffer::EmplaceInternal(const void* buffer, size_t length, size_t align) {
if (align == 0 || (GetLength() % align) == 0) {
return EmplaceInternal(buffer, length);
}
{
auto padding = align - (GetLength() % align);
if (offset_ + padding < kAllocatorBlockSize) {
offset_ += padding;
} else {
MaybeCreateNewBuffer();
}
}
return EmplaceInternal(buffer, length);
}
void HostBuffer::Reset() {
// When resetting the host buffer state at the end of the frame, check if
// there are any unused buffers and remove them.
while (device_buffers_[frame_index_].size() > current_buffer_ + 1) {
device_buffers_[frame_index_].pop_back();
}
offset_ = 0u;
current_buffer_ = 0u;
frame_index_ = (frame_index_ + 1) % kHostBufferArenaSize;
}
} // namespace impeller