[Impeller] use BufferSubData to update gles device buffer. (flutter/engine#55330)

Measures as substantially faster on windows. See the bufferSubData docs:

> When replacing the entire data store, consider using glBufferSubData rather than completely recreating the data store with glBufferData. This avoids the cost of reallocating the data store.

Tracks invalidated ranges and only updates those ranges using the Flush API.

Fixes https://github.com/flutter/flutter/issues/104447
This commit is contained in:
Jonah Williams 2024-09-23 11:40:08 -07:00 committed by GitHub
parent 4b1e309dbe
commit d3235504c1
5 changed files with 87 additions and 14 deletions

View File

@ -79,5 +79,52 @@ TEST(AllocatorTest, TextureDescriptorCompatibility) {
}
}
TEST(AllocatorTest, RangeTest) {
{
Range a = Range{0, 10};
Range b = Range{10, 20};
auto merged = a.Merge(b);
EXPECT_EQ(merged.offset, 0u);
EXPECT_EQ(merged.length, 30u);
}
{
Range a = Range{0, 10};
Range b = Range{100, 20};
auto merged = a.Merge(b);
EXPECT_EQ(merged.offset, 0u);
EXPECT_EQ(merged.length, 120u);
}
{
Range a = Range{0, 10};
Range b = Range{100, 20};
auto merged = b.Merge(a);
EXPECT_EQ(merged.offset, 0u);
EXPECT_EQ(merged.length, 120u);
}
{
Range a = Range{0, 10};
Range b = Range{100, 0};
auto merged = b.Merge(a);
EXPECT_EQ(merged.offset, 0u);
EXPECT_EQ(merged.length, 10u);
}
{
Range a = Range{0, 10};
Range b = Range{0, 10};
auto merged = b.Merge(a);
EXPECT_EQ(merged.offset, 0u);
EXPECT_EQ(merged.length, 10u);
}
}
} // namespace testing
} // namespace impeller

View File

@ -5,6 +5,7 @@
#ifndef FLUTTER_IMPELLER_CORE_RANGE_H_
#define FLUTTER_IMPELLER_CORE_RANGE_H_
#include <algorithm>
#include <cstddef>
namespace impeller {
@ -21,6 +22,19 @@ struct Range {
constexpr bool operator==(const Range& o) const {
return offset == o.offset && length == o.length;
}
/// @brief Create a new range that is a union of this range and other.
constexpr Range Merge(const Range& other) {
if (other.length == 0) {
return *this;
}
if (length == 0) {
return other;
}
auto end_offset = std::max(offset + length, other.offset + other.length);
auto start_offset = std::min(offset, other.offset);
return Range{start_offset, end_offset - start_offset};
}
};
} // namespace impeller

View File

@ -7,10 +7,8 @@
#include <cstring>
#include <memory>
#include "flutter/fml/trace_event.h"
#include "impeller/base/allocation.h"
#include "impeller/base/config.h"
#include "impeller/base/validation.h"
namespace impeller {
@ -53,13 +51,22 @@ bool DeviceBufferGLES::OnCopyHostBuffer(const uint8_t* source,
std::memmove(backing_store_->GetBuffer() + offset,
source + source_range.offset, source_range.length);
++generation_;
Flush(Range{offset, source_range.length});
return true;
}
void DeviceBufferGLES::Flush(std::optional<Range> range) const {
generation_++;
if (!range.has_value()) {
dirty_range_ = Range{
0, static_cast<size_t>(backing_store_->GetLength().GetByteSize())};
} else {
if (dirty_range_.has_value()) {
dirty_range_ = dirty_range_->Merge(range.value());
} else {
dirty_range_ = range.value();
}
}
}
static GLenum ToTarget(DeviceBufferGLES::BindingType type) {
@ -86,14 +93,17 @@ bool DeviceBufferGLES::BindAndUploadDataIfNecessary(BindingType type) const {
const auto& gl = reactor_->GetProcTable();
gl.BindBuffer(target_type, buffer.value());
if (upload_generation_ != generation_) {
TRACE_EVENT1(
"impeller", "BufferData", "Bytes",
std::to_string(backing_store_->GetLength().GetByteSize()).c_str());
if (!initialized_) {
gl.BufferData(target_type, backing_store_->GetLength().GetByteSize(),
backing_store_->GetBuffer(), GL_STATIC_DRAW);
upload_generation_ = generation_;
nullptr, GL_DYNAMIC_DRAW);
initialized_ = true;
}
if (dirty_range_.has_value()) {
auto range = dirty_range_.value();
gl.BufferSubData(target_type, range.offset, range.length,
backing_store_->GetBuffer() + range.offset);
dirty_range_ = std::nullopt;
}
return true;
@ -122,7 +132,8 @@ void DeviceBufferGLES::UpdateBufferData(
if (update_buffer_data) {
update_buffer_data(backing_store_->GetBuffer(),
backing_store_->GetLength().GetByteSize());
++generation_;
Flush(Range{
0, static_cast<size_t>(backing_store_->GetLength().GetByteSize())});
}
}

View File

@ -44,8 +44,8 @@ class DeviceBufferGLES final
ReactorGLES::Ref reactor_;
HandleGLES handle_;
mutable std::shared_ptr<Allocation> backing_store_;
mutable uint32_t generation_ = 0;
mutable uint32_t upload_generation_ = 0;
mutable std::optional<Range> dirty_range_ = std::nullopt;
mutable bool initialized_ = false;
// |DeviceBuffer|
uint8_t* OnGetContents() const override;

View File

@ -104,6 +104,7 @@ struct GLProc {
PROC(BlendEquationSeparate); \
PROC(BlendFuncSeparate); \
PROC(BufferData); \
PROC(BufferSubData); \
PROC(CheckFramebufferStatus); \
PROC(Clear); \
PROC(ClearColor); \