Skip to content

Commit 1f183b1

Browse files
committed
Metal: Stable argument buffers; GPU rendering crashes; visionOS exports
Supersedes #110683
1 parent ab6c6ee commit 1f183b1

24 files changed

+2586
-2035
lines changed

drivers/d3d12/rendering_shader_container_d3d12.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ uint32_t RenderingShaderContainerD3D12::_to_bytes_footer_extra_data(uint8_t *p_b
266266
}
267267

268268
#if NIR_ENABLED
269-
bool RenderingShaderContainerD3D12::_convert_spirv_to_nir(Span<ReflectedShaderStage> p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed) {
269+
bool RenderingShaderContainerD3D12::_convert_spirv_to_nir(Span<ReflectShaderStage> p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed) {
270270
r_stages_processed.clear();
271271

272272
dxil_spirv_runtime_conf dxil_runtime_conf = {};
@@ -428,7 +428,7 @@ bool RenderingShaderContainerD3D12::_convert_nir_to_dxil(const HashMap<int, nir_
428428
return true;
429429
}
430430

431-
bool RenderingShaderContainerD3D12::_convert_spirv_to_dxil(Span<ReflectedShaderStage> p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed) {
431+
bool RenderingShaderContainerD3D12::_convert_spirv_to_dxil(Span<ReflectShaderStage> p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed) {
432432
r_dxil_blobs.clear();
433433

434434
HashMap<int, nir_shader *> stages_nir_shaders;
@@ -763,7 +763,7 @@ void RenderingShaderContainerD3D12::_nir_report_bitcode_bit_offset(uint64_t p_bi
763763
}
764764
#endif
765765

766-
void RenderingShaderContainerD3D12::_set_from_shader_reflection_post(const RenderingDeviceCommons::ShaderReflection &p_reflection) {
766+
void RenderingShaderContainerD3D12::_set_from_shader_reflection_post(const ReflectShader &p_shader) {
767767
reflection_binding_set_uniforms_data_d3d12.resize(reflection_binding_set_uniforms_data.size());
768768
reflection_specialization_data_d3d12.resize(reflection_specialization_data.size());
769769

@@ -779,8 +779,9 @@ void RenderingShaderContainerD3D12::_set_from_shader_reflection_post(const Rende
779779
}
780780
}
781781

782-
bool RenderingShaderContainerD3D12::_set_code_from_spirv(Span<ReflectedShaderStage> p_spirv) {
782+
bool RenderingShaderContainerD3D12::_set_code_from_spirv(const ReflectShader &p_shader) {
783783
#if NIR_ENABLED
784+
const LocalVector<ReflectShaderStage> &p_spirv = p_shader.shader_stages;
784785
reflection_data_d3d12.nir_runtime_data_root_param_idx = UINT32_MAX;
785786

786787
for (int64_t i = 0; i < reflection_specialization_data.size(); i++) {

drivers/d3d12/rendering_shader_container_d3d12.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ class RenderingShaderContainerD3D12 : public RenderingShaderContainer {
122122
uint32_t root_signature_crc = 0;
123123

124124
#if NIR_ENABLED
125-
bool _convert_spirv_to_nir(Span<ReflectedShaderStage> p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
125+
bool _convert_spirv_to_nir(Span<ReflectShaderStage> p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
126126
bool _convert_nir_to_dxil(const HashMap<int, nir_shader *> &p_stages_nir_shaders, BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs);
127-
bool _convert_spirv_to_dxil(Span<ReflectedShaderStage> p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
127+
bool _convert_spirv_to_dxil(Span<ReflectShaderStage> p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
128128
bool _generate_root_signature(BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed);
129129

130130
// GodotNirCallbacks.
@@ -146,8 +146,8 @@ class RenderingShaderContainerD3D12 : public RenderingShaderContainer {
146146
virtual uint32_t _to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
147147
virtual uint32_t _to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
148148
virtual uint32_t _to_bytes_footer_extra_data(uint8_t *p_bytes) const override;
149-
virtual void _set_from_shader_reflection_post(const RenderingDeviceCommons::ShaderReflection &p_reflection) override;
150-
virtual bool _set_code_from_spirv(Span<ReflectedShaderStage> p_spirv) override;
149+
virtual void _set_from_shader_reflection_post(const ReflectShader &p_shader) override;
150+
virtual bool _set_code_from_spirv(const ReflectShader &p_shader) override;
151151

152152
public:
153153
struct ShaderReflectionD3D12 {

drivers/metal/SCsub

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ env_metal.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
4646
driver_obj = []
4747

4848
env_metal.add_source_files(driver_obj, "*.mm")
49+
env_metal.add_source_files(driver_obj, "*.cpp")
4950
env.drivers_sources += driver_obj
5051

5152
# Needed to force rebuilding the driver files when the thirdparty library is updated.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**************************************************************************/
2+
/* metal_device_profile.cpp */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#include "metal_device_profile.h"
32+
33+
#include "metal_utils.h"
34+
35+
Mutex MetalDeviceProfile::profiles_lock;
36+
HashMap<MetalDeviceProfile::ProfileKey, MetalDeviceProfile> MetalDeviceProfile::profiles;
37+
38+
const MetalDeviceProfile *MetalDeviceProfile::get_profile(Platform p_platform, GPU p_gpu, MinOsVersion p_min_os_version) {
39+
DEV_ASSERT(p_platform == Platform::macOS || p_platform == Platform::iOS || p_platform == Platform::visionOS);
40+
41+
MutexLock lock(profiles_lock);
42+
43+
ProfileKey key(p_min_os_version, p_platform, p_gpu);
44+
if (MetalDeviceProfile *profile = profiles.getptr(key)) {
45+
return profile;
46+
}
47+
48+
MetalDeviceProfile res;
49+
res.platform = p_platform;
50+
res.gpu = p_gpu;
51+
res.min_os_version = p_min_os_version;
52+
53+
switch (p_platform) {
54+
case Platform::macOS: {
55+
if (p_min_os_version >= os_version::MACOS_26_0) {
56+
res.features.msl_version = MSL_VERSION_40;
57+
} else if (p_min_os_version >= os_version::MACOS_15_0) {
58+
res.features.msl_version = MSL_VERSION_32;
59+
} else if (p_min_os_version >= os_version::MACOS_14_0) {
60+
res.features.msl_version = MSL_VERSION_31;
61+
} else if (p_min_os_version >= os_version::MACOS_13_0) {
62+
res.features.msl_version = MSL_VERSION_30;
63+
} else if (p_min_os_version >= os_version::MACOS_12_0) {
64+
res.features.msl_version = MSL_VERSION_24;
65+
} else {
66+
res.features.msl_version = MSL_VERSION_23;
67+
}
68+
res.features.use_argument_buffers = p_min_os_version >= os_version::MACOS_13_0;
69+
res.features.simdPermute = true;
70+
} break;
71+
72+
case Platform::iOS: {
73+
if (p_min_os_version >= os_version::IOS_26_0) {
74+
res.features.msl_version = MSL_VERSION_40;
75+
} else if (p_min_os_version >= os_version::IOS_18_0) {
76+
res.features.msl_version = MSL_VERSION_32;
77+
} else if (p_min_os_version >= os_version::IOS_17_0) {
78+
res.features.msl_version = MSL_VERSION_31;
79+
} else if (p_min_os_version >= os_version::IOS_16_0) {
80+
res.features.msl_version = MSL_VERSION_30;
81+
} else if (p_min_os_version >= os_version::IOS_15_0) {
82+
res.features.msl_version = MSL_VERSION_24;
83+
} else {
84+
res.features.msl_version = MSL_VERSION_23;
85+
}
86+
87+
switch (p_gpu) {
88+
case GPU::Apple1:
89+
case GPU::Apple2:
90+
case GPU::Apple3:
91+
case GPU::Apple4:
92+
case GPU::Apple5: {
93+
res.features.simdPermute = false;
94+
res.features.use_argument_buffers = false;
95+
} break;
96+
case GPU::Apple6:
97+
case GPU::Apple7:
98+
case GPU::Apple8:
99+
case GPU::Apple9: {
100+
res.features.use_argument_buffers = p_min_os_version >= os_version::IOS_16_0;
101+
res.features.simdPermute = true;
102+
} break;
103+
}
104+
} break;
105+
106+
case Platform::visionOS: {
107+
if (p_min_os_version >= os_version::VISIONOS_26_0) {
108+
res.features.msl_version = MSL_VERSION_40;
109+
} else if (p_min_os_version >= os_version::VISIONOS_02_4) {
110+
res.features.msl_version = MSL_VERSION_32;
111+
} else {
112+
ERR_FAIL_V_MSG(nullptr, "visionOS 2.4 is the minimum supported version for visionOS.");
113+
}
114+
115+
switch (p_gpu) {
116+
case GPU::Apple8:
117+
case GPU::Apple9: {
118+
res.features.use_argument_buffers = true;
119+
res.features.simdPermute = true;
120+
} break;
121+
default: {
122+
CRASH_NOW_MSG("visionOS hardware has a minimum Apple8 GPU.");
123+
}
124+
}
125+
} break;
126+
}
127+
128+
return &profiles.insert(key, res)->value;
129+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**************************************************************************/
2+
/* metal_device_profile.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "core/os/mutex.h"
34+
#include "core/string/ustring.h"
35+
#include "core/templates/hash_map.h"
36+
#include "core/typedefs.h"
37+
38+
class MinOsVersion {
39+
uint32_t version;
40+
41+
public:
42+
String to_compiler_os_version() const;
43+
bool is_null() const { return version == UINT32_MAX; }
44+
bool is_valid() const { return version != UINT32_MAX; }
45+
46+
MinOsVersion(const String &p_version);
47+
constexpr explicit MinOsVersion(uint32_t p_version) :
48+
version(p_version) {}
49+
constexpr MinOsVersion(uint32_t p_major, uint32_t p_minor, uint32_t p_patch = 0) :
50+
version(p_major * 10000 + p_minor * 100 + p_patch) {}
51+
constexpr MinOsVersion() :
52+
version(UINT32_MAX) {}
53+
54+
bool operator>(uint32_t p_other) {
55+
return version > p_other;
56+
}
57+
constexpr operator uint32_t() const { return version; }
58+
};
59+
60+
namespace os_version {
61+
62+
constexpr MinOsVersion MACOS_26_0(26'00'00);
63+
constexpr MinOsVersion MACOS_15_0(15'00'00);
64+
constexpr MinOsVersion MACOS_14_0(14'00'00);
65+
constexpr MinOsVersion MACOS_13_0(13'00'00);
66+
constexpr MinOsVersion MACOS_12_0(12'00'00);
67+
constexpr MinOsVersion MACOS_11_0(11'00'00);
68+
69+
constexpr MinOsVersion IOS_26_0(26'00'00);
70+
constexpr MinOsVersion IOS_18_0(18'00'00);
71+
constexpr MinOsVersion IOS_17_0(17'00'00);
72+
constexpr MinOsVersion IOS_16_0(16'00'00);
73+
constexpr MinOsVersion IOS_15_0(15'00'00);
74+
75+
constexpr MinOsVersion VISIONOS_26_0(26'00'00);
76+
constexpr MinOsVersion VISIONOS_02_4(02'04'00);
77+
78+
} //namespace os_version
79+
80+
/// @brief A minimal structure that defines a device profile for Metal.
81+
///
82+
/// This structure is used by the `RenderingShaderContainerMetal` class to
83+
/// determine options for compiling SPIR-V to Metal source. It currently only
84+
/// contains the minimum properties required to transform shaders from SPIR-V to Metal
85+
/// and potentially compile to a `.metallib`.
86+
struct MetalDeviceProfile {
87+
enum class Platform : uint32_t {
88+
macOS = 0,
89+
iOS = 1,
90+
visionOS = 2,
91+
};
92+
93+
/*! @brief The GPU family.
94+
*
95+
* NOTE: These values match Apple's MTLGPUFamily
96+
*/
97+
enum class GPU : uint32_t {
98+
Apple1 = 1001,
99+
Apple2 = 1002,
100+
Apple3 = 1003,
101+
Apple4 = 1004,
102+
Apple5 = 1005,
103+
Apple6 = 1006,
104+
Apple7 = 1007,
105+
Apple8 = 1008,
106+
Apple9 = 1009,
107+
};
108+
109+
enum class ArgumentBuffersTier : uint32_t {
110+
Tier1 = 0,
111+
Tier2 = 1,
112+
};
113+
114+
struct Features {
115+
uint32_t msl_version = 0;
116+
bool use_argument_buffers = false;
117+
bool simdPermute = false;
118+
};
119+
120+
Platform platform = Platform::macOS;
121+
GPU gpu = GPU::Apple4;
122+
MinOsVersion min_os_version;
123+
Features features;
124+
125+
static const MetalDeviceProfile *get_profile(Platform p_platform, GPU p_gpu, MinOsVersion p_min_os_version);
126+
127+
MetalDeviceProfile() = default;
128+
129+
private:
130+
static Mutex profiles_lock; ///< Mutex to protect access to the profiles map.
131+
132+
struct ProfileKey {
133+
friend struct HashMapHasherDefaultImpl<ProfileKey>;
134+
union {
135+
struct {
136+
uint32_t min_os_version;
137+
uint16_t platform;
138+
uint16_t gpu;
139+
};
140+
uint64_t value = 0;
141+
};
142+
143+
ProfileKey() = default;
144+
ProfileKey(MinOsVersion p_min_os_version, Platform p_platform, GPU p_gpu) :
145+
min_os_version(p_min_os_version), platform((uint16_t)p_platform), gpu((uint16_t)p_gpu) {}
146+
147+
_FORCE_INLINE_ uint32_t hash() const {
148+
return hash_one_uint64(value);
149+
}
150+
151+
bool operator==(const ProfileKey &p_other) const {
152+
return value == p_other.value;
153+
}
154+
};
155+
156+
static HashMap<ProfileKey, MetalDeviceProfile> profiles;
157+
};

0 commit comments

Comments
 (0)