gfx-rs / metal-rs Goto Github PK
View Code? Open in Web Editor NEWRust bindings for Metal
License: Apache License 2.0
Rust bindings for Metal
License: Apache License 2.0
According to the documentation on Apple's webpage, you can try to achieve vsync with the following command:
https://developer.apple.com/documentation/metal/mtlcommandbuffer/2806849-present
See here for some extra info:
However, changing
pub fn present_drawable<T>(&self, drawable: id<T>)
in commandbuffer.rs to take the additional afterMinimumDuration parameter, leads to a crash with the following error message:
2017-09-25 19:03:52.139 window[51292:591089] -[MTLIGAccelCommandBuffer presentDrawable:afterMinimumDuration:]: unrecognized selector sent to instance
Vsync is necessary for my work, so any ideas?
Unfortunately, I have no experience with Metal, Cocoa, AppKit, or the like, but willing to do whatever I can to help.
Best,
Rob
MTLPixelFormat
has values which are missing (at least as of macOS 10.13).
There are also some formats which are available on iOS only which I'm assuming were omitted on purpose.
These are the missing formats (copied from MTLPixelFormat.h):
MTLPixelFormatBGR10A2Unorm NS_AVAILABLE(10_13, 11_0) = 94,
MTLPixelFormatDepth16Unorm NS_AVAILABLE_MAC(10_12) = 250,
MTLPixelFormatX32_Stencil8 NS_AVAILABLE(10_12, 10_0) = 261,
MTLPixelFormatX24_Stencil8 NS_AVAILABLE_MAC(10_12) = 262,
Gecko is cross-compiling with macOS SDK 10.11 from Linux, getting these errors:
undefined symbols for architecture x86_64:
[task 2019-10-30T20:17:30.236Z] 20:17:30 INFO - "_MTLCopyAllDevices", referenced from:
[task 2019-10-30T20:17:30.236Z] 20:17:30 INFO - __ZN5metal6device6Device3all17hcc249f5610afec5fE in libgkrust_gtest.a(metal-7ef41ee34b16f6d0.metal.97s9ik07-cgu.2.rcgu.o)
[task 2019-10-30T20:17:30.236Z] 20:17:30 INFO - "_MTLCreateSystemDefaultDevice", referenced from:
[task 2019-10-30T20:17:30.236Z] 20:17:30 INFO - __ZN5metal6device6Device14system_default17hbaf7fd9f5662bfcbE in libgkrust_gtest.a(metal-7ef41ee34b16f6d0.metal.97s9ik07-cgu.2.rcgu.o)
[task 2019-10-30T20:17:30.236Z] 20:17:30 INFO - ld: symbol(s) not found for architecture x86_64
There was one API added: set_contents_scale
. This is a backward-compatible change.
It's currently configured not to, this is blocking our CI coverage.
Most of the project rely on docs.rs
for documentation, however it only builds Linux at the moment. We need to find a good way of providing the documentation here.
Given that all the interaction is done via message sending, I don't see why Send+Sync wouldn't be the case?
Running examples (from HEAD of this repository) fails:
alexbool@alexbool-osx ~/D/I/metal-rs> cargo run --example compute
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/examples/compute`
fatal runtime error: allocator memory exhausted
fish: 'cargo run --example compute' terminated by signal SIGILL (Illegal instruction)
alexbool@alexbool-osx ~/D/I/metal-rs> rust-lldb -- target/debug/examples/compute
(lldb) command source -s 0 '/tmp/rust-lldb-commands.9JfQTt'
Executing commands in '/tmp/rust-lldb-commands.9JfQTt'.
(lldb) command script import "/Users/alexbool/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/etc/lldb_rust_formatters.py"
(lldb) type summary add --no-value --python-function lldb_rust_formatters.print_val -x ".*" --category Rust
(lldb) type category enable Rust
(lldb) target create "target/debug/examples/compute"
Current executable set to 'target/debug/examples/compute' (x86_64).
(lldb) r
Process 4860 launched: '/Users/alexbool/Documents/IdeaProjects/metal-rs/target/debug/examples/compute' (x86_64)
fatal runtime error: allocator memory exhausted
compute was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 4860 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
frame #0: 0x000000010004e6fa compute`alloc_system::platform::_$LT$impl$u20$core..heap..Alloc$u20$for$u20$$RF$$u27$a$u20$alloc_system..System$GT$::oom::haae5aa67e0537221 at lib.rs:215 [opt]
Target 0: (compute) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
* frame #0: 0x000000010004e6fa compute`alloc_system::platform::_$LT$impl$u20$core..heap..Alloc$u20$for$u20$$RF$$u27$a$u20$alloc_system..System$GT$::oom::haae5aa67e0537221 at lib.rs:215 [opt]
frame #1: 0x000000010004e698 compute`_$LT$alloc_system..System$u20$as$u20$core..heap..Alloc$GT$::oom::h6b14a7ed86d10e67 at lib.rs:78 [opt]
frame #2: 0x000000010002800c compute`__rde_oom at lib.rs:124 [opt]
frame #3: 0x000000010001aff9 compute`_$LT$alloc..heap..Heap$u20$as$u20$core..heap..Alloc$GT$::oom::h6fe185a73e220d75 at heap.rs:98 [opt]
frame #4: 0x000000010001ae83 compute`_$LT$alloc..raw_vec..RawVec$LT$T$C$$u20$A$GT$$GT$::reserve::h31aa44fa05bc4469 at raw_vec.rs:558 [opt]
frame #5: 0x000000010001582f compute`_$LT$core..fmt..Write..write_fmt..Adapter$LT$$u27$a$C$$u20$T$GT$$u20$as$u20$core..fmt..Write$GT$::write_str::h5b108bba9b49131a [inlined] _$LT$alloc..vec..Vec$LT$T$GT$$GT$::reserve::h68934e9fd0fc8180 at vec.rs:465 [opt]
frame #6: 0x0000000100015823 compute`_$LT$core..fmt..Write..write_fmt..Adapter$LT$$u27$a$C$$u20$T$GT$$u20$as$u20$core..fmt..Write$GT$::write_str::h5b108bba9b49131a [inlined] _$LT$alloc..vec..Vec$LT$T$GT$$u20$as$u20$alloc..vec..SpecExtend$LT$$RF$$u27$a$u20$T$C$$u20$core..slice..Iter$LT$$u27$a$C$$u20$T$GT$$GT$$GT$::spec_extend::h126e6b75cdf416ca at vec.rs:1859 [opt]
frame #7: 0x0000000100015823 compute`_$LT$core..fmt..Write..write_fmt..Adapter$LT$$u27$a$C$$u20$T$GT$$u20$as$u20$core..fmt..Write$GT$::write_str::h5b108bba9b49131a [inlined] _$LT$alloc..vec..Vec$LT$T$GT$$GT$::extend_from_slice::h7fec1563e64426ee at vec.rs:1346 [opt]
frame #8: 0x0000000100015823 compute`_$LT$core..fmt..Write..write_fmt..Adapter$LT$$u27$a$C$$u20$T$GT$$u20$as$u20$core..fmt..Write$GT$::write_str::h5b108bba9b49131a [inlined] alloc::string::String::push_str::hd3e5f3c347641c54 at string.rs:805 [opt]
frame #9: 0x0000000100015823 compute`_$LT$core..fmt..Write..write_fmt..Adapter$LT$$u27$a$C$$u20$T$GT$$u20$as$u20$core..fmt..Write$GT$::write_str::h5b108bba9b49131a [inlined] _$LT$alloc..string..String$u20$as$u20$core..fmt..Write$GT$::write_str::h3fb28a11f607cf9d at string.rs:2277 [opt]
frame #10: 0x0000000100015823 compute`_$LT$core..fmt..Write..write_fmt..Adapter$LT$$u27$a$C$$u20$T$GT$$u20$as$u20$core..fmt..Write$GT$::write_str::h5b108bba9b49131a at mod.rs:214 [opt]
frame #11: 0x000000010000e172 compute`_$LT$alloc..string..String$u20$as$u20$core..fmt..Display$GT$::fmt::hc867ffd54fb6e644(self=&0x7ffeefbff0f0, f=&0x7ffeefbfef78) at string.rs:1860
frame #12: 0x000000010000e798 compute`_$LT$objc..message..MessageError$u20$as$u20$core..fmt..Display$GT$::fmt::hdca7e778728ba39e(self=&0x7ffeefbff0f0, f=&0x7ffeefbfef78) at mod.rs:157
frame #13: 0x0000000100051999 compute`core::fmt::write::hd6c4ef3dd62ed92b [inlined] core::fmt::Formatter::run::h1e9c4a7161cf2c85 at mod.rs:1100 [opt]
frame #14: 0x0000000100051814 compute`core::fmt::write::hd6c4ef3dd62ed92b at mod.rs:1047 [opt]
frame #15: 0x0000000100016dc4 compute`std::panicking::begin_panic_fmt::hccd237cdae61db17 [inlined] core::fmt::Write::write_fmt::h1f0dbd48320f1e1a at mod.rs:226 [opt]
frame #16: 0x0000000100016d89 compute`std::panicking::begin_panic_fmt::hccd237cdae61db17 at panicking.rs:348 [opt]
frame #17: 0x0000000100008211 compute`metal_rs::encoder::CommandEncoderRef::end_encoding::h7ba072b0c0038804(self=&0x100211440) at encoder.rs:135
frame #18: 0x000000010000581b compute`compute::main::h3c95dc9e471369d8 at main.rs:78
frame #19: 0x0000000100002e22 compute`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h07c3d44bb113f1a4 at rt.rs:74
frame #20: 0x0000000100016d08 compute`std::panicking::try::do_call::h7faedbe63ef0ecf9 [inlined] std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::h73acc47e0a9ff971 at rt.rs:59 [opt]
frame #21: 0x0000000100016cfc compute`std::panicking::try::do_call::h7faedbe63ef0ecf9 at panicking.rs:306 [opt]
frame #22: 0x0000000100027b4f compute`__rust_maybe_catch_panic at lib.rs:102 [opt]
frame #23: 0x0000000100014cf8 compute`std::rt::lang_start_internal::hbde0134cdd993de4 [inlined] std::panicking::try::h66d036c9b5e0ac3f at panicking.rs:285 [opt]
frame #24: 0x0000000100014cc5 compute`std::rt::lang_start_internal::hbde0134cdd993de4 [inlined] std::panic::catch_unwind::hdc6db1d58a689477 at panic.rs:361 [opt]
frame #25: 0x0000000100014cc5 compute`std::rt::lang_start_internal::hbde0134cdd993de4 at rt.rs:58 [opt]
frame #26: 0x0000000100002e02 compute`std::rt::lang_start::he2ae847addd0cd4a(main=&0x100005050, argc=1, argv=&0x7ffeefbff8a0) at rt.rs:74
frame #27: 0x0000000100005d25 compute`main + 37
frame #28: 0x00007fff6fd39115 libdyld.dylib`start + 1
(lldb) ^D
alexbool@alexbool-osx ~/D/I/metal-rs> rustc -vV
rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)
binary: rustc
commit-hash: fb44b4c0eb1d344f84f7bb2c90f28e31a8a180be
commit-date: 2018-04-04
host: x86_64-apple-darwin
release: 1.27.0-nightly
LLVM version: 6.0
Still works on stable (1.25.0). I think this is rust issue, but cannot figure it out.
This approach seems like less of a hassle since you don't need to wire up build configurations in Xcode.
Xcode becomes nothing more than a place to view your .gpuTrace
file.
the binary metallib checked into the repository works, but this .metal file cannot be compiled to produce that metallib.
This is closer:
// code: language=c++
#include <metal_stdlib>
using namespace metal;
typedef struct {
float x, y, r, g, b;
} vertex_t;
static_assert(alignof(vertex_t) == 4, "vertex_t must match a rust array of f32");
static_assert(sizeof(vertex_t) == 4 * 5, "vertex_t must match a rust array of f32");
struct ColorInOut {
float4 position [[position]];
float4 color;
};
// vertex shader function
vertex ColorInOut triangle_vertex(device vertex_t* vertex_array [[ buffer(0) ]],
uint32_t vid [[ vertex_id ]])
{
ColorInOut out;
auto device const &v = vertex_array[vid];
out.position = float4(v.x, v.y, 0.0, 1.0);
out.color = float4(v.r, v.g, v.b, 1.0);
return out;
}
// fragment shader function
fragment float4 triangle_fragment(ColorInOut in [[stage_in]])
{
return in.color;
}
I suspect the example should include a build.rs to compile the metallib from source to avoid this problem recurring.
In 302d246 the dependency on objc-foundation
was removed. Seems like it was mainly used for strings where an ad-hoc helper was used instead. However, at 302d246#diff-ae230649f72f8f72105a4ebc1edfb748L243 there was a usage of NSArray
there was translated into an untyped object.
Before the change the following code worked:
println!("Found the following functions in the Metal library:");
let function_names = metal_matrix_lib.function_names().to_vec();
for name in function_names {
println!("{}", name.as_str());
}
After the change however, the to_vec
function does not exist.
What are your thoughts on this break? Would you consider re-introducing the dependency to support it, or possibly roll your own array/vec mapping?
I guess this specific call is not that useful in a production setting, but can be quite nifty for debugging during development.
We need a way to determine whether certain functions are supported, which depends on the device OS and Metal version.
Ideally metal-rs could provide some way to query this information (i.e. fn supports_xyz() -> bool
) or guard against these cases (i.e. return a certain Err
type), instead of callers having to perform their own version checks and maintain feature tables for each version.
Currently the argument buffer example fails (at least on nightly), but all other examples appear to work correctly:
metal-rs:master λ cargo run --example argument-buffer
Finished dev [unoptimized + debuginfo] target(s) in 0.15s
Running `target/debug/examples/argument-buffer`
<MTLEmulationIndirectArgumentEncoder: 0x7f867d5146a0>
<MTLIGSampler: 0x7f867d60f690>
label = <none>
device = <null>
fish: 'cargo run --example argument-bu…' terminated by signal SIGSEGV (Address boundary error)
I’d like to add an example of taking a Metal app that’s completely written in Rust and debugging it in Xcode.
I’ve never actually done this - but I have some thoughts on how to approach it.
On mobile so might have typos
Add an example that just imports one of the other examples (such as the textured quad example). So the imported example would have to expose a “run” method. Use an environment variable to make the shader compilation include debug info. (So that when people copy paste the build script for their real project they aren’t accidentally generating debug builds without realizing)
Our Xcode debugging example would compile to a dylib that exposed a single method to run it
Xcode example would have the smallest possible Xcode project that links to and runs the Rust generated dylib
In the README of the example we include a screenshot or two of an Xcode debug session as well as any important notes / caveats if there end up being any
The nice part would be that the Xcode project would be immediately re-usable for any application since it does nothing but run a dylib.
One reason that I can think of against adding this example is that in general Xcode tends to feel bloaty (or at least that was my experience 6 or so years ago). I think that this is mitigated by having a project that does nothing more than run a linked library.
Another question might be - does this belong in metal-rs or a separate repo?
My thinking would be that being able to debug your shaders can be such a timesaver as well as such a cool demo as well as something you might not have thought of (I can still use Xcode tools with my entirely Rust app?!?!) that it belongs alongside the core examples.
Once people know how to run Xcode tools on their Rust app this means they now also know how to profile shaders, as well as anything else you might want to lean on Xcode’s user interface for (anything you want to visualize).
So this one example would unlock all Xcode tooling for the developer.
To repeat - I’ve never actually done this so it sounds good on paper but who knows what gotchas might exist.
But that’s the point of the example. We discover the solutions to those and present it in a nice tidy little example.
Let me know your thoughts and where you agree or disagree!
MTLPixelFormat
should derive PartialEq
, or really Eq
, since it's just a plain enum.
Most of the time it's going to be used with match
, but having it implement PartialEq
lets us do useful things like
debug_assert_eq!(src.raw.pixel_format(), dst.raw.pixel_format());
I'm about ready to publish my crate but it requires some of the recent metal crate changes. Will a new version be published soon? Otherwise I'll need to publish a temporary fork like this one: https://crates.io/crates/cart-tmp-wgpu
This issue may not be related specifically to metal-rs
, but may be more of a metal
/cocoa
rust thing. I'm filing it here, because I'm running into some troubles getting memory deallocation of Metal buffers working, and I haven't been able to solve it using existing resources online.
Ask: It would be very helpful having some more advanced examples of how to manage buffer memory in more complicated scenarios.
Concretely I'm trying to implement a matrix and operations on matrices utilising Metal. The code is currently in a state where it seems to produce correct results, but the application memory seems to increase more or less linearly with the number of operations performed (currently most operations will allocate a new matrix).
In my code I've chosen to have the data of the matrices living solely on the GPU using the StorageModePrivate flag on the buffer, in hope that the data won't needed to be synchronised to the CPU all the time. When transferring data to/from the CPU, I allocate a temporary CPUCacheModeDefaultCache
buffer and use the Metal API to copy between the two buffers (see https://github.com/kasper0406/neuralnetwork/blob/master/src/metalmatrixhandle.rs#L424). What I currently don't understand is how to properly deallocate the memory associated with both the private and the temporary buffer.
I am wrapping the entire application in a NSAutoreleasePool
, however this only seems to actually free anything when the pool is released, which does not happen frequently enough when doing many matrix allocations.
The private matrix buffer should basically follow the lifetime of the MetalMatrixHandle
object. These may not live in some well-defined scope in the code, making it hard to use additional NSAutoreleasePool
s to handle it.
I have tried calling retain
/release
directly on the buffers, but it doesn't really seem to do any deallocation (inspecting by looking at the memory consumption when debugging through Xcode), and also I'm unsure how this behaves when a buffer is queued up for use in a command buffer. I've tried testing the above using a loop allocating buffers that can basically be freed right after they are constructed https://github.com/kasper0406/neuralnetwork/blob/master/src/metalmatrixhandle.rs#L593.
Just noticed that our methods sometimes diverge from Metal:
pub fn new_blit_command_encoder(&self) -> &BlitCommandEncoderRef {
unsafe {
msg_send![self, blitCommandEncoder]
}
}
I think we should fix all of those diverging cases and follow Metal more strictly. That would be a breaking change.
cc @grovesNL
ARC uses these functions to avoid some of the objc_msgSend overhead. It seams reasonable to use them in obj_clone and obj_drop.
@grovesNL
We need to upload a version with deprecation description, also update the README.
I see that neither fences nor barriers are exposed by the rust API, and also I see that gfx-hal just uses a CPU sleep to emulate a fence.
Is that really equivalent to a GPU-side fence in metal, or am I missing something fundamental here?
There are some functions like new_buffer_with_data
which accept raw pointers, but not marked unsafe
even though certain guarantees are necessary to safely use it. There are already many functions marked unsafe
but we're not applying it consistently across the entire crate yet.
For new_buffer_with_data
specifically we might consider accepting a slice (as mentioned in #42). But for other functions (especially any dealing with raw pointers) we should try to add unsafe
and document any safety requirements.
There's a leak in 0.15.0
in CommandBufferRef.rs
under set_label()
which is fixed in the current master branch. I tested wgpu-rs
with patched metal-rs
and it works fine without API breakage.
See #197 (comment)
Example by @bsekura
/// Only available on (macos(10.12), ios(10.0)
fn foo(&self) {
}
The nsstring_from_str
shows up in the release profiles a bit. We should expose a way to avoid any conversions (e.g. when it's a constant string that's already null terminated).
cc @aleksijuvani @grovesNL
In Apple's documentation, the order of function parameters might look like this:
func replace(region: MTLRegion,
mipmapLevel level: Int,
withBytes pixelBytes: UnsafeRawPointer,
bytesPerRow: Int)
whereas in metal-rs
it might look like this
pub fn replace_region(
&self,
region: MTLRegion,
mipmap_level: NSUInteger,
stride: NSUInteger,
bytes: *const std::ffi::c_void,
) {
// ...
}
I found that the order differences (last two parameters swapped) and name differences (stride
vs. bytes_per_row
) made it difficult to cross reference Apple's Metal docs when working with metal-rs
.
My understanding is that these were all written by hand over time (I'm just basing this assumption on the first few commits from 2016 https://github.com/gfx-rs/metal-rs/commits/master?after=09433e64682ffe4498988118c062e608a4971eb6+220)
Given that - it's probably quite a bit of manual work and breaking changes to get consistent (or at least to the degree possible) with the official metal APIs.
How does metal-rs
typically think about breaking changes? Is this a no-go? Or could this fit in at some point in the future?
I could use some insight here.
Cheers!
Hello! Currently metal-rs
provides no APIs to synchronize GPU/CPU work (like MTLEvent and MTLSharedEvent, example: Synchronizing Events Within a Single Device).
Would it be ok to open a PR to support that?
Unfortunately, this is going to be a breaking change...
@grovesNL in https://github.com/gfx-rs/gfx/pull/2016/files#r187777699:
IMO we may want to remove this function from metal-rs – it seems a bit sketchy because it creates
MTLRenderPassDescriptorInternal directly. There is a renderPassDescriptor convenience function that should set up the default attachments that we use here though.
Hi,
I am quite new to this repo. I was looking for Vertex Amplification support(https://developer.apple.com/documentation/metal/generating_multiple_output_vertex_streams_from_one_input_stream?language=objc) in the repo. But I have not found any. Am I missing something? Thank you.
We are currently not consistent w.r.t. receiving &[u8]
versus bare pointer + size. The former is a safer API but requires the user (sometimes) to convert whatever they have to a slice of u8
. The former is tricky, since our methods are not marked as unsafe...
Similar to gfx-rs/d3d12-rs#19, we need to avoid implicit linking of Metal framework if we want to run (any application that uses metal-rs in some way) on macOS 10.10 and earlier.
One workaround that the clients could do is "-weak_framework Metal" instead of just "-framework". See https://phabricator.services.mozilla.com/D54946 for Gecko. They would still need to make sure that no entry points are used.
I think loading the libraries dynamically and explicitly by metal-rs is still better for the following reasons:
Right now core-graphics
is used on these two lines (excluding in examples. I'm talking strictly metal-rs
source)
Lines 27 to 28 in c17acf1
Effectively we're using it to define type aliases for a couple of scalar types.
Given that the chances of these aliases ever changing is roughly 0%, I'm proposing that we ditch the core-graphics
dependency and instead just copy-paste those two types inline in metal-rs
.
The end result would be inlining the following code:
#[cfg(target_pointer_width = "64")]
type CGFloat = f64;
#[cfg(not(target_pointer_width = "64"))]
type CGFloat = f32;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
struct CGSize {
pub width: CGFloat,
pub height: CGFloat,
}
Am I missing anything here?
I'm considering adding wrappers for MetalKit to this project. Is there any interest in this or not?
I noticed that metal-rs is using its own infrastructure (foreign_obj_type!) for managing Objective-C objects instead of objc_id. I was wondering if there was a particular reason for this and if the metal-rs strategy is better for some reason.
Hi!
I am a member of the team developing monocodus — a service that performs automatic code review of GitHub pull requests to help organizations ensure a high quality of code.
We’ve developed some useful features to the moment, and now we’re looking for early users and feedback to find out what we should improve and which features the community needs the most.
We ran monocodus on a pre-created fork of your repo on GitHub https://github.com/monocodus-demonstrations/metal-rs/pulls, and it found 15 formatting suggestions according to rustfmt. I hope that this information will be useful to you and would be happy to receive any feedback here or on my email [email protected]
We really love the Rust community, and we would love to hear from you on what automated checkers you might want to have. If there is something you cannot do with clippy, or cargo-fix, something you might have seen as a tool for some other language ecosystem, we can step forward and create it if you have any idea what you need!
If you want to try our service, feel free to follow the link: https://www.monocodus.com
The service is entirely free of charge for open source projects. Hope you’ ll like it :)
Our current Travis setup no longer sends proper messages.
We should consider implementing Index
+ IndexMut
on array-like objects (e.g. PipelineBufferDescriptorArray
) instead of exposing object_at
+ set_object_at
methods.
We have new_render_command_encoder
that corresponds to https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-rendercommandencoderwithdescript?language=objc , which doesn't have "new" in it and so it returns a reference. We should make our methods similarly named.
TLDR
Take Metal Objective C and Swift API and change names to Rust idiomatic names where applicable.
Rust bindings are based on Objective C protocol bindings.
Follow Objective C protocol definition to determine properties and methods.
Swift API adds some higher level stuff that is beyond the scope of Rust bindings.
Same as Objective C/Swift API, e.g.
MTLDevice
, MTLCommandBufferStatus
, MTLCommandBuffer
etc.
Swift
enum MTLCommandBufferStatus : UInt {
case notEnqueued
case enqueued
case committed
case scheduled
case completed
case error
}
Rust: change variant names following Rust conventions:
(basically capitalize first word)
pub enum MTLCommandBufferStatus {
NotEnqueued,
Enqueued,
Committed,
Scheduled,
Completed,
Error,
}
NOTE: Some enums contain Apple brand names that intentionally start with lowercase.
In those cases respect that, adding necessary #allow to keep Rust happy. For example:
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
enum OS {
iOS,
tvOS,
macOS,
}
Objective C
- (void)addCompletedHandler:(MTLCommandBufferHandler)block;
Swift
func addCompletedHandler(_ block: @escaping MTLCommandBufferHandler)
Rust: change to lowercase snake case:
pub fn add_completed_handler(&self, block: &CommandBufferHandler)
Some methods create new objects (aka factory methods).
Objective C:
- (id<MTLComputeCommandEncoder>)computeCommandEncoderWithDescriptor:(MTLComputePassDescriptor *)computePassDescriptor;
- (id<MTLComputeCommandEncoder>)computeCommandEncoderWithDispatchType:(MTLDispatchType)dispatchType;
- (id<MTLComputeCommandEncoder>)computeCommandEncoder;
Swift:
NOTE: overloading
func makeComputeCommandEncoder(descriptor computePassDescriptor: MTLComputePassDescriptor) -> MTLComputeCommandEncoder?
func makeComputeCommandEncoder(dispatchType: MTLDispatchType) -> MTLComputeCommandEncoder?
func makeComputeCommandEncoder() -> MTLComputeCommandEncoder?
Rust:
make
to new
, as we do in Rust:pub fn new_compute_command_encoder_with_descriptor(desc: MTLComputePassDescriptor) -> &ComputeCommandEncoderRef {}
pub fn new_compute_command_encoder_with_dispatch_type(&self, ty: MTLDispatchType) -> &ComputeCommandEncoderRef {}
pub fn new_compute_command_encoder(&self) -> &ComputeCommandEncoderRef {}
Methods on the MTLCaptureManager
such as startCaptureWithDevice
are deprecated in Metal
in favor of using an MTLCaptureDescriptor
, which does not currently exist in metal-rs
.
Example deprecation -> https://developer.apple.com/documentation/metal/mtlcapturemanager/2869714-startcapture
I am currently going through the amethyst tutorial. Since I'm currently working on a MacBook Pro, I've been using metal (thanks for the great tool, btw!).
The amethyst demo compiled and ran fine (I think). However, when I started going through the tutorial, I wasn't able to compile due to an error when compiling metal.
If you think this should be reported to amethyst instead, let me know.
Cargo.toml:
[package]
name = "pong"
version = "0.1.0"
authors = []
edition = "2018"
[dependencies.amethyst]
version = "0.12.0"
features = ["metal"]
main.rs:
//! Pong tutorial Harrison McCullough
use amethyst::{
prelude::*,
renderer::{
plugins::{RenderFlat2D, RenderToWindow},
types::DefaultBackend,
RenderingBundle,
},
utils::application_root_dir,
};
pub struct Pong;
impl SimpleState for Pong {}
fn main() -> amethyst::Result<()> {
amethyst::start_logger(Default::default());
let app_root = application_root_dir()?;
let display_config_path = app_root.join("config").join("display.ron");
let game_date = GameDataBuilder::default();
let assets_dir = app_root.join("assets");
let mut world = World::new();
let mut game = Application::new(assets_dir, Pong, game_date);
game.run();
Ok(())
}
Output:
$ cargo build
Compiling libc v0.2.66
Compiling proc-macro2 v1.0.6
Compiling unicode-xid v0.2.0
... <truncated> ...
Compiling tokio-io v0.1.12
Compiling tokio-threadpool v0.1.17
error[E0282]: type annotations needed
--> /Users/harrisonmccullough/.cargo/registry/src/github.com-1ecc6299db9ec823/metal-0.15.0/src/lib.rs:125:13
|
125 | msg_send![self.0, release];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ consider giving `result` a type
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
Compiling want v0.2.0
Compiling glyph_brush v0.6.1
Compiling font-kit v0.1.0
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
error: could not compile `metal`.
warning: build failed, waiting for other jobs to finish...
error: build failed
$ rustup override list
/Users/harrisonmccullough/git/pong stable-x86_64-apple-darwin
$ cargo run --example=caps
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/examples/caps`
Vendor: "Intel"
Family: "Iris Pro Graphics"
Max threads per threadgroup: MTLSize { width: 512, height: 512, depth: 512 }
Integrated GPU: true
Headless: false
D24S8: false
2017-09-14 02:48:20.562 caps[691:20772] -[MTLIGAccelDevice argumentBuffersSupport]: unrecognized selector sent to instance 0x7fc75f006e00
Segmentation fault: 11
$ cargo run --example=caps --release
Finished release [optimized] target(s) in 0.0 secs
Running `target/release/examples/caps`
Vendor: "Intel"
Family: "Iris Pro Graphics"
Max threads per threadgroup: MTLSize { width: 512, height: 512, depth: 512 }
Integrated GPU: true
Headless: false
D24S8: false
2017-09-14 02:50:18.811 caps[792:23178] -[MTLIGAccelDevice argumentBuffersSupport]: unrecognized selector sent to instance 0x7ff27881e800
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:335:20
note: Run with `RUST_BACKTRACE=1` for a backtrace.
fatal runtime error: failed to initiate panic, error 5
Abort trap: 6
I'm getting a very strange assertion with this program:
fn main() {
let argument_descriptor = rafx::api::metal_rs::ArgumentDescriptor::new();
println!("settings access");
argument_descriptor.set_access(rafx::api::metal_rs::MTLArgumentAccess::ReadWrite);
println!("has set access");
let argument_descriptor = rafx::api::metal_rs::ArgumentDescriptor::new();
println!("settings access");
argument_descriptor.set_access(rafx::api::metal_rs::MTLArgumentAccess::ReadWrite);
println!("has set access");
It prints
settings access
has set access
settings access
validateMTLArgumentAccess:27: failed assertion `access (4294967297) is not a valid MTLArgumentAccess.'
The bizarre thing is that I cannot reproduce this with an "empty" project. If I take the exact code and all the same dependencies and move them to another project, it works fine. The only difference I can think of at this point is the one that fails is an example of another crate and the in-theory identical project that doesn't crash links that same crate as a downstream binary.
The only thing I can think of that would cause a crash like this would be incorrect bindings or something happening before main() that causes undefined behavior. I use static variables and things like lazy_static extremely sparingly. The app runs fine if I use my vulkan backend instead of the metal backend and all other metal bindings seem to work. (The original project that is crashing calls quite a few metal APIs before reaching this point).
The only thing I saw that looks suspicious is that MTLArgumentAccess is repr(u32) and the docs point out that this is Uint, which would actually be u64 on most systems (i.e. usize)
I tried to find examples of this function actually being used but was unable to get the gfx-hal examples to call it and didn't see it used in the examples of this crate.
Just posting here hoping that someone has an idea!
See https://docs.rs/foreign-types/0.4.0/foreign_types/
What's not clear to me is how this works with a parent type semantics: current macro in metal-rs supports it and uses it, but I don't see it in the foreign_types
crate. cc @sfackler
I find it rather obscure that we have no control over when something is released if it decides to use an auto-release pool. I wonder if we could encode this into the type system, so that a user-provided auto-release context is required in any operation that ends up using it implicitly. Something along these lines:
impl Foo {
fn some_operation(&self, arc: &AutoReleaseContext) {..}
}
fn main() {
autoreleasepool(|context| {
foo.some_operation(context);
})
}
is this feasible or totally unrealistic? @grovesNL @scoopr @SSheldon
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.