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    /// Disablle 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    ///
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
465/// The version of Metal Shading Language to compile to.
466///
467/// Defaults to MSL 1.2.
468#[derive(Copy, Clone, Eq, PartialEq)]
469pub struct MslVersion {
470    /// The major version of MSL.
471    pub major: u32,
472    /// The minor version of MSL.
473    pub minor: u32,
474    /// The patch version of MSL.
475    pub patch: u32,
476}
477
478impl MslVersion {
479    /// Create a new `MslVersion`.
480    pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
481        Self {
482            major,
483            minor,
484            patch,
485        }
486    }
487}
488
489impl Default for MslVersion {
490    fn default() -> Self {
491        MslVersion::from((1, 2))
492    }
493}
494
495impl Debug for MslVersion {
496    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
497        write!(
498            f,
499            "MslVersion({}.{}.{})",
500            self.major, self.minor, self.patch
501        )
502    }
503}
504
505impl From<MslVersion> for u32 {
506    fn from(value: MslVersion) -> Self {
507        (value.major * 10000) + (value.minor * 100) + value.patch
508    }
509}
510
511impl From<(u32, u32)> for MslVersion {
512    fn from(value: (u32, u32)) -> Self {
513        Self {
514            major: value.0,
515            minor: value.1,
516            patch: 0,
517        }
518    }
519}
520
521impl From<(u32, u32, u32)> for MslVersion {
522    fn from(value: (u32, u32, u32)) -> Self {
523        Self {
524            major: value.0,
525            minor: value.1,
526            patch: value.2,
527        }
528    }
529}
530
531/// When using Metal argument buffers, indicates the Metal argument buffer tier level supported by the Metal platform.
532///
533/// Tier capabilities based on recommendations from Apple engineering.
534#[repr(u32)]
535#[derive(Debug, Copy, Clone)]
536pub enum ArgumentBuffersTier {
537    /// Tier1 supports writable images on macOS, but not on iOS.
538    Tier1 = 0,
539    /// Tier2 supports writable images on macOS and iOS, and higher resource count limits.
540    Tier2 = 1,
541}
542
543/// The platform that the Metal runtime will be on.
544#[repr(u32)]
545#[derive(Debug, Copy, Clone)]
546pub enum MetalPlatform {
547    #[allow(non_camel_case_types)]
548    /// iOS (mobile and iPad)
549    iOS = 0,
550    /// macOS (Desktop)
551    MacOS = 1,
552}
553
554/// The type of index in the index buffer.
555#[repr(u32)]
556#[derive(Debug, Copy, Clone)]
557pub enum IndexType {
558    /// No index
559    None = 0,
560    /// Uint16 indices.
561    Uint16 = 1,
562    /// Uint32 indices.
563    Uint32 = 2,
564}
565
566impl From<MetalPlatform> for u32 {
567    fn from(value: MetalPlatform) -> Self {
568        match value {
569            MetalPlatform::iOS => 0,
570            MetalPlatform::MacOS => 1,
571        }
572    }
573}
574
575impl From<IndexType> for u32 {
576    fn from(value: IndexType) -> Self {
577        match value {
578            IndexType::None => 0,
579            IndexType::Uint16 => 1,
580            IndexType::Uint32 => 1,
581        }
582    }
583}
584
585impl From<ArgumentBuffersTier> for u32 {
586    fn from(value: ArgumentBuffersTier) -> Self {
587        match value {
588            ArgumentBuffersTier::Tier1 => 0,
589            ArgumentBuffersTier::Tier2 => 1,
590        }
591    }
592}
593
594/// Buffers that need to be provided to the MSL shader.
595#[non_exhaustive]
596#[derive(Debug, Clone)]
597pub struct BufferRequirements {
598    /// Whether an auxiliary swizzle buffer is needed by the shader.
599    pub needs_swizzle_buffer: bool,
600    /// Whether a buffer containing `STORAGE_BUFFER` buffer sizes to support OpArrayLength
601    /// is needed by the shader.
602    pub needs_buffer_size_buffer: bool,
603    /// Whether an output buffer is needed by the shader.
604    pub needs_output_buffer: bool,
605    /// Whether a patch output buffer is needed by the shader.
606    pub needs_patch_output_buffer: bool,
607    /// Whether an input threadgroup buffer is needed by the shader.
608    pub needs_input_threadgroup_buffer: bool,
609}
610
611/// Pipeline binding information for a resource.
612///
613/// Used to map a SPIR-V resource to an MSL buffer.
614#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
615pub enum ResourceBinding {
616    /// A resource with a qualified layout.
617    ///
618    /// i.e. `layout(set = 0, binding = 1)` in GLSL.
619    Qualified {
620        /// The descriptor set of the qualified layout.
621        set: u32,
622        /// The binding number of the qualified layout.
623        binding: u32,
624    },
625    /// The push constant buffer.
626    PushConstantBuffer,
627    /// The swizzle buffer, at the given index.
628    SwizzleBuffer(u32),
629    /// The buffer binding for buffer size
630    /// buffers to support `OpArrayLength`.
631    BufferSizeBuffer(u32),
632    /// The argument buffer, at the given index.
633    ///
634    /// This buffer binding should be kept as small as possible as all automatic bindings for buffers
635    /// will start at `max(ResourceBinding::ArgumentBuffer) + 1`.
636    ArgumentBuffer(u32),
637}
638
639impl ResourceBinding {
640    /// Create a resource binding for a qualified SPIR-V layout
641    /// specifier.
642    pub const fn from_qualified(set: u32, binding: u32) -> Self {
643        ResourceBinding::Qualified { set, binding }
644    }
645
646    const fn descriptor_set(&self) -> u32 {
647        const PUSH_CONSTANT_DESCRIPTOR_SET: u32 = !0;
648        match self {
649            ResourceBinding::Qualified { set, .. }
650            | ResourceBinding::SwizzleBuffer(set)
651            | ResourceBinding::BufferSizeBuffer(set)
652            | ResourceBinding::ArgumentBuffer(set) => *set,
653            ResourceBinding::PushConstantBuffer => PUSH_CONSTANT_DESCRIPTOR_SET,
654        }
655    }
656
657    const fn binding(&self) -> u32 {
658        const PUSH_CONSTANT_BINDING: u32 = 0;
659        const SWIZZLE_BUFFER_BINDING: u32 = !1;
660        const BUFFER_SIZE_BUFFER_BINDING: u32 = !2;
661        const ARGUMENT_BUFFER_BINDING: u32 = !3;
662
663        match self {
664            ResourceBinding::Qualified { binding, .. } => *binding,
665            ResourceBinding::PushConstantBuffer => PUSH_CONSTANT_BINDING,
666            ResourceBinding::SwizzleBuffer(_) => SWIZZLE_BUFFER_BINDING,
667            ResourceBinding::BufferSizeBuffer(_) => BUFFER_SIZE_BUFFER_BINDING,
668            ResourceBinding::ArgumentBuffer(_) => ARGUMENT_BUFFER_BINDING,
669        }
670    }
671}
672
673/// The MSL target to bind a resource to.
674///
675/// A single SPIR-V binding may bind to multiple
676/// registers for multiple resource types.
677///
678/// The count field indicates the number of resources consumed by this binding, if the binding
679/// represents an array of resources.
680///
681/// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value
682/// will be used to declare the array size in MSL, which does not support run-time-sized arrays.
683///
684/// If using MSL 2.0 argument buffers (for iOS only) the resource is not a storage image,
685/// the binding reference we remap to will become an `[[id(N)]]` attribute within
686/// the argument buffer index specified in
687/// [`ResourceBinding::ArgumentBuffer`].
688///
689/// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will
690/// become a `[[buffer(N)]]`, `[[texture(N)]]` or `[[sampler(N)]]` depending on the resource types used.
691#[derive(Debug, Clone, PartialEq, Eq, Hash)]
692pub struct BindTarget {
693    /// The buffer index to bind to, if applicable.
694    pub buffer: u32,
695    /// The texture index to bind to, if applicable.
696    pub texture: u32,
697    /// The sampler index to bind to, if applicable.
698    pub sampler: u32,
699    /// The number of resources consumed by this binding,
700    /// if the binding is an array of resources.
701    pub count: Option<NonZeroU32>,
702}
703
704/// Defines MSL characteristics of a shader interface variable.
705#[derive(Debug, Clone, PartialEq, Eq, Hash)]
706pub struct ShaderInterfaceVariable {
707    /// The builtin for the variable, if any.
708    pub builtin: Option<spirv::BuiltIn>,
709    /// The `vecsize` for this variable, if applicable.
710    ///
711    /// If `vecsize` is Some, it must be greater than or equal to the `vecsize` declared in the shader,
712    /// or behavior in the generated shader is undefined.
713    pub vecsize: Option<NonZeroU32>,
714    /// The format of a shader interface variable.
715    pub format: ShaderVariableFormat,
716    /// Indicates the rate at which a variable changes value.
717    pub rate: ShaderVariableRate,
718}
719
720impl ShaderInterfaceVariable {
721    /// We need to be maybeuninit, because None builtin is represented by i32::MAX,
722    /// which is invalid in Rust. I don't want to expose it just for this, so we'll just
723    /// do some magic.
724    #[must_use]
725    fn to_raw(&self, location: u32) -> MslShaderInterfaceVar2 {
726        let mut base = MslShaderInterfaceVar2 {
727            location,
728            format: self.format,
729            builtin: SpvBuiltIn::Position,
730            vecsize: self.vecsize.map_or(0, NonZeroU32::get),
731            rate: self.rate,
732        };
733
734        if let Some(builtin) = self.builtin {
735            // happy path, we can just set the builtin.
736            base.builtin = SpvBuiltIn(builtin as u32 as i32);
737        } else {
738            base.builtin = SpvBuiltIn(i32::MAX);
739        }
740
741        base
742    }
743}
744
745/// MSL specific APIs.
746impl Compiler<Msl> {
747    /// Get whether the vertex shader requires rasterization to be disabled.
748    pub fn is_rasterization_disabled(&self) -> bool {
749        unsafe { sys::spvc_compiler_msl_is_rasterization_disabled(self.ptr.as_ptr()) }
750    }
751
752    /// Get information such as required buffers for the MSL shader
753    pub fn buffer_requirements(&self) -> BufferRequirements {
754        unsafe {
755            let needs_swizzle_buffer =
756                sys::spvc_compiler_msl_needs_swizzle_buffer(self.ptr.as_ptr());
757            let needs_buffer_size_buffer =
758                sys::spvc_compiler_msl_needs_buffer_size_buffer(self.ptr.as_ptr());
759            let needs_output_buffer = sys::spvc_compiler_msl_needs_output_buffer(self.ptr.as_ptr());
760            let needs_patch_output_buffer =
761                sys::spvc_compiler_msl_needs_patch_output_buffer(self.ptr.as_ptr());
762            let needs_input_threadgroup_buffer =
763                sys::spvc_compiler_msl_needs_input_threadgroup_mem(self.ptr.as_ptr());
764
765            BufferRequirements {
766                needs_swizzle_buffer,
767                needs_buffer_size_buffer,
768                needs_output_buffer,
769                needs_patch_output_buffer,
770                needs_input_threadgroup_buffer,
771            }
772        }
773    }
774
775    /// Add a shader interface variable description used to fix up shader input variables.
776    ///
777    /// If shader inputs are provided, [`CompiledArtifact::is_shader_input_used`] will return true after
778    /// calling [`Compiler::compile`] if the location were used by the MSL code.
779    ///
780    /// Note: this covers the functionality implemented by the SPIR-V Cross
781    /// C API `spvc_compiler_msl_add_vertex_attribute`.
782    pub fn add_shader_input(
783        &mut self,
784        location: u32,
785        variable: &ShaderInterfaceVariable,
786    ) -> error::Result<()> {
787        let variable = variable.to_raw(location);
788        unsafe {
789            sys::spvc_compiler_msl_add_shader_input_2(self.ptr.as_ptr(), &variable).ok(&*self)
790        }
791    }
792
793    /// Add a shader interface variable description used to fix up shader output variables.
794    ///
795    /// If shader outputs are provided, [`CompiledArtifact::is_shader_input_used`] will return true after
796    /// calling [`Compiler::compile`] if the location were used by the MSL code.
797    ///
798    /// Note: this covers the functionality implemented by the SPIR-V Cross
799    /// C API `spvc_compiler_msl_add_vertex_attribute`.
800    pub fn add_shader_output(
801        &mut self,
802        location: u32,
803        variable: &ShaderInterfaceVariable,
804    ) -> error::Result<()> {
805        let variable = variable.to_raw(location);
806        unsafe {
807            sys::spvc_compiler_msl_add_shader_output_2(self.ptr.as_ptr(), &variable).ok(&*self)
808        }
809    }
810
811    /// Add a resource binding to indicate the MSL buffer, texture or sampler index to use for a
812    /// particular resource.
813    ///
814    /// If resource bindings are provided,
815    /// [`CompiledArtifact<Msl>::is_resource_used`] will return true after [`Compiler::compile`] if
816    /// the set/binding combination was used by the MSL code.
817    pub fn add_resource_binding(
818        &mut self,
819        stage: spirv::ExecutionModel,
820        binding: ResourceBinding,
821        bind_target: &BindTarget,
822    ) -> error::Result<()> {
823        let binding = MslResourceBinding2 {
824            stage: SpvExecutionModel(stage as u32 as i32),
825            desc_set: binding.descriptor_set(),
826            binding: binding.binding(),
827            count: bind_target.count.map_or(0, NonZeroU32::get),
828            msl_buffer: bind_target.buffer,
829            msl_texture: bind_target.texture,
830            msl_sampler: bind_target.sampler,
831        };
832        unsafe {
833            sys::spvc_compiler_msl_add_resource_binding_2(self.ptr.as_ptr(), &binding).ok(&*self)
834        }
835    }
836
837    /// When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets.
838    /// This corresponds to VK_KHR_push_descriptor in Vulkan.
839    pub fn add_discrete_descriptor_set(&mut self, desc_set: u32) -> error::Result<()> {
840        unsafe {
841            sys::spvc_compiler_msl_add_discrete_descriptor_set(self.ptr.as_ptr(), desc_set)
842                .ok(&*self)
843        }
844    }
845
846    /// This function marks a resource as using a dynamic offset
847    /// (`VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC` or `VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC`).
848    ///
849    /// `desc_set` and `binding` are the SPIR-V descriptor set and binding of a buffer resource
850    /// in this shader.
851    ///
852    /// `index` is the index within the dynamic offset buffer to use.
853    ///
854    /// This function only has any effect if argument buffers are enabled.
855    /// If so, the buffer will have its address adjusted at the beginning of the shader with
856    /// an offset taken from the dynamic offset buffer.
857    pub fn add_dynamic_buffer(
858        &mut self,
859        desc_set: u32,
860        binding: u32,
861        index: u32,
862    ) -> error::Result<()> {
863        unsafe {
864            sys::spvc_compiler_msl_add_dynamic_buffer(self.ptr.as_ptr(), desc_set, binding, index)
865                .ok(&*self)
866        }
867    }
868
869    /// This function marks a resource an inline uniform block
870    /// (`VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT`)
871    ///
872    /// `desc_set` and `binding` are the SPIR-V descriptor set and binding of a buffer resource
873    /// in this shader.
874    ///
875    /// This function only has any effect if argument buffers are enabled.
876    /// If so, the buffer block will be directly embedded into the argument
877    /// buffer, instead of being referenced indirectly via pointer.
878    pub fn add_inline_uniform_block(&mut self, desc_set: u32, binding: u32) -> error::Result<()> {
879        unsafe {
880            sys::spvc_compiler_msl_add_inline_uniform_block(self.ptr.as_ptr(), desc_set, binding)
881                .ok(&*self)
882        }
883    }
884
885    /// If an argument buffer is large enough, it may need to be in the device storage space rather than
886    /// constant. Opt-in to this behavior here on a per-set basis.
887    pub fn set_argument_buffer_device_address_space(
888        &mut self,
889        desc_set: u32,
890        device_address: bool,
891    ) -> error::Result<()> {
892        unsafe {
893            sys::spvc_compiler_msl_set_argument_buffer_device_address_space(
894                self.ptr.as_ptr(),
895                desc_set,
896                device_address,
897            )
898            .ok(&*self)
899        }
900    }
901
902    /// Remap a sampler with ID to a constexpr sampler.
903    /// Older iOS targets must use constexpr samplers in certain cases (PCF),
904    /// so a static sampler must be used.
905    ///
906    /// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
907    /// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
908    /// The remapped sampler must not be an array of samplers.
909    ///
910    /// Prefer [`Compiler<Msl>::remap_constexpr_sampler_by_binding`] unless you're also doing reflection anyways.
911    pub fn remap_constexpr_sampler(
912        &mut self,
913        variable: impl Into<Handle<VariableId>>,
914        sampler: &ConstexprSampler,
915        ycbcr: Option<&SamplerYcbcrConversion>,
916    ) -> error::Result<()> {
917        let variable = variable.into();
918        let id = self.yield_id(variable)?;
919        if let Some(ycbcr) = ycbcr {
920            unsafe {
921                sys::spvc_compiler_msl_remap_constexpr_sampler_ycbcr(
922                    self.ptr.as_ptr(),
923                    id,
924                    sampler,
925                    ycbcr,
926                )
927                .ok(&*self)
928            }
929        } else {
930            unsafe {
931                sys::spvc_compiler_msl_remap_constexpr_sampler(self.ptr.as_ptr(), id, sampler)
932                    .ok(&*self)
933            }
934        }
935    }
936
937    /// Remap a sampler with set/binding, to a constexpr sampler.
938    /// Older iOS targets must use constexpr samplers in certain cases (PCF),
939    /// so a static sampler must be used.
940    ///
941    /// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
942    /// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
943    /// The remapped sampler must not be an array of samplers.
944    ///
945    /// Remaps based on ID take priority over set/binding remaps.
946    pub fn remap_constexpr_sampler_by_binding(
947        &mut self,
948        desc_set: u32,
949        binding: u32,
950        sampler: &ConstexprSampler,
951        ycbcr: Option<&SamplerYcbcrConversion>,
952    ) -> error::Result<()> {
953        if let Some(ycbcr) = ycbcr {
954            unsafe {
955                sys::spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(
956                    self.ptr.as_ptr(),
957                    desc_set,
958                    binding,
959                    sampler,
960                    ycbcr,
961                )
962                .ok(&*self)
963            }
964        } else {
965            unsafe {
966                sys::spvc_compiler_msl_remap_constexpr_sampler_by_binding(
967                    self.ptr.as_ptr(),
968                    desc_set,
969                    binding,
970                    sampler,
971                )
972                .ok(&*self)
973            }
974        }
975    }
976
977    /// If using [`CompilerOptions::pad_fragment_output_components`], override the number of components we expect
978    /// to use for a particular location. The default is 4 if number of components is not overridden.
979    pub fn set_fragment_output_components(
980        &mut self,
981        location: u32,
982        components: u32,
983    ) -> error::Result<()> {
984        unsafe {
985            sys::spvc_compiler_msl_set_fragment_output_components(
986                self.ptr.as_ptr(),
987                location,
988                components,
989            )
990            .ok(&*self)
991        }
992    }
993
994    /// Set the suffix for combined image samplers.
995    pub fn set_combined_sampler_suffix<'str>(
996        &mut self,
997        str: impl Into<CompilerStr<'str>>,
998    ) -> error::Result<()> {
999        unsafe {
1000            let str = str.into();
1001
1002            let suffix = str.into_cstring_ptr()?;
1003
1004            sys::spvc_compiler_msl_set_combined_sampler_suffix(self.ptr.as_ptr(), suffix.as_ptr())
1005                .ok(&*self)
1006        }
1007    }
1008
1009    /// Get the suffix for combined image samplers.
1010    pub fn combined_sampler_suffix(&self) -> CompilerStr {
1011        unsafe {
1012            let suffix = sys::spvc_compiler_msl_get_combined_sampler_suffix(self.ptr.as_ptr());
1013            CompilerStr::from_ptr(suffix, self.ctx.drop_guard())
1014        }
1015    }
1016
1017    /// Mask a stage output by location.
1018    ///
1019    /// If a shader output is active in this stage, but inactive in a subsequent stage,
1020    /// this can be signalled here. This can be used to work around certain cross-stage matching problems
1021    /// which plagues MSL in certain scenarios.
1022    ///
1023    /// An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private
1024    /// variable.
1025    ///
1026    /// This option is only meaningful for MSL and HLSL, since GLSL matches by location directly.
1027    ///
1028    pub fn mask_stage_output_by_location(
1029        &mut self,
1030        location: u32,
1031        component: u32,
1032    ) -> crate::error::Result<()> {
1033        unsafe {
1034            sys::spvc_compiler_mask_stage_output_by_location(self.ptr.as_ptr(), location, component)
1035                .ok(&*self)
1036        }
1037    }
1038
1039    /// Mask a stage output by builtin. Masking builtins only takes effect if the builtin in question is part of the stage output interface.
1040    ///
1041    /// If a shader output is active in this stage, but inactive in a subsequent stage,
1042    /// this can be signalled here. This can be used to work around certain cross-stage matching problems
1043    /// which plagues MSL in certain scenarios.
1044    ///
1045    /// An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private
1046    /// variable.
1047    ///
1048    /// This option is only meaningful for MSL and HLSL, since GLSL matches by location directly.
1049    /// Masking builtins only takes effect if the builtin in question is part of the stage output interface.
1050    pub fn mask_stage_output_by_builtin(
1051        &mut self,
1052        builtin: spirv::BuiltIn,
1053    ) -> crate::error::Result<()> {
1054        unsafe {
1055            sys::spvc_compiler_mask_stage_output_by_builtin(
1056                self.ptr.as_ptr(),
1057                SpvBuiltIn(builtin as u32 as i32),
1058            )
1059            .ok(&*self)
1060        }
1061    }
1062}
1063
1064#[derive(Copy, Clone, Debug, Default)]
1065#[non_exhaustive]
1066/// The tier of automatic resource binding.
1067///
1068/// Note that tertiary and quaternary bindings are not accessible via
1069/// the SPIR-V Cross C API.
1070pub enum AutomaticResourceBindingTier {
1071    #[default]
1072    /// The primary automatic resource binding.
1073    Primary,
1074
1075    /// Should only be used for combined image samplers, in which case the
1076    /// sampler's binding is returned instead.
1077    ///
1078    /// Also used for the auxillary image atomic buffer.
1079    Secondary,
1080}
1081
1082impl CompiledArtifact<Msl> {
1083    /// Returns whether the set/binding combination provided in [`Compiler<Msl>::add_resource_binding`]
1084    /// was used.
1085    pub fn is_resource_used(&self, model: spirv::ExecutionModel, binding: ResourceBinding) -> bool {
1086        unsafe {
1087            sys::spvc_compiler_msl_is_resource_used(
1088                self.compiler.ptr.as_ptr(),
1089                SpvExecutionModel(model as u32 as i32),
1090                binding.descriptor_set(),
1091                binding.binding(),
1092            )
1093        }
1094    }
1095
1096    /// Returns whether the location provided in [`Compiler<Msl>::add_shader_input`]
1097    /// was used.
1098    pub fn is_shader_input_used(&self, location: u32) -> bool {
1099        unsafe { sys::spvc_compiler_msl_is_shader_input_used(self.compiler.ptr.as_ptr(), location) }
1100    }
1101
1102    /// Returns whether the location provided in [`Compiler<Msl>::add_shader_output`]
1103    /// was used.
1104    pub fn is_shader_output_used(&self, location: u32) -> bool {
1105        unsafe {
1106            sys::spvc_compiler_msl_is_shader_output_used(self.compiler.ptr.as_ptr(), location)
1107        }
1108    }
1109
1110    /// For a variable resource ID, report the automatically assigned resource index.
1111    ///
1112    /// If the descriptor set was part of an argument buffer, report the `[[id(N)]]`,
1113    /// or `[[buffer/texture/sampler]]` binding for other resources.
1114    ///
1115    /// If the resource was a combined image sampler, report the image binding for [`AutomaticResourceBindingTier::Primary`],
1116    /// or the sampler half for [`AutomaticResourceBindingTier::Secondary`].
1117    ///
1118    /// If no binding exists, None is returned.
1119    pub fn automatic_resource_binding(
1120        &self,
1121        handle: impl Into<Handle<VariableId>>,
1122        tier: AutomaticResourceBindingTier,
1123    ) -> error::Result<Option<u32>> {
1124        let handle = handle.into();
1125        let id = self.yield_id(handle)?;
1126
1127        let res = match tier {
1128            AutomaticResourceBindingTier::Primary => unsafe {
1129                sys::spvc_compiler_msl_get_automatic_resource_binding(self.ptr.as_ptr(), id)
1130            },
1131            AutomaticResourceBindingTier::Secondary => unsafe {
1132                sys::spvc_compiler_msl_get_automatic_resource_binding_secondary(
1133                    self.ptr.as_ptr(),
1134                    id,
1135                )
1136            },
1137        };
1138
1139        if res == u32::MAX {
1140            Ok(None)
1141        } else {
1142            Ok(Some(res))
1143        }
1144    }
1145
1146    /// Query if a variable ID was used as a depth resource.
1147    ///
1148    /// This is meaningful for MSL since descriptor types depend on this knowledge.
1149    /// Cases which return true:
1150    /// - Images which are declared with depth = 1 image type.
1151    /// - Samplers which are statically used at least once with Dref opcodes.
1152    /// - Images which are statically used at least once with Dref opcodes.
1153    pub fn variable_is_depth_or_compare(
1154        &self,
1155        variable: impl Into<Handle<VariableId>>,
1156    ) -> error::Result<bool> {
1157        let variable = variable.into();
1158        let id = self.yield_id(variable)?;
1159        unsafe {
1160            Ok(sys::spvc_compiler_variable_is_depth_or_compare(
1161                self.ptr.as_ptr(),
1162                id,
1163            ))
1164        }
1165    }
1166}
1167
1168#[cfg(test)]
1169mod test {
1170    use crate::compile::msl::CompilerOptions;
1171    use spirv_cross_sys::spvc_compiler_create_compiler_options;
1172
1173    use crate::compile::sealed::ApplyCompilerOptions;
1174    use crate::error::{SpirvCrossError, ToContextError};
1175    use crate::Compiler;
1176    use crate::{targets, Module};
1177
1178    static BASIC_SPV: &[u8] = include_bytes!("../../../basic.spv");
1179
1180    #[test]
1181    pub fn msl_opts() -> Result<(), SpirvCrossError> {
1182        let words = Vec::from(BASIC_SPV);
1183        let words = Module::from_words(bytemuck::cast_slice(&words));
1184
1185        let compiler: Compiler<targets::Msl> = Compiler::new(words)?;
1186        let resources = compiler.shader_resources()?.all_resources()?;
1187
1188        let mut opts_ptr = std::ptr::null_mut();
1189
1190        unsafe {
1191            spvc_compiler_create_compiler_options(compiler.ptr.as_ptr(), &mut opts_ptr)
1192                .ok(&compiler)?;
1193        }
1194
1195        // println!("{:#?}", resources);
1196        let opts = CompilerOptions::default();
1197        unsafe {
1198            opts.apply(opts_ptr, &compiler)?;
1199        }
1200
1201        // match ty.inner {
1202        //     TypeInner::Struct(ty) => {
1203        //         compiler.get_type(ty.members[0].id)?;
1204        //     }
1205        //     TypeInner::Vector { .. } => {}
1206        //     _ => {}
1207        // }
1208        Ok(())
1209    }
1210}