rune
rune copied to clipboard
Allocate Capabilities Declaratively
At the moment we initialize a capability imperatively by asking for a blank capability of a particular type and then setting its parameters one-by-one before asking for data.
This works, but you suffer from the problem that the runtime doesn't see the full picture and can't know when the setup phase has finished.
@Ge-te mentioned on slack that it may be better to pass a full "capability spec" to the runtime when requesting capabilities.
Proposed Change
I propose altering the Rune-runtime interface.
// runic-types/src/wasm32/intrinsics.rs
extern "C" {
+ /// Allocate a new capability based on its JSON spec, returning an ID which can be
+ /// used to refer to the allocated capability.
+ ///
+ /// If the Runtime is unable to create the capability, execution will halt and this
+ /// function won't return.
+ ///
+ /// The `spec` string is only guaranteed to stay valid for the duration of the function
+ /// call.
+ fn rune_new_capability(spec: *const c_char, spec_len: u32) -> u32;
- pub fn request_capability(capability_type: u32) -> u32;
-
- pub fn request_capability_set_param(
- capability_id: u32,
- key_ptr: *const u8,
- key_len: u32,
- value_ptr: *const u8,
- value_len: u32,
- value_type: u32,
- ) -> u32;
}
Given this line in the Runefile...
CAPABILITY<U8[3, 640, 480]> some_capability IMAGE --pixel-format rgb --camera front
... the following "capability spec" is a JSON document will be passed to the runtime:
{
"capability": "IMAGE",
"output_type": "u8",
"output_shape": [3, 640, 480],
"args": {
"pixel_format": "rgb",
"camera": "front"
}
}
Where:
-
"capability"
contains one of"IMAGE"
,"RANDOM"
,"ACCELEROMETER"
,"SOUND"
,"RAW"
-
"output_shape"
is the shape of the (row major) multi-dimensional array returned by the capability -
"output_type"
specifies the tensor's element type, using the Rust names (i8
,u64
,f32
, etc.) -
"args"
is a map of key-value pairs where the value can be any arbitrary JSON value and makes sense to the capability
The rune-codegen
crate will then compile this down to:
// lib.rs
extern "C" fn _manifest() -> u32 {
let some_capability: Capability<u8> = Capability::new("{ ... }");
...
}
impl<T> Capability<T> {
fn new(spec: &str) -> Self {
let id = unsafe { intrinsics::rune_new_capability(spec.as_ptr(), spec.len()) };
Capability { id }
}
}
Actions
Implementing this will involve:
- [ ] Update
rune-codegen
to serialize the capability spec as JSON (derived from our Runefile) and arrange for it to be passed to the runtime - [ ] Create a
runic_types::wasm32::Capability<T>
type which will call the new host function with this JSON-serialized capability when instantiated - [ ] Remove the now-redundant
runic_types::Source
trait - [ ] Update the host function in our
runicos/base
image so the Rust runtime is able to use the new rune - [ ] Updating the host function in our C++ runtime,
rune_vm
, to use the new method of initializing capabilities - [ ] Wire this up to the Flutter app so we can actually create capabilities that read from the camera/accelerometer/whatever
- [ ] Recompile all Runes and re-upload them to the registry
- [ ] (optional) Figure out some sort of versioning mechanism so the app can detect when you are using an old/unsupported Rune and emit a human-friendly error instead of failing with an opaque "function not found", possibly using WebAssembly custom sections