madsmtm / objc2

Bindings to Apple's frameworks in Rust
https://docs.rs/objc2/
MIT License
280 stars 35 forks source link

Add some examples/documentation for standard use cases (e..g, getting an ObjC struct into rust) #582

Closed marcpabst closed 1 week ago

marcpabst commented 3 months ago

I'm attempting to use objc2 for Apple iOS API interoperability, and I'm really struggling with basic tasks, such as retrieving an Objective-C struct (simd_float4x4, https://developer.apple.com/documentation/arkit/aranchor/2867981-transform?language=objc) from an object into Rust. Naively, I assumed this would work, but it doesn't:

#[repr(C)]
struct SimdFloat4x4 {
    columns: [SimdFloat4; 4],
}

#[repr(C)]
struct SimdFloat4 {
    x: f32,
    y: f32,
    z: f32,
    w: f32,
}

and then

let face_transform:  SimdFloat4x4 = unsafe { msg_send![anchor, transform] };

Could we have some more examples for things like this?

marcpabst commented 3 months ago

I think I will need to revise this question - turns out this is probbaly related with how Objective C handled SIMD types. I will close this one (tho I think this librry still lacks examples) and open a new one.

madsmtm commented 3 months ago

Well, it does say in the docs for msg_send!:

All arguments, as well as the return type, must implement Encode

And SimdFloat4 doesn't, because you haven't implemented that trait.

But I definitely recognize that this is not visible, and there should be some more introductory-level docs for this!

marcpabst commented 3 months ago

I think I was a bit tired and frustrated when writing this, my aplogies for that. I actually tried implementing Encode, but the code I copied in here is objc code (which actually works as intended). But as it turns out, SimdFloat4 is super opaque and apple seems to do some funny things under the hood. I only managed to get the first column out, the rest is all gibberish.

Now using some ObjectiveC glue code (basically reading all elements out and creating an array from that on the Objective C side) it works. But maybe worth keeping in mind when support for simd types is added!

madsmtm commented 3 months ago

I think I was a bit tired and frustrated when writing this, my aplogies for that

No problem, it didn't register as rude to me.

I actually tried implementing Encode

Huh, yeah, I see what you mean, Encoding can't actually represent the encoding of SIMD types, as they don't actually have an encoding (@encode(simd_float4) == "" and @encode(simd_float4x4) == "{?=[4]}", which our encoding routines can't handle).

This may actually a problem ABI-wise, because we use the encoding as part of figuring out the correct calling convention; but then again, a type like typedef __attribute__((__ext_vector_type__(1))) long double simd_longdouble; also breaks in Clang, so maybe it's not really an issue in practice.

I'll reopen this until I've investigated things a bit more, this is an issue that should be fixed.

marcpabst commented 3 months ago

Just to add some context, using objc I can get the first column of the matrix by "lying" and telling msg_send! I'm expecting a [f32, 4] array (or something memory-equivalent). But when I tell msg_send! I want a [f32, 16], it all breaks and all values are off.

madsmtm commented 3 months ago

From my reading of Clang's source code, the ABI expects us to use objc_msgSend and not objc_msgSend_stret in the following cases:

Though the code is quite involved, and I glossed over a lot of the details.

But it does seem like vector types at the very least need some sort of different handling to have the correct message-sending ABI. I don't know how vector types relate to SIMD, but they sound similar, so maybe SIMD types do also use a different ABI?

marcpabst commented 3 months ago

Yep, that's what I saw as well. I'm on aarch64 (iPad/MacBook Pro M2), so objc_msgSend should be the way to go, Does msg_send! default to objc_msgSend_stret?

madsmtm commented 3 months ago

msg_send! picks the right message sending function based on the return type, see: https://github.com/madsmtm/objc2/blob/155edc19bb3f1f3851679b277ad321cda4db365a/crates/objc2/src/runtime/message_receiver.rs#L53-L154

But I think this logic is subtly wrong for vector types, and that's what I want to fix.

All of this is only tangentially related to your problem of not being able to implement Encode correctly, and then panicking, that part should be fairly easy to solve.

marcpabst commented 3 months ago

But I tried with the objc crate that I assume used the same or simmiliar logics (but lacks the verification of encodings by default) - I presume this might be related to the way structs are handled?

madsmtm commented 3 months ago

I actually suspect the code example you provided might technically be wrong too on Aarch64, as the ABI of SIMD types isn't the same as the ABI of a C struct - it happens to work since you use the large simd_float4x4, but passing a type like simd_float4 across an ABI boundary requires you to use the corresponding Simd<f32, 4> / core::arch::aarch64::float32x4_t.

In general, I'm unsure of the state of SIMD FFI support? There's feature(simd_ffi), but that hasn't gotten far.

madsmtm commented 3 months ago

Re examples: There are some in objc2::encode, but is there another place where it would have been more visible / more easily understandable?

marcpabst commented 3 months ago

I think the examples are quite good already, but as some point it might be worth to add a short user guide / getting started page and then link to the examples.

madsmtm commented 1 week ago

I've added Encoding::None in https://github.com/madsmtm/objc2/pull/584 to allow you to specify when Clang doesn't emit an encoding for a type, no timeline on a release.

Note that this does not mean that objc2 officially supports SIMD types; the wrong message-sending function may still be chosen, and Rust's own support for it is yet lacking.

marcpabst commented 1 week ago

Great, thank you! I haven't looked into this for a while but will definitely come back to this shortly for an ongoing project.