-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use objc2
instead of objc
#241
base: master
Are you sure you want to change the base?
Conversation
46f2ffa
to
9d20a2f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kvark @cwfitzgerald I think I'm ready for some feedback on this before I proceed; there's a lot of things going on here, but I'd like you to take a look at e.g. the last commit, where I've changed things to use the extern_class!
, extern_protocol!
and msg_send_id!
macros.
Feel free to ask me to clarify things about objc2
in general, I recognize that the documentation of it is not (yet) ideal.
The pull request title appears to be wrong, shouldn't objc and objc2 be swapped in the title? |
An update on this: I decided to make an automatically generated wrapper for Objective-C frameworks called In madsmtm/objc2#329 I've added support for Metal, with the primary addition being the file This library fixes the same issues as described in the PR description, as well as paving a way forwards for many other improvements, such as:
I'm still working on improving how protocols are mapped, which has a big effect on Metal - would like some help with this in madsmtm/objc2#291 (and maybe Metal protocols should work differently from other frameworks?). I would also like to discuss with the maintainers (@kvark @cwfitzgerald); what do you think the best way forwards is? I shall be blunt 😉: My ideal future would be for the To achieve this I'm very open to changing things that you may be unhappy with, like the method naming scheme or adding collaborators to I'm reachable on Matrix as |
Looks really great so far! Awesome work and I'm excited about fixing some of the autorelease pool and selector issues we've discussed over the years. I'm a little hesitant about generating all bindings and placing them all in icrate. A few other projects have taken a similar approach to replace objc (e.g., objr, fruity, objrs, etc.) but have never achieved broad adoption by the existing ecosystem, so I'm not confident that objc2/icrate will become the standard yet (though I acknowledge that this is a bit of a chicken-and-egg problem). For example, ideally I don't think it should be the metal's crate responsibility to say that we only work with Any of the following would help a lot:
I'm also curious if there's a way we could adopt generated bindings internally without breaking the existing API too much (instead of, or as a temporary measure while we work towards being able to do
For what it's worth, we wouldn't want compile-time detection for this in all cases. For example, wgpu's Metal backend would want to be able to compile against the highest possible deployment target and decide at runtime which methods to use. |
Thanks! It's great to hear you raise some concerns, I'll try to respond to them below:
Yeah, I'm aware, though still very much a valid point. If it helps, I'm the maintainer of
Metal doesn't actually use In my defence,
Yeah, will hopefully get better with time. I'll probably make a call out on TWiR at some point for starters.
I think it actually does really well on this mark already (I was surprised myself, I thought I'd have to implement a bunch more hacks for Metal), the only real burden was in creating the data file for which methods are
That's indeed on my roadmap: madsmtm/objc2#174
Could you provide an example of what you mean here?
Wrt. compilation time, I'm working on a system to help with this in madsmtm/objc2#311. Runtime performance is kinda difficult to prove before the entirety of the migration is done, but I can at least say that the project has assembly-level tests for ensuring that the autorelease optimizations do happen.
It is very much possible to do something like this: use icrate::Metal::MTLDepthStencilDescriptor;
#[repr(u64)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MTLCompareFunction {
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7,
}
// from_icrate and into_icrate for MTLCompareFunction, unless we simply export icrate's MTLCompareFunction
foreign_obj_type! {
type CType = MTLDepthStencilDescriptor;
pub struct DepthStencilDescriptor;
}
// Will now translate to roughly
{
pub struct DepthStencilDescriptor(Id<MTLDepthStencilDescriptor, Shared>);
#[repr(transparent)]
pub struct DepthStencilDescriptorRef(MTLDepthStencilDescriptor);
impl Deref<Target = DepthStencilDescriptorRef> for DepthStencilDescriptor { ... }
impl DepthStencilDescriptor {
// Could also be a `From` implementation, depending on how much you want
// to expose that you're using `icrate` under the hood.
fn from_icrate(obj: Id<MTLDepthStencilDescriptor, Shared>) -> Self {
Self(obj)
}
fn into_icrate(...) { ... }
}
// from_icrate and into_icrate for DepthStencilDescriptorRef
}
impl DepthStencilDescriptor {
pub fn new() -> Self {
Self(MTLDepthStencilDescriptor::new())
}
}
impl DepthStencilDescriptorRef {
pub fn depth_compare_function(&self) -> MTLCompareFunction {
MTLCompareFunction::from_objc2(self.0.depthCompareFunction())
}
pub fn set_depth_compare_function(&self, func: MTLCompareFunction) {
self.0.setDepthCompareFunction(func.into_objc2());
}
pub fn depth_write_enabled(&self) -> bool {
self.0.isDepthWriteEnabled();
}
pub fn set_depth_write_enabled(&self, enabled: bool) {
self.0.setDepthWriteEnabled(enabled);
}
pub fn front_face_stencil(&self) -> Option<StencilDescriptor> {
self.frontFaceStencil().map(StencilDescriptor::from_objc2)
}
pub fn set_front_face_stencil(&self, descriptor: Option<&StencilDescriptorRef>) {
self.setFrontFaceStencil(descriptor.map(|d| d.into_objc2()))
}
// ...
// Inefficient, but correct! An alternative would be to return a newtype `MetalString(Id<NSString, Shared>)` instead.
pub fn label(&self) -> String {
self.0.label().to_string()
}
pub fn set_label(&self, label: &str) {
self.0.setLabel(&NSString::from_str(label));
}
} And this But yes, it would definitely be a way forward, and I could probably do it at some point if you want.
The plan is to have the checks be made at runtime, but in a way such that you'll get a panic if you use some functionality without having made the runtime check beforehand, similar to Swift's Again, thanks for the kind response! |
96fb19c
to
88b3aa2
Compare
Okay, so to get this moving forwards, I've reduced this PR to only a "move to So, the primary thing that's changed is that all types now implement Re compilation time, a clean build has not changed much, we've traded the Note: This PR is a breaking change, both because of the changed
|
- argument.rs: These previously returned the owned types, which would cause a double-release - library.rs: These returned `&*mut NSArray` instead of `&NSArray` - device.rs: `retain` returns a pointer to the object, not `void`/`()`
NSUInteger is now usize, so a bunch of `as u64` casts were removed
Just tested, all the examples except the ones in #282 now work (the errors in that issue are caught by debug assertions, which is why they don't). |
@grovesNL: I'm a bit unsure because I think you asked about a lot of things that are not strictly relevant to this PR anymore (since I've kept the autogenerated bindings out for now), but think I've resolved/answered your questions, are there any outstanding concerns, or something else? CC @kvark, @cwfitzgerald |
Is there anything blocking the merge of this PR? I'm happy to help if needed. |
Replace
objc
with my fork,objc2
.This crate contains many improvements, in particular:
bool
inmsg_send!
.msg_send_id!
macro greatly helps with follow memory management rules (similar to Objective-C ARC), potentially also alleviating a lot of the need for autoreleasepools.extern_protocol!
macro is used in place offoreign_obj_type!
Notable breaking changes:
objc2::Message
differs fromobjc::Message
, so message sending is not compatibleNSUInteger
is nowusize
instead ofu32
/u64
depending on platform (equivalently forNSInteger
)There is no longer three types for each class (e.g.
MTLFoo
,Foo
andFooRef
); these have been combined into one:Foo
. Pointers to this can be used across FFI boundaries, andobjc2::rc::Id<Foo, Shared>
can be used as a strong pointer. In table form:*mut MTLFoo
*mut Foo
&FooRef
&Foo
Foo
Id<Foo, Shared>
When done, this should resolve the following issues:
new
convention #128new_render_command_encoder
assumes the return value can never be NULL #282new_texture
ifnewTextureWithDescriptor
fails? #284