createPipelineAsync method
- ShaderHandle vertex,
- ShaderHandle fragment, {
- VertexLayoutDescriptor? layout,
createPipeline, and the real pipelines it will need built off the
calling turn, through createRenderPipelineAsync.
What can be realised up front, and what cannot. A pipeline here is
a stage pair until a draw says the rest: the targets it draws into, the
blend, the depth test, the cull, the stencil. Those are baked into a
GPURenderPipeline, one per distinct state, and none of them is in
this call. So the pair is recorded as createPipeline records it, and
then warmed: built asynchronously in every draw state this device
has already built a pipeline in (the most recent
warmStateLimit of them), and kept in the same cache a draw looks in.
A draw in one of those states then finds its pipeline ready; a draw in
a state nobody drew before builds it synchronously, as always.
The future completes when the warming has settled. A pipeline the
browser refuses in one of those states is skipped — the state was a
guess, and a draw that really asks for it builds it and reports a
refusal on pipelineRefusals — while a pair that cannot be recorded at all (two
fragment stages, a stage from another backend, a layout that misses an
input) completes the future with the error.
Implementation
@override
Future<PipelineHandle> createPipelineAsync(
ShaderHandle vertex,
ShaderHandle fragment, {
VertexLayoutDescriptor? layout,
}) async {
final handle = _held(
createWebGpuPipeline(vertex, fragment, layout: layout, owner: this),
);
final pipeline = handle.backend as WebGpuPipeline;
// A pair whose layouts are refused has nothing to warm: its draws are
// skipped, and the first of them reports why.
final layouts = _bindingsFor(pipeline);
if (layouts == null) return handle;
await Future.wait(<Future<void>>[
for (final state in _drawStates.values.toList())
_warm(pipeline, layouts, state.forPipeline(pipeline)),
]);
return handle;
}