1 /*
2  * Copyright 2016-2019 The Brenwill Workshop Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SPIRV_CROSS_MSL_HPP
18 #define SPIRV_CROSS_MSL_HPP
19 
20 #include "spirv_glsl.hpp"
21 #include <map>
22 #include <set>
23 #include <stddef.h>
24 #include <unordered_map>
25 #include <unordered_set>
26 
27 namespace SPIRV_CROSS_NAMESPACE
28 {
29 
30 // Indicates the format of the vertex attribute. Currently limited to specifying
31 // if the attribute is an 8-bit unsigned integer, 16-bit unsigned integer, or
32 // some other format.
33 enum MSLVertexFormat
34 {
35 	MSL_VERTEX_FORMAT_OTHER = 0,
36 	MSL_VERTEX_FORMAT_UINT8 = 1,
37 	MSL_VERTEX_FORMAT_UINT16 = 2,
38 	MSL_VERTEX_FORMAT_INT_MAX = 0x7fffffff
39 };
40 
41 // Defines MSL characteristics of a vertex attribute at a particular location.
42 // After compilation, it is possible to query whether or not this location was used.
43 struct MSLVertexAttr
44 {
45 	uint32_t location = 0;
46 	uint32_t msl_buffer = 0;
47 	uint32_t msl_offset = 0;
48 	uint32_t msl_stride = 0;
49 	bool per_instance = false;
50 	MSLVertexFormat format = MSL_VERTEX_FORMAT_OTHER;
51 	spv::BuiltIn builtin = spv::BuiltInMax;
52 };
53 
54 // Matches the binding index of a MSL resource for a binding within a descriptor set.
55 // Taken together, the stage, desc_set and binding combine to form a reference to a resource
56 // descriptor used in a particular shading stage.
57 // If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set,
58 // and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we
59 // remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure.
60 // For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will become a
61 // [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
62 struct MSLResourceBinding
63 {
64 	spv::ExecutionModel stage = spv::ExecutionModelMax;
65 	uint32_t desc_set = 0;
66 	uint32_t binding = 0;
67 	uint32_t msl_buffer = 0;
68 	uint32_t msl_texture = 0;
69 	uint32_t msl_sampler = 0;
70 };
71 
72 enum MSLSamplerCoord
73 {
74 	MSL_SAMPLER_COORD_NORMALIZED = 0,
75 	MSL_SAMPLER_COORD_PIXEL = 1,
76 	MSL_SAMPLER_INT_MAX = 0x7fffffff
77 };
78 
79 enum MSLSamplerFilter
80 {
81 	MSL_SAMPLER_FILTER_NEAREST = 0,
82 	MSL_SAMPLER_FILTER_LINEAR = 1,
83 	MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff
84 };
85 
86 enum MSLSamplerMipFilter
87 {
88 	MSL_SAMPLER_MIP_FILTER_NONE = 0,
89 	MSL_SAMPLER_MIP_FILTER_NEAREST = 1,
90 	MSL_SAMPLER_MIP_FILTER_LINEAR = 2,
91 	MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff
92 };
93 
94 enum MSLSamplerAddress
95 {
96 	MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0,
97 	MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1,
98 	MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2,
99 	MSL_SAMPLER_ADDRESS_REPEAT = 3,
100 	MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4,
101 	MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff
102 };
103 
104 enum MSLSamplerCompareFunc
105 {
106 	MSL_SAMPLER_COMPARE_FUNC_NEVER = 0,
107 	MSL_SAMPLER_COMPARE_FUNC_LESS = 1,
108 	MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2,
109 	MSL_SAMPLER_COMPARE_FUNC_GREATER = 3,
110 	MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4,
111 	MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5,
112 	MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6,
113 	MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7,
114 	MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff
115 };
116 
117 enum MSLSamplerBorderColor
118 {
119 	MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0,
120 	MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1,
121 	MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2,
122 	MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
123 };
124 
125 enum MSLFormatResolution
126 {
127 	MSL_FORMAT_RESOLUTION_444 = 0,
128 	MSL_FORMAT_RESOLUTION_422,
129 	MSL_FORMAT_RESOLUTION_420,
130 	MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff
131 };
132 
133 enum MSLChromaLocation
134 {
135 	MSL_CHROMA_LOCATION_COSITED_EVEN = 0,
136 	MSL_CHROMA_LOCATION_MIDPOINT,
137 	MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff
138 };
139 
140 enum MSLComponentSwizzle
141 {
142 	MSL_COMPONENT_SWIZZLE_IDENTITY = 0,
143 	MSL_COMPONENT_SWIZZLE_ZERO,
144 	MSL_COMPONENT_SWIZZLE_ONE,
145 	MSL_COMPONENT_SWIZZLE_R,
146 	MSL_COMPONENT_SWIZZLE_G,
147 	MSL_COMPONENT_SWIZZLE_B,
148 	MSL_COMPONENT_SWIZZLE_A,
149 	MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff
150 };
151 
152 enum MSLSamplerYCbCrModelConversion
153 {
154 	MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0,
155 	MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY,
156 	MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709,
157 	MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601,
158 	MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020,
159 	MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff
160 };
161 
162 enum MSLSamplerYCbCrRange
163 {
164 	MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0,
165 	MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW,
166 	MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff
167 };
168 
169 struct MSLConstexprSampler
170 {
171 	MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED;
172 	MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST;
173 	MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST;
174 	MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE;
175 	MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE;
176 	MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE;
177 	MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE;
178 	MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER;
179 	MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK;
180 	float lod_clamp_min = 0.0f;
181 	float lod_clamp_max = 1000.0f;
182 	int max_anisotropy = 1;
183 
184 	// Sampler Y'CbCr conversion parameters
185 	uint32_t planes = 0;
186 	MSLFormatResolution resolution = MSL_FORMAT_RESOLUTION_444;
187 	MSLSamplerFilter chroma_filter = MSL_SAMPLER_FILTER_NEAREST;
188 	MSLChromaLocation x_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN;
189 	MSLChromaLocation y_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN;
190 	MSLComponentSwizzle swizzle[4]; // IDENTITY, IDENTITY, IDENTITY, IDENTITY
191 	MSLSamplerYCbCrModelConversion ycbcr_model = MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
192 	MSLSamplerYCbCrRange ycbcr_range = MSL_SAMPLER_YCBCR_RANGE_ITU_FULL;
193 	uint32_t bpc = 8;
194 
195 	bool compare_enable = false;
196 	bool lod_clamp_enable = false;
197 	bool anisotropy_enable = false;
198 	bool ycbcr_conversion_enable = false;
199 
MSLConstexprSamplerSPIRV_CROSS_NAMESPACE::MSLConstexprSampler200 	MSLConstexprSampler()
201 	{
202 		for (uint32_t i = 0; i < 4; i++)
203 			swizzle[i] = MSL_COMPONENT_SWIZZLE_IDENTITY;
204 	}
swizzle_is_identitySPIRV_CROSS_NAMESPACE::MSLConstexprSampler205 	bool swizzle_is_identity() const
206 	{
207 		return (swizzle[0] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[1] == MSL_COMPONENT_SWIZZLE_IDENTITY &&
208 		        swizzle[2] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[3] == MSL_COMPONENT_SWIZZLE_IDENTITY);
209 	}
swizzle_has_one_or_zeroSPIRV_CROSS_NAMESPACE::MSLConstexprSampler210 	bool swizzle_has_one_or_zero() const
211 	{
212 		return (swizzle[0] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[0] == MSL_COMPONENT_SWIZZLE_ONE ||
213 		        swizzle[1] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[1] == MSL_COMPONENT_SWIZZLE_ONE ||
214 		        swizzle[2] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[2] == MSL_COMPONENT_SWIZZLE_ONE ||
215 		        swizzle[3] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[3] == MSL_COMPONENT_SWIZZLE_ONE);
216 	}
217 };
218 
219 // Special constant used in a MSLResourceBinding desc_set
220 // element to indicate the bindings for the push constants.
221 static const uint32_t kPushConstDescSet = ~(0u);
222 
223 // Special constant used in a MSLResourceBinding binding
224 // element to indicate the bindings for the push constants.
225 static const uint32_t kPushConstBinding = 0;
226 
227 // Special constant used in a MSLResourceBinding binding
228 // element to indicate the buffer binding for swizzle buffers.
229 static const uint32_t kSwizzleBufferBinding = ~(1u);
230 
231 // Special constant used in a MSLResourceBinding binding
232 // element to indicate the buffer binding for buffer size buffers to support OpArrayLength.
233 static const uint32_t kBufferSizeBufferBinding = ~(2u);
234 
235 // Special constant used in a MSLResourceBinding binding
236 // element to indicate the buffer binding used for the argument buffer itself.
237 // This buffer binding should be kept as small as possible as all automatic bindings for buffers
238 // will start at max(kArgumentBufferBinding) + 1.
239 static const uint32_t kArgumentBufferBinding = ~(3u);
240 
241 static const uint32_t kMaxArgumentBuffers = 8;
242 
243 // Decompiles SPIR-V to Metal Shading Language
244 class CompilerMSL : public CompilerGLSL
245 {
246 public:
247 	// Options for compiling to Metal Shading Language
248 	struct Options
249 	{
250 		typedef enum
251 		{
252 			iOS = 0,
253 			macOS = 1
254 		} Platform;
255 
256 		Platform platform = macOS;
257 		uint32_t msl_version = make_msl_version(1, 2);
258 		uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers
259 		uint32_t swizzle_buffer_index = 30;
260 		uint32_t indirect_params_buffer_index = 29;
261 		uint32_t shader_output_buffer_index = 28;
262 		uint32_t shader_patch_output_buffer_index = 27;
263 		uint32_t shader_tess_factor_buffer_index = 26;
264 		uint32_t buffer_size_buffer_index = 25;
265 		uint32_t view_mask_buffer_index = 24;
266 		uint32_t dynamic_offsets_buffer_index = 23;
267 		uint32_t shader_input_wg_index = 0;
268 		uint32_t device_index = 0;
269 		bool enable_point_size_builtin = true;
270 		bool disable_rasterization = false;
271 		bool capture_output_to_buffer = false;
272 		bool swizzle_texture_samples = false;
273 		bool tess_domain_origin_lower_left = false;
274 		bool multiview = false;
275 		bool view_index_from_device_index = false;
276 		bool dispatch_base = false;
277 		bool texture_1D_as_2D = false;
278 
279 		// Enable use of MSL 2.0 indirect argument buffers.
280 		// MSL 2.0 must also be enabled.
281 		bool argument_buffers = false;
282 
283 		// Ensures vertex and instance indices start at zero. This reflects the behavior of HLSL with SV_VertexID and SV_InstanceID.
284 		bool enable_base_index_zero = false;
285 
286 		// Fragment output in MSL must have at least as many components as the render pass.
287 		// Add support to explicit pad out components.
288 		bool pad_fragment_output_components = false;
289 
290 		// Specifies whether the iOS target version supports the [[base_vertex]] and [[base_instance]] attributes.
291 		bool ios_support_base_vertex_instance = false;
292 
293 		// Use Metal's native frame-buffer fetch API for subpass inputs.
294 		bool ios_use_framebuffer_fetch_subpasses = false;
295 
296 		// Enables use of "fma" intrinsic for invariant float math
297 		bool invariant_float_math = false;
298 
299 		// Emulate texturecube_array with texture2d_array for iOS where this type is not available
300 		bool emulate_cube_array = false;
301 
302 		// Allow user to enable decoration binding
303 		bool enable_decoration_binding = false;
304 
305 		// Requires MSL 2.1, use the native support for texel buffers.
306 		bool texture_buffer_native = false;
307 
is_iosSPIRV_CROSS_NAMESPACE::CompilerMSL::Options308 		bool is_ios()
309 		{
310 			return platform == iOS;
311 		}
312 
is_macosSPIRV_CROSS_NAMESPACE::CompilerMSL::Options313 		bool is_macos()
314 		{
315 			return platform == macOS;
316 		}
317 
set_msl_versionSPIRV_CROSS_NAMESPACE::CompilerMSL::Options318 		void set_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0)
319 		{
320 			msl_version = make_msl_version(major, minor, patch);
321 		}
322 
supports_msl_versionSPIRV_CROSS_NAMESPACE::CompilerMSL::Options323 		bool supports_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) const
324 		{
325 			return msl_version >= make_msl_version(major, minor, patch);
326 		}
327 
make_msl_versionSPIRV_CROSS_NAMESPACE::CompilerMSL::Options328 		static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0)
329 		{
330 			return (major * 10000) + (minor * 100) + patch;
331 		}
332 	};
333 
get_msl_options() const334 	const Options &get_msl_options() const
335 	{
336 		return msl_options;
337 	}
338 
set_msl_options(const Options & opts)339 	void set_msl_options(const Options &opts)
340 	{
341 		msl_options = opts;
342 	}
343 
344 	// Provide feedback to calling API to allow runtime to disable pipeline
345 	// rasterization if vertex shader requires rasterization to be disabled.
get_is_rasterization_disabled() const346 	bool get_is_rasterization_disabled() const
347 	{
348 		return is_rasterization_disabled && (get_entry_point().model == spv::ExecutionModelVertex ||
349 		                                     get_entry_point().model == spv::ExecutionModelTessellationControl ||
350 		                                     get_entry_point().model == spv::ExecutionModelTessellationEvaluation);
351 	}
352 
353 	// Provide feedback to calling API to allow it to pass an auxiliary
354 	// swizzle buffer if the shader needs it.
needs_swizzle_buffer() const355 	bool needs_swizzle_buffer() const
356 	{
357 		return used_swizzle_buffer;
358 	}
359 
360 	// Provide feedback to calling API to allow it to pass a buffer
361 	// containing STORAGE_BUFFER buffer sizes to support OpArrayLength.
needs_buffer_size_buffer() const362 	bool needs_buffer_size_buffer() const
363 	{
364 		return !buffers_requiring_array_length.empty();
365 	}
366 
367 	// Provide feedback to calling API to allow it to pass a buffer
368 	// containing the view mask for the current multiview subpass.
needs_view_mask_buffer() const369 	bool needs_view_mask_buffer() const
370 	{
371 		return msl_options.multiview && !msl_options.view_index_from_device_index;
372 	}
373 
374 	// Provide feedback to calling API to allow it to pass a buffer
375 	// containing the dispatch base workgroup ID.
needs_dispatch_base_buffer() const376 	bool needs_dispatch_base_buffer() const
377 	{
378 		return msl_options.dispatch_base && !msl_options.supports_msl_version(1, 2);
379 	}
380 
381 	// Provide feedback to calling API to allow it to pass an output
382 	// buffer if the shader needs it.
needs_output_buffer() const383 	bool needs_output_buffer() const
384 	{
385 		return capture_output_to_buffer && stage_out_var_id != ID(0);
386 	}
387 
388 	// Provide feedback to calling API to allow it to pass a patch output
389 	// buffer if the shader needs it.
needs_patch_output_buffer() const390 	bool needs_patch_output_buffer() const
391 	{
392 		return capture_output_to_buffer && patch_stage_out_var_id != ID(0);
393 	}
394 
395 	// Provide feedback to calling API to allow it to pass an input threadgroup
396 	// buffer if the shader needs it.
needs_input_threadgroup_mem() const397 	bool needs_input_threadgroup_mem() const
398 	{
399 		return capture_output_to_buffer && stage_in_var_id != ID(0);
400 	}
401 
402 	explicit CompilerMSL(std::vector<uint32_t> spirv);
403 	CompilerMSL(const uint32_t *ir, size_t word_count);
404 	explicit CompilerMSL(const ParsedIR &ir);
405 	explicit CompilerMSL(ParsedIR &&ir);
406 
407 	// attr is a vertex attribute binding used to match
408 	// vertex content locations to MSL attributes. If vertex attributes are provided,
409 	// is_msl_vertex_attribute_used() will return true after calling ::compile() if
410 	// the location was used by the MSL code.
411 	void add_msl_vertex_attribute(const MSLVertexAttr &attr);
412 
413 	// resource is a resource binding to indicate the MSL buffer,
414 	// texture or sampler index to use for a particular SPIR-V description set
415 	// and binding. If resource bindings are provided,
416 	// is_msl_resource_binding_used() will return true after calling ::compile() if
417 	// the set/binding combination was used by the MSL code.
418 	void add_msl_resource_binding(const MSLResourceBinding &resource);
419 
420 	// desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource
421 	// in this shader. index is the index within the dynamic offset buffer to use. This
422 	// function marks that resource as using a dynamic offset (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
423 	// or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC). This function only has any effect if argument buffers
424 	// are enabled. If so, the buffer will have its address adjusted at the beginning of the shader with
425 	// an offset taken from the dynamic offset buffer.
426 	void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index);
427 
428 	// When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets.
429 	// This corresponds to VK_KHR_push_descriptor in Vulkan.
430 	void add_discrete_descriptor_set(uint32_t desc_set);
431 
432 	// If an argument buffer is large enough, it may need to be in the device storage space rather than
433 	// constant. Opt-in to this behavior here on a per set basis.
434 	void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage);
435 
436 	// Query after compilation is done. This allows you to check if a location or set/binding combination was used by the shader.
437 	bool is_msl_vertex_attribute_used(uint32_t location);
438 
439 	// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
440 	// Constexpr samplers are always assumed to be emitted.
441 	// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
442 	// by remap_constexpr_sampler(_by_binding).
443 	bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding);
444 
445 	// This must only be called after a successful call to CompilerMSL::compile().
446 	// For a variable resource ID obtained through reflection API, report the automatically assigned resource index.
447 	// If the descriptor set was part of an argument buffer, report the [[id(N)]],
448 	// or [[buffer/texture/sampler]] binding for other resources.
449 	// If the resource was a combined image sampler, report the image binding here,
450 	// use the _secondary version of this call to query the sampler half of the resource.
451 	// If no binding exists, uint32_t(-1) is returned.
452 	uint32_t get_automatic_msl_resource_binding(uint32_t id) const;
453 
454 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the
455 	// sampler's binding is returned instead. For any other resource type, -1 is returned.
456 	uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const;
457 
458 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images,
459 	// in which case the second plane's binding is returned instead. For any other resource type, -1 is returned.
460 	uint32_t get_automatic_msl_resource_binding_tertiary(uint32_t id) const;
461 
462 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for triplanar images,
463 	// in which case the third plane's binding is returned instead. For any other resource type, -1 is returned.
464 	uint32_t get_automatic_msl_resource_binding_quaternary(uint32_t id) const;
465 
466 	// Compiles the SPIR-V code into Metal Shading Language.
467 	std::string compile() override;
468 
469 	// Remap a sampler with ID to a constexpr sampler.
470 	// Older iOS targets must use constexpr samplers in certain cases (PCF),
471 	// so a static sampler must be used.
472 	// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
473 	// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
474 	// The remapped sampler must not be an array of samplers.
475 	// Prefer remap_constexpr_sampler_by_binding unless you're also doing reflection anyways.
476 	void remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler);
477 
478 	// Same as remap_constexpr_sampler, except you provide set/binding, rather than variable ID.
479 	// Remaps based on ID take priority over set/binding remaps.
480 	void remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, const MSLConstexprSampler &sampler);
481 
482 	// If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect
483 	// to use for a particular location. The default is 4 if number of components is not overridden.
484 	void set_fragment_output_components(uint32_t location, uint32_t components);
485 
486 protected:
487 	// An enum of SPIR-V functions that are implemented in additional
488 	// source code that is added to the shader if necessary.
489 	enum SPVFuncImpl
490 	{
491 		SPVFuncImplNone,
492 		SPVFuncImplMod,
493 		SPVFuncImplRadians,
494 		SPVFuncImplDegrees,
495 		SPVFuncImplFindILsb,
496 		SPVFuncImplFindSMsb,
497 		SPVFuncImplFindUMsb,
498 		SPVFuncImplSSign,
499 		SPVFuncImplArrayCopyMultidimBase,
500 		// Unfortunately, we cannot use recursive templates in the MSL compiler properly,
501 		// so stamp out variants up to some arbitrary maximum.
502 		SPVFuncImplArrayCopy = SPVFuncImplArrayCopyMultidimBase + 1,
503 		SPVFuncImplArrayOfArrayCopy2Dim = SPVFuncImplArrayCopyMultidimBase + 2,
504 		SPVFuncImplArrayOfArrayCopy3Dim = SPVFuncImplArrayCopyMultidimBase + 3,
505 		SPVFuncImplArrayOfArrayCopy4Dim = SPVFuncImplArrayCopyMultidimBase + 4,
506 		SPVFuncImplArrayOfArrayCopy5Dim = SPVFuncImplArrayCopyMultidimBase + 5,
507 		SPVFuncImplArrayOfArrayCopy6Dim = SPVFuncImplArrayCopyMultidimBase + 6,
508 		SPVFuncImplTexelBufferCoords,
509 		SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations
510 		SPVFuncImplFMul,
511 		SPVFuncImplFAdd,
512 		SPVFuncImplCubemapTo2DArrayFace,
513 		SPVFuncImplUnsafeArray, // Allow Metal to use the array<T> template to make arrays a value type
514 		SPVFuncImplInverse4x4,
515 		SPVFuncImplInverse3x3,
516 		SPVFuncImplInverse2x2,
517 		// It is very important that this come before *Swizzle and ChromaReconstruct*, to ensure it's
518 		// emitted before them.
519 		SPVFuncImplForwardArgs,
520 		// Likewise, this must come before *Swizzle.
521 		SPVFuncImplGetSwizzle,
522 		SPVFuncImplTextureSwizzle,
523 		SPVFuncImplGatherSwizzle,
524 		SPVFuncImplGatherCompareSwizzle,
525 		SPVFuncImplSubgroupBallot,
526 		SPVFuncImplSubgroupBallotBitExtract,
527 		SPVFuncImplSubgroupBallotFindLSB,
528 		SPVFuncImplSubgroupBallotFindMSB,
529 		SPVFuncImplSubgroupBallotBitCount,
530 		SPVFuncImplSubgroupAllEqual,
531 		SPVFuncImplReflectScalar,
532 		SPVFuncImplRefractScalar,
533 		SPVFuncImplFaceForwardScalar,
534 		SPVFuncImplChromaReconstructNearest2Plane,
535 		SPVFuncImplChromaReconstructNearest3Plane,
536 		SPVFuncImplChromaReconstructLinear422CositedEven2Plane,
537 		SPVFuncImplChromaReconstructLinear422CositedEven3Plane,
538 		SPVFuncImplChromaReconstructLinear422Midpoint2Plane,
539 		SPVFuncImplChromaReconstructLinear422Midpoint3Plane,
540 		SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane,
541 		SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane,
542 		SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane,
543 		SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane,
544 		SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane,
545 		SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane,
546 		SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane,
547 		SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane,
548 		SPVFuncImplExpandITUFullRange,
549 		SPVFuncImplExpandITUNarrowRange,
550 		SPVFuncImplConvertYCbCrBT709,
551 		SPVFuncImplConvertYCbCrBT601,
552 		SPVFuncImplConvertYCbCrBT2020,
553 		SPVFuncImplDynamicImageSampler,
554 
555 		SPVFuncImplArrayCopyMultidimMax = 6
556 	};
557 
558 	// If the underlying resource has been used for comparison then duplicate loads of that resource must be too
559 	// Use Metal's native frame-buffer fetch API for subpass inputs.
560 	void emit_texture_op(const Instruction &i) override;
561 	void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
562 	void emit_instruction(const Instruction &instr) override;
563 	void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
564 	                  uint32_t count) override;
565 	void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op,
566 	                                           const uint32_t *args, uint32_t count) override;
567 	void emit_header() override;
568 	void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
569 	void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
570 	void emit_subgroup_op(const Instruction &i) override;
571 	std::string to_texture_op(const Instruction &i, bool *forward,
572 	                          SmallVector<uint32_t> &inherited_expressions) override;
573 	void emit_fixup() override;
574 	std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
575 	                             const std::string &qualifier = "");
576 	void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
577 	                        const std::string &qualifier = "", uint32_t base_offset = 0) override;
578 	void emit_struct_padding_target(const SPIRType &type) override;
579 	std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
580 
581 	// Allow Metal to use the array<T> template to make arrays a value type
582 	std::string type_to_array_glsl(const SPIRType &type) override;
583 
584 	// Threadgroup arrays can't have a wrapper type
585 	std::string variable_decl(const SPIRVariable &variable) override;
586 
587 	// GCC workaround of lambdas calling protected functions (for older GCC versions)
588 	std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override;
589 
590 	std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
591 	std::string sampler_type(const SPIRType &type);
592 	std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
593 	std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override;
594 	std::string to_name(uint32_t id, bool allow_alias = true) const override;
595 	std::string to_function_name(VariableID img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
596 	                             bool has_array_offsets, bool has_offset, bool has_grad, bool has_dref, uint32_t lod,
597 	                             uint32_t minlod) override;
598 	std::string to_function_args(VariableID img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
599 	                             uint32_t coord, uint32_t coord_components, uint32_t dref, uint32_t grad_x,
600 	                             uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias,
601 	                             uint32_t comp, uint32_t sample, uint32_t minlod, bool *p_forward) override;
602 	std::string to_initializer_expression(const SPIRVariable &var) override;
603 
604 	std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id,
605 	                                   bool is_packed, bool row_major) override;
606 
607 	// Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but [[sample_mask]] is a scalar in Metal.
608 	bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override;
609 
610 	std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
611 	bool skip_argument(uint32_t id) const override;
612 	std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain) override;
613 	std::string to_qualifiers_glsl(uint32_t id) override;
614 	void replace_illegal_names() override;
615 	void declare_undefined_values() override;
616 	void declare_constant_arrays();
617 
618 	// Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries
619 	void declare_complex_constant_arrays();
620 
621 	bool is_patch_block(const SPIRType &type);
622 	bool is_non_native_row_major_matrix(uint32_t id) override;
623 	bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
624 	std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id,
625 	                                     bool is_packed) override;
626 
627 	void preprocess_op_codes();
628 	void localize_global_variables();
629 	void extract_global_variables_from_functions();
630 	void mark_packable_structs();
631 	void mark_as_packable(SPIRType &type);
632 
633 	std::unordered_map<uint32_t, std::set<uint32_t>> function_global_vars;
634 	void extract_global_variables_from_function(uint32_t func_id, std::set<uint32_t> &added_arg_ids,
635 	                                            std::unordered_set<uint32_t> &global_var_ids,
636 	                                            std::unordered_set<uint32_t> &processed_func_ids);
637 	uint32_t add_interface_block(spv::StorageClass storage, bool patch = false);
638 	uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage);
639 
640 	void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
641 	                                     SPIRVariable &var, bool strip_array);
642 	void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
643 	                                               SPIRType &ib_type, SPIRVariable &var, bool strip_array);
644 	void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
645 	                                           SPIRType &ib_type, SPIRVariable &var, bool strip_array);
646 	void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
647 	                                                  SPIRType &ib_type, SPIRVariable &var, uint32_t index,
648 	                                                  bool strip_array);
649 	void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
650 	                                                      SPIRType &ib_type, SPIRVariable &var, uint32_t index,
651 	                                                      bool strip_array);
652 	uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array);
653 	void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
654 
655 	void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id);
656 
657 	void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
658 	uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
659 	uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location);
660 
661 	void emit_custom_templates();
662 	void emit_custom_functions();
663 	void emit_resources();
664 	void emit_specialization_constants_and_structs();
665 	void emit_interface_block(uint32_t ib_var_id);
666 	bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs);
667 
668 	void fix_up_shader_inputs_outputs();
669 
670 	std::string func_type_decl(SPIRType &type);
671 	std::string entry_point_args_classic(bool append_comma);
672 	std::string entry_point_args_argument_buffer(bool append_comma);
673 	std::string entry_point_arg_stage_in();
674 	void entry_point_args_builtin(std::string &args);
675 	void entry_point_args_discrete_descriptors(std::string &args);
676 	std::string to_qualified_member_name(const SPIRType &type, uint32_t index);
677 	std::string ensure_valid_name(std::string name, std::string pfx);
678 	std::string to_sampler_expression(uint32_t id);
679 	std::string to_swizzle_expression(uint32_t id);
680 	std::string to_buffer_size_expression(uint32_t id);
681 	std::string builtin_qualifier(spv::BuiltIn builtin);
682 	std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0);
683 	std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma);
684 	std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
685 	std::string argument_decl(const SPIRFunction::Parameter &arg);
686 	std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
687 	uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
688 	uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
689 
690 	// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
691 	// These values can change depending on various extended decorations which control packing rules.
692 	// We need to make these rules match up with SPIR-V declared rules.
693 	uint32_t get_declared_type_size_msl(const SPIRType &type, bool packed, bool row_major) const;
694 	uint32_t get_declared_type_array_stride_msl(const SPIRType &type, bool packed, bool row_major) const;
695 	uint32_t get_declared_type_matrix_stride_msl(const SPIRType &type, bool packed, bool row_major) const;
696 	uint32_t get_declared_type_alignment_msl(const SPIRType &type, bool packed, bool row_major) const;
697 
698 	uint32_t get_declared_struct_member_size_msl(const SPIRType &struct_type, uint32_t index) const;
699 	uint32_t get_declared_struct_member_array_stride_msl(const SPIRType &struct_type, uint32_t index) const;
700 	uint32_t get_declared_struct_member_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const;
701 	uint32_t get_declared_struct_member_alignment_msl(const SPIRType &struct_type, uint32_t index) const;
702 
703 	const SPIRType &get_physical_member_type(const SPIRType &struct_type, uint32_t index) const;
704 
705 	uint32_t get_declared_struct_size_msl(const SPIRType &struct_type, bool ignore_alignment = false,
706 	                                      bool ignore_padding = false) const;
707 
708 	std::string to_component_argument(uint32_t id);
709 	void align_struct(SPIRType &ib_type, std::unordered_set<uint32_t> &aligned_structs);
710 	void mark_scalar_layout_structs(const SPIRType &ib_type);
711 	void mark_struct_members_packed(const SPIRType &type);
712 	void ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index);
713 	bool validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const;
714 	std::string get_argument_address_space(const SPIRVariable &argument);
715 	std::string get_type_address_space(const SPIRType &type, uint32_t id, bool argument = false);
716 	const char *to_restrict(uint32_t id, bool space = true);
717 	SPIRType &get_stage_in_struct_type();
718 	SPIRType &get_stage_out_struct_type();
719 	SPIRType &get_patch_stage_in_struct_type();
720 	SPIRType &get_patch_stage_out_struct_type();
721 	std::string get_tess_factor_struct_name();
722 	void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, uint32_t mem_order_1,
723 	                         uint32_t mem_order_2, bool has_mem_order_2, uint32_t op0, uint32_t op1 = 0,
724 	                         bool op1_is_pointer = false, bool op1_is_literal = false, uint32_t op2 = 0);
725 	const char *get_memory_order(uint32_t spv_mem_sem);
726 	void add_pragma_line(const std::string &line);
727 	void add_typedef_line(const std::string &line);
728 	void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
729 	void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage,
730 	                     spv::StorageClass rhs_storage) override;
731 	void build_implicit_builtins();
732 	uint32_t build_constant_uint_array_pointer();
733 	void emit_entry_point_declarations() override;
734 	uint32_t builtin_frag_coord_id = 0;
735 	uint32_t builtin_sample_id_id = 0;
736 	uint32_t builtin_vertex_idx_id = 0;
737 	uint32_t builtin_base_vertex_id = 0;
738 	uint32_t builtin_instance_idx_id = 0;
739 	uint32_t builtin_base_instance_id = 0;
740 	uint32_t builtin_view_idx_id = 0;
741 	uint32_t builtin_layer_id = 0;
742 	uint32_t builtin_invocation_id_id = 0;
743 	uint32_t builtin_primitive_id_id = 0;
744 	uint32_t builtin_subgroup_invocation_id_id = 0;
745 	uint32_t builtin_subgroup_size_id = 0;
746 	uint32_t builtin_dispatch_base_id = 0;
747 	uint32_t swizzle_buffer_id = 0;
748 	uint32_t buffer_size_buffer_id = 0;
749 	uint32_t view_mask_buffer_id = 0;
750 	uint32_t dynamic_offsets_buffer_id = 0;
751 
752 	void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
753 	void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override;
754 	void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override;
755 
756 	void analyze_sampled_image_usage();
757 
758 	bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length);
759 	bool emit_tessellation_io_load(uint32_t result_type, uint32_t id, uint32_t ptr);
760 	bool is_out_of_bounds_tessellation_level(uint32_t id_lhs);
761 
762 	void ensure_builtin(spv::StorageClass storage, spv::BuiltIn builtin);
763 
764 	void mark_implicit_builtin(spv::StorageClass storage, spv::BuiltIn builtin, uint32_t id);
765 
766 	std::string convert_to_f32(const std::string &expr, uint32_t components);
767 
768 	Options msl_options;
769 	std::set<SPVFuncImpl> spv_function_implementations;
770 	std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_location;
771 	std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_builtin;
772 	std::unordered_set<uint32_t> vtx_attrs_in_use;
773 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
774 	std::set<std::string> pragma_lines;
775 	std::set<std::string> typedef_lines;
776 	SmallVector<uint32_t> vars_needing_early_declaration;
777 
778 	struct SetBindingPair
779 	{
780 		uint32_t desc_set;
781 		uint32_t binding;
782 		bool operator==(const SetBindingPair &other) const;
783 		bool operator<(const SetBindingPair &other) const;
784 	};
785 
786 	struct StageSetBinding
787 	{
788 		spv::ExecutionModel model;
789 		uint32_t desc_set;
790 		uint32_t binding;
791 		bool operator==(const StageSetBinding &other) const;
792 	};
793 
794 	struct InternalHasher
795 	{
796 		size_t operator()(const SetBindingPair &value) const;
797 		size_t operator()(const StageSetBinding &value) const;
798 	};
799 
800 	std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
801 
802 	uint32_t next_metal_resource_index_buffer = 0;
803 	uint32_t next_metal_resource_index_texture = 0;
804 	uint32_t next_metal_resource_index_sampler = 0;
805 	// Intentionally uninitialized, works around MSVC 2013 bug.
806 	uint32_t next_metal_resource_ids[kMaxArgumentBuffers];
807 
808 	VariableID stage_in_var_id = 0;
809 	VariableID stage_out_var_id = 0;
810 	VariableID patch_stage_in_var_id = 0;
811 	VariableID patch_stage_out_var_id = 0;
812 	VariableID stage_in_ptr_var_id = 0;
813 	VariableID stage_out_ptr_var_id = 0;
814 
815 	// Handle HLSL-style 0-based vertex/instance index.
816 	enum class TriState
817 	{
818 		Neutral,
819 		No,
820 		Yes
821 	};
822 	TriState needs_base_vertex_arg = TriState::Neutral;
823 	TriState needs_base_instance_arg = TriState::Neutral;
824 
825 	bool has_sampled_images = false;
826 	bool builtin_declaration = false; // Handle HLSL-style 0-based vertex/instance index.
827 	bool use_builtin_array = false; // Force the use of C style array declaration.
828 	bool is_rasterization_disabled = false;
829 	bool capture_output_to_buffer = false;
830 	bool needs_swizzle_buffer_def = false;
831 	bool used_swizzle_buffer = false;
832 	bool added_builtin_tess_level = false;
833 	bool needs_subgroup_invocation_id = false;
834 	std::string qual_pos_var_name;
835 	std::string stage_in_var_name = "in";
836 	std::string stage_out_var_name = "out";
837 	std::string patch_stage_in_var_name = "patchIn";
838 	std::string patch_stage_out_var_name = "patchOut";
839 	std::string sampler_name_suffix = "Smplr";
840 	std::string swizzle_name_suffix = "Swzl";
841 	std::string buffer_size_name_suffix = "BufferSize";
842 	std::string plane_name_suffix = "Plane";
843 	std::string input_wg_var_name = "gl_in";
844 	std::string output_buffer_var_name = "spvOut";
845 	std::string patch_output_buffer_var_name = "spvPatchOut";
846 	std::string tess_factor_buffer_var_name = "spvTessLevel";
847 	spv::Op previous_instruction_opcode = spv::OpNop;
848 
849 	// Must be ordered since declaration is in a specific order.
850 	std::map<uint32_t, MSLConstexprSampler> constexpr_samplers_by_id;
851 	std::unordered_map<SetBindingPair, MSLConstexprSampler, InternalHasher> constexpr_samplers_by_binding;
852 	const MSLConstexprSampler *find_constexpr_sampler(uint32_t id) const;
853 
854 	std::unordered_set<uint32_t> buffers_requiring_array_length;
855 	SmallVector<uint32_t> buffer_arrays;
856 	std::unordered_set<uint32_t> atomic_image_vars; // Emulate texture2D atomic operations
857 
858 	// Must be ordered since array is in a specific order.
859 	std::map<SetBindingPair, std::pair<uint32_t, uint32_t>> buffers_requiring_dynamic_offset;
860 
861 	uint32_t argument_buffer_ids[kMaxArgumentBuffers];
862 	uint32_t argument_buffer_discrete_mask = 0;
863 	uint32_t argument_buffer_device_storage_mask = 0;
864 
865 	void analyze_argument_buffers();
866 	bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
867 
868 	uint32_t get_target_components_for_fragment_location(uint32_t location) const;
869 	uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components);
870 
871 	bool suppress_missing_prototypes = false;
872 
873 	void add_spv_func_and_recompile(SPVFuncImpl spv_func);
874 
875 	// OpcodeHandler that handles several MSL preprocessing operations.
876 	struct OpCodePreprocessor : OpcodeHandler
877 	{
OpCodePreprocessorSPIRV_CROSS_NAMESPACE::CompilerMSL::OpCodePreprocessor878 		OpCodePreprocessor(CompilerMSL &compiler_)
879 		    : compiler(compiler_)
880 		{
881 		}
882 
883 		bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
884 		CompilerMSL::SPVFuncImpl get_spv_func_impl(spv::Op opcode, const uint32_t *args);
885 		void check_resource_write(uint32_t var_id);
886 
887 		CompilerMSL &compiler;
888 		std::unordered_map<uint32_t, uint32_t> result_types;
889 		std::unordered_map<uint32_t, uint32_t> image_pointers; // Emulate texture2D atomic operations
890 		bool suppress_missing_prototypes = false;
891 		bool uses_atomics = false;
892 		bool uses_resource_write = false;
893 		bool needs_subgroup_invocation_id = false;
894 	};
895 
896 	// OpcodeHandler that scans for uses of sampled images
897 	struct SampledImageScanner : OpcodeHandler
898 	{
SampledImageScannerSPIRV_CROSS_NAMESPACE::CompilerMSL::SampledImageScanner899 		SampledImageScanner(CompilerMSL &compiler_)
900 		    : compiler(compiler_)
901 		{
902 		}
903 
904 		bool handle(spv::Op opcode, const uint32_t *args, uint32_t) override;
905 
906 		CompilerMSL &compiler;
907 	};
908 
909 	// Sorts the members of a SPIRType and associated Meta info based on a settable sorting
910 	// aspect, which defines which aspect of the struct members will be used to sort them.
911 	// Regardless of the sorting aspect, built-in members always appear at the end of the struct.
912 	struct MemberSorter
913 	{
914 		enum SortAspect
915 		{
916 			Location,
917 			LocationReverse,
918 			Offset,
919 			OffsetThenLocationReverse,
920 			Alphabetical
921 		};
922 
923 		void sort();
924 		bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2);
925 		MemberSorter(SPIRType &t, Meta &m, SortAspect sa);
926 
927 		SPIRType &type;
928 		Meta &meta;
929 		SortAspect sort_aspect;
930 	};
931 };
932 } // namespace SPIRV_CROSS_NAMESPACE
933 
934 #endif
935