You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Flutter GPU] Support configurable vertex layouts. #145013
Time to revisit this. The HAL work tracked in #116168 landed in October 2024, and after the cross-HAL design pass below the next step is a minimum-safe v1 that ships useful capability today without backing us into a corner once the deferred features come online. Posting this as a proposal so it can be torn apart before code.
v1 scope (no Impeller HAL changes required)
Everything below is expressible with ShaderStageIOSlot + ShaderStageBufferLayout + VertexDescriptor exactly as they exist today.
Dart surface:
// Closed enum of the 12 four-byte-element vertex formats that Impeller's// (type, bit_width, vec_size, columns) tuple can already express. Packed,// normalized, half-float, BGRA-swizzled, and 64-bit formats are deferred// until the HAL grows a real format enum or a normalize bit. Adding them// later is purely additive (new enum values).enumVertexFormat {
float32, float32x2, float32x3, float32x4,
uint32, uint32x2, uint32x3, uint32x4,
sint32, sint32x2, sint32x3, sint32x4;
intget bytesPerElement;
intget componentCount;
}
classVertexAttribute {
constVertexAttribute({
requiredthis.location,
requiredthis.bufferBinding,
requiredthis.offsetInBytes,
requiredthis.format,
});
finalint location;
finalint bufferBinding;
finalint offsetInBytes;
finalVertexFormat format;
}
classVertexBufferLayout {
constVertexBufferLayout({
requiredthis.binding,
requiredthis.strideInBytes,
// No stepMode/stepRate parameters in v1. Both can be added later as// named parameters with defaults without breaking existing callers.
});
finalint binding;
finalint strideInBytes;
}
classVertexLayout {
constVertexLayout({
requiredthis.buffers,
requiredthis.attributes,
});
finalList<VertexBufferLayout> buffers;
finalList<VertexAttribute> attributes;
}
Pipeline + RenderPass:
// GpuContext gains an optional vertexLayout parameter.// null means "use the impellerc-generated interleaved default" (today's behavior).RenderPipelinecreateRenderPipeline(
Shader vertex,
Shader fragment, {
VertexLayout? vertexLayout,
});
// RenderPass.bindVertexBuffer gains an optional slot parameter so callers// can bind multiple vertex buffers in an SoA layout. Default is 0, which// preserves the current single-buffer call sites unchanged.voidbindVertexBuffer(
BufferView bufferView,
int vertexCount, {
int slot =0,
});
Validation at createRenderPipeline time (each throws a Dart exception with a specific message):
format is one of the 12 enum values above.
attribute.offsetInBytes + format.bytesPerElement is at most layout.strideInBytes.
attribute.bufferBinding references a VertexBufferLayout.binding actually present in vertexLayout.buffers.
attribute.location corresponds to a vertex shader input declared in the bound shader (impellerc reflection has the data).
The number of buffers and attributes is within the limits reported by the device's Capabilities.
What v1 enables
Multi-buffer SoA layouts (positions in one buffer, normals plus UVs in another, colors in a third). Standard glTF loading shape.
Custom attribute orderings that diverge from the impellerc-generated default.
This closes the "no instancing, 4-byte-element formats only" slice of #145013, which is what most 3D asset pipelines actually need to load a glTF and draw it.
What v1 explicitly defers, and why each is a non-corner
Feature
HAL gate
How it gets added without breaking v1
VertexStepMode { vertex, instance }
Add inputRate to ShaderStageBufferLayout
New VertexStepMode stepMode = VertexStepMode.vertex named parameter on VertexBufferLayout. Existing const calls compile unchanged.
stepRate: int (instance divisor > 1)
VK_KHR_vertex_attribute_divisor (or 1.4 core) on the Vulkan backend
New int stepRate = 1 named parameter after stepMode.
Impeller doesn't currently use VK_EXT_vertex_input_dynamic_state
New RenderPass.setVertexLayout(VertexLayout), gated on Capabilities.supportsDynamicVertexLayout. Doesn't touch the v1 API surface.
64-bit attributes
Vulkan-only optional feature, vanishingly rare in practice
New enum values.
defaultVertexLayout retrieval (start from impellerc's layout and modify it)
Pure ergonomics, nothing in v1 prevents it
Add RenderPipeline.defaultVertexLayout getter or a VertexLayout.fromShader(Shader) constructor.
The two ways v1 could have locked us in are format expressiveness and attribute keying. Both are deliberately closed:
Format: a closed enum, not a tuple of (VertexElementType, bitWidth, components). Adding packed and normalized formats later is appending to the enum. No call-site migration.
Attribute keying: integer location, matching WebGPU, Vulkan, and Metal. impellerc already emits locations for the default layout. Name-based lookup is a future ergonomic helper, not load-bearing.
Concrete first PR
Add the four Dart classes (VertexFormat, VertexAttribute, VertexBufferLayout, VertexLayout).
Plumb the optional vertexLayout through GpuContext.createRenderPipeline to flutter::gpu::RenderPipeline, which builds an impeller::VertexDescriptor from the supplied layout instead of falling through to the impellerc reflection default.
Add the optional slot named parameter to RenderPass.bindVertexBuffer.
Validation plus a gpu_test.dart SoA test case (a triangle drawn from one position buffer plus one color buffer).
Spinoff issues to file alongside the PR
[Impeller] Add step mode (VkVertexInputRate-equivalent) to ShaderStageBufferLayout. Required for Flutter GPU instancing.
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.
Activity
bdero commentedon Oct 17, 2024
This is unblocked now that flutter-team-archive/engine#55856 has landed.
bdero commentedon May 10, 2026
Time to revisit this. The HAL work tracked in #116168 landed in October 2024, and after the cross-HAL design pass below the next step is a minimum-safe v1 that ships useful capability today without backing us into a corner once the deferred features come online. Posting this as a proposal so it can be torn apart before code.
v1 scope (no Impeller HAL changes required)
Everything below is expressible with
ShaderStageIOSlot+ShaderStageBufferLayout+VertexDescriptorexactly as they exist today.Dart surface:
Pipeline + RenderPass:
Validation at
createRenderPipelinetime (each throws a Dart exception with a specific message):formatis one of the 12 enum values above.attribute.offsetInBytes + format.bytesPerElementis at mostlayout.strideInBytes.attribute.bufferBindingreferences aVertexBufferLayout.bindingactually present invertexLayout.buffers.attribute.locationcorresponds to a vertex shader input declared in the bound shader (impellerc reflection has the data).Capabilities.What v1 enables
This closes the "no instancing, 4-byte-element formats only" slice of #145013, which is what most 3D asset pipelines actually need to load a glTF and draw it.
What v1 explicitly defers, and why each is a non-corner
VertexStepMode { vertex, instance }inputRatetoShaderStageBufferLayoutVertexStepMode stepMode = VertexStepMode.vertexnamed parameter onVertexBufferLayout. Existingconstcalls compile unchanged.stepRate: int(instance divisor > 1)VK_KHR_vertex_attribute_divisor(or 1.4 core) on the Vulkan backendint stepRate = 1named parameter afterstepMode.unorm8x4,snorm16x2, ...)normalizebit onShaderStageIOSlotor a real flat format enumVertexFormatenum values. Existing values keep their semantics.unorm10_10_10_2)(type, bit_width, vec_size, columns)VertexFormatenum values.unorm8x4_bgraswizzled formatstride=0ordivisor=0)VertexStepMode.constantorstepRate=0once it lands.vkCmdSetVertexInputEXTshape)VK_EXT_vertex_input_dynamic_stateRenderPass.setVertexLayout(VertexLayout), gated onCapabilities.supportsDynamicVertexLayout. Doesn't touch the v1 API surface.defaultVertexLayoutretrieval (start from impellerc's layout and modify it)RenderPipeline.defaultVertexLayoutgetter or aVertexLayout.fromShader(Shader)constructor.The two ways v1 could have locked us in are format expressiveness and attribute keying. Both are deliberately closed:
enum, not a tuple of(VertexElementType, bitWidth, components). Adding packed and normalized formats later is appending to the enum. No call-site migration.location, matching WebGPU, Vulkan, and Metal. impellerc already emits locations for the default layout. Name-based lookup is a future ergonomic helper, not load-bearing.Concrete first PR
VertexFormat,VertexAttribute,VertexBufferLayout,VertexLayout).vertexLayoutthroughGpuContext.createRenderPipelinetoflutter::gpu::RenderPipeline, which builds animpeller::VertexDescriptorfrom the supplied layout instead of falling through to the impellerc reflection default.slotnamed parameter toRenderPass.bindVertexBuffer.gpu_test.dartSoA test case (a triangle drawn from one position buffer plus one color buffer).Spinoff issues to file alongside the PR
VkVertexInputRate-equivalent) toShaderStageBufferLayout. Required for Flutter GPU instancing.ShaderStageIOSlot. Required for normalized, packed, BGRA, half-float, and 64-bit vertex formats. Closes [Impeller] Add feature to allow attribute mapping of uint32s host-side to vec4s shader-side #102778 once landed.VK_EXT_vertex_input_dynamic_state.[Flutter GPU] Allow customizing the vertex layout on a RenderPipeline
[Flutter GPU] Allow customizing the vertex layout on a RenderPipeline
[Flutter GPU] Allow customizing the vertex layout on a RenderPipeline
flutter-triage-bot commentedon May 10, 2026
The
triaged-enginelabel is irrelevant if there is noteam-enginelabel orfyi-enginelabel.[Flutter GPU] Allow customizing the vertex layout on a RenderPipeline
[Flutter GPU] Allow customizing the vertex layout on a RenderPipeline (…
github-actions commentedon May 26, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.