Răsfoiți Sursa

Metal: Ensure baking to binary sets minimum target OS

Co-authored-by: Travis Lange <[email protected]>
Stuart Carnie 5 zile în urmă
părinte
comite
b7aac81366

+ 4 - 0
drivers/metal/metal_device_properties.h

@@ -143,11 +143,15 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalDeviceProperties {
 private:
 private:
 	void init_features(id<MTLDevice> p_device);
 	void init_features(id<MTLDevice> p_device);
 	void init_limits(id<MTLDevice> p_device);
 	void init_limits(id<MTLDevice> p_device);
+	void init_os_props();
 
 
 public:
 public:
 	MetalFeatures features;
 	MetalFeatures features;
 	MetalLimits limits;
 	MetalLimits limits;
 
 
+	// maj * 10000 + min * 100 + patch
+	uint32_t os_version;
+
 	SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
 	SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
 
 
 	MetalDeviceProperties(id<MTLDevice> p_device);
 	MetalDeviceProperties(id<MTLDevice> p_device);

+ 6 - 0
drivers/metal/metal_device_properties.mm

@@ -311,9 +311,15 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
 #endif
 #endif
 }
 }
 
 
+void MetalDeviceProperties::init_os_props() {
+	NSOperatingSystemVersion ver = NSProcessInfo.processInfo.operatingSystemVersion;
+	os_version = (uint32_t)ver.majorVersion * 10000 + (uint32_t)ver.minorVersion * 100 + (uint32_t)ver.patchVersion;
+}
+
 MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
 MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
 	init_features(p_device);
 	init_features(p_device);
 	init_limits(p_device);
 	init_limits(p_device);
+	init_os_props();
 }
 }
 
 
 MetalDeviceProperties::~MetalDeviceProperties() {
 MetalDeviceProperties::~MetalDeviceProperties() {

+ 7 - 2
drivers/metal/metal_utils.h

@@ -103,6 +103,11 @@ extern os_log_t LOG_DRIVER;
 // Used for dynamic tracing.
 // Used for dynamic tracing.
 extern os_log_t LOG_INTERVALS;
 extern os_log_t LOG_INTERVALS;
 
 
-_FORCE_INLINE_ static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) {
-	return (major * 10000) + (minor * 100) + patch;
+_FORCE_INLINE_ static uint32_t make_msl_version(uint32_t p_major, uint32_t p_minor = 0, uint32_t p_patch = 0) {
+	return (p_major * 10000) + (p_minor * 100) + p_patch;
+}
+
+_FORCE_INLINE_ static void parse_msl_version(uint32_t p_version, uint32_t &r_major, uint32_t &r_minor) {
+	r_major = p_version / 10000;
+	r_minor = (p_version % 10000) / 100;
 }
 }

+ 10 - 2
drivers/metal/rendering_device_driver_metal.mm

@@ -1135,12 +1135,17 @@ RDD::ShaderID RenderingDeviceDriverMetal::shader_create_from_container(const Ref
 	// We need to regenerate the shader if the cache is moved to an incompatible device.
 	// We need to regenerate the shader if the cache is moved to an incompatible device.
 	ERR_FAIL_COND_V_MSG(device_properties->features.argument_buffers_tier < MTLArgumentBuffersTier2 && mtl_reflection_data.uses_argument_buffers(),
 	ERR_FAIL_COND_V_MSG(device_properties->features.argument_buffers_tier < MTLArgumentBuffersTier2 && mtl_reflection_data.uses_argument_buffers(),
 			RDD::ShaderID(),
 			RDD::ShaderID(),
-			"Shader was generated with argument buffers, but device has limited support");
+			"Shader was compiled with argument buffers enabled, but this device does not support them");
 
 
 	uint32_t msl_version = make_msl_version(device_properties->features.mslVersionMajor, device_properties->features.mslVersionMinor);
 	uint32_t msl_version = make_msl_version(device_properties->features.mslVersionMajor, device_properties->features.mslVersionMinor);
 	ERR_FAIL_COND_V_MSG(msl_version < mtl_reflection_data.msl_version,
 	ERR_FAIL_COND_V_MSG(msl_version < mtl_reflection_data.msl_version,
 			RDD::ShaderID(),
 			RDD::ShaderID(),
-			"Shader was compiled with a newer version of Metal than is available on the device.");
+			"Shader was compiled for a newer version of Metal");
+
+	MTLGPUFamily compiled_gpu_family = static_cast<MTLGPUFamily>(mtl_reflection_data.profile.gpu);
+	ERR_FAIL_COND_V_MSG(device_properties->features.highestFamily < compiled_gpu_family,
+			RDD::ShaderID(),
+			"Shader was generated for a newer Apple GPU");
 
 
 	MTLCompileOptions *options = [MTLCompileOptions new];
 	MTLCompileOptions *options = [MTLCompileOptions new];
 	uint32_t major = mtl_reflection_data.msl_version / 10000;
 	uint32_t major = mtl_reflection_data.msl_version / 10000;
@@ -1181,6 +1186,9 @@ RDD::ShaderID RenderingDeviceDriverMetal::shader_create_from_container(const Ref
 
 
 		MDLibrary *library = nil;
 		MDLibrary *library = nil;
 		if (shader_data.library_size > 0) {
 		if (shader_data.library_size > 0) {
+			ERR_FAIL_COND_V_MSG(mtl_reflection_data.os_min_version > device_properties->os_version,
+					RDD::ShaderID(),
+					"Metal shader binary was generated for a newer target OS");
 			dispatch_data_t binary = dispatch_data_create(decompressed_code.ptr() + shader_data.source_size, shader_data.library_size, dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_DEFAULT);
 			dispatch_data_t binary = dispatch_data_create(decompressed_code.ptr() + shader_data.source_size, shader_data.library_size, dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_DEFAULT);
 			library = [MDLibrary newLibraryWithCacheEntry:cd
 			library = [MDLibrary newLibraryWithCacheEntry:cd
 												   device:device
 												   device:device

+ 56 - 11
drivers/metal/rendering_shader_container_metal.h

@@ -41,6 +41,25 @@ const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
 
 
 class RenderingShaderContainerFormatMetal;
 class RenderingShaderContainerFormatMetal;
 
 
+class MinOsVersion {
+	uint32_t version;
+
+public:
+	String to_compiler_os_version() const;
+	bool is_null() const { return version == UINT32_MAX; }
+	bool is_valid() const { return version != UINT32_MAX; }
+
+	MinOsVersion(const String &p_version);
+	explicit MinOsVersion(uint32_t p_version) :
+			version(p_version) {}
+	MinOsVersion() :
+			version(UINT32_MAX) {}
+
+	bool operator>(uint32_t p_other) {
+		return version > p_other;
+	}
+};
+
 /// @brief A minimal structure that defines a device profile for Metal.
 /// @brief A minimal structure that defines a device profile for Metal.
 ///
 ///
 /// This structure is used by the `RenderingShaderContainerMetal` class to
 /// This structure is used by the `RenderingShaderContainerMetal` class to
@@ -53,17 +72,20 @@ struct MetalDeviceProfile {
 		iOS = 1,
 		iOS = 1,
 	};
 	};
 
 
-	/// @brief The GPU family.
+	/*! @brief The GPU family.
+	 *
+	 * NOTE: These values match Apple's MTLGPUFamily
+	 */
 	enum class GPU : uint32_t {
 	enum class GPU : uint32_t {
-		Apple1,
-		Apple2,
-		Apple3,
-		Apple4,
-		Apple5,
-		Apple6,
-		Apple7,
-		Apple8,
-		Apple9,
+		Apple1 = 1001,
+		Apple2 = 1002,
+		Apple3 = 1003,
+		Apple4 = 1004,
+		Apple5 = 1005,
+		Apple6 = 1006,
+		Apple7 = 1007,
+		Apple8 = 1008,
+		Apple9 = 1009,
 	};
 	};
 
 
 	enum class ArgumentBuffersTier : uint32_t {
 	enum class ArgumentBuffersTier : uint32_t {
@@ -108,6 +130,13 @@ public:
 		/// The Metal language version specified when compiling SPIR-V to MSL.
 		/// The Metal language version specified when compiling SPIR-V to MSL.
 		/// Format is major * 10000 + minor * 100 + patch.
 		/// Format is major * 10000 + minor * 100 + patch.
 		uint32_t msl_version = UINT32_MAX;
 		uint32_t msl_version = UINT32_MAX;
+		/*! @brief The minimum supported OS version for shaders baked to a `.metallib`.
+		 *
+		 * NOTE: This property is only valid when shaders are baked to a .metalllib
+		 *
+		 * Format is major * 10000 + minor * 100 + patch.
+		 */
+		MinOsVersion os_min_version;
 		uint32_t flags = NONE;
 		uint32_t flags = NONE;
 
 
 		/// @brief Returns `true` if the shader is compiled with multi-view support.
 		/// @brief Returns `true` if the shader is compiled with multi-view support.
@@ -210,9 +239,23 @@ public:
 	HeaderData mtl_reflection_data; // compliment to reflection_data
 	HeaderData mtl_reflection_data; // compliment to reflection_data
 	Vector<StageData> mtl_shaders; // compliment to shaders
 	Vector<StageData> mtl_shaders; // compliment to shaders
 
 
+private:
+	struct ToolchainProperties {
+		MinOsVersion os_version_min_required;
+		uint32_t metal_version = UINT32_MAX;
+
+		_FORCE_INLINE_ bool is_null() const { return os_version_min_required.is_null() || metal_version == UINT32_MAX; }
+		_FORCE_INLINE_ bool is_valid() const { return !is_null(); }
+	};
+
+	ToolchainProperties compiler_props;
+
+	void _initialize_toolchain_properties();
+
 private:
 private:
 	const MetalDeviceProfile *device_profile = nullptr;
 	const MetalDeviceProfile *device_profile = nullptr;
 	bool export_mode = false;
 	bool export_mode = false;
+	MinOsVersion min_os_version;
 
 
 	Vector<UniformData> mtl_reflection_binding_set_uniforms_data; // compliment to reflection_binding_set_uniforms_data
 	Vector<UniformData> mtl_reflection_binding_set_uniforms_data; // compliment to reflection_binding_set_uniforms_data
 	Vector<SpecializationData> mtl_reflection_specialization_data; // compliment to reflection_specialization_data
 	Vector<SpecializationData> mtl_reflection_specialization_data; // compliment to reflection_specialization_data
@@ -224,6 +267,7 @@ public:
 
 
 	void set_export_mode(bool p_export_mode) { export_mode = p_export_mode; }
 	void set_export_mode(bool p_export_mode) { export_mode = p_export_mode; }
 	void set_device_profile(const MetalDeviceProfile *p_device_profile) { device_profile = p_device_profile; }
 	void set_device_profile(const MetalDeviceProfile *p_device_profile) { device_profile = p_device_profile; }
+	void set_min_os_version(const MinOsVersion p_min_os_version) { min_os_version = p_min_os_version; }
 
 
 	struct MetalShaderReflection {
 	struct MetalShaderReflection {
 		Vector<Vector<UniformData>> uniform_sets;
 		Vector<Vector<UniformData>> uniform_sets;
@@ -253,6 +297,7 @@ protected:
 
 
 class RenderingShaderContainerFormatMetal : public RenderingShaderContainerFormat {
 class RenderingShaderContainerFormatMetal : public RenderingShaderContainerFormat {
 	bool export_mode = false;
 	bool export_mode = false;
+	MinOsVersion min_os_version;
 
 
 	const MetalDeviceProfile *device_profile = nullptr;
 	const MetalDeviceProfile *device_profile = nullptr;
 
 
@@ -260,6 +305,6 @@ public:
 	virtual Ref<RenderingShaderContainer> create_container() const override;
 	virtual Ref<RenderingShaderContainer> create_container() const override;
 	virtual ShaderLanguageVersion get_shader_language_version() const override;
 	virtual ShaderLanguageVersion get_shader_language_version() const override;
 	virtual ShaderSpirvVersion get_shader_spirv_version() const override;
 	virtual ShaderSpirvVersion get_shader_spirv_version() const override;
-	RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export = false);
+	RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export = false, const MinOsVersion p_min_os_version = MinOsVersion());
 	virtual ~RenderingShaderContainerFormatMetal() = default;
 	virtual ~RenderingShaderContainerFormatMetal() = default;
 };
 };

+ 151 - 17
drivers/metal/rendering_shader_container_metal.mm

@@ -28,11 +28,12 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 /**************************************************************************/
 
 
-#include "rendering_shader_container_metal.h"
+#import "rendering_shader_container_metal.h"
 
 
-#include "servers/rendering/rendering_device.h"
+#import "metal_utils.h"
 
 
 #import "core/io/marshalls.h"
 #import "core/io/marshalls.h"
+#import "servers/rendering/rendering_device.h"
 
 
 #import <Metal/Metal.h>
 #import <Metal/Metal.h>
 #import <spirv.hpp>
 #import <spirv.hpp>
@@ -85,6 +86,71 @@ const MetalDeviceProfile *MetalDeviceProfile::get_profile(MetalDeviceProfile::Pl
 	return &profiles.insert(key, res)->value;
 	return &profiles.insert(key, res)->value;
 }
 }
 
 
+void RenderingShaderContainerMetal::_initialize_toolchain_properties() {
+	if (compiler_props.is_valid()) {
+		return;
+	}
+
+	String sdk;
+	switch (device_profile->platform) {
+		case MetalDeviceProfile::Platform::macOS:
+			sdk = "macosx";
+			break;
+		case MetalDeviceProfile::Platform::iOS:
+			sdk = "iphoneos";
+			break;
+	}
+
+	Vector<String> parts{ "echo", R"("")", "|", "/usr/bin/xcrun", "-sdk", sdk, "metal", "-E", "-dM", "-x", "metal", "-", "|", "grep", "-E", R"(\"__METAL_VERSION__|__ENVIRONMENT_OS\")" };
+
+	// Compile metal shaders for the minimum supported target instead of the host machine
+	if (min_os_version.is_valid()) {
+		switch (device_profile->platform) {
+			case MetalDeviceProfile::Platform::macOS: {
+				parts.push_back("-mmacosx-version-min=" + min_os_version.to_compiler_os_version());
+				break;
+			}
+			case MetalDeviceProfile::Platform::iOS: {
+				parts.push_back("-mios-version-min=" + min_os_version.to_compiler_os_version());
+				break;
+			}
+		}
+	}
+
+	String s = " ";
+	List<String> args = { "-c", String(" ").join(parts) };
+
+	String r_pipe;
+	int exit_code;
+	Error err = OS::get_singleton()->execute("sh", args, &r_pipe, &exit_code, true);
+	ERR_FAIL_COND_MSG(err != OK, "Failed to determine Metal toolchain properties");
+
+	// Parse the lines, which are in the form:
+	//
+	// #define VARNAME VALUE
+	Vector<String> lines = r_pipe.split("\n", false);
+	for (String &line : lines) {
+		Vector<String> name_val = line.trim_prefix("#define ").split(" ");
+		if (name_val.size() != 2) {
+			continue;
+		}
+		if (name_val[0] == "__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__") {
+			compiler_props.os_version_min_required = MinOsVersion((uint32_t)name_val[1].to_int());
+		} else if (name_val[0] == "__METAL_VERSION__") {
+			uint32_t ver = (uint32_t)name_val[1].to_int();
+			uint32_t maj = ver / 100;
+			uint32_t min = (ver % 100) / 10;
+			compiler_props.metal_version = make_msl_version(maj, min);
+		}
+
+		if (compiler_props.is_valid()) {
+			break;
+		}
+	}
+
+	return;
+}
+
 Error RenderingShaderContainerMetal::compile_metal_source(const char *p_source, const StageData &p_stage_data, Vector<uint8_t> &r_binary_data) {
 Error RenderingShaderContainerMetal::compile_metal_source(const char *p_source, const StageData &p_stage_data, Vector<uint8_t> &r_binary_data) {
 	String name(shader_name.ptr());
 	String name(shader_name.ptr());
 	if (name.contains_char(':')) {
 	if (name.contains_char(':')) {
@@ -115,9 +181,26 @@ Error RenderingShaderContainerMetal::compile_metal_source(const char *p_source,
 			break;
 			break;
 	}
 	}
 
 
-	// Build the metallib binary.
+	// Build the .metallib binary.
 	{
 	{
 		List<String> args{ "-sdk", sdk, "metal", "-O3" };
 		List<String> args{ "-sdk", sdk, "metal", "-O3" };
+
+		// Compile metal shaders for the minimum supported target instead of the host machine.
+		if (min_os_version.is_valid()) {
+			switch (device_profile->platform) {
+				case MetalDeviceProfile::Platform::macOS: {
+					args.push_back("-mmacosx-version-min=" + min_os_version.to_compiler_os_version());
+					break;
+				}
+				case MetalDeviceProfile::Platform::iOS: {
+					args.push_back("-mios-version-min=" + min_os_version.to_compiler_os_version());
+					break;
+				}
+			}
+		} else {
+			WARN_PRINT_ONCE(vformat("Minimum target OS version is not set, so baking shaders for Metal will target the default version of your toolchain: %s", compiler_props.os_version_min_required.to_compiler_os_version()));
+		}
+
 		if (p_stage_data.is_position_invariant) {
 		if (p_stage_data.is_position_invariant) {
 			args.push_back("-fpreserve-invariance");
 			args.push_back("-fpreserve-invariance");
 		}
 		}
@@ -175,6 +258,10 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const Vector<RenderingD
 	using spirv_cross::CompilerMSL;
 	using spirv_cross::CompilerMSL;
 	using spirv_cross::Resource;
 	using spirv_cross::Resource;
 
 
+	if (export_mode) {
+		_initialize_toolchain_properties();
+	}
+
 	// initialize Metal-specific reflection data
 	// initialize Metal-specific reflection data
 	shaders.resize(p_spirv.size());
 	shaders.resize(p_spirv.size());
 	mtl_shaders.resize(p_spirv.size());
 	mtl_shaders.resize(p_spirv.size());
@@ -182,6 +269,7 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const Vector<RenderingD
 	mtl_reflection_specialization_data.resize(reflection_specialization_data.size());
 	mtl_reflection_specialization_data.resize(reflection_specialization_data.size());
 
 
 	mtl_reflection_data.set_needs_view_mask_buffer(reflection_data.has_multiview);
 	mtl_reflection_data.set_needs_view_mask_buffer(reflection_data.has_multiview);
+	mtl_reflection_data.profile = *device_profile;
 
 
 	// set_indexes will contain the starting offsets of each descriptor set in the binding set uniforms data
 	// set_indexes will contain the starting offsets of each descriptor set in the binding set uniforms data
 	// including the last one, which is the size of reflection_binding_set_uniforms_count.
 	// including the last one, which is the size of reflection_binding_set_uniforms_count.
@@ -199,10 +287,25 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const Vector<RenderingD
 		set_indexes[set_indexes_size - 1] = offset;
 		set_indexes[set_indexes_size - 1] = offset;
 	}
 	}
 	CompilerMSL::Options msl_options{};
 	CompilerMSL::Options msl_options{};
-	// MAJOR * 10000 + MINOR * 100
-	uint32_t msl_version = CompilerMSL::Options::make_msl_version(device_profile->features.mslVersionMajor, device_profile->features.mslVersionMinor);
-	msl_options.set_msl_version(device_profile->features.mslVersionMajor, device_profile->features.mslVersionMinor);
-	mtl_reflection_data.msl_version = msl_options.msl_version;
+
+	// Determine Metal language version.
+	uint32_t msl_version = 0;
+	{
+		if (export_mode && compiler_props.is_valid()) {
+			// Use the properties determined by the toolchain and minimum OS version.
+			msl_version = compiler_props.metal_version;
+			mtl_reflection_data.os_min_version = compiler_props.os_version_min_required;
+		} else {
+			msl_version = make_msl_version(device_profile->features.mslVersionMajor, device_profile->features.mslVersionMinor);
+			mtl_reflection_data.os_min_version = MinOsVersion();
+		}
+		uint32_t msl_ver_maj = 0;
+		uint32_t msl_ver_min = 0;
+		parse_msl_version(msl_version, msl_ver_maj, msl_ver_min);
+		msl_options.set_msl_version(msl_ver_maj, msl_ver_min);
+		mtl_reflection_data.msl_version = msl_version;
+	}
+
 	msl_options.platform = device_profile->platform == MetalDeviceProfile::Platform::macOS ? CompilerMSL::Options::macOS : CompilerMSL::Options::iOS;
 	msl_options.platform = device_profile->platform == MetalDeviceProfile::Platform::macOS ? CompilerMSL::Options::macOS : CompilerMSL::Options::iOS;
 
 
 	if (device_profile->platform == MetalDeviceProfile::Platform::iOS) {
 	if (device_profile->platform == MetalDeviceProfile::Platform::iOS) {
@@ -238,7 +341,7 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const Vector<RenderingD
 		msl_options.multiview_layered_rendering = true;
 		msl_options.multiview_layered_rendering = true;
 		msl_options.view_mask_buffer_index = VIEW_MASK_BUFFER_INDEX;
 		msl_options.view_mask_buffer_index = VIEW_MASK_BUFFER_INDEX;
 	}
 	}
-	if (msl_version >= CompilerMSL::Options::make_msl_version(3, 2)) {
+	if (msl_version >= make_msl_version(3, 2)) {
 		// All 3.2+ versions support device coherence, so we can disable texture fences.
 		// All 3.2+ versions support device coherence, so we can disable texture fences.
 		msl_options.readwrite_texture_fences = false;
 		msl_options.readwrite_texture_fences = false;
 	}
 	}
@@ -571,13 +674,19 @@ bool RenderingShaderContainerMetal::_set_code_from_spirv(const Vector<RenderingD
 		memcpy(binary_data.ptrw(), source.c_str(), stage_data.source_size);
 		memcpy(binary_data.ptrw(), source.c_str(), stage_data.source_size);
 
 
 		if (export_mode) {
 		if (export_mode) {
-			// Try to compile the Metal source code
-			::Vector<uint8_t> library_data;
-			Error compile_err = compile_metal_source(source.c_str(), stage_data, library_data);
-			if (compile_err == OK) {
-				stage_data.library_size = library_data.size();
-				binary_data.resize(stage_data.source_size + stage_data.library_size);
-				memcpy(binary_data.ptrw() + stage_data.source_size, library_data.ptr(), stage_data.library_size);
+			if (compiler_props.is_valid()) {
+				// Try to compile the Metal source code.
+				::Vector<uint8_t> library_data;
+				Error compile_err = compile_metal_source(source.c_str(), stage_data, library_data);
+				if (compile_err == OK) {
+					// If we successfully compiled to a `.metallib`, there are greater restrictions on target platforms,
+					// so we must update the properties.
+					stage_data.library_size = library_data.size();
+					binary_data.resize(stage_data.source_size + stage_data.library_size);
+					memcpy(binary_data.ptrw() + stage_data.source_size, library_data.ptr(), stage_data.library_size);
+				}
+			} else {
+				WARN_PRINT_ONCE("Metal shader baking limited to SPIR-V: Unable to determine toolchain properties to compile .metallib");
 			}
 			}
 		}
 		}
 
 
@@ -693,6 +802,7 @@ Ref<RenderingShaderContainer> RenderingShaderContainerFormatMetal::create_contai
 	result.instantiate();
 	result.instantiate();
 	result->set_export_mode(export_mode);
 	result->set_export_mode(export_mode);
 	result->set_device_profile(device_profile);
 	result->set_device_profile(device_profile);
+	result->set_min_os_version(min_os_version);
 	return result;
 	return result;
 }
 }
 
 
@@ -704,6 +814,30 @@ RenderingDeviceCommons::ShaderSpirvVersion RenderingShaderContainerFormatMetal::
 	return SHADER_SPIRV_VERSION_1_6;
 	return SHADER_SPIRV_VERSION_1_6;
 }
 }
 
 
-RenderingShaderContainerFormatMetal::RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export) :
-		export_mode(p_export), device_profile(p_device_profile) {
+RenderingShaderContainerFormatMetal::RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export, const MinOsVersion p_min_os_version) :
+		export_mode(p_export), min_os_version(p_min_os_version), device_profile(p_device_profile) {
+}
+
+String MinOsVersion::to_compiler_os_version() const {
+	if (version == UINT32_MAX) {
+		return "";
+	}
+
+	uint32_t major = version / 10000;
+	uint32_t minor = (version % 10000) / 100;
+	return vformat("%d.%d", major, minor);
+}
+
+MinOsVersion::MinOsVersion(const String &p_version) {
+	int pos = p_version.find_char('.');
+	if (pos > 0) {
+		version = (uint32_t)(p_version.substr(0, pos).to_int() * 10000 +
+				p_version.substr(pos + 1).to_int() * 100);
+	} else {
+		version = (uint32_t)(p_version.to_int() * 10000);
+	}
+
+	if (version == 0) {
+		version = UINT32_MAX;
+	}
 }
 }

+ 3 - 3
editor/export/shader_baker_export_plugin.cpp

@@ -54,7 +54,7 @@ bool ShaderBakerExportPlugin::_is_active(const Vector<String> &p_features) const
 	return RendererSceneRenderRD::get_singleton() != nullptr && RendererRD::MaterialStorage::get_singleton() != nullptr && p_features.has("shader_baker");
 	return RendererSceneRenderRD::get_singleton() != nullptr && RendererRD::MaterialStorage::get_singleton() != nullptr && p_features.has("shader_baker");
 }
 }
 
 
-bool ShaderBakerExportPlugin::_initialize_container_format(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) {
+bool ShaderBakerExportPlugin::_initialize_container_format(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features, const Ref<EditorExportPreset> &p_preset) {
 	Variant driver_variant = GLOBAL_GET("rendering/rendering_device/driver." + p_platform->get_os_name().to_lower());
 	Variant driver_variant = GLOBAL_GET("rendering/rendering_device/driver." + p_platform->get_os_name().to_lower());
 	if (!driver_variant.is_string()) {
 	if (!driver_variant.is_string()) {
 		driver_variant = GLOBAL_GET("rendering/rendering_device/driver");
 		driver_variant = GLOBAL_GET("rendering/rendering_device/driver");
@@ -67,7 +67,7 @@ bool ShaderBakerExportPlugin::_initialize_container_format(const Ref<EditorExpor
 
 
 	for (Ref<ShaderBakerExportPluginPlatform> platform : platforms) {
 	for (Ref<ShaderBakerExportPluginPlatform> platform : platforms) {
 		if (platform->matches_driver(shader_container_driver)) {
 		if (platform->matches_driver(shader_container_driver)) {
-			shader_container_format = platform->create_shader_container_format(p_platform);
+			shader_container_format = platform->create_shader_container_format(p_platform, get_export_preset());
 			ERR_FAIL_NULL_V_MSG(shader_container_format, false, "Unable to create shader container format for the export platform.");
 			ERR_FAIL_NULL_V_MSG(shader_container_format, false, "Unable to create shader container format for the export platform.");
 			return true;
 			return true;
 		}
 		}
@@ -99,7 +99,7 @@ bool ShaderBakerExportPlugin::_begin_customize_resources(const Ref<EditorExportP
 		return false;
 		return false;
 	}
 	}
 
 
-	if (!_initialize_container_format(p_platform, p_features)) {
+	if (!_initialize_container_format(p_platform, p_features, get_export_preset())) {
 		return false;
 		return false;
 	}
 	}
 
 

+ 2 - 2
editor/export/shader_baker_export_plugin.h

@@ -38,7 +38,7 @@ class ShaderBakerExportPluginPlatform : public RefCounted {
 	GDCLASS(ShaderBakerExportPluginPlatform, RefCounted);
 	GDCLASS(ShaderBakerExportPluginPlatform, RefCounted);
 
 
 public:
 public:
-	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) = 0;
+	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) = 0;
 	virtual bool matches_driver(const String &p_driver) = 0;
 	virtual bool matches_driver(const String &p_driver) = 0;
 	virtual ~ShaderBakerExportPluginPlatform() {}
 	virtual ~ShaderBakerExportPluginPlatform() {}
 };
 };
@@ -82,7 +82,7 @@ protected:
 
 
 	virtual String get_name() const override;
 	virtual String get_name() const override;
 	virtual bool _is_active(const Vector<String> &p_features) const;
 	virtual bool _is_active(const Vector<String> &p_features) const;
-	virtual bool _initialize_container_format(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features);
+	virtual bool _initialize_container_format(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features, const Ref<EditorExportPreset> &p_preset);
 	virtual void _cleanup_container_format();
 	virtual void _cleanup_container_format();
 	virtual bool _initialize_cache_directory();
 	virtual bool _initialize_cache_directory();
 	virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;
 	virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override;

+ 1 - 1
editor/shader/shader_baker/shader_baker_export_plugin_platform_d3d12.cpp

@@ -34,7 +34,7 @@
 
 
 #include <windows.h>
 #include <windows.h>
 
 
-RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformD3D12::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) {
+RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformD3D12::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) {
 	if (lib_d3d12 == nullptr) {
 	if (lib_d3d12 == nullptr) {
 		lib_d3d12 = LoadLibraryW(L"D3D12.dll");
 		lib_d3d12 = LoadLibraryW(L"D3D12.dll");
 		ERR_FAIL_NULL_V_MSG(lib_d3d12, nullptr, "Unable to load D3D12.dll.");
 		ERR_FAIL_NULL_V_MSG(lib_d3d12, nullptr, "Unable to load D3D12.dll.");

+ 1 - 1
editor/shader/shader_baker/shader_baker_export_plugin_platform_d3d12.h

@@ -39,7 +39,7 @@ private:
 	void *lib_d3d12 = nullptr;
 	void *lib_d3d12 = nullptr;
 
 
 public:
 public:
-	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) override;
+	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) override;
 	virtual bool matches_driver(const String &p_driver) override;
 	virtual bool matches_driver(const String &p_driver) override;
 	virtual ~ShaderBakerExportPluginPlatformD3D12() override;
 	virtual ~ShaderBakerExportPluginPlatformD3D12() override;
 };
 };

+ 6 - 2
editor/shader/shader_baker/shader_baker_export_plugin_platform_metal.cpp

@@ -32,18 +32,22 @@
 
 
 #include "drivers/metal/rendering_shader_container_metal.h"
 #include "drivers/metal/rendering_shader_container_metal.h"
 
 
-RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformMetal::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) {
+RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformMetal::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) {
 	const String &os_name = p_platform->get_os_name();
 	const String &os_name = p_platform->get_os_name();
 	const MetalDeviceProfile *profile;
 	const MetalDeviceProfile *profile;
+	String min_os_version;
 
 
 	if (os_name == U"macOS") {
 	if (os_name == U"macOS") {
 		profile = MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform::macOS, MetalDeviceProfile::GPU::Apple7);
 		profile = MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform::macOS, MetalDeviceProfile::GPU::Apple7);
+		// Godot metal doesn't support x86_64 mac so no need to worry about that version
+		min_os_version = p_preset->get("application/min_macos_version_arm64");
 	} else if (os_name == U"iOS") {
 	} else if (os_name == U"iOS") {
 		profile = MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform::iOS, MetalDeviceProfile::GPU::Apple7);
 		profile = MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform::iOS, MetalDeviceProfile::GPU::Apple7);
+		min_os_version = p_preset->get("application/min_ios_version");
 	} else {
 	} else {
 		ERR_FAIL_V_MSG(nullptr, vformat("Unsupported platform: %s", os_name));
 		ERR_FAIL_V_MSG(nullptr, vformat("Unsupported platform: %s", os_name));
 	}
 	}
-	return memnew(RenderingShaderContainerFormatMetal(profile, true));
+	return memnew(RenderingShaderContainerFormatMetal(profile, true, min_os_version));
 }
 }
 
 
 bool ShaderBakerExportPluginPlatformMetal::matches_driver(const String &p_driver) {
 bool ShaderBakerExportPluginPlatformMetal::matches_driver(const String &p_driver) {

+ 1 - 1
editor/shader/shader_baker/shader_baker_export_plugin_platform_metal.h

@@ -34,6 +34,6 @@
 
 
 class ShaderBakerExportPluginPlatformMetal : public ShaderBakerExportPluginPlatform {
 class ShaderBakerExportPluginPlatformMetal : public ShaderBakerExportPluginPlatform {
 public:
 public:
-	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) override;
+	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) override;
 	virtual bool matches_driver(const String &p_driver) override;
 	virtual bool matches_driver(const String &p_driver) override;
 };
 };

+ 1 - 1
editor/shader/shader_baker/shader_baker_export_plugin_platform_vulkan.cpp

@@ -32,7 +32,7 @@
 
 
 #include "drivers/vulkan/rendering_shader_container_vulkan.h"
 #include "drivers/vulkan/rendering_shader_container_vulkan.h"
 
 
-RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformVulkan::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) {
+RenderingShaderContainerFormat *ShaderBakerExportPluginPlatformVulkan::create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) {
 	return memnew(RenderingShaderContainerFormatVulkan);
 	return memnew(RenderingShaderContainerFormatVulkan);
 }
 }
 
 

+ 1 - 1
editor/shader/shader_baker/shader_baker_export_plugin_platform_vulkan.h

@@ -36,6 +36,6 @@ class ShaderBakerExportPluginPlatformVulkan : public ShaderBakerExportPluginPlat
 	GDCLASS(ShaderBakerExportPluginPlatformVulkan, ShaderBakerExportPluginPlatform);
 	GDCLASS(ShaderBakerExportPluginPlatformVulkan, ShaderBakerExportPluginPlatform);
 
 
 public:
 public:
-	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform) override;
+	virtual RenderingShaderContainerFormat *create_shader_container_format(const Ref<EditorExportPlatform> &p_platform, const Ref<EditorExportPreset> &p_preset) override;
 	virtual bool matches_driver(const String &p_driver) override;
 	virtual bool matches_driver(const String &p_driver) override;
 };
 };