Skip to main content

spirv_cross2/compile/msl/
mod.rs

1use crate::compile::{CommonOptions, CompiledArtifact};
2use spirv_cross_sys as sys;
3
4/// An MSL `constexpr` inlined sampler.
5pub use spirv_cross_sys::MslConstexprSampler as ConstexprSampler;
6
7/// Sampler addressing mode for inlined samplers.
8pub use spirv_cross_sys::MslSamplerAddress as SamplerAddress;
9/// Sampler border color for inlined samplers.
10pub use spirv_cross_sys::MslSamplerBorderColor as SamplerBorderColor;
11/// Sampler compare function for inline samplers.
12pub use spirv_cross_sys::MslSamplerCompareFunc as SamplerCompareFunc;
13/// Whether texture coordinates are normalized or not for inline samplers.
14pub use spirv_cross_sys::MslSamplerCoord as SamplerCoord;
15/// Mag and min filter mode for inline samplers.
16pub use spirv_cross_sys::MslSamplerFilter as SamplerFilter;
17/// Mip filtering mode for inline samplers.
18pub use spirv_cross_sys::MslSamplerMipFilter as SamplerMipFilter;
19
20/// Y′CbCr chroma location.
21pub use spirv_cross_sys::MslChromaLocation as YcbcrChromaLocation;
22/// Y′CbCr conversion component swizzle.
23pub use spirv_cross_sys::MslComponentSwizzle as YcbcrComponentSwizzle;
24/// Y′CbCr conversion format resolution.
25pub use spirv_cross_sys::MslFormatResolution as YcbcrFormatResolution;
26/// Y′CbCr model conversion options for inline samplers.
27pub use spirv_cross_sys::MslSamplerYcbcrConversion as SamplerYcbcrConversion;
28/// Format to convert when converting Y′CbCr.
29pub use spirv_cross_sys::MslSamplerYcbcrModelConversion as YcbcrTargetFormat;
30
31/// Conversion range for Y′CbCr.
32pub use spirv_cross_sys::MslSamplerYcbcrRange as YcbcrConversionRange;
33
34/// Indicates the format of a shader interface variable. Currently limited to specifying
35/// if the input is an 8-bit unsigned integer, 16-bit unsigned integer, or
36/// some other format.
37pub use spirv_cross_sys::MslShaderVariableFormat as ShaderVariableFormat;
38
39/// Indicates the rate at which a variable changes value, one of: per-vertex,
40/// per-primitive, or per-patch.
41pub use spirv_cross_sys::MslShaderVariableRate as ShaderVariableRate;
42
43/// Maximum number of argument buffers supported.
44pub const MAX_ARGUMENT_BUFFERS: u32 = 8;
45
46use crate::error::ToContextError;
47use crate::handle::{Handle, VariableId};
48use crate::sealed::Sealed;
49use crate::string::CompilerStr;
50use crate::targets::Msl;
51use crate::{error, Compiler, ContextRooted};
52use spirv_cross_sys::{MslResourceBinding2, MslShaderInterfaceVar2, SpvBuiltIn, SpvExecutionModel};
53use std::fmt::{Debug, Formatter};
54use std::num::NonZeroU32;
55
56impl Sealed for CompilerOptions {}
57/// MSL compiler options
58#[non_exhaustive]
59#[derive(Debug, spirv_cross2_derive::CompilerOptions)]
60pub struct CompilerOptions {
61    /// Compile options common to GLSL, HLSL, and MSL.
62    #[expand]
63    pub common: CommonOptions,
64
65    /// The MSL version to compile to.
66    ///
67    /// Defaults to MSL 1.2.
68    #[option(SPVC_COMPILER_OPTION_MSL_VERSION)]
69    pub version: MslVersion,
70
71    /// Width of 2D Metal textures used as 1D texel buffers.
72    #[option(SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH, 4096)]
73    pub texel_buffer_texture_width: u32,
74
75    /// Index of the swizzle buffer.
76    ///
77    /// The default is 30.
78    #[option(SPVC_COMPILER_OPTION_MSL_SWIZZLE_BUFFER_INDEX, 30)]
79    pub swizzle_buffer_index: u32,
80
81    /// Index of the indirect params buffer.
82    ///
83    /// The default is 29.
84    #[option(SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX, 29)]
85    pub indirect_params_buffer_index: u32,
86
87    /// Index of the shader output buffer.
88    ///
89    /// The default is 28.
90    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX, 28)]
91    pub shader_output_buffer_index: u32,
92
93    /// Index of the shader patch output buffer.
94    ///
95    /// The default is 27.
96    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX, 27)]
97    pub shader_patch_output_buffer_index: u32,
98
99    /// Index of the shader tesselation factor output buffer.
100    ///
101    /// The default is 26.
102    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX, 26)]
103    pub shader_tess_factor_output_buffer_index: u32,
104
105    /// Index of the buffer size buffer.
106    ///
107    /// The default is 25.
108    #[option(SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX, 25)]
109    pub buffer_size_buffer_index: u32,
110
111    /// Index of the view mask buffer.
112    ///
113    /// The default is 24
114    #[option(SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX, 24)]
115    pub view_mask_buffer_index: u32,
116
117    /// Index of the dynamic offsets buffer.
118    ///
119    /// The default is 23
120    #[option(SPVC_COMPILER_OPTION_MSL_DYNAMIC_OFFSETS_BUFFER_INDEX, 23)]
121    pub dynamic_offsets_buffer_index: u32,
122
123    /// Index of the shader input buffer.
124    ///
125    /// The default is 22.
126    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX, 22)]
127    pub shader_input_buffer_index: u32,
128
129    /// Index of the shader index buffer.
130    ///
131    /// The default is 21.
132    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX, 21)]
133    pub shader_index_buffer_index: u32,
134
135    /// Index of the shader patch input buffer.
136    ///
137    /// The default is 20.
138    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_INPUT_BUFFER_INDEX, 20)]
139    pub shader_patch_input_buffer_index: u32,
140
141    /// Index of the input workgroup index buffer.
142    ///
143    /// The default is 0
144    #[option(SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX, 0)]
145    pub shader_input_workgroup_index: u32,
146
147    /// Enable `point_size` builtin.
148    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN, true)]
149    pub enable_point_size_builtin: bool,
150
151    /// Enable the `FragDepth` builtin.
152    ///
153    /// Disable if pipeline does not enable depth, as pipeline
154    /// creation might otherwise fail.
155    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN, true)]
156    pub enable_frag_depth_builtin: bool,
157
158    /// Enable the `FragStencilRef` output.
159    ///
160    /// Disable if pipeline does not enable stencil output,
161    /// as pipeline creation might otherwise fail.
162    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN, true)]
163    pub enable_frag_stencil_ref_builtin: bool,
164
165    /// Disables rasterization and returns void from vertex-like entry points.
166    #[option(SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION, false)]
167    pub disable_rasterization: bool,
168
169    /// Writes geometry varyings to a buffer instead of as stage-outputs.
170    #[option(SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER, false)]
171    pub capture_output_to_buffer: bool,
172
173    /// Works around lack of support for VkImageView component swizzles.
174    /// Recent Metal versions do not require this workaround.
175    /// This has a massive impact on performance and bloat.
176    ///
177    /// Do not use this unless you are absolutely forced to.
178    ///
179    /// To use this feature, the API side must pass down swizzle buffers.
180    /// Should only be used by translation layers as a last resort.
181    #[option(SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES, false)]
182    pub swizzle_texture_samples: bool,
183
184    /// Always emit color outputs as 4-component variables.
185    ///
186    /// In Metal, the fragment shader must emit at least as many components
187    /// as the render target format.
188    #[option(SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS, false)]
189    pub pad_fragment_output_components: bool,
190
191    /// Use a lower-left tessellation domain.
192    #[option(SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT, false)]
193    pub tess_domain_origin_lower_left: bool,
194
195    /// The plattform to output MSL for. Defaults to macOS.
196    #[option(SPVC_COMPILER_OPTION_MSL_PLATFORM, MetalPlatform::MacOS)]
197    pub platform: MetalPlatform,
198
199    /// Enable use of Metal argument buffers.
200    ///
201    /// MSL 2.0 or higher must be used.
202    #[option(SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS, false)]
203    pub argument_buffers: bool,
204
205    /// Defines Metal argument buffer tier levels.
206    /// Uses same values as Metal `MTLArgumentBuffersTier` enumeration.
207    #[option(
208        SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS_TIER,
209        ArgumentBuffersTier::Tier1
210    )]
211    pub argument_buffers_tier: ArgumentBuffersTier,
212
213    /// Requires MSL 2.1, use the native support for texel buffers.
214    #[option(SPVC_COMPILER_OPTION_MSL_TEXTURE_BUFFER_NATIVE, false)]
215    pub texture_buffer_native: bool,
216
217    /// Enable SPV_KHR_multiview emulation.
218    #[option(SPVC_COMPILER_OPTION_MSL_MULTIVIEW, false)]
219    pub multiview: bool,
220
221    /// If disabled, don't set `[[render_target_array_index]]` in multiview shaders.
222    ///
223    /// Useful for devices which don't support layered rendering.
224    ///
225    /// Only effective when [`CompilerOptions::multiview`] is enabled.
226    #[option(SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING, true)]
227    pub multiview_layered_rendering: bool,
228
229    /// The index of the device
230    #[option(SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX, 0)]
231    pub device_index: u32,
232
233    /// Treat the view index as the device index instead. For multi-GPU rendering.
234    #[option(SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX, false)]
235    pub view_index_from_device_index: bool,
236
237    /// Add  support for `vkCmdDispatchBase()` or similar APIs.
238    ///
239    /// Offsets the workgroup ID based on a buffer.
240    #[option(SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE, false)]
241    pub dispatch_base: bool,
242
243    /// Emit Image variables of dimension Dim1D as `texture2d`.
244    ///
245    /// In Metal, 1D textures do not support all features that 2D textures do.
246    ///
247    /// Use this option if your code relies on these features.
248    #[option(SPVC_COMPILER_OPTION_MSL_TEXTURE_1D_AS_2D, false)]
249    pub texture_1d_as_2d: bool,
250
251    /// Ensures vertex and instance indices start at zero.
252    ///
253    /// This reflects the behavior of HLSL with SV_VertexID and SV_InstanceID.
254    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_BASE_INDEX_ZERO, false)]
255    pub enable_base_index_zero: bool,
256
257    /// Use Metal's native frame-buffer fetch API for subpass inputs.
258    #[option(SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS, false)]
259    pub framebuffer_fetch_subpass: bool,
260
261    /// Enables use of "fma" intrinsic for invariant float math
262    #[option(SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH, false)]
263    pub invariant_fp_math: bool,
264
265    /// Emulate texturecube_array with texture2d_array for iOS where this type is not available
266    #[option(SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY, false)]
267    pub emulate_cubemap_array: bool,
268
269    /// Allow user to enable decoration binding
270    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING, false)]
271    pub enable_decoration_binding: bool,
272
273    /// Forces all resources which are part of an argument buffer to be considered active.
274    ///
275    /// This ensures ABI compatibility between shaders where some resources might be unused,
276    /// and would otherwise declare a different ABI.
277    #[option(SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES, false)]
278    pub force_active_argument_buffer_resources: bool,
279
280    /// Forces the use of plain arrays, which works around certain driver bugs on certain versions
281    /// of Intel Macbooks.
282    ///
283    /// See <https://github.com/KhronosGroup/SPIRV-Cross/issues/1210>.
284    /// May reduce performance in scenarios where arrays are copied around as value-types.
285    #[option(SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS, false)]
286    pub force_native_arrays: bool,
287
288    /// Only selectively enable fragment outputs.
289    ///
290    /// Useful if pipeline does not enable
291    /// fragment output for certain locations, as pipeline creation might otherwise fail.
292    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK, 0xffffffff)]
293    pub enable_frag_output_mask: u32,
294
295    /// If a shader writes clip distance, also emit user varyings which
296    /// can be read in subsequent stages.
297    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING, true)]
298    pub enable_clip_distance_user_varying: bool,
299
300    /// In a tessellation control shader, assume that more than one patch can be processed in a
301    /// single workgroup. This requires changes to the way the InvocationId and PrimitiveId
302    /// builtins are processed, but should result in more efficient usage of the GPU.
303    #[option(SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP, false)]
304    pub multi_patch_workgroup: bool,
305
306    /// If set, a vertex shader will be compiled as part of a tessellation pipeline.
307    /// It will be translated as a compute kernel, so it can use the global invocation ID
308    /// to index the output buffer.
309    #[option(SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION, false)]
310    pub vertex_for_tessellation: bool,
311
312    /// The type of index in the index buffer, if present. For a compute shader, Metal
313    /// requires specifying the indexing at pipeline creation, rather than at draw time
314    /// as with graphics pipelines. This means we must create three different pipelines,
315    /// for no indexing, 16-bit indices, and 32-bit indices. Each requires different
316    /// handling for the gl_VertexIndex builtin. We may as well, then, create three
317    /// different shaders for these three scenarios.
318    #[option(SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE, IndexType::None)]
319    pub vertex_index_type: IndexType,
320
321    /// Assume that SubpassData images have multiple layers. Layered input attachments
322    /// are addressed relative to the Layer output from the vertex pipeline. This option
323    /// has no effect with multiview, since all input attachments are assumed to be layered
324    /// and will be addressed using the current ViewIndex.
325    #[option(SPVC_COMPILER_OPTION_MSL_ARRAYED_SUBPASS_INPUT, false)]
326    pub arrayed_subpass_input: bool,
327
328    /// The required alignment of linear textures of format `MTLPixelFormatR32Uint`.
329    ///
330    /// This is used to align the row stride for atomic accesses to such images.
331    #[option(SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT, 4)]
332    pub r32ui_linear_texture_alignment: u32,
333
334    /// The function constant ID to use for the linear texture alignment.
335    ///
336    /// On MSL 1.2 or later, you can override the alignment by setting this function constant.
337    #[option(SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID, 65535)]
338    pub r32ui_alignment_constant_id: u32,
339
340    /// Whether to use SIMD-group or quadgroup functions to implement group non-uniform
341    /// operations. Some GPUs on iOS do not support the SIMD-group functions, only the
342    /// quadgroup functions.
343    #[option(SPVC_COMPILER_OPTION_MSL_IOS_USE_SIMDGROUP_FUNCTIONS, false)]
344    pub ios_use_simdgroup_functions: bool,
345
346    /// If set, the subgroup size will be assumed to be one, and subgroup-related
347    /// builtins and operations will be emitted accordingly.
348    ///
349    /// This mode is intended to be used by MoltenVK on hardware/software configurations
350    /// which do not provide sufficient support for subgroups.
351    #[option(SPVC_COMPILER_OPTION_MSL_EMULATE_SUBGROUPS, false)]
352    pub emulate_subgroups: bool,
353
354    /// If nonzero, a fixed subgroup size to assume. Metal, similarly to VK_EXT_subgroup_size_control,
355    /// allows the SIMD-group size (aka thread execution width) to vary depending on
356    /// register usage and requirements.
357    ///
358    /// In certain circumstances--for example, a pipeline
359    /// in MoltenVK without VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT--
360    /// this is undesirable. This fixes the value of the SubgroupSize builtin, instead of
361    /// mapping it to the Metal builtin `[[thread_execution_width]]`. If the thread
362    /// execution width is reduced, the extra invocations will appear to be inactive.
363    ///
364    /// If zero, the SubgroupSize will be allowed to vary, and the builtin will be mapped
365    /// to the Metal `[[thread_execution_width]]` builtin.
366    #[option(SPVC_COMPILER_OPTION_MSL_FIXED_SUBGROUP_SIZE, 0)]
367    pub fixed_subgroup_size: u32,
368
369    /// If set, a dummy `[[sample_id]]` input is added to a fragment shader if none is present.
370    ///
371    /// This will force the shader to run at sample rate, assuming Metal does not optimize
372    /// the extra threads away.
373    #[option(SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING, false)]
374    pub force_sample_rate_shading: bool,
375
376    /// Specifies whether the iOS target version supports the `[[base_vertex]]`
377    /// and `[[base_instance]]` attributes.
378    #[option(SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE, false)]
379    pub ios_support_base_vertex_instance: bool,
380
381    /// Use storage buffers instead of vertex-style attributes for tessellation evaluation
382    /// input.
383    ///
384    /// This may require conversion of inputs in the generated post-tessellation
385    /// vertex shader, but allows the use of nested arrays.
386    #[option(SPVC_COMPILER_OPTION_MSL_RAW_BUFFER_TESE_INPUT, false)]
387    pub raw_buffer_tese_input: bool,
388
389    /// If set, gl_HelperInvocation will be set manually whenever a fragment is discarded.
390    /// Some Metal devices have a bug where `simd_is_helper_thread()` does not return true
391    /// after a fragment has been discarded.
392    ///
393    /// This is a workaround that is only expected to be needed
394    /// until the bug is fixed in Metal; it is provided as an option to allow disabling it when that occurs.
395    #[option(SPVC_COMPILER_OPTION_MSL_MANUAL_HELPER_INVOCATION_UPDATES, true)]
396    pub manual_helper_invocation_updates: bool,
397
398    /// If set, extra checks will be emitted in fragment shaders to prevent writes
399    /// from discarded fragments. Some Metal devices have a bug where writes to storage resources
400    /// from discarded fragment threads continue to occur, despite the fragment being
401    /// discarded.
402    ///
403    /// This is a workaround that is only expected to be needed until the
404    /// bug is fixed in Metal; it is provided as an option so it can be enabled
405    /// only when the bug is present.
406    #[option(SPVC_COMPILER_OPTION_MSL_CHECK_DISCARDED_FRAG_STORES, false)]
407    pub check_discarded_frag_stores: bool,
408
409    /// If set, Lod operands to OpImageSample*DrefExplicitLod for 1D and 2D array images
410    /// will be implemented using a gradient instead of passing the level operand directly.
411    ///
412    /// Some Metal devices have a bug where the `level()` argument to `depth2d_array<T>::sample_compare()`
413    /// in a fragment shader is biased by some unknown amount, possibly dependent on the
414    /// partial derivatives of the texture coordinates.
415    ///
416    /// This is a workaround that is only
417    /// expected to be needed until the bug is fixed in Metal; it is provided as an option
418    /// so it can be enabled only when the bug is present.
419    #[option(SPVC_COMPILER_OPTION_MSL_SAMPLE_DREF_LOD_ARRAY_AS_GRAD, false)]
420    pub sample_dref_lod_array_as_grad: bool,
421
422    /// MSL doesn't guarantee coherence between writes and subsequent reads of read_write textures.
423    /// This inserts fences before each read of a read_write texture to ensure coherency.
424    /// If you're sure you never rely on this, you can set this to false for a possible performance improvement.
425    /// Note: Only Apple's GPU compiler takes advantage of the lack of coherency, so make sure to test on Apple GPUs if you disable this.
426    #[option(SPVC_COMPILER_OPTION_MSL_READWRITE_TEXTURE_FENCES, true)]
427    pub readwrite_texture_fences: bool,
428
429    /// Metal 3.1 introduced a Metal regression bug which causes infinite recursion during
430    /// Metal's analysis of an entry point input structure that is itself recursive. Enabling
431    /// this option will replace the recursive input declaration with a alternate variable of
432    /// type void*, and then cast to the correct type at the top of the entry point function.
433    /// The bug has been reported to Apple, and will hopefully be fixed in future releases.
434    #[option(SPVC_COMPILER_OPTION_MSL_REPLACE_RECURSIVE_INPUTS, false)]
435    pub replace_recursive_inputs: bool,
436
437    /// If set, manual fixups of gradient vectors for cube texture lookups will be performed.
438    /// All released Apple Silicon GPUs to date behave incorrectly when sampling a cube texture
439    /// with explicit gradients. They will ignore one of the three partial derivatives based
440    /// on the selected major axis, and expect the remaining derivatives to be partially
441    /// transformed.
442    #[option(SPVC_COMPILER_OPTION_MSL_AGX_MANUAL_CUBE_GRAD_FIXUP, false)]
443    pub agx_manual_cube_grad_fixup: bool,
444
445    /// Metal will discard fragments with side effects under certain circumstances prematurely.
446    /// Example: CTS test dEQP-VK.fragment_operations.early_fragment.discard_no_early_fragment_tests_depth
447    /// Test will render a full screen quad with varying depth `[0,1]` for each fragment.
448    /// Each fragment will do an operation with side effects, modify the depth value and
449    /// discard the fragment. The test expects the fragment to be run due to:
450    /// <https://registry.khronos.org/vulkan/specs/1.0-extensions/html/vkspec.html#fragops-shader-depthreplacement>
451    /// which states that the fragment shader must be run due to replacing the depth in shader.
452    ///
453    /// However, Metal may prematurely discards fragments without executing them
454    /// (I believe this to be due to a greedy optimization on their end) making the test fail.
455    ///
456    /// This option enforces fragment execution for such cases where the fragment has operations
457    /// with side effects. Provided as an option hoping Metal will fix this issue in the future.
458    #[option(
459        SPVC_COMPILER_OPTION_MSL_FORCE_FRAGMENT_WITH_SIDE_EFFECTS_EXECUTION,
460        false
461    )]
462    pub force_fragment_with_side_effects_execution: bool,
463
464    /// Disables rasterization if BuiltInPosition is not written.
465    #[option(SPVC_COMPILER_OPTION_MSL_AUTO_DISABLE_RASTERIZATION, false)]
466    pub auto_disable_rasterization: bool,
467
468    /// Applies a default value if BuiltInPointSize is not written.
469    #[option(SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_DEFAULT, false)]
470    pub enable_point_size_default: bool,
471}
472
473/// The version of Metal Shading Language to compile to.
474///
475/// Defaults to MSL 1.2.
476#[derive(Copy, Clone, Eq, PartialEq)]
477pub struct MslVersion {
478    /// The major version of MSL.
479    pub major: u32,
480    /// The minor version of MSL.
481    pub minor: u32,
482    /// The patch version of MSL.
483    pub patch: u32,
484}
485
486impl MslVersion {
487    /// Create a new `MslVersion`.
488    pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
489        Self {
490            major,
491            minor,
492            patch,
493        }
494    }
495}
496
497impl Default for MslVersion {
498    fn default() -> Self {
499        MslVersion::from((1, 2))
500    }
501}
502
503impl Debug for MslVersion {
504    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
505        write!(
506            f,
507            "MslVersion({}.{}.{})",
508            self.major, self.minor, self.patch
509        )
510    }
511}
512
513impl From<MslVersion> for u32 {
514    fn from(value: MslVersion) -> Self {
515        (value.major * 10000) + (value.minor * 100) + value.patch
516    }
517}
518
519impl From<(u32, u32)> for MslVersion {
520    fn from(value: (u32, u32)) -> Self {
521        Self {
522            major: value.0,
523            minor: value.1,
524            patch: 0,
525        }
526    }
527}
528
529impl From<(u32, u32, u32)> for MslVersion {
530    fn from(value: (u32, u32, u32)) -> Self {
531        Self {
532            major: value.0,
533            minor: value.1,
534            patch: value.2,
535        }
536    }
537}
538
539/// When using Metal argument buffers, indicates the Metal argument buffer tier level supported by the Metal platform.
540///
541/// Tier capabilities based on recommendations from Apple engineering.
542#[repr(u32)]
543#[derive(Debug, Copy, Clone)]
544pub enum ArgumentBuffersTier {
545    /// Tier1 supports writable images on macOS, but not on iOS.
546    Tier1 = 0,
547    /// Tier2 supports writable images on macOS and iOS, and higher resource count limits.
548    Tier2 = 1,
549}
550
551/// The platform that the Metal runtime will be on.
552#[repr(u32)]
553#[derive(Debug, Copy, Clone)]
554pub enum MetalPlatform {
555    #[allow(non_camel_case_types)]
556    /// iOS (mobile and iPad)
557    iOS = 0,
558    /// macOS (Desktop)
559    MacOS = 1,
560}
561
562/// The type of index in the index buffer.
563#[repr(u32)]
564#[derive(Debug, Copy, Clone)]
565pub enum IndexType {
566    /// No index
567    None = 0,
568    /// Uint16 indices.
569    Uint16 = 1,
570    /// Uint32 indices.
571    Uint32 = 2,
572}
573
574impl From<MetalPlatform> for u32 {
575    fn from(value: MetalPlatform) -> Self {
576        match value {
577            MetalPlatform::iOS => 0,
578            MetalPlatform::MacOS => 1,
579        }
580    }
581}
582
583impl From<IndexType> for u32 {
584    fn from(value: IndexType) -> Self {
585        match value {
586            IndexType::None => 0,
587            IndexType::Uint16 => 1,
588            IndexType::Uint32 => 1,
589        }
590    }
591}
592
593impl From<ArgumentBuffersTier> for u32 {
594    fn from(value: ArgumentBuffersTier) -> Self {
595        match value {
596            ArgumentBuffersTier::Tier1 => 0,
597            ArgumentBuffersTier::Tier2 => 1,
598        }
599    }
600}
601
602/// Buffers that need to be provided to the MSL shader.
603#[non_exhaustive]
604#[derive(Debug, Clone)]
605pub struct BufferRequirements {
606    /// Whether an auxiliary swizzle buffer is needed by the shader.
607    pub needs_swizzle_buffer: bool,
608    /// Whether a buffer containing `STORAGE_BUFFER` buffer sizes to support OpArrayLength
609    /// is needed by the shader.
610    pub needs_buffer_size_buffer: bool,
611    /// Whether an output buffer is needed by the shader.
612    pub needs_output_buffer: bool,
613    /// Whether a patch output buffer is needed by the shader.
614    pub needs_patch_output_buffer: bool,
615    /// Whether an input threadgroup buffer is needed by the shader.
616    pub needs_input_threadgroup_buffer: bool,
617}
618
619/// Pipeline binding information for a resource.
620///
621/// Used to map a SPIR-V resource to an MSL buffer.
622#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
623pub enum ResourceBinding {
624    /// A resource with a qualified layout.
625    ///
626    /// i.e. `layout(set = 0, binding = 1)` in GLSL.
627    Qualified {
628        /// The descriptor set of the qualified layout.
629        set: u32,
630        /// The binding number of the qualified layout.
631        binding: u32,
632    },
633    /// The push constant buffer.
634    PushConstantBuffer,
635    /// The swizzle buffer, at the given index.
636    SwizzleBuffer(u32),
637    /// The buffer binding for buffer size
638    /// buffers to support `OpArrayLength`.
639    BufferSizeBuffer(u32),
640    /// The argument buffer, at the given index.
641    ///
642    /// This buffer binding should be kept as small as possible as all automatic bindings for buffers
643    /// will start at `max(ResourceBinding::ArgumentBuffer) + 1`.
644    ArgumentBuffer(u32),
645}
646
647impl ResourceBinding {
648    /// Create a resource binding for a qualified SPIR-V layout
649    /// specifier.
650    pub const fn from_qualified(set: u32, binding: u32) -> Self {
651        ResourceBinding::Qualified { set, binding }
652    }
653
654    const fn descriptor_set(&self) -> u32 {
655        const PUSH_CONSTANT_DESCRIPTOR_SET: u32 = !0;
656        match self {
657            ResourceBinding::Qualified { set, .. }
658            | ResourceBinding::SwizzleBuffer(set)
659            | ResourceBinding::BufferSizeBuffer(set)
660            | ResourceBinding::ArgumentBuffer(set) => *set,
661            ResourceBinding::PushConstantBuffer => PUSH_CONSTANT_DESCRIPTOR_SET,
662        }
663    }
664
665    const fn binding(&self) -> u32 {
666        const PUSH_CONSTANT_BINDING: u32 = 0;
667        const SWIZZLE_BUFFER_BINDING: u32 = !1;
668        const BUFFER_SIZE_BUFFER_BINDING: u32 = !2;
669        const ARGUMENT_BUFFER_BINDING: u32 = !3;
670
671        match self {
672            ResourceBinding::Qualified { binding, .. } => *binding,
673            ResourceBinding::PushConstantBuffer => PUSH_CONSTANT_BINDING,
674            ResourceBinding::SwizzleBuffer(_) => SWIZZLE_BUFFER_BINDING,
675            ResourceBinding::BufferSizeBuffer(_) => BUFFER_SIZE_BUFFER_BINDING,
676            ResourceBinding::ArgumentBuffer(_) => ARGUMENT_BUFFER_BINDING,
677        }
678    }
679}
680
681/// The MSL target to bind a resource to.
682///
683/// A single SPIR-V binding may bind to multiple
684/// registers for multiple resource types.
685///
686/// The count field indicates the number of resources consumed by this binding, if the binding
687/// represents an array of resources.
688///
689/// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value
690/// will be used to declare the array size in MSL, which does not support run-time-sized arrays.
691///
692/// If using MSL 2.0 argument buffers (for iOS only) the resource is not a storage image,
693/// the binding reference we remap to will become an `[[id(N)]]` attribute within
694/// the argument buffer index specified in
695/// [`ResourceBinding::ArgumentBuffer`].
696///
697/// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will
698/// become a `[[buffer(N)]]`, `[[texture(N)]]` or `[[sampler(N)]]` depending on the resource types used.
699#[derive(Debug, Clone, PartialEq, Eq, Hash)]
700pub struct BindTarget {
701    /// The buffer index to bind to, if applicable.
702    pub buffer: u32,
703    /// The texture index to bind to, if applicable.
704    pub texture: u32,
705    /// The sampler index to bind to, if applicable.
706    pub sampler: u32,
707    /// The number of resources consumed by this binding,
708    /// if the binding is an array of resources.
709    pub count: Option<NonZeroU32>,
710}
711
712/// Defines MSL characteristics of a shader interface variable.
713#[derive(Debug, Clone, PartialEq, Eq, Hash)]
714pub struct ShaderInterfaceVariable {
715    /// The builtin for the variable, if any.
716    pub builtin: Option<spirv::BuiltIn>,
717    /// The `vecsize` for this variable, if applicable.
718    ///
719    /// If `vecsize` is Some, it must be greater than or equal to the `vecsize` declared in the shader,
720    /// or behavior in the generated shader is undefined.
721    pub vecsize: Option<NonZeroU32>,
722    /// The format of a shader interface variable.
723    pub format: ShaderVariableFormat,
724    /// Indicates the rate at which a variable changes value.
725    pub rate: ShaderVariableRate,
726}
727
728impl ShaderInterfaceVariable {
729    /// We need to be maybeuninit, because None builtin is represented by i32::MAX,
730    /// which is invalid in Rust. I don't want to expose it just for this, so we'll just
731    /// do some magic.
732    #[must_use]
733    fn to_raw(&self, location: u32) -> MslShaderInterfaceVar2 {
734        let mut base = MslShaderInterfaceVar2 {
735            location,
736            format: self.format,
737            builtin: SpvBuiltIn::Position,
738            vecsize: self.vecsize.map_or(0, NonZeroU32::get),
739            rate: self.rate,
740        };
741
742        if let Some(builtin) = self.builtin {
743            // happy path, we can just set the builtin.
744            base.builtin = SpvBuiltIn(builtin as u32 as i32);
745        } else {
746            base.builtin = SpvBuiltIn(i32::MAX);
747        }
748
749        base
750    }
751}
752
753/// MSL specific APIs.
754impl Compiler<Msl> {
755    /// Get whether the vertex shader requires rasterization to be disabled.
756    pub fn is_rasterization_disabled(&self) -> bool {
757        unsafe { sys::spvc_compiler_msl_is_rasterization_disabled(self.ptr.as_ptr()) }
758    }
759
760    /// Get information such as required buffers for the MSL shader
761    pub fn buffer_requirements(&self) -> BufferRequirements {
762        unsafe {
763            let needs_swizzle_buffer =
764                sys::spvc_compiler_msl_needs_swizzle_buffer(self.ptr.as_ptr());
765            let needs_buffer_size_buffer =
766                sys::spvc_compiler_msl_needs_buffer_size_buffer(self.ptr.as_ptr());
767            let needs_output_buffer = sys::spvc_compiler_msl_needs_output_buffer(self.ptr.as_ptr());
768            let needs_patch_output_buffer =
769                sys::spvc_compiler_msl_needs_patch_output_buffer(self.ptr.as_ptr());
770            let needs_input_threadgroup_buffer =
771                sys::spvc_compiler_msl_needs_input_threadgroup_mem(self.ptr.as_ptr());
772
773            BufferRequirements {
774                needs_swizzle_buffer,
775                needs_buffer_size_buffer,
776                needs_output_buffer,
777                needs_patch_output_buffer,
778                needs_input_threadgroup_buffer,
779            }
780        }
781    }
782
783    /// Add a shader interface variable description used to fix up shader input variables.
784    ///
785    /// If shader inputs are provided, [`CompiledArtifact::is_shader_input_used`] will return true after
786    /// calling [`Compiler::compile`] if the location were used by the MSL code.
787    ///
788    /// Note: this covers the functionality implemented by the SPIR-V Cross
789    /// C API `spvc_compiler_msl_add_vertex_attribute`.
790    pub fn add_shader_input(
791        &mut self,
792        location: u32,
793        variable: &ShaderInterfaceVariable,
794    ) -> error::Result<()> {
795        let variable = variable.to_raw(location);
796        unsafe {
797            sys::spvc_compiler_msl_add_shader_input_2(self.ptr.as_ptr(), &variable).ok(&*self)
798        }
799    }
800
801    /// Add a shader interface variable description used to fix up shader output variables.
802    ///
803    /// If shader outputs are provided, [`CompiledArtifact::is_shader_input_used`] will return true after
804    /// calling [`Compiler::compile`] if the location were used by the MSL code.
805    ///
806    /// Note: this covers the functionality implemented by the SPIR-V Cross
807    /// C API `spvc_compiler_msl_add_vertex_attribute`.
808    pub fn add_shader_output(
809        &mut self,
810        location: u32,
811        variable: &ShaderInterfaceVariable,
812    ) -> error::Result<()> {
813        let variable = variable.to_raw(location);
814        unsafe {
815            sys::spvc_compiler_msl_add_shader_output_2(self.ptr.as_ptr(), &variable).ok(&*self)
816        }
817    }
818
819    /// Add a resource binding to indicate the MSL buffer, texture or sampler index to use for a
820    /// particular resource.
821    ///
822    /// If resource bindings are provided,
823    /// [`CompiledArtifact<Msl>::is_resource_used`] will return true after [`Compiler::compile`] if
824    /// the set/binding combination was used by the MSL code.
825    pub fn add_resource_binding(
826        &mut self,
827        stage: spirv::ExecutionModel,
828        binding: ResourceBinding,
829        bind_target: &BindTarget,
830    ) -> error::Result<()> {
831        let binding = MslResourceBinding2 {
832            stage: SpvExecutionModel(stage as u32 as i32),
833            desc_set: binding.descriptor_set(),
834            binding: binding.binding(),
835            count: bind_target.count.map_or(0, NonZeroU32::get),
836            msl_buffer: bind_target.buffer,
837            msl_texture: bind_target.texture,
838            msl_sampler: bind_target.sampler,
839        };
840        unsafe {
841            sys::spvc_compiler_msl_add_resource_binding_2(self.ptr.as_ptr(), &binding).ok(&*self)
842        }
843    }
844
845    /// When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets.
846    /// This corresponds to VK_KHR_push_descriptor in Vulkan.
847    pub fn add_discrete_descriptor_set(&mut self, desc_set: u32) -> error::Result<()> {
848        unsafe {
849            sys::spvc_compiler_msl_add_discrete_descriptor_set(self.ptr.as_ptr(), desc_set)
850                .ok(&*self)
851        }
852    }
853
854    /// This function marks a resource as using a dynamic offset
855    /// (`VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC` or `VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC`).
856    ///
857    /// `desc_set` and `binding` are the SPIR-V descriptor set and binding of a buffer resource
858    /// in this shader.
859    ///
860    /// `index` is the index within the dynamic offset buffer to use.
861    ///
862    /// This function only has any effect if argument buffers are enabled.
863    /// If so, the buffer will have its address adjusted at the beginning of the shader with
864    /// an offset taken from the dynamic offset buffer.
865    pub fn add_dynamic_buffer(
866        &mut self,
867        desc_set: u32,
868        binding: u32,
869        index: u32,
870    ) -> error::Result<()> {
871        unsafe {
872            sys::spvc_compiler_msl_add_dynamic_buffer(self.ptr.as_ptr(), desc_set, binding, index)
873                .ok(&*self)
874        }
875    }
876
877    /// This function marks a resource an inline uniform block
878    /// (`VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT`)
879    ///
880    /// `desc_set` and `binding` are the SPIR-V descriptor set and binding of a buffer resource
881    /// in this shader.
882    ///
883    /// This function only has any effect if argument buffers are enabled.
884    /// If so, the buffer block will be directly embedded into the argument
885    /// buffer, instead of being referenced indirectly via pointer.
886    pub fn add_inline_uniform_block(&mut self, desc_set: u32, binding: u32) -> error::Result<()> {
887        unsafe {
888            sys::spvc_compiler_msl_add_inline_uniform_block(self.ptr.as_ptr(), desc_set, binding)
889                .ok(&*self)
890        }
891    }
892
893    /// If an argument buffer is large enough, it may need to be in the device storage space rather than
894    /// constant. Opt-in to this behavior here on a per-set basis.
895    pub fn set_argument_buffer_device_address_space(
896        &mut self,
897        desc_set: u32,
898        device_address: bool,
899    ) -> error::Result<()> {
900        unsafe {
901            sys::spvc_compiler_msl_set_argument_buffer_device_address_space(
902                self.ptr.as_ptr(),
903                desc_set,
904                device_address,
905            )
906            .ok(&*self)
907        }
908    }
909
910    /// Remap a sampler with ID to a constexpr sampler.
911    /// Older iOS targets must use constexpr samplers in certain cases (PCF),
912    /// so a static sampler must be used.
913    ///
914    /// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
915    /// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
916    /// The remapped sampler must not be an array of samplers.
917    ///
918    /// Prefer [`Compiler<Msl>::remap_constexpr_sampler_by_binding`] unless you're also doing reflection anyways.
919    pub fn remap_constexpr_sampler(
920        &mut self,
921        variable: impl Into<Handle<VariableId>>,
922        sampler: &ConstexprSampler,
923        ycbcr: Option<&SamplerYcbcrConversion>,
924    ) -> error::Result<()> {
925        let variable = variable.into();
926        let id = self.yield_id(variable)?;
927        if let Some(ycbcr) = ycbcr {
928            unsafe {
929                sys::spvc_compiler_msl_remap_constexpr_sampler_ycbcr(
930                    self.ptr.as_ptr(),
931                    id,
932                    sampler,
933                    ycbcr,
934                )
935                .ok(&*self)
936            }
937        } else {
938            unsafe {
939                sys::spvc_compiler_msl_remap_constexpr_sampler(self.ptr.as_ptr(), id, sampler)
940                    .ok(&*self)
941            }
942        }
943    }
944
945    /// Remap a sampler with set/binding, to a constexpr sampler.
946    /// Older iOS targets must use constexpr samplers in certain cases (PCF),
947    /// so a static sampler must be used.
948    ///
949    /// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
950    /// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
951    /// The remapped sampler must not be an array of samplers.
952    ///
953    /// Remaps based on ID take priority over set/binding remaps.
954    pub fn remap_constexpr_sampler_by_binding(
955        &mut self,
956        desc_set: u32,
957        binding: u32,
958        sampler: &ConstexprSampler,
959        ycbcr: Option<&SamplerYcbcrConversion>,
960    ) -> error::Result<()> {
961        if let Some(ycbcr) = ycbcr {
962            unsafe {
963                sys::spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(
964                    self.ptr.as_ptr(),
965                    desc_set,
966                    binding,
967                    sampler,
968                    ycbcr,
969                )
970                .ok(&*self)
971            }
972        } else {
973            unsafe {
974                sys::spvc_compiler_msl_remap_constexpr_sampler_by_binding(
975                    self.ptr.as_ptr(),
976                    desc_set,
977                    binding,
978                    sampler,
979                )
980                .ok(&*self)
981            }
982        }
983    }
984
985    /// If using [`CompilerOptions::pad_fragment_output_components`], override the number of components we expect
986    /// to use for a particular location. The default is 4 if number of components is not overridden.
987    pub fn set_fragment_output_components(
988        &mut self,
989        location: u32,
990        components: u32,
991    ) -> error::Result<()> {
992        unsafe {
993            sys::spvc_compiler_msl_set_fragment_output_components(
994                self.ptr.as_ptr(),
995                location,
996                components,
997            )
998            .ok(&*self)
999        }
1000    }
1001
1002    /// Set the suffix for combined image samplers.
1003    pub fn set_combined_sampler_suffix<'str>(
1004        &mut self,
1005        str: impl Into<CompilerStr<'str>>,
1006    ) -> error::Result<()> {
1007        unsafe {
1008            let str = str.into();
1009
1010            let suffix = str.into_cstring_ptr()?;
1011
1012            sys::spvc_compiler_msl_set_combined_sampler_suffix(self.ptr.as_ptr(), suffix.as_ptr())
1013                .ok(&*self)
1014        }
1015    }
1016
1017    /// Get the suffix for combined image samplers.
1018    pub fn combined_sampler_suffix(&self) -> CompilerStr<'_> {
1019        unsafe {
1020            let suffix = sys::spvc_compiler_msl_get_combined_sampler_suffix(self.ptr.as_ptr());
1021            CompilerStr::from_ptr(suffix, self.ctx.drop_guard())
1022        }
1023    }
1024
1025    /// Mask a stage output by location.
1026    ///
1027    /// If a shader output is active in this stage, but inactive in a subsequent stage,
1028    /// this can be signalled here. This can be used to work around certain cross-stage matching problems
1029    /// which plagues MSL in certain scenarios.
1030    ///
1031    /// An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private
1032    /// variable.
1033    ///
1034    /// This option is only meaningful for MSL and HLSL, since GLSL matches by location directly.
1035    ///
1036    pub fn mask_stage_output_by_location(
1037        &mut self,
1038        location: u32,
1039        component: u32,
1040    ) -> crate::error::Result<()> {
1041        unsafe {
1042            sys::spvc_compiler_mask_stage_output_by_location(self.ptr.as_ptr(), location, component)
1043                .ok(&*self)
1044        }
1045    }
1046
1047    /// Mask a stage output by builtin. Masking builtins only takes effect if the builtin in question is part of the stage output interface.
1048    ///
1049    /// If a shader output is active in this stage, but inactive in a subsequent stage,
1050    /// this can be signalled here. This can be used to work around certain cross-stage matching problems
1051    /// which plagues MSL in certain scenarios.
1052    ///
1053    /// An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private
1054    /// variable.
1055    ///
1056    /// This option is only meaningful for MSL and HLSL, since GLSL matches by location directly.
1057    /// Masking builtins only takes effect if the builtin in question is part of the stage output interface.
1058    pub fn mask_stage_output_by_builtin(
1059        &mut self,
1060        builtin: spirv::BuiltIn,
1061    ) -> crate::error::Result<()> {
1062        unsafe {
1063            sys::spvc_compiler_mask_stage_output_by_builtin(
1064                self.ptr.as_ptr(),
1065                SpvBuiltIn(builtin as u32 as i32),
1066            )
1067            .ok(&*self)
1068        }
1069    }
1070}
1071
1072#[derive(Copy, Clone, Debug, Default)]
1073#[non_exhaustive]
1074/// The tier of automatic resource binding.
1075///
1076/// Note that tertiary and quaternary bindings are not accessible via
1077/// the SPIR-V Cross C API.
1078pub enum AutomaticResourceBindingTier {
1079    #[default]
1080    /// The primary automatic resource binding.
1081    Primary,
1082
1083    /// Should only be used for combined image samplers, in which case the
1084    /// sampler's binding is returned instead.
1085    ///
1086    /// Also used for the auxillary image atomic buffer.
1087    Secondary,
1088}
1089
1090impl CompiledArtifact<Msl> {
1091    /// Returns whether the set/binding combination provided in [`Compiler<Msl>::add_resource_binding`]
1092    /// was used.
1093    pub fn is_resource_used(&self, model: spirv::ExecutionModel, binding: ResourceBinding) -> bool {
1094        unsafe {
1095            sys::spvc_compiler_msl_is_resource_used(
1096                self.compiler.ptr.as_ptr(),
1097                SpvExecutionModel(model as u32 as i32),
1098                binding.descriptor_set(),
1099                binding.binding(),
1100            )
1101        }
1102    }
1103
1104    /// Returns whether the location provided in [`Compiler<Msl>::add_shader_input`]
1105    /// was used.
1106    pub fn is_shader_input_used(&self, location: u32) -> bool {
1107        unsafe { sys::spvc_compiler_msl_is_shader_input_used(self.compiler.ptr.as_ptr(), location) }
1108    }
1109
1110    /// Returns whether the location provided in [`Compiler<Msl>::add_shader_output`]
1111    /// was used.
1112    pub fn is_shader_output_used(&self, location: u32) -> bool {
1113        unsafe {
1114            sys::spvc_compiler_msl_is_shader_output_used(self.compiler.ptr.as_ptr(), location)
1115        }
1116    }
1117
1118    /// For a variable resource ID, report the automatically assigned resource index.
1119    ///
1120    /// If the descriptor set was part of an argument buffer, report the `[[id(N)]]`,
1121    /// or `[[buffer/texture/sampler]]` binding for other resources.
1122    ///
1123    /// If the resource was a combined image sampler, report the image binding for [`AutomaticResourceBindingTier::Primary`],
1124    /// or the sampler half for [`AutomaticResourceBindingTier::Secondary`].
1125    ///
1126    /// If no binding exists, None is returned.
1127    pub fn automatic_resource_binding(
1128        &self,
1129        handle: impl Into<Handle<VariableId>>,
1130        tier: AutomaticResourceBindingTier,
1131    ) -> error::Result<Option<u32>> {
1132        let handle = handle.into();
1133        let id = self.yield_id(handle)?;
1134
1135        let res = match tier {
1136            AutomaticResourceBindingTier::Primary => unsafe {
1137                sys::spvc_compiler_msl_get_automatic_resource_binding(self.ptr.as_ptr(), id)
1138            },
1139            AutomaticResourceBindingTier::Secondary => unsafe {
1140                sys::spvc_compiler_msl_get_automatic_resource_binding_secondary(
1141                    self.ptr.as_ptr(),
1142                    id,
1143                )
1144            },
1145        };
1146
1147        if res == u32::MAX {
1148            Ok(None)
1149        } else {
1150            Ok(Some(res))
1151        }
1152    }
1153
1154    /// Query if a variable ID was used as a depth resource.
1155    ///
1156    /// This is meaningful for MSL since descriptor types depend on this knowledge.
1157    /// Cases which return true:
1158    /// - Images which are declared with depth = 1 image type.
1159    /// - Samplers which are statically used at least once with Dref opcodes.
1160    /// - Images which are statically used at least once with Dref opcodes.
1161    pub fn variable_is_depth_or_compare(
1162        &self,
1163        variable: impl Into<Handle<VariableId>>,
1164    ) -> error::Result<bool> {
1165        let variable = variable.into();
1166        let id = self.yield_id(variable)?;
1167        unsafe {
1168            Ok(sys::spvc_compiler_variable_is_depth_or_compare(
1169                self.ptr.as_ptr(),
1170                id,
1171            ))
1172        }
1173    }
1174}
1175
1176#[cfg(test)]
1177mod test {
1178    use crate::compile::msl::CompilerOptions;
1179    use spirv_cross_sys::spvc_compiler_create_compiler_options;
1180
1181    use crate::compile::sealed::ApplyCompilerOptions;
1182    use crate::error::{SpirvCrossError, ToContextError};
1183    use crate::Compiler;
1184    use crate::{targets, Module};
1185
1186    static BASIC_SPV: &[u8] = include_bytes!("../../../basic.spv");
1187
1188    #[test]
1189    pub fn msl_opts() -> Result<(), SpirvCrossError> {
1190        let words = Vec::from(BASIC_SPV);
1191        let words = Module::from_words(bytemuck::cast_slice(&words));
1192
1193        let compiler: Compiler<targets::Msl> = Compiler::new(words)?;
1194        let resources = compiler.shader_resources()?.all_resources()?;
1195
1196        let mut opts_ptr = std::ptr::null_mut();
1197
1198        unsafe {
1199            spvc_compiler_create_compiler_options(compiler.ptr.as_ptr(), &mut opts_ptr)
1200                .ok(&compiler)?;
1201        }
1202
1203        // println!("{:#?}", resources);
1204        let opts = CompilerOptions::default();
1205        unsafe {
1206            opts.apply(opts_ptr, &compiler)?;
1207        }
1208
1209        // match ty.inner {
1210        //     TypeInner::Struct(ty) => {
1211        //         compiler.get_type(ty.members[0].id)?;
1212        //     }
1213        //     TypeInner::Vector { .. } => {}
1214        //     _ => {}
1215        // }
1216        Ok(())
1217    }
1218}