Skip to content

[Flutter GPU] Reuse HostBuffer blocks after a block boundary, and view only the write - #192398

Draft
dzolotov wants to merge 1 commit into
flutter:masterfrom
pleiondev:hostbuffer-reuse-blocks
Draft

dzolotov wants to merge 1 commit into
flutter:masterfrom
pleiondev:hostbuffer-reuse-blocks

Conversation

@dzolotov

@dzolotov dzolotov commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

HostBuffer._allocateEmplacement's block-rollover branch always allocated a new block and appended it, without checking whether the frame slot already held one at that cursor from an earlier frame. reset() only rewinds the cursors, so every frame whose transient writes crossed a block boundary appended another blockLengthInBytes block, for the life of the HostBuffer. The same branch also returned a BufferView describing the whole block rather than the write it had just placed.

Fixes #192392

The off-by-one in the size check itself is left alone here — that is #186526, with PR #186539 already open for it.

The two new tests need a block boundary, so they create a HostBuffer with a two-alignment block rather than the 1 MB default. The existing HostBuffer reuses DeviceBuffers after N frames test only ever exercises the first block, which is allocated in the constructor, and that is why the missing reuse of later blocks went unnoticed.

Verified locally on macOS/Metal, host_debug_unopt_arm64. gpu_test.dart run under flutter_tester --enable-impeller --impeller-backend=metal --enable-flutter-gpu passes: 99 passed, 1 skipped. Reverting only the buffer.dart change and rebuilding makes exactly the two new tests fail, which is what they are there for:

HostBuffer.emplace across a block boundary views only the write
  Expected: <4>
    Actual: <512>
HostBuffer reuses blocks allocated after a block boundary
  Expected: <Instance of 'DeviceBuffer'>
    Actual: <Instance of 'DeviceBuffer'>

The reproduction from the issue, run on macOS/Metal against a 3.47.0 SDK with the same patch applied:

before after
distinct DeviceBuffers over 200 crossings 200 4 (frameCount)
blocks never handed back 196 (~191 MB of blocks) 0
rollover BufferView.lengthInBytes for a 256-byte write 1024000 256

(An earlier version of this description also quoted process RSS. I've dropped it: the reproduction allocates 200 MB of ByteData of its own, so RSS varies by ~120 MB between runs of the same binary and says nothing useful here. The block count is the stable measurement.)

Pre-launch Checklist

@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. flutter-gpu team-fluttergpu Owned by Flutter GPU team labels Sep 7, 2026
@github-project-automation github-project-automation Bot moved this to 🤔 Needs Triage in Flutter GPU Sep 7, 2026
@google-cla

google-cla Bot commented Sep 7, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@dzolotov
dzolotov force-pushed the hostbuffer-reuse-blocks branch from a3d3cf9 to 9b81dd9 Compare September 8, 2026 08:16
@dzolotov
dzolotov marked this pull request as ready for review September 8, 2026 08:26

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates HostBuffer in the Flutter GPU library to reuse previously allocated blocks across frames when crossing block boundaries, and corrects the lengthInBytes of the returned BufferView to match the actual written bytes. Unit tests were added to verify these changes. Feedback suggests simplifying the block retrieval logic in buffer.dart by extracting the frame buffers list into a local variable and refactoring the conditional check to improve readability.

Comment on lines +271 to +277
final DeviceBuffer buffer;
if (_bufferCursor < _buffers[_frameCursor].length) {
buffer = _buffers[_frameCursor][_bufferCursor];
} else {
buffer = _allocateNewBlock(blockLengthInBytes);
_buffers[_frameCursor].add(buffer);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block can be simplified to improve readability and maintainability. By extracting _buffers[_frameCursor] into a local variable, we can avoid repeated list lookups. Additionally, we can eliminate the uninitialized final DeviceBuffer buffer; declaration and the else block by checking if we need to append a new block first, and then unconditionally retrieving the buffer at _bufferCursor.

      final List<DeviceBuffer> frameBuffers = _buffers[_frameCursor];
      if (_bufferCursor >= frameBuffers.length) {
        frameBuffers.add(_allocateNewBlock(blockLengthInBytes));
      }
      final DeviceBuffer buffer = frameBuffers[_bufferCursor];
References
  1. Suggest simplification and refactoring: Assess whether the code can be made simpler or refactored to enhance readability and maintainability. (link)

The block-rollover branch of _allocateEmplacement always allocated a new
block and appended it, without checking whether the frame slot already
held one at that cursor from an earlier frame. reset() only rewinds the
cursors, so every frame whose transient writes crossed a boundary
appended another block, for the life of the HostBuffer.

The same branch also returned a BufferView describing the whole block
rather than the write it had just placed.
@dzolotov
dzolotov force-pushed the hostbuffer-reuse-blocks branch from 9b81dd9 to 4266af5 Compare September 8, 2026 08:33
@dzolotov dzolotov changed the title [Flutter GPU] Reuse HostBuffer blocks after a block boundary [Flutter GPU] Reuse HostBuffer blocks after a block boundary, and view only the write Sep 8, 2026
@dzolotov
dzolotov marked this pull request as draft October 21, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine flutter/engine related. See also e: labels. flutter-gpu team-fluttergpu Owned by Flutter GPU team

Projects

Status: 🤔 Needs Triage

Development

Successfully merging this pull request may close these issues.

[Flutter GPU] HostBuffer never reuses a block after a boundary crossing, leaking one block per frame

1 participant