Skip to content

Commit c8f2f16

Browse files
auto-submit[bot]auto-submit[bot]
andauthored
Reverts "[Flutter GPU] Add explicit draw counts (flutter#186639)" (flutter#187170)
<!-- start_original_pr_link --> Reverts: flutter#186639 <!-- end_original_pr_link --> <!-- start_initiating_author --> Initiated by: cbracken <!-- end_initiating_author --> <!-- start_revert_reason --> Reason for reverting: doesn't compile on mac, or Linux. Mac: ``` ../../flutter/testing/dart/gpu_shader_reload_test.dart:64:36: Error: Too many positional arguments: 1 allowed, but 2 found. Try removing the extra positional arguments. state.renderPass.bindVertexBuffer( ^ ../../flutter/testing/dart/gpu_shader_reload_test.dart:72:24: Error: Too few positional arguments: 1 requ <!-- end_revert_reason --> <!-- start_original_pr_author --> Original PR Author: bdero <!-- end_original_pr_author --> <!-- start_reviewers --> Reviewed By: {walley892} <!-- end_reviewers --> <!-- start_revert_body --> This change reverts the following previous change: Moves the vertex and index count off the buffer-binding calls and onto the draw call. A simple (breaking) change that fixes a very silly design flaw that I built into the API near the very beginning. Moves Flutter GPU draws towards a design that matches virtually all modern graphics APIs, including today's Impeller! Now is the right time to make changes like this to Flutter GPU, since the API is not yet considered stable. Before, `bindVertexBuffer` and `bindIndexBuffer` each took a count, and a single argument-less `draw()` guessed vertex versus index based on whether an index buffer happened to be bound. Sooooo many reasons this was the wrong design: - **Wrong object.** A draw count belongs on the draw, not on a buffer binding. - **Implicit kind.** Indexed vs. non-indexed was inferred from what happened to be bound, not stated. - **Colliding counts.** Vertex and index counts shared one field; the vertex count was dropped when an index buffer was bound. - **Slot-0-only.** The count came from vertex slot 0; values on other slots were silently ignored. - **Opaque draws.** `draw()` told you nothing; you had to trace every bind to know what it drew. - **Sticky state.** The count leaked across draws in a pass. - **Redundant re-binds.** Changing only the count forced re-binding the buffer. - **No room to grow.** Nowhere natural for per-draw params like `instanceCount` or `baseVertex`. - **Unconventional.** WebGPU, Vulkan, Metal, and D3D all put counts on the draw. - **Late errors.** Bad counts failed at `draw()` time, far from the mistaken line. Now: - `bindVertexBuffer` and `bindIndexBuffer` only bind buffers. - `draw(vertexCount)` does a non-indexed draw. - `drawIndexed(indexCount)` does an indexed draw. Instanced draws can be landed later via `draw` and `drawIndexed` gaining an `instanceCount` parameter later on, assuming the Impeller-side instancing support in flutter#186653 lands. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md <!-- end_revert_body --> Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
1 parent e2ef7d8 commit c8f2f16

6 files changed

Lines changed: 106 additions & 166 deletions

File tree

engine/src/flutter/impeller/fixtures/dart_tests.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ void canCreateRenderPassAndSubmit(int width, int height) {
101101
0, 1, 0, 1, // color
102102
]),
103103
);
104-
encoder.bindVertexBuffer(vertices);
104+
encoder.bindVertexBuffer(vertices, 3);
105105

106106
final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo');
107107
encoder.bindUniform(vertInfo, vertInfoData);
108-
encoder.draw(3);
108+
encoder.draw();
109109

110110
commandBuffer.submit();
111111

engine/src/flutter/lib/gpu/lib/src/buffer.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 {
5858
RenderPass renderPass,
5959
int offsetInBytes,
6060
int lengthInBytes,
61+
int vertexCount,
6162
int slot,
6263
) {
6364
renderPass._bindVertexBufferDevice(
6465
this,
6566
offsetInBytes,
6667
lengthInBytes,
68+
vertexCount,
6769
slot,
6870
);
6971
}
@@ -73,12 +75,14 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 {
7375
int offsetInBytes,
7476
int lengthInBytes,
7577
IndexType indexType,
78+
int indexCount,
7679
) {
7780
renderPass._bindIndexBufferDevice(
7881
this,
7982
offsetInBytes,
8083
lengthInBytes,
8184
indexType.index,
85+
indexCount,
8286
);
8387
}
8488

engine/src/flutter/lib/gpu/lib/src/render_pass.dart

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -302,22 +302,26 @@ base class RenderPass extends NativeFieldWrapperClass1 {
302302
/// pipeline's `VertexLayout`. The default of 0 matches the default
303303
/// layout declared by a shader bundle, which is what every single-buffer
304304
/// call site expects. To bind multiple structure-of-arrays vertex
305-
/// buffers, call this method once per slot.
306-
///
307-
/// The number of vertices to draw is passed separately, to [draw].
305+
/// buffers, call this method once per slot. The [vertexCount] takes
306+
/// effect only when no index buffer is bound, and is read from the
307+
/// binding at slot 0, so values supplied at higher slots are ignored.
308308
///
309309
/// Sparse bindings are not supported. Every slot in `[0, highestBound]`
310-
/// must have been bound before [draw] or [drawIndexed] is called,
311-
/// otherwise they throw a [StateError] naming the unbound slots. Slots
312-
/// can be bound in any order.
310+
/// must have been bound before [draw] is called, otherwise [draw] throws
311+
/// a [StateError] naming the unbound slots. Slots can be bound in any
312+
/// order.
313313
///
314314
/// [slot] must be in `[0, 16)` (Impeller's HAL caps vertex buffer
315315
/// bindings at 16). On the OpenGL ES backend, the per-pipeline limit on
316316
/// the *total attribute count* across all bound buffers is whatever the
317317
/// device reports for `GL_MAX_VERTEX_ATTRIBS` (minimum 8 on GL ES 2.0,
318318
/// minimum 16 on GL ES 3.0+), and is enforced by the driver rather than
319319
/// by this method.
320-
void bindVertexBuffer(BufferView bufferView, {int slot = 0}) {
320+
void bindVertexBuffer(
321+
BufferView bufferView,
322+
int vertexCount, {
323+
int slot = 0,
324+
}) {
321325
if (slot < 0 || slot >= _kMaxVertexBufferSlots) {
322326
throw RangeError.range(
323327
slot,
@@ -335,20 +339,22 @@ base class RenderPass extends NativeFieldWrapperClass1 {
335339
this,
336340
bufferView.offsetInBytes,
337341
bufferView.lengthInBytes,
342+
vertexCount,
338343
slot,
339344
);
340345
}
341346

342-
/// Binds [bufferView] as the index buffer.
343-
///
344-
/// [indexType] is the width of each index. The number of indices to draw
345-
/// is passed separately, to [drawIndexed].
346-
void bindIndexBuffer(BufferView bufferView, IndexType indexType) {
347+
void bindIndexBuffer(
348+
BufferView bufferView,
349+
IndexType indexType,
350+
int indexCount,
351+
) {
347352
bufferView.buffer._bindAsIndexBuffer(
348353
this,
349354
bufferView.offsetInBytes,
350355
bufferView.lengthInBytes,
351356
indexType,
357+
indexCount,
352358
);
353359
}
354360

@@ -499,29 +505,7 @@ base class RenderPass extends NativeFieldWrapperClass1 {
499505
_setWindingOrder(windingOrder.index);
500506
}
501507

502-
/// Appends a non-indexed draw of [vertexCount] vertices, read from the
503-
/// bound vertex buffers.
504-
void draw(int vertexCount) {
505-
RangeError.checkNotNegative(vertexCount, 'vertexCount');
506-
_validateVertexBindings();
507-
if (!_draw(vertexCount)) {
508-
throw Exception("Failed to append draw");
509-
}
510-
}
511-
512-
/// Appends an indexed draw of [indexCount] indices, read from the index
513-
/// buffer bound with [bindIndexBuffer].
514-
void drawIndexed(int indexCount) {
515-
RangeError.checkNotNegative(indexCount, 'indexCount');
516-
_validateVertexBindings();
517-
if (!_drawIndexed(indexCount)) {
518-
throw Exception("Failed to append drawIndexed");
519-
}
520-
}
521-
522-
/// Throws a [StateError] when the bound vertex buffer slots are sparse,
523-
/// naming the slots in `[0, highestBound]` that were left unbound.
524-
void _validateVertexBindings() {
508+
void draw() {
525509
if (_maxBoundVertexSlot >= 0) {
526510
final int expectedMask = (1 << (_maxBoundVertexSlot + 1)) - 1;
527511
if (_boundVertexSlotsMask != expectedMask) {
@@ -530,12 +514,15 @@ base class RenderPass extends NativeFieldWrapperClass1 {
530514
if ((_boundVertexSlotsMask & (1 << i)) == 0) i,
531515
];
532516
throw StateError(
533-
'draw called with sparse vertex buffer bindings: slot(s) '
517+
'draw() called with sparse vertex buffer bindings: slot(s) '
534518
'${missing.join(', ')} were not bound but slot $_maxBoundVertexSlot '
535519
'was. Bind every slot in [0, $_maxBoundVertexSlot] before drawing.',
536520
);
537521
}
538522
}
523+
if (!_draw()) {
524+
throw Exception("Failed to append draw");
525+
}
539526
}
540527

541528
/// Wrap with native counterpart.
@@ -604,24 +591,26 @@ base class RenderPass extends NativeFieldWrapperClass1 {
604591
)
605592
external void _bindPipeline(RenderPipeline pipeline);
606593

607-
@Native<Void Function(Pointer<Void>, Pointer<Void>, Int, Int, Int)>(
594+
@Native<Void Function(Pointer<Void>, Pointer<Void>, Int, Int, Int, Int)>(
608595
symbol: 'InternalFlutterGpu_RenderPass_BindVertexBufferDevice',
609596
)
610597
external void _bindVertexBufferDevice(
611598
DeviceBuffer buffer,
612599
int offsetInBytes,
613600
int lengthInBytes,
601+
int vertexCount,
614602
int slot,
615603
);
616604

617-
@Native<Void Function(Pointer<Void>, Pointer<Void>, Int, Int, Int)>(
605+
@Native<Void Function(Pointer<Void>, Pointer<Void>, Int, Int, Int, Int)>(
618606
symbol: 'InternalFlutterGpu_RenderPass_BindIndexBufferDevice',
619607
)
620608
external void _bindIndexBufferDevice(
621609
DeviceBuffer buffer,
622610
int offsetInBytes,
623611
int lengthInBytes,
624612
int indexType,
613+
int indexCount,
625614
);
626615

627616
@Native<
@@ -747,13 +736,8 @@ base class RenderPass extends NativeFieldWrapperClass1 {
747736
)
748737
external void _setPolygonMode(int polygonMode);
749738

750-
@Native<Bool Function(Pointer<Void>, Int)>(
739+
@Native<Bool Function(Pointer<Void>)>(
751740
symbol: 'InternalFlutterGpu_RenderPass_Draw',
752741
)
753-
external bool _draw(int vertexCount);
754-
755-
@Native<Bool Function(Pointer<Void>, Int)>(
756-
symbol: 'InternalFlutterGpu_RenderPass_DrawIndexed',
757-
)
758-
external bool _drawIndexed(int indexCount);
742+
external bool _draw();
759743
}

engine/src/flutter/lib/gpu/render_pass.cc

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ void RenderPass::ClearBindings() {
100100
vertex_buffer_count = 0;
101101
index_buffer = {};
102102
index_buffer_type = impeller::IndexType::kNone;
103+
element_count = 0;
103104
}
104105

105106
std::shared_ptr<impeller::Pipeline<impeller::PipelineDescriptor>>
@@ -181,12 +182,7 @@ RenderPass::GetOrCreatePipeline() {
181182
return pipeline;
182183
}
183184

184-
bool RenderPass::Draw(size_t element_count, bool indexed) {
185-
if (indexed && index_buffer_type == impeller::IndexType::kNone) {
186-
// drawIndexed was called without an index buffer bound.
187-
return false;
188-
}
189-
185+
bool RenderPass::Draw() {
190186
render_pass_->SetPipeline(impeller::PipelineRef(GetOrCreatePipeline()));
191187

192188
for (const auto& [_, buffer] : vertex_uniform_bindings) {
@@ -221,12 +217,7 @@ bool RenderPass::Draw(size_t element_count, bool indexed) {
221217
}
222218

223219
render_pass_->SetVertexBuffer(vertex_buffers.data(), vertex_buffer_count);
224-
if (indexed) {
225-
render_pass_->SetIndexBuffer(index_buffer, index_buffer_type);
226-
} else {
227-
render_pass_->SetIndexBuffer(impeller::BufferView{},
228-
impeller::IndexType::kNone);
229-
}
220+
render_pass_->SetIndexBuffer(index_buffer, index_buffer_type);
230221
render_pass_->SetElementCount(element_count);
231222

232223
render_pass_->SetStencilReference(stencil_reference);
@@ -343,6 +334,7 @@ static void BindVertexBuffer(
343334
const std::shared_ptr<const impeller::DeviceBuffer>& buffer,
344335
int offset_in_bytes,
345336
int length_in_bytes,
337+
int vertex_count,
346338
int slot) {
347339
if (slot < 0 || static_cast<size_t>(slot) >=
348340
flutter::gpu::RenderPass::kMaxVertexBufferSlots) {
@@ -353,37 +345,62 @@ static void BindVertexBuffer(
353345
if (static_cast<size_t>(slot) >= wrapper->vertex_buffer_count) {
354346
wrapper->vertex_buffer_count = static_cast<size_t>(slot) + 1;
355347
}
348+
349+
// `vertex_count` is only meaningful for slot 0: the Dart-side docstring
350+
// for `bindVertexBuffer` states that for slots > 0 the value is ignored
351+
// (the slot-0 binding determines the draw range). When the index type is
352+
// set, `vertex_count` would become the index count and is already set by
353+
// the index-buffer binding path, so we only assign it here if no index
354+
// buffer has been bound.
355+
// TODO(bdero): Consider just doing a more traditional API with
356+
// draw(vertexCount) and drawIndexed(indexCount). This is fine,
357+
// but overall it would be a bit more explicit and we wouldn't
358+
// have to document this behavior where the presence of the index
359+
// buffer always takes precedent.
360+
if (slot == 0 && !wrapper->has_index_buffer) {
361+
wrapper->element_count = vertex_count;
362+
}
356363
}
357364

358365
void InternalFlutterGpu_RenderPass_BindVertexBufferDevice(
359366
flutter::gpu::RenderPass* wrapper,
360367
flutter::gpu::DeviceBuffer* device_buffer,
361368
int offset_in_bytes,
362369
int length_in_bytes,
370+
int vertex_count,
363371
int slot) {
364372
BindVertexBuffer(wrapper, device_buffer->GetBuffer(), offset_in_bytes,
365-
length_in_bytes, slot);
373+
length_in_bytes, vertex_count, slot);
366374
}
367375

368376
static void BindIndexBuffer(
369377
flutter::gpu::RenderPass* wrapper,
370378
const std::shared_ptr<const impeller::DeviceBuffer>& buffer,
371379
int offset_in_bytes,
372380
int length_in_bytes,
373-
int index_type) {
381+
int index_type,
382+
int index_count) {
383+
impeller::IndexType type = flutter::gpu::ToImpellerIndexType(index_type);
374384
wrapper->index_buffer = impeller::BufferView(
375385
buffer, impeller::Range(offset_in_bytes, length_in_bytes));
376-
wrapper->index_buffer_type = flutter::gpu::ToImpellerIndexType(index_type);
386+
wrapper->index_buffer_type = type;
387+
388+
bool setting_index_buffer = type != impeller::IndexType::kNone;
389+
if (setting_index_buffer) {
390+
wrapper->element_count = index_count;
391+
}
392+
wrapper->has_index_buffer = setting_index_buffer;
377393
}
378394

379395
void InternalFlutterGpu_RenderPass_BindIndexBufferDevice(
380396
flutter::gpu::RenderPass* wrapper,
381397
flutter::gpu::DeviceBuffer* device_buffer,
382398
int offset_in_bytes,
383399
int length_in_bytes,
384-
int index_type) {
400+
int index_type,
401+
int index_count) {
385402
BindIndexBuffer(wrapper, device_buffer->GetBuffer(), offset_in_bytes,
386-
length_in_bytes, index_type);
403+
length_in_bytes, index_type, index_count);
387404
}
388405

389406
static bool BindUniform(
@@ -648,15 +665,6 @@ void InternalFlutterGpu_RenderPass_SetPolygonMode(
648665
flutter::gpu::ToImpellerPolygonMode(polygon_mode));
649666
}
650667

651-
bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper,
652-
int vertex_count) {
653-
// Guard the cast to the size_t element count; a negative value would wrap.
654-
return vertex_count >= 0 && wrapper->Draw(vertex_count, /*indexed=*/false);
655-
}
656-
657-
bool InternalFlutterGpu_RenderPass_DrawIndexed(
658-
flutter::gpu::RenderPass* wrapper,
659-
int index_count) {
660-
// Guard the cast to the size_t element count; a negative value would wrap.
661-
return index_count >= 0 && wrapper->Draw(index_count, /*indexed=*/true);
668+
bool InternalFlutterGpu_RenderPass_Draw(flutter::gpu::RenderPass* wrapper) {
669+
return wrapper->Draw();
662670
}

engine/src/flutter/lib/gpu/render_pass.h

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,7 @@ class RenderPass : public RefCountedDartWrappable<RenderPass> {
5656

5757
void ClearBindings();
5858

59-
/// Append a draw to the underlying render pass. [element_count] is the
60-
/// vertex count for a non-indexed draw, or the index count when
61-
/// [indexed] is true.
62-
bool Draw(size_t element_count, bool indexed);
59+
bool Draw();
6360

6461
struct BufferAndUniformSlot {
6562
impeller::ShaderUniformSlot slot;
@@ -91,11 +88,16 @@ class RenderPass : public RefCountedDartWrappable<RenderPass> {
9188
size_t vertex_buffer_count = 0;
9289
impeller::BufferView index_buffer;
9390
impeller::IndexType index_buffer_type = impeller::IndexType::kNone;
91+
size_t element_count = 0;
9492

9593
uint32_t stencil_reference = 0;
9694
std::optional<impeller::IRect32> scissor;
9795
std::optional<impeller::Viewport> viewport;
9896

97+
// Helper flag to determine whether the vertex_count should override the
98+
// element count. The index count takes precedent.
99+
bool has_index_buffer = false;
100+
99101
private:
100102
/// Lookup an Impeller pipeline by building a descriptor based on the current
101103
/// command state.
@@ -172,6 +174,7 @@ extern void InternalFlutterGpu_RenderPass_BindVertexBufferDevice(
172174
flutter::gpu::DeviceBuffer* device_buffer,
173175
int offset_in_bytes,
174176
int length_in_bytes,
177+
int vertex_count,
175178
int slot);
176179

177180
FLUTTER_GPU_EXPORT
@@ -180,7 +183,8 @@ extern void InternalFlutterGpu_RenderPass_BindIndexBufferDevice(
180183
flutter::gpu::DeviceBuffer* device_buffer,
181184
int offset_in_bytes,
182185
int length_in_bytes,
183-
int index_type);
186+
int index_type,
187+
int index_count);
184188

185189
FLUTTER_GPU_EXPORT
186190
extern bool InternalFlutterGpu_RenderPass_BindUniformDevice(
@@ -290,13 +294,7 @@ extern void InternalFlutterGpu_RenderPass_SetPolygonMode(
290294

291295
FLUTTER_GPU_EXPORT
292296
extern bool InternalFlutterGpu_RenderPass_Draw(
293-
flutter::gpu::RenderPass* wrapper,
294-
int vertex_count);
295-
296-
FLUTTER_GPU_EXPORT
297-
extern bool InternalFlutterGpu_RenderPass_DrawIndexed(
298-
flutter::gpu::RenderPass* wrapper,
299-
int index_count);
297+
flutter::gpu::RenderPass* wrapper);
300298

301299
} // extern "C"
302300

0 commit comments

Comments
 (0)