Skip to content

Conversation

@pavelkouril
Copy link
Contributor

@pavelkouril pavelkouril commented Sep 28, 2025

Creating as a draft, since there are unresolved things at the moment.

Things I am aware of at the moment:

1/ Parser
[x] Error in parser that puts the CreateDefaultSystemDevice in MTLArgumentDescriptor
[x] Inheritance across multiple levels is not propagated (Buffer -> Resource -> Allocation)
[x] No parsing of the obsolete tags

2/ Code Emitting
[x] Missing properties in MTL4 files
[x] Wrong emitted types
[x] Static properties are emited as method instead of property (e.g. NSBundle::allBundles())
[x] CAMetalLayer.PixelFormat emits only getter and uses the setter selector
[x] Missing MTL4BufferRange
[x] simd::float4x4 emit

3/ Examples
[x] Cannot be compiled due to the moving of CreateDefaultSystemDevice
[x] Examples crash upon start, probably due to some wrong selector being used now

Will tackle them one by one over the upcoming weeks, but it feels like some of these will require more substantial changes in the parsing/codegen (e.g. separation of the collecting and emitting passes - which would be a good idea anyways tbh).

(Will be squashed properly at the end before merging.)

@pavelkouril pavelkouril force-pushed the update-to-mtl4 branch 2 times, most recently from 7323e16 to 9d9a86b Compare October 5, 2025 11:32
- Removed automatic scoping of unscoped methods since they might fall into wrong type with header updates; logging them instead to provide the handwritten bindings seems like more safe option
- Fixed inheritance resolution (still not resolving the multiple inheritance, but that should not be anywhere in the project, i think)
- Fixed collecting of selectors split over 2 lines
- Fixed the get/set property pairs for is/set+is/setIs
- Support for duplicated obsolete properties
- Support for static properties
@pavelkouril
Copy link
Contributor Author

pavelkouril commented Oct 5, 2025

You can review it now. Not sure why the checks are failing, maybe because the PR is too big? :/

I tested the three examples and they work, but I might have introduced some other random error that is hard to find out based on the diffs. I also tested my small WIP project and that one works as well.

Since I made the property logic a little bit more robust, there are some small BC breaks in the C# API (when something was previously a method and now is consistently a property).

If you'd prefer the 2 codegen changes squashed together, as well as the 2 generated code ones, I can squash them into 1 codegen + 1 generated code.

If you want any other changes to the PR, I'll fix it.

P.S. I noticed some parts of the parsing that are kinda PITA, but this PR is bigger than I wanted it to be in the first place, so some places are kinda hacked-in.

@pavelkouril pavelkouril marked this pull request as ready for review October 5, 2025 15:25
@pavelkouril pavelkouril changed the title [WIP] Add support for MTL4 bindings Add support for MTL4 bindings Oct 5, 2025
{
public class PropertyInstance : IEquatable<PropertyInstance>
{
public readonly ClassInstance ContainingClass;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClassInstance should be made nullable to account for those cases safely.

Copy link
Contributor Author

@pavelkouril pavelkouril Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cherrypicked my local change from small cleanup I did along the way that splits the property instance into separate types for properties/function parameters/member variables of structs -- since those are logically different anyways, it doesn't IMO make sense to share the model representation type for it.

Feels better way to me to tackle the nullable thing, since the field is never needed anyways for the structs/function parameters.

@IsaacMarovitz
Copy link
Owner

IsaacMarovitz commented Oct 5, 2025

I've noticed one significant failure in these generator changes in MTLDepthStencilDescriptor.

Metal 4 deprecates the function depthWriteEnabled, replacing it with isDepthWriteEnabled, the setter setDepthWriteEnabled remains unchanged.

depthWriteEnabled has been marked with a deprecated tag. The generated code is the following:

[System.Obsolete]
public bool DepthWriteEnabled
{
    get => ObjectiveCRuntime.bool_objc_msgSend(NativePtr, sel_isDepthWriteEnabled);
    set => ObjectiveCRuntime.objc_msgSend(NativePtr, sel_setDepthWriteEnabled, value);
}

public bool IsDepthWriteEnabled
{
    get => ObjectiveCRuntime.bool_objc_msgSend(NativePtr, sel_isDepthWriteEnabled);
    set => ObjectiveCRuntime.objc_msgSend(NativePtr, sel_setDepthWriteEnabled, value);
}

Both use the same new selector, and the logical name for this property in C# DepthWriteEnabled is marked as obsolete in favour of IsDepthWriteEnabled. In my opinion, this case should just use the new selector over the old, and only have one property exposed as DepthWriteEnabled.

@IsaacMarovitz
Copy link
Owner

Actually, thinking about it IsDepthWriteEnabled is the logical name for C#, but the old property using the new selector is still an error.

@IsaacMarovitz
Copy link
Owner

It seems this error also occurs with RasterizationEnabled in MTLRenderPipelineDescriptor

@pavelkouril
Copy link
Contributor Author

There is a lot of the obsoletes. However, for the (Is)DepthWriteEnabled, even C++ uses the same selector.

_MTL_INLINE bool MTL::DepthStencilDescriptor::depthWriteEnabled() const
{
    return Object::sendMessage<bool>(this, _MTL_PRIVATE_SEL(isDepthWriteEnabled));
}

_MTL_INLINE bool MTL::DepthStencilDescriptor::isDepthWriteEnabled() const
{
    return Object::sendMessage<bool>(this, _MTL_PRIVATE_SEL(isDepthWriteEnabled));
}

@IsaacMarovitz
Copy link
Owner

Ah, I didn't notice that, I guess this behaviour is acceptable then

@pavelkouril
Copy link
Contributor Author

As for the multiple properties with some being obsolete - IMO it makes sense to expose either both (with old one marked as obsolete), or just the new ones for consistency naming, since new ones will have the new naming scheme -- but removing the old ones means extra BC breakes in C# usage, so it IMO makes sense to mimic the C++ binding behaviour.

Copy link
Owner

@IsaacMarovitz IsaacMarovitz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few places where the generator is still not stable:

  • MTLCommandQueue.CommandBuffer()
  • MTLCommandBuffer.RenderCommandEncoder()
  • NSString.LengthOfBytes()

But I'll leave that to future PRs. Lgtm, nice work.

@IsaacMarovitz IsaacMarovitz merged commit 3545ff4 into IsaacMarovitz:master Oct 5, 2025
2 checks passed
@pavelkouril
Copy link
Contributor Author

Yeah, I am aware of these, but these will be harder to fix.

The resolution mechanism for selectors is not ideal, and basically the only fix IMO will be properly parsing the _x_PRIVATE_SEL(y) and using that to match.

If I had to guess, these 3 are "known" (since they were handpicked & fixed in previous versions), but there will be a few more of these hidden errors in random methods that are just lost in the generated code.

This is however something I want to tackle in a next PR - having the generator stable and matching all the selectors should be a priority.

@IsaacMarovitz
Copy link
Owner

@pavelkouril Just realised that this actually is currently unusable. MTL4CommandQueue only allows committing arrays of MTL4CommandBuffer, meaning its impossible to commit a command buffer since we currently don't support array-based APIs.

Thankfully in this case it's not NSArray based...

@pavelkouril
Copy link
Contributor Author

@IsaacMarovitz I see. Sorry, I completely missed that. I knew the array APIs are an issue, but never occured to me that it's the only way to do stuff in MTL4. 🤦

I'll add support for the Foo[] arrays in the codegen as part of #17 then. This should be quite easy. Speaking of which, Span<Foo> in this case could also make sense?

@IsaacMarovitz
Copy link
Owner

@pavelkouril No worries, I completely missed it too, I should've written the example before merging...

Yeah, I'm happy with any such low-level collection being supported, Foo[], Span<Foo>, Foo*, whatever else you can think of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants