Skip to content

Commit 0e8df80

Browse files
authored
Merge pull request godotengine#110264 from stuartcarnie/109846/metal_version
Metal: Ensure baked Metal binaries can be loaded on the minimum target OS
2 parents e98d608 + b7aac81 commit 0e8df80

14 files changed

+250
-44
lines changed

drivers/metal/metal_device_properties.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,15 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalDeviceProperties {
143143
private:
144144
void init_features(id<MTLDevice> p_device);
145145
void init_limits(id<MTLDevice> p_device);
146+
void init_os_props();
146147

147148
public:
148149
MetalFeatures features;
149150
MetalLimits limits;
150151

152+
// maj * 10000 + min * 100 + patch
153+
uint32_t os_version;
154+
151155
SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
152156

153157
MetalDeviceProperties(id<MTLDevice> p_device);

drivers/metal/metal_device_properties.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,15 @@
311311
#endif
312312
}
313313

314+
void MetalDeviceProperties::init_os_props() {
315+
NSOperatingSystemVersion ver = NSProcessInfo.processInfo.operatingSystemVersion;
316+
os_version = (uint32_t)ver.majorVersion * 10000 + (uint32_t)ver.minorVersion * 100 + (uint32_t)ver.patchVersion;
317+
}
318+
314319
MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
315320
init_features(p_device);
316321
init_limits(p_device);
322+
init_os_props();
317323
}
318324

319325
MetalDeviceProperties::~MetalDeviceProperties() {

drivers/metal/metal_utils.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ extern os_log_t LOG_DRIVER;
103103
// Used for dynamic tracing.
104104
extern os_log_t LOG_INTERVALS;
105105

106-
_FORCE_INLINE_ static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) {
107-
return (major * 10000) + (minor * 100) + patch;
106+
_FORCE_INLINE_ static uint32_t make_msl_version(uint32_t p_major, uint32_t p_minor = 0, uint32_t p_patch = 0) {
107+
return (p_major * 10000) + (p_minor * 100) + p_patch;
108+
}
109+
110+
_FORCE_INLINE_ static void parse_msl_version(uint32_t p_version, uint32_t &r_major, uint32_t &r_minor) {
111+
r_major = p_version / 10000;
112+
r_minor = (p_version % 10000) / 100;
108113
}

drivers/metal/rendering_device_driver_metal.mm

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,12 +1135,17 @@ static BindingInfo from_binding_info_data(const RenderingShaderContainerMetal::B
11351135
// We need to regenerate the shader if the cache is moved to an incompatible device.
11361136
ERR_FAIL_COND_V_MSG(device_properties->features.argument_buffers_tier < MTLArgumentBuffersTier2 && mtl_reflection_data.uses_argument_buffers(),
11371137
RDD::ShaderID(),
1138-
"Shader was generated with argument buffers, but device has limited support");
1138+
"Shader was compiled with argument buffers enabled, but this device does not support them");
11391139

11401140
uint32_t msl_version = make_msl_version(device_properties->features.mslVersionMajor, device_properties->features.mslVersionMinor);
11411141
ERR_FAIL_COND_V_MSG(msl_version < mtl_reflection_data.msl_version,
11421142
RDD::ShaderID(),
1143-
"Shader was compiled with a newer version of Metal than is available on the device.");
1143+
"Shader was compiled for a newer version of Metal");
1144+
1145+
MTLGPUFamily compiled_gpu_family = static_cast<MTLGPUFamily>(mtl_reflection_data.profile.gpu);
1146+
ERR_FAIL_COND_V_MSG(device_properties->features.highestFamily < compiled_gpu_family,
1147+
RDD::ShaderID(),
1148+
"Shader was generated for a newer Apple GPU");
11441149

11451150
MTLCompileOptions *options = [MTLCompileOptions new];
11461151
uint32_t major = mtl_reflection_data.msl_version / 10000;
@@ -1181,6 +1186,9 @@ static BindingInfo from_binding_info_data(const RenderingShaderContainerMetal::B
11811186

11821187
MDLibrary *library = nil;
11831188
if (shader_data.library_size > 0) {
1189+
ERR_FAIL_COND_V_MSG(mtl_reflection_data.os_min_version > device_properties->os_version,
1190+
RDD::ShaderID(),
1191+
"Metal shader binary was generated for a newer target OS");
11841192
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);
11851193
library = [MDLibrary newLibraryWithCacheEntry:cd
11861194
device:device

drivers/metal/rendering_shader_container_metal.h

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@ const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
4141

4242
class RenderingShaderContainerFormatMetal;
4343

44+
class MinOsVersion {
45+
uint32_t version;
46+
47+
public:
48+
String to_compiler_os_version() const;
49+
bool is_null() const { return version == UINT32_MAX; }
50+
bool is_valid() const { return version != UINT32_MAX; }
51+
52+
MinOsVersion(const String &p_version);
53+
explicit MinOsVersion(uint32_t p_version) :
54+
version(p_version) {}
55+
MinOsVersion() :
56+
version(UINT32_MAX) {}
57+
58+
bool operator>(uint32_t p_other) {
59+
return version > p_other;
60+
}
61+
};
62+
4463
/// @brief A minimal structure that defines a device profile for Metal.
4564
///
4665
/// This structure is used by the `RenderingShaderContainerMetal` class to
@@ -53,17 +72,20 @@ struct MetalDeviceProfile {
5372
iOS = 1,
5473
};
5574

56-
/// @brief The GPU family.
75+
/*! @brief The GPU family.
76+
*
77+
* NOTE: These values match Apple's MTLGPUFamily
78+
*/
5779
enum class GPU : uint32_t {
58-
Apple1,
59-
Apple2,
60-
Apple3,
61-
Apple4,
62-
Apple5,
63-
Apple6,
64-
Apple7,
65-
Apple8,
66-
Apple9,
80+
Apple1 = 1001,
81+
Apple2 = 1002,
82+
Apple3 = 1003,
83+
Apple4 = 1004,
84+
Apple5 = 1005,
85+
Apple6 = 1006,
86+
Apple7 = 1007,
87+
Apple8 = 1008,
88+
Apple9 = 1009,
6789
};
6890

6991
enum class ArgumentBuffersTier : uint32_t {
@@ -108,6 +130,13 @@ class RenderingShaderContainerMetal : public RenderingShaderContainer {
108130
/// The Metal language version specified when compiling SPIR-V to MSL.
109131
/// Format is major * 10000 + minor * 100 + patch.
110132
uint32_t msl_version = UINT32_MAX;
133+
/*! @brief The minimum supported OS version for shaders baked to a `.metallib`.
134+
*
135+
* NOTE: This property is only valid when shaders are baked to a .metalllib
136+
*
137+
* Format is major * 10000 + minor * 100 + patch.
138+
*/
139+
MinOsVersion os_min_version;
111140
uint32_t flags = NONE;
112141

113142
/// @brief Returns `true` if the shader is compiled with multi-view support.
@@ -210,9 +239,23 @@ class RenderingShaderContainerMetal : public RenderingShaderContainer {
210239
HeaderData mtl_reflection_data; // compliment to reflection_data
211240
Vector<StageData> mtl_shaders; // compliment to shaders
212241

242+
private:
243+
struct ToolchainProperties {
244+
MinOsVersion os_version_min_required;
245+
uint32_t metal_version = UINT32_MAX;
246+
247+
_FORCE_INLINE_ bool is_null() const { return os_version_min_required.is_null() || metal_version == UINT32_MAX; }
248+
_FORCE_INLINE_ bool is_valid() const { return !is_null(); }
249+
};
250+
251+
ToolchainProperties compiler_props;
252+
253+
void _initialize_toolchain_properties();
254+
213255
private:
214256
const MetalDeviceProfile *device_profile = nullptr;
215257
bool export_mode = false;
258+
MinOsVersion min_os_version;
216259

217260
Vector<UniformData> mtl_reflection_binding_set_uniforms_data; // compliment to reflection_binding_set_uniforms_data
218261
Vector<SpecializationData> mtl_reflection_specialization_data; // compliment to reflection_specialization_data
@@ -224,6 +267,7 @@ class RenderingShaderContainerMetal : public RenderingShaderContainer {
224267

225268
void set_export_mode(bool p_export_mode) { export_mode = p_export_mode; }
226269
void set_device_profile(const MetalDeviceProfile *p_device_profile) { device_profile = p_device_profile; }
270+
void set_min_os_version(const MinOsVersion p_min_os_version) { min_os_version = p_min_os_version; }
227271

228272
struct MetalShaderReflection {
229273
Vector<Vector<UniformData>> uniform_sets;
@@ -253,13 +297,14 @@ class RenderingShaderContainerMetal : public RenderingShaderContainer {
253297

254298
class RenderingShaderContainerFormatMetal : public RenderingShaderContainerFormat {
255299
bool export_mode = false;
300+
MinOsVersion min_os_version;
256301

257302
const MetalDeviceProfile *device_profile = nullptr;
258303

259304
public:
260305
virtual Ref<RenderingShaderContainer> create_container() const override;
261306
virtual ShaderLanguageVersion get_shader_language_version() const override;
262307
virtual ShaderSpirvVersion get_shader_spirv_version() const override;
263-
RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export = false);
308+
RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export = false, const MinOsVersion p_min_os_version = MinOsVersion());
264309
virtual ~RenderingShaderContainerFormatMetal() = default;
265310
};

0 commit comments

Comments
 (0)