[Impeller] Emplace directly into host buffer (avoid VBB) for text data (flutter/engine#42484)

From local testing, this shaves off about 0.3-4 ms of pure allocation overhead from https://github.com/flutter/flutter/issues/127760

### Before

![image](https://github.com/flutter/engine/assets/8975114/55701559-fba8-4f11-b606-f819d197626e)

### After

![image](https://github.com/flutter/engine/assets/8975114/b6843c13-d6c7-4364-86b1-c78e216307b3)
This commit is contained in:
Jonah Williams 2023-06-01 15:34:58 -07:00 committed by GitHub
parent d72aace3ef
commit b06489fa4b
3 changed files with 77 additions and 47 deletions

View File

@ -56,6 +56,22 @@ BufferView HostBuffer::Emplace(const void* buffer, size_t length) {
return BufferView{shared_from_this(), GetBuffer(), Range{old_length, length}};
}
BufferView HostBuffer::Emplace(size_t length,
size_t align,
const EmplaceProc& cb) {
if (!cb) {
return {};
}
auto old_length = GetLength();
if (!Truncate(old_length + length)) {
return {};
}
generation_++;
cb(GetBuffer() + old_length);
return BufferView{shared_from_this(), GetBuffer(), Range{old_length, length}};
}
std::shared_ptr<const DeviceBuffer> HostBuffer::GetDeviceBuffer(
Allocator& allocator) const {
if (generation_ == device_buffer_generation_) {

View File

@ -95,6 +95,22 @@ class HostBuffer final : public std::enable_shared_from_this<HostBuffer>,
size_t length,
size_t align);
using EmplaceProc = std::function<void(uint8_t* buffer)>;
//----------------------------------------------------------------------------
/// @brief Emplaces undefined data onto the managed buffer and gives the
/// caller a chance to update it using the specified callback. The
/// buffer is guaranteed to have enough space for length bytes. It
/// is the responsibility of the caller to not exceed the bounds
/// of the buffer returned in the EmplaceProc.
///
/// @param[in] cb A callback that will be passed a ptr to the
/// underlying host buffer.
///
/// @return The buffer view.
///
BufferView Emplace(size_t length, size_t align, const EmplaceProc& cb);
private:
mutable std::shared_ptr<DeviceBuffer> device_buffer_;
mutable size_t device_buffer_generation_ = 0u;

View File

@ -135,60 +135,58 @@ static bool CommonRender(const ContentContext& renderer,
// interpolated vertex information is also used in the fragment shader to
// sample from the glyph atlas.
constexpr std::array<Point, 4> unit_points = {Point{0, 0}, Point{1, 0},
constexpr std::array<Point, 6> unit_points = {Point{0, 0}, Point{1, 0},
Point{0, 1}, Point{1, 0},
Point{0, 1}, Point{1, 1}};
constexpr std::array<uint32_t, 6> indices = {0, 1, 2, 1, 2, 3};
VertexBufferBuilder<typename VS::PerVertexData> vertex_builder;
size_t count = 0;
auto& host_buffer = pass.GetTransientsBuffer();
size_t vertex_count = 0;
for (const auto& run : frame.GetRuns()) {
count += run.GetGlyphPositions().size();
vertex_count += run.GetGlyphPositions().size();
}
vertex_count *= 6;
vertex_builder.Reserve(count * 4);
vertex_builder.ReserveIndices(count * 6);
auto buffer_view = host_buffer.Emplace(
vertex_count * sizeof(VS::PerVertexData), alignof(VS::PerVertexData),
[&](uint8_t* contents) {
VS::PerVertexData vtx;
size_t vertex_offset = 0;
for (const auto& run : frame.GetRuns()) {
const Font& font = run.GetFont();
for (const auto& glyph_position : run.GetGlyphPositions()) {
FontGlyphPair font_glyph_pair{font, glyph_position.glyph};
auto maybe_atlas_glyph_bounds =
atlas->FindFontGlyphBounds(font_glyph_pair);
if (!maybe_atlas_glyph_bounds.has_value()) {
VALIDATION_LOG << "Could not find glyph position in the atlas.";
continue;
}
auto atlas_glyph_bounds = maybe_atlas_glyph_bounds.value();
vtx.atlas_glyph_bounds = Vector4(
atlas_glyph_bounds.origin.x, atlas_glyph_bounds.origin.y,
atlas_glyph_bounds.size.width, atlas_glyph_bounds.size.height);
vtx.glyph_bounds = Vector4(glyph_position.glyph.bounds.origin.x,
glyph_position.glyph.bounds.origin.y,
glyph_position.glyph.bounds.size.width,
glyph_position.glyph.bounds.size.height);
vtx.glyph_position = glyph_position.position;
uint32_t index_offset = 0u;
for (auto i = 0u; i < count; i++) {
for (const auto& index : indices) {
vertex_builder.AppendIndex(index + index_offset);
}
index_offset += 4;
}
for (const auto& point : unit_points) {
vtx.unit_position = point;
::memcpy(contents + vertex_offset, &vtx,
sizeof(VS::PerVertexData));
vertex_offset += sizeof(VS::PerVertexData);
}
}
}
});
for (const auto& run : frame.GetRuns()) {
const Font& font = run.GetFont();
for (const auto& glyph_position : run.GetGlyphPositions()) {
FontGlyphPair font_glyph_pair{font, glyph_position.glyph};
auto atlas_glyph_bounds = atlas->FindFontGlyphBounds(font_glyph_pair);
if (!atlas_glyph_bounds.has_value()) {
VALIDATION_LOG << "Could not find glyph position in the atlas.";
return false;
}
Vector4 atlas_glyph_bounds_vec = Vector4(
atlas_glyph_bounds->origin.x, atlas_glyph_bounds->origin.y,
atlas_glyph_bounds->size.width, atlas_glyph_bounds->size.height);
Vector4 glyph_bounds_vec =
Vector4(glyph_position.glyph.bounds.origin.x,
glyph_position.glyph.bounds.origin.y,
glyph_position.glyph.bounds.size.width,
glyph_position.glyph.bounds.size.height);
for (const auto& point : unit_points) {
vertex_builder.AppendVertex(VS::PerVertexData{
.atlas_glyph_bounds = atlas_glyph_bounds_vec,
.glyph_bounds = glyph_bounds_vec,
.unit_position = point,
.glyph_position = glyph_position.position,
});
}
}
}
auto vertex_buffer =
vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer());
cmd.BindVertices(vertex_buffer);
cmd.BindVertices({
.vertex_buffer = buffer_view,
.index_buffer = {},
.vertex_count = vertex_count,
.index_type = IndexType::kNone,
});
return pass.AddCommand(cmd);
}