Path: blob/master/drivers/metal/rendering_shader_container_metal.cpp
20919 views
/**************************************************************************/1/* rendering_shader_container_metal.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "rendering_shader_container_metal.h"3132#include "metal_utils.h"3334#include "core/io/file_access.h"35#include "core/io/marshalls.h"36#include "core/templates/fixed_vector.h"37#include "servers/rendering/rendering_device.h"3839#include "thirdparty/spirv-reflect/spirv_reflect.h"4041#include <Metal/Metal.hpp>42#include <spirv.hpp>43#include <spirv_msl.hpp>44#include <spirv_parser.hpp>4546void RenderingShaderContainerMetal::_initialize_toolchain_properties() {47if (compiler_props.is_valid()) {48return;49}5051String sdk;52switch (device_profile->platform) {53case MetalDeviceProfile::Platform::macOS:54sdk = "macosx";55break;56case MetalDeviceProfile::Platform::iOS:57sdk = "iphoneos";58break;59case MetalDeviceProfile::Platform::visionOS:60sdk = "xros";61break;62}6364Vector<String> parts{ "echo", R"("")", "|", "/usr/bin/xcrun", "-sdk", sdk, "metal", "-E", "-dM", "-x", "metal" };6566switch (device_profile->platform) {67case MetalDeviceProfile::Platform::macOS: {68parts.push_back("-mtargetos=macos" + device_profile->min_os_version.to_compiler_os_version());69break;70}71case MetalDeviceProfile::Platform::iOS: {72parts.push_back("-mtargetos=ios" + device_profile->min_os_version.to_compiler_os_version());73break;74}75case MetalDeviceProfile::Platform::visionOS: {76parts.push_back("-mtargetos=xros" + device_profile->min_os_version.to_compiler_os_version());77break;78}79}8081parts.append_array({ "-", "|", "grep", "-E", R"(\"__METAL_VERSION__|__ENVIRONMENT_OS\")" });8283List<String> args = { "-c", String(" ").join(parts) };8485String r_pipe;86int exit_code;87Error err = OS::get_singleton()->execute("sh", args, &r_pipe, &exit_code, true);88ERR_FAIL_COND_MSG(err != OK, "Failed to determine Metal toolchain properties");8990// Parse the lines, which are in the form:91//92// #define VARNAME VALUE93Vector<String> lines = r_pipe.split("\n", false);94for (String &line : lines) {95Vector<String> name_val = line.trim_prefix("#define ").split(" ");96if (name_val.size() != 2) {97continue;98}99if (name_val[0] == "__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__") {100compiler_props.os_version_min_required = MinOsVersion((uint32_t)name_val[1].to_int());101} else if (name_val[0] == "__METAL_VERSION__") {102uint32_t ver = (uint32_t)name_val[1].to_int();103uint32_t maj = ver / 100;104uint32_t min = (ver % 100) / 10;105compiler_props.metal_version = make_msl_version(maj, min);106}107108if (compiler_props.is_valid()) {109break;110}111}112}113114Error RenderingShaderContainerMetal::compile_metal_source(const char *p_source, const StageData &p_stage_data, Vector<uint8_t> &r_binary_data) {115String name(shader_name.ptr());116if (name.contains_char(':')) {117name = name.replace_char(':', '_');118}119Error r_error;120Ref<FileAccess> source_file = FileAccess::create_temp(FileAccess::ModeFlags::READ_WRITE,121name + "_" + itos(p_stage_data.hash.short_sha()),122"metal", false, &r_error);123ERR_FAIL_COND_V_MSG(r_error != OK, r_error, "Unable to create temporary source file.");124if (!source_file->store_buffer((const uint8_t *)p_source, strlen(p_source))) {125ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Unable to write temporary source file");126}127source_file->flush();128Ref<FileAccess> result_file = FileAccess::create_temp(FileAccess::ModeFlags::READ_WRITE,129name + "_" + itos(p_stage_data.hash.short_sha()),130"metallib", false, &r_error);131132ERR_FAIL_COND_V_MSG(r_error != OK, r_error, "Unable to create temporary target file");133134String sdk;135switch (device_profile->platform) {136case MetalDeviceProfile::Platform::macOS:137sdk = "macosx";138break;139case MetalDeviceProfile::Platform::iOS:140sdk = "iphoneos";141break;142case MetalDeviceProfile::Platform::visionOS:143sdk = "xros";144break;145}146147// Build the .metallib binary.148{149List<String> args{ "-sdk", sdk, "metal", "-O3" };150151// Compile metal shaders for the minimum supported target instead of the host machine.152switch (device_profile->platform) {153case MetalDeviceProfile::Platform::macOS: {154args.push_back("-mtargetos=macos" + device_profile->min_os_version.to_compiler_os_version());155break;156}157case MetalDeviceProfile::Platform::iOS: {158args.push_back("-mtargetos=ios" + device_profile->min_os_version.to_compiler_os_version());159break;160}161case MetalDeviceProfile::Platform::visionOS: {162args.push_back("-mtargetos=xros" + device_profile->min_os_version.to_compiler_os_version());163break;164}165}166167if (p_stage_data.is_position_invariant) {168args.push_back("-fpreserve-invariance");169}170args.push_back("-fmetal-math-mode=fast");171args.push_back(source_file->get_path_absolute());172args.push_back("-o");173args.push_back(result_file->get_path_absolute());174String r_pipe;175int exit_code;176Error err = OS::get_singleton()->execute("/usr/bin/xcrun", args, &r_pipe, &exit_code, true);177if (!r_pipe.is_empty()) {178print_line(r_pipe);179}180if (err != OK) {181ERR_PRINT(vformat("Metal compiler returned error code: %d", err));182}183184if (exit_code != 0) {185ERR_PRINT(vformat("Metal compiler exited with error code: %d", exit_code));186}187int len = result_file->get_length();188ERR_FAIL_COND_V_MSG(len == 0, ERR_CANT_CREATE, "Metal compiler created empty library");189}190191// Strip the source from the binary.192{193List<String> args{ "-sdk", sdk, "metal-dsymutil", "--remove-source", result_file->get_path_absolute() };194String r_pipe;195int exit_code;196Error err = OS::get_singleton()->execute("/usr/bin/xcrun", args, &r_pipe, &exit_code, true);197if (!r_pipe.is_empty()) {198print_line(r_pipe);199}200if (err != OK) {201ERR_PRINT(vformat("metal-dsymutil tool returned error code: %d", err));202}203204if (exit_code != 0) {205ERR_PRINT(vformat("metal-dsymutil Compiler exited with error code: %d", exit_code));206}207int len = result_file->get_length();208ERR_FAIL_COND_V_MSG(len == 0, ERR_CANT_CREATE, "metal-dsymutil tool created empty library");209}210211r_binary_data = result_file->get_buffer(result_file->get_length());212213return OK;214}215216#pragma clang diagnostic push217#pragma clang diagnostic ignored "-Wunguarded-availability"218219static spv::ExecutionModel SHADER_STAGE_REMAP[RDD::SHADER_STAGE_MAX] = {220[RDD::SHADER_STAGE_VERTEX] = spv::ExecutionModelVertex,221[RDD::SHADER_STAGE_FRAGMENT] = spv::ExecutionModelFragment,222[RDD::SHADER_STAGE_TESSELATION_CONTROL] = spv::ExecutionModelTessellationControl,223[RDD::SHADER_STAGE_TESSELATION_EVALUATION] = spv::ExecutionModelTessellationEvaluation,224[RDD::SHADER_STAGE_COMPUTE] = spv::ExecutionModelGLCompute,225};226227spv::ExecutionModel get_stage(uint32_t p_stages_mask, RDD::ShaderStage p_stage) {228if (p_stages_mask & (1 << p_stage)) {229return SHADER_STAGE_REMAP[p_stage];230}231return spv::ExecutionModel::ExecutionModelMax;232}233234spv::ExecutionModel map_stage(RDD::ShaderStage p_stage) {235return SHADER_STAGE_REMAP[p_stage];236}237238Error RenderingShaderContainerMetal::reflect_spirv(const ReflectShader &p_shader) {239// const LocalVector<ReflectShaderStage> &p_spirv = p_shader.shader_stages;240//241// using ShaderStage = RenderingDeviceCommons::ShaderStage;242//243// const uint32_t spirv_size = p_spirv.size();244//245// HashSet<uint32_t> atomic_spirv_ids;246// bool atomics_scanned = false;247// auto scan_atomic_accesses = [&atomic_spirv_ids, &p_spirv, spirv_size, &atomics_scanned]() {248// if (atomics_scanned) {249// return;250// }251//252// for (uint32_t i = 0; i < spirv_size + 0; i++) {253// const uint32_t STARTING_WORD_INDEX = 5;254// Span<uint32_t> spirv = p_spirv[i].spirv();255// const uint32_t *words = spirv.ptr() + STARTING_WORD_INDEX;256// while (words < spirv.end()) {257// uint32_t instruction = *words;258// uint16_t word_count = instruction >> 16;259// SpvOp opcode = (SpvOp)(instruction & 0xFFFF);260// if (opcode == SpvOpImageTexelPointer) {261// uint32_t image_var_id = words[3];262// atomic_spirv_ids.insert(image_var_id);263// }264// words += word_count;265// }266// }267//268// atomics_scanned = true;269// };270//271// for (uint32_t i = 0; i < spirv_size + 0; i++) {272// ShaderStage stage = p_spirv[i].shader_stage;273// ShaderStage stage_flag = (ShaderStage)(1 << p_spirv[i].shader_stage);274// SpvReflectResult result;275//276// const SpvReflectShaderModule &module = p_spirv[i].module();277//278// uint32_t binding_count = 0;279// result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr);280// CRASH_COND(result != SPV_REFLECT_RESULT_SUCCESS);281//282// if (binding_count > 0) {283// LocalVector<SpvReflectDescriptorBinding *> bindings;284// bindings.resize_uninitialized(binding_count);285// result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptr());286//287// for (uint32_t j = 0; j < binding_count; j++) {288// const SpvReflectDescriptorBinding &binding = *bindings[j];289//290// switch (binding.descriptor_type) {291// case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:292// case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:293// case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE:294// case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:295// break;296// default:297// break;298// }299// }300// }301// }302//303return OK;304}305306bool RenderingShaderContainerMetal::_set_code_from_spirv(const ReflectShader &p_shader) {307using namespace spirv_cross;308using spirv_cross::CompilerMSL;309using spirv_cross::Resource;310311const LocalVector<ReflectShaderStage> &p_spirv = p_shader.shader_stages;312313if (export_mode) {314_initialize_toolchain_properties();315}316317// initialize Metal-specific reflection data318shaders.resize(p_spirv.size());319mtl_shaders.resize(p_spirv.size());320mtl_reflection_binding_set_uniforms_data.resize(reflection_binding_set_uniforms_data.size());321322mtl_reflection_data.set_needs_view_mask_buffer(reflection_data.has_multiview);323mtl_reflection_data.profile = *device_profile;324325CompilerMSL::Options msl_options{};326327// Determine Metal language version.328uint32_t msl_version = 0;329{330if (export_mode && compiler_props.is_valid()) {331// Use the properties determined by the toolchain and minimum OS version.332msl_version = compiler_props.metal_version;333mtl_reflection_data.os_min_version = compiler_props.os_version_min_required;334} else {335msl_version = device_profile->features.msl_version;336mtl_reflection_data.os_min_version = MinOsVersion();337}338uint32_t msl_ver_maj = 0;339uint32_t msl_ver_min = 0;340parse_msl_version(msl_version, msl_ver_maj, msl_ver_min);341msl_options.set_msl_version(msl_ver_maj, msl_ver_min);342mtl_reflection_data.msl_version = msl_version;343}344345msl_options.platform = device_profile->platform == MetalDeviceProfile::Platform::macOS ? CompilerMSL::Options::macOS : CompilerMSL::Options::iOS;346347if (device_profile->platform == MetalDeviceProfile::Platform::iOS) {348msl_options.ios_use_simdgroup_functions = device_profile->features.simdPermute;349msl_options.ios_support_base_vertex_instance = true;350}351352if (device_profile->features.use_argument_buffers) {353msl_options.argument_buffers_tier = CompilerMSL::Options::ArgumentBuffersTier::Tier2;354msl_options.argument_buffers = true;355mtl_reflection_data.set_uses_argument_buffers(true);356} else {357msl_options.argument_buffers_tier = CompilerMSL::Options::ArgumentBuffersTier::Tier1;358// Tier 1 argument buffers don't support writable textures, so we disable them completely.359msl_options.argument_buffers = false;360mtl_reflection_data.set_uses_argument_buffers(false);361}362msl_options.force_active_argument_buffer_resources = true;363msl_options.pad_argument_buffer_resources = true;364msl_options.texture_buffer_native = true; // Enable texture buffer support.365msl_options.use_framebuffer_fetch_subpasses = false;366msl_options.pad_fragment_output_components = true;367msl_options.r32ui_alignment_constant_id = R32UI_ALIGNMENT_CONSTANT_ID;368msl_options.agx_manual_cube_grad_fixup = true;369if (reflection_data.has_multiview) {370msl_options.multiview = true;371msl_options.multiview_layered_rendering = true;372msl_options.view_mask_buffer_index = VIEW_MASK_BUFFER_INDEX;373}374if (msl_version >= MSL_VERSION_32) {375// All 3.2+ versions support device coherence, so we can disable texture fences.376msl_options.readwrite_texture_fences = false;377}378379CompilerGLSL::Options options{};380options.vertex.flip_vert_y = true;381#if DEV_ENABLED382options.emit_line_directives = true;383#endif384385// Assign MSL bindings for all the descriptor sets.386typedef std::pair<MSLResourceBinding, uint32_t> MSLBindingInfo;387LocalVector<MSLBindingInfo> spirv_bindings;388MSLResourceBinding push_constant_resource_binding;389{390enum IndexType {391Texture,392Buffer,393Sampler,394Max,395};396397uint32_t dset_count = p_shader.uniform_sets.size();398uint32_t size = reflection_binding_set_uniforms_data.size();399spirv_bindings.resize(size);400401uint32_t indices[IndexType::Max] = { 0 };402auto next_index = [&indices](IndexType p_t, uint32_t p_stride) -> uint32_t {403uint32_t v = indices[p_t];404indices[p_t] += p_stride;405return v;406};407408uint32_t idx_dset = 0;409MSLBindingInfo *iter = spirv_bindings.ptr();410UniformData *found = mtl_reflection_binding_set_uniforms_data.ptrw();411UniformData::IndexType shader_index_type = msl_options.argument_buffers ? UniformData::IndexType::ARG : UniformData::IndexType::SLOT;412413for (const ReflectDescriptorSet &dset : p_shader.uniform_sets) {414// Reset the index count for each descriptor set, as this is an index in to the argument table.415uint32_t next_arg_buffer_index = 0;416auto next_arg_index = [&next_arg_buffer_index](uint32_t p_stride) -> uint32_t {417uint32_t v = next_arg_buffer_index;418next_arg_buffer_index += p_stride;419return v;420};421422for (const ReflectUniform &uniform : dset) {423const SpvReflectDescriptorBinding &binding = uniform.get_spv_reflect();424425found->active_stages = uniform.stages;426427RD::UniformType type = RD::UniformType(uniform.type);428uint32_t binding_stride = 1; // If this is an array, stride will be the length of the array.429if (uniform.length > 1) {430switch (type) {431case RDC::UNIFORM_TYPE_UNIFORM_BUFFER_DYNAMIC:432case RDC::UNIFORM_TYPE_STORAGE_BUFFER_DYNAMIC:433case RDC::UNIFORM_TYPE_UNIFORM_BUFFER:434case RDC::UNIFORM_TYPE_STORAGE_BUFFER:435// Buffers's length is its size, in bytes, so there is no stride.436break;437default: {438binding_stride = uniform.length;439found->array_length = uniform.length;440} break;441}442}443444// Determine access type.445switch (binding.descriptor_type) {446case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: {447if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE)) {448if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_READABLE)) {449found->access = MTL::BindingAccessReadWrite;450} else {451found->access = MTL::BindingAccessWriteOnly;452}453}454} break;455case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:456case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: {457if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE)) {458if (!(binding.decoration_flags & SPV_REFLECT_DECORATION_NON_READABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_READABLE)) {459found->access = MTL::BindingAccessReadWrite;460} else {461found->access = MTL::BindingAccessWriteOnly;462}463}464} break;465default:466break;467}468469switch (found->access) {470case MTL::BindingAccessReadOnly:471found->usage = MTL::ResourceUsageRead;472break;473case MTL::BindingAccessWriteOnly:474found->usage = MTL::ResourceUsageWrite;475break;476case MTL::BindingAccessReadWrite:477found->usage = MTL::ResourceUsageRead | MTL::ResourceUsageWrite;478break;479}480481iter->second = uniform.stages;482MSLResourceBinding &rb = iter->first;483rb.desc_set = idx_dset;484rb.binding = uniform.binding;485rb.count = binding_stride;486487switch (type) {488case RDC::UNIFORM_TYPE_SAMPLER: {489found->data_type = MTL::DataTypeSampler;490found->get_indexes(UniformData::IndexType::SLOT).sampler = next_index(Sampler, binding_stride);491found->get_indexes(UniformData::IndexType::ARG).sampler = next_arg_index(binding_stride);492493rb.basetype = SPIRType::BaseType::Sampler;494495} break;496case RDC::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE:497case RDC::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: {498found->data_type = MTL::DataTypeTexture;499found->get_indexes(UniformData::IndexType::SLOT).texture = next_index(Texture, binding_stride);500found->get_indexes(UniformData::IndexType::SLOT).sampler = next_index(Sampler, binding_stride);501found->get_indexes(UniformData::IndexType::ARG).texture = next_arg_index(binding_stride);502found->get_indexes(UniformData::IndexType::ARG).sampler = next_arg_index(binding_stride);503rb.basetype = SPIRType::BaseType::SampledImage;504} break;505case RDC::UNIFORM_TYPE_TEXTURE:506case RDC::UNIFORM_TYPE_IMAGE:507case RDC::UNIFORM_TYPE_TEXTURE_BUFFER: {508found->data_type = MTL::DataTypeTexture;509found->get_indexes(UniformData::IndexType::SLOT).texture = next_index(Texture, binding_stride);510found->get_indexes(UniformData::IndexType::ARG).texture = next_arg_index(binding_stride);511rb.basetype = SPIRType::BaseType::Image;512} break;513case RDC::UNIFORM_TYPE_IMAGE_BUFFER:514CRASH_NOW_MSG("Unimplemented!"); // TODO.515break;516case RDC::UNIFORM_TYPE_UNIFORM_BUFFER_DYNAMIC:517case RDC::UNIFORM_TYPE_STORAGE_BUFFER_DYNAMIC:518case RDC::UNIFORM_TYPE_UNIFORM_BUFFER:519case RDC::UNIFORM_TYPE_STORAGE_BUFFER: {520found->data_type = MTL::DataTypePointer;521found->get_indexes(UniformData::IndexType::SLOT).buffer = next_index(Buffer, binding_stride);522found->get_indexes(UniformData::IndexType::ARG).buffer = next_arg_index(binding_stride);523rb.basetype = SPIRType::BaseType::Void;524} break;525case RDC::UNIFORM_TYPE_INPUT_ATTACHMENT: {526found->data_type = MTL::DataTypeTexture;527found->get_indexes(UniformData::IndexType::SLOT).texture = next_index(Texture, binding_stride);528found->get_indexes(UniformData::IndexType::ARG).texture = next_arg_index(binding_stride);529rb.basetype = SPIRType::BaseType::Image;530} break;531case RDC::UNIFORM_TYPE_MAX:532default:533CRASH_NOW_MSG("Unreachable");534}535536// Specify the MSL resource bindings based on how the binding mode used by the shader.537rb.msl_buffer = found->get_indexes(shader_index_type).buffer;538rb.msl_texture = found->get_indexes(shader_index_type).texture;539rb.msl_sampler = found->get_indexes(shader_index_type).sampler;540541if (found->data_type == MTL::DataTypeTexture) {542const SpvReflectImageTraits &image = uniform.get_spv_reflect().image;543544switch (image.dim) {545case SpvDim1D: {546if (image.arrayed) {547found->texture_type = MTL::TextureType1DArray;548} else {549found->texture_type = MTL::TextureType1D;550}551} break;552case SpvDimSubpassData:553case SpvDim2D: {554if (image.arrayed && image.ms) {555found->texture_type = MTL::TextureType2DMultisampleArray;556} else if (image.arrayed) {557found->texture_type = MTL::TextureType2DArray;558} else if (image.ms) {559found->texture_type = MTL::TextureType2DMultisample;560} else {561found->texture_type = MTL::TextureType2D;562}563} break;564case SpvDim3D: {565found->texture_type = MTL::TextureType3D;566} break;567case SpvDimCube: {568if (image.arrayed) {569found->texture_type = MTL::TextureTypeCubeArray;570} else {571found->texture_type = MTL::TextureTypeCube;572}573} break;574case SpvDimRect: {575// Ignored.576} break;577case SpvDimBuffer: {578found->texture_type = MTL::TextureTypeTextureBuffer;579// If this is used with atomics, we need to use a read-write texture.580// scan_atomic_accesses();581// if (atomic_spirv_ids.find(uniform.spirv_id) != atomic_spirv_ids.end()) {582// rb.access = MTLBindingAccessReadWrite;583// found->access = MTLBindingAccessReadWrite;584// } else {585// rb.access = MTLBindingAccessReadOnly;586// found->access = MTLBindingAccessReadOnly;587// }588} break;589case SpvDimTileImageDataEXT: {590// Godot does not use this extension.591// See: https://registry.khronos.org/vulkan/specs/latest/man/html/VK_EXT_shader_tile_image.html592} break;593case SpvDimMax: {594// Add all enumerations to silence the compiler warning595// and generate future warnings, should a new one be added.596} break;597}598}599600iter++;601found++;602}603idx_dset++;604}605606if (reflection_data.push_constant_size > 0) {607push_constant_resource_binding.desc_set = ResourceBindingPushConstantDescriptorSet;608push_constant_resource_binding.basetype = SPIRType::BaseType::Void;609if (msl_options.argument_buffers) {610push_constant_resource_binding.msl_buffer = dset_count;611} else {612push_constant_resource_binding.msl_buffer = next_index(Buffer, 1);613}614mtl_reflection_data.push_constant_binding = push_constant_resource_binding.msl_buffer;615}616}617618for (uint32_t i = 0; i < p_spirv.size(); i++) {619StageData &stage_data = mtl_shaders.write[i];620const ReflectShaderStage &v = p_spirv[i];621RD::ShaderStage stage = v.shader_stage;622Span<uint32_t> spirv = v.spirv();623Parser parser(spirv.ptr(), spirv.size());624try {625parser.parse();626} catch (CompilerError &e) {627ERR_FAIL_V_MSG(false, "Failed to parse IR at stage " + String(RD::SHADER_STAGE_NAMES[stage]) + ": " + e.what());628}629630CompilerMSL compiler(std::move(parser.get_parsed_ir()));631compiler.set_msl_options(msl_options);632compiler.set_common_options(options);633634spv::ExecutionModel execution_model = map_stage(stage);635for (uint32_t jj = 0; jj < spirv_bindings.size(); jj++) {636MSLResourceBinding &rb = spirv_bindings.ptr()[jj].first;637rb.stage = execution_model;638compiler.add_msl_resource_binding(rb);639}640641if (push_constant_resource_binding.desc_set == ResourceBindingPushConstantDescriptorSet) {642push_constant_resource_binding.stage = execution_model;643compiler.add_msl_resource_binding(push_constant_resource_binding);644}645646std::unordered_set<VariableID> active = compiler.get_active_interface_variables();647ShaderResources resources = compiler.get_shader_resources();648649std::string source;650try {651source = compiler.compile();652} catch (CompilerError &e) {653ERR_FAIL_V_MSG(false, "Failed to compile stage " + String(RD::SHADER_STAGE_NAMES[stage]) + ": " + e.what());654}655656ERR_FAIL_COND_V_MSG(compiler.get_entry_points_and_stages().size() != 1, false, "Expected a single entry point and stage.");657658SmallVector<EntryPoint> entry_pts_stages = compiler.get_entry_points_and_stages();659EntryPoint &entry_point_stage = entry_pts_stages.front();660SPIREntryPoint &entry_point = compiler.get_entry_point(entry_point_stage.name, entry_point_stage.execution_model);661662for (auto ext : compiler.get_declared_extensions()) {663if (ext == "SPV_KHR_non_semantic_info" || ext == "SPV_KHR_printf") {664mtl_reflection_data.set_needs_debug_logging(true);665break;666}667}668669if (!resources.stage_inputs.empty()) {670for (Resource const &res : resources.stage_inputs) {671uint32_t binding = compiler.get_automatic_msl_resource_binding(res.id);672if (binding != (uint32_t)-1) {673stage_data.vertex_input_binding_mask |= 1 << binding;674}675}676}677678stage_data.is_position_invariant = compiler.is_position_invariant();679stage_data.supports_fast_math = !entry_point.flags.get(spv::ExecutionModeSignedZeroInfNanPreserve);680stage_data.hash = SHA256Digest(source.c_str(), source.length());681stage_data.source_size = source.length();682::Vector<uint8_t> binary_data;683binary_data.resize(stage_data.source_size);684memcpy(binary_data.ptrw(), source.c_str(), stage_data.source_size);685686if (export_mode) {687if (compiler_props.is_valid()) {688// Try to compile the Metal source code.689::Vector<uint8_t> library_data;690Error compile_err = compile_metal_source(source.c_str(), stage_data, library_data);691if (compile_err == OK) {692// If we successfully compiled to a `.metallib`, there are greater restrictions on target platforms,693// so we must update the properties.694stage_data.library_size = library_data.size();695binary_data.resize(stage_data.source_size + stage_data.library_size);696memcpy(binary_data.ptrw() + stage_data.source_size, library_data.ptr(), stage_data.library_size);697}698} else {699WARN_PRINT_ONCE("Metal shader baking limited to SPIR-V: Unable to determine toolchain properties to compile .metallib");700}701}702703uint32_t binary_data_size = binary_data.size();704Shader &shader = shaders.write[i];705shader.shader_stage = stage;706shader.code_decompressed_size = binary_data_size;707shader.code_compressed_bytes.resize(binary_data_size);708709uint32_t compressed_size = 0;710bool compressed = compress_code(binary_data.ptr(), binary_data_size, shader.code_compressed_bytes.ptrw(), &compressed_size, &shader.code_compression_flags);711ERR_FAIL_COND_V_MSG(!compressed, false, vformat("Failed to compress native code to native for SPIR-V #%d.", i));712713shader.code_compressed_bytes.resize(compressed_size);714}715716return true;717}718719#pragma clang diagnostic pop720721uint32_t RenderingShaderContainerMetal::_to_bytes_reflection_extra_data(uint8_t *p_bytes) const {722if (p_bytes != nullptr) {723*(HeaderData *)p_bytes = mtl_reflection_data;724}725return sizeof(HeaderData);726}727728uint32_t RenderingShaderContainerMetal::_to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const {729if (p_bytes != nullptr) {730*(UniformData *)p_bytes = mtl_reflection_binding_set_uniforms_data[p_index];731}732return sizeof(UniformData);733}734735uint32_t RenderingShaderContainerMetal::_to_bytes_shader_extra_data(uint8_t *p_bytes, uint32_t p_index) const {736if (p_bytes != nullptr) {737*(StageData *)p_bytes = mtl_shaders[p_index];738}739return sizeof(StageData);740}741742uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_extra_data(const uint8_t *p_bytes) {743mtl_reflection_data = *(HeaderData *)p_bytes;744return sizeof(HeaderData);745}746747uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) {748mtl_reflection_binding_set_uniforms_data.resize(reflection_binding_set_uniforms_data.size());749return 0;750}751752uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) {753mtl_reflection_binding_set_uniforms_data.ptrw()[p_index] = *(UniformData *)p_bytes;754return sizeof(UniformData);755}756757uint32_t RenderingShaderContainerMetal::_from_bytes_shader_extra_data_start(const uint8_t *p_bytes) {758mtl_shaders.resize(shaders.size());759return 0;760}761762uint32_t RenderingShaderContainerMetal::_from_bytes_shader_extra_data(const uint8_t *p_bytes, uint32_t p_index) {763mtl_shaders.ptrw()[p_index] = *(StageData *)p_bytes;764return sizeof(StageData);765}766767RenderingShaderContainerMetal::MetalShaderReflection RenderingShaderContainerMetal::get_metal_shader_reflection() const {768MetalShaderReflection res;769770uint32_t uniform_set_count = reflection_binding_set_uniforms_count.size();771uint32_t start = 0;772res.uniform_sets.resize(uniform_set_count);773for (uint32_t i = 0; i < uniform_set_count; i++) {774Vector<UniformData> &set = res.uniform_sets.ptrw()[i];775uint32_t count = reflection_binding_set_uniforms_count.get(i);776set.resize(count);777memcpy(set.ptrw(), &mtl_reflection_binding_set_uniforms_data.ptr()[start], count * sizeof(UniformData));778start += count;779}780781return res;782}783784uint32_t RenderingShaderContainerMetal::_format() const {785return 0x42424242;786}787788uint32_t RenderingShaderContainerMetal::_format_version() const {789return FORMAT_VERSION;790}791792Ref<RenderingShaderContainer> RenderingShaderContainerFormatMetal::create_container() const {793Ref<RenderingShaderContainerMetal> result;794result.instantiate();795result->set_export_mode(export_mode);796result->set_device_profile(device_profile);797return result;798}799800RenderingDeviceCommons::ShaderLanguageVersion RenderingShaderContainerFormatMetal::get_shader_language_version() const {801return SHADER_LANGUAGE_VULKAN_VERSION_1_1;802}803804RenderingDeviceCommons::ShaderSpirvVersion RenderingShaderContainerFormatMetal::get_shader_spirv_version() const {805return SHADER_SPIRV_VERSION_1_6;806}807808RenderingShaderContainerFormatMetal::RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export) :809export_mode(p_export), device_profile(p_device_profile) {810}811812String MinOsVersion::to_compiler_os_version() const {813if (version == UINT32_MAX) {814return "";815}816817uint32_t major = version / 10000;818uint32_t minor = (version % 10000) / 100;819return vformat("%d.%d", major, minor);820}821822MinOsVersion::MinOsVersion(const String &p_version) {823int pos = p_version.find_char('.');824if (pos > 0) {825version = (uint32_t)(p_version.substr(0, pos).to_int() * 10000 +826p_version.substr(pos + 1).to_int() * 100);827} else {828version = (uint32_t)(p_version.to_int() * 10000);829}830831if (version == 0) {832version = UINT32_MAX;833}834}835836837