Skip to content

[Flutter GPU] DeviceBuffer.flush() with defaults flushes the whole buffer - #194162

Draft
dzolotov wants to merge 1 commit into
flutter:masterfrom
pleiondev:gpu-fix/device-buffer-flush-whole-buffer
Draft

dzolotov wants to merge 1 commit into
flutter:masterfrom
pleiondev:gpu-fix/device-buffer-flush-whole-buffer

Conversation

@dzolotov

Copy link
Copy Markdown
Contributor

DeviceBuffer.flush() defaults lengthInBytes to -1, documented as "the whole buffer", but InternalFlutterGpu_DeviceBuffer_Flush passed it straight into an impeller::Range of size_t, so flush() became Flush(Range{0, SIZE_MAX}). On OpenGL ES, DeviceBufferGLES keeps that as its dirty range and uploads it with glBufferSubData(..., SIZE_MAX, ...), which fails with GL_INVALID_VALUE: the data never reaches the GPU, and a debug engine aborts on the GL error check. Metal gets a didModifyRange: past the end of the buffer. Vulkan only worked because SIZE_MAX happens to equal VK_WHOLE_SIZE.

The native entry point now treats a negative length as "to the end of the buffer" and returns false for a range outside the buffer, with the same offset bound the Dart side checks. The Dart binding was declared as returning Void, so that result was dropped; it is Bool now, and flush throws when the engine refuses, the way overwrite does. -1 is normalized in one place, on the native side, so the entry point can't build an oversized range whoever calls it.

device_buffer_unittests.cc is new and uses a MockDeviceBuffer with MOCK_METHOD(Flush) to check the Range that reaches Flush.

Where it was verified. macOS arm64, host_debug_unopt_arm64, rebased on current main. Without the change three of the four C++ tests fail (Flush(Range{0, 0xFFFFFFFFFFFFFFFF})), and a new gpu_test.dart test that draws a triangle from a buffer flushed with defaults aborts under flutter_tester_opengles with Fatal GL Error GL_INVALID_VALUE(1281) encountered on call to glBufferSubData; Metal and Vulkan (SwiftShader) pass it. With the change gpu_unittests --gtest_filter='FlutterGpuDeviceBufferTest.*:FlutterGpuRenderPassTest.*:FlutterGpuRenderPipelineTest.*:FlutterGpuShaderLibraryTest.*' is 17/17 and gpu_test.dart passes on all three backends. FlutterGpuCommandBufferTest.* is left out of the filter because it aborts in unopt builds on main as well (#194149).

Fixes #194152

Pre-launch Checklist

…ffer

The default lengthInBytes of -1 reached the native side unchanged and
became impeller::Range(offset, SIZE_MAX). On OpenGL ES that range is
later uploaded with glBufferSubData, which fails with GL_INVALID_VALUE,
so the flushed data never reached the GPU (and a debug engine aborted).

The native entry point now maps a negative length to the rest of the
buffer and rejects an offset outside the buffer (the same bound as the
Dart checks) or a range past its end. It already returned a bool, but
Dart declared the call as returning void and dropped it; Dart now reads
the result and throws when the flush is refused, as overwrite does.
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. flutter-gpu team-fluttergpu Owned by Flutter GPU team labels Oct 11, 2026
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] DeviceBuffer.flush() with default arguments flushes a range of SIZE_MAX bytes

1 participant