-
-
Notifications
You must be signed in to change notification settings - Fork 22
Description
I would like to preface this by apologizing for the constant API changes to the prepareDispatch
API 🙇
root.prepare*
Though I was initially resistant to putting prepareDispatch
on the root, as it seemed that putting more and more utilities on it would quickly balloon its size, the prepare
APIs (prepareDispatch being the one implemented so far) seem to cover most compute and rendering use-cases, meaning it would make sense to showcase them early in the documentation and use them extensively in our examples. That would mean shoving them into the "Utils" section of our docs is counterintuitive.
It also, imo, looks better:
const foo = prepareDispatch(root, () => {
// ...
});
// vs
const foo = root.prepareDispatch(() => {
// ...
});
Note
If you agree with the above, l suggest we put the "prepare" APIs on the root
What should root.prepare* return?
After recent changes to the API to allow "root.prepareDispatch" to use bind group layouts, the API no longer returns a function, but an object with a "dispatch" method. After taking a closer look, it really seems not that far away from creating a compute pipeline. We call an API on the root, we provide a shader function, and it returns an object that we call dispatch (or dispatchWorkgroups) on. The only problem is that dispatch
works in units of threads, and dispatchWorkgroups
works in units of workgroups. It's fine for prepareDispatch
to work in units of threads, as it has automatic boundary checks. We could do the same for regular pipelines, but we either introduce additional overhead, or let users debug why it's not running the exact number of threads they requested.
We could make this behavior more explicit by renaming dispatch
to e.g. dispatchAtLeast
🤔
This change would also remove the confusion as to how to call objects returned by prepareDispatch
, since they're just pipelines.
prepareCompute
and prepareRender
If we do go ahead with returning pipelines, prepare*
APIs become basically shorthands for creating compute and render pipelines. To avoid adding another keyword to the mix ("dispatch"), I suggest we call the APIs prepareCompute
and prepareRender
respectively.
// Compute pipeline
const pipeline = root.prepareCompute(() => {
'kernel';
counter.$ += 1;
});
pipeline.dispatchAtLeast(1);
// Render pipeline
const pipeline = root.prepareRender({
// -- No attributes by default
// attributes: {},
// -- Preferred presentation format by default
// output: {},
vertex: ({ $vertexIndex }) => {
'kernel';
const pos = [d.vec2f(-1, -1), d.vec2f(3, -1), d.vec2f(-1, 3)];
return {
$position: d.vec4f(pos[$vertexIndex], 0, 1),
};
},
fragment: () => {
'kernel';
return d.vec4f(1, 0, 0, 1);
},
});
pipeline.withColorAttachment({ view }).draw(3);
Radical idea number 1543
We're sure that the pipelines returned by prepareCompute
can execute an "exact" number of threads. We could tag them on the type level, which would unlock the dispatch()
method.