Path: blob/master/drivers/metal/rendering_shader_container_metal.h
10277 views
/**************************************************************************/1/* rendering_shader_container_metal.h */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#pragma once3132#import "sha256_digest.h"3334#import "servers/rendering/rendering_device_driver.h"35#import "servers/rendering/rendering_shader_container.h"3637constexpr uint32_t R32UI_ALIGNMENT_CONSTANT_ID = 65535;38/// Metal buffer index for the view mask when rendering multi-view.39const uint32_t VIEW_MASK_BUFFER_INDEX = 24;4041class RenderingShaderContainerFormatMetal;4243class MinOsVersion {44uint32_t version;4546public:47String to_compiler_os_version() const;48bool is_null() const { return version == UINT32_MAX; }49bool is_valid() const { return version != UINT32_MAX; }5051MinOsVersion(const String &p_version);52explicit MinOsVersion(uint32_t p_version) :53version(p_version) {}54MinOsVersion() :55version(UINT32_MAX) {}5657bool operator>(uint32_t p_other) {58return version > p_other;59}60};6162/// @brief A minimal structure that defines a device profile for Metal.63///64/// This structure is used by the `RenderingShaderContainerMetal` class to65/// determine options for compiling SPIR-V to Metal source. It currently only66/// contains the minimum properties required to transform shaders from SPIR-V to Metal67/// and potentially compile to a `.metallib`.68struct MetalDeviceProfile {69enum class Platform : uint32_t {70macOS = 0,71iOS = 1,72};7374/*! @brief The GPU family.75*76* NOTE: These values match Apple's MTLGPUFamily77*/78enum class GPU : uint32_t {79Apple1 = 1001,80Apple2 = 1002,81Apple3 = 1003,82Apple4 = 1004,83Apple5 = 1005,84Apple6 = 1006,85Apple7 = 1007,86Apple8 = 1008,87Apple9 = 1009,88};8990enum class ArgumentBuffersTier : uint32_t {91Tier1 = 0,92Tier2 = 1,93};9495struct Features {96uint32_t mslVersionMajor = 0;97uint32_t mslVersionMinor = 0;98ArgumentBuffersTier argument_buffers_tier = ArgumentBuffersTier::Tier1;99bool simdPermute = false;100};101102Platform platform = Platform::macOS;103GPU gpu = GPU::Apple4;104Features features;105106static const MetalDeviceProfile *get_profile(Platform p_platform, GPU p_gpu);107108MetalDeviceProfile() = default;109110private:111static Mutex profiles_lock; ///< Mutex to protect access to the profiles map.112static HashMap<uint32_t, MetalDeviceProfile> profiles;113};114115class RenderingShaderContainerMetal : public RenderingShaderContainer {116GDSOFTCLASS(RenderingShaderContainerMetal, RenderingShaderContainer);117118public:119struct HeaderData {120enum Flags : uint32_t {121NONE = 0,122NEEDS_VIEW_MASK_BUFFER = 1 << 0,123USES_ARGUMENT_BUFFERS = 1 << 1,124};125126/// The base profile that was used to generate this shader.127MetalDeviceProfile profile;128129/// The Metal language version specified when compiling SPIR-V to MSL.130/// Format is major * 10000 + minor * 100 + patch.131uint32_t msl_version = UINT32_MAX;132/*! @brief The minimum supported OS version for shaders baked to a `.metallib`.133*134* NOTE: This property is only valid when shaders are baked to a .metalllib135*136* Format is major * 10000 + minor * 100 + patch.137*/138MinOsVersion os_min_version;139uint32_t flags = NONE;140141/// @brief Returns `true` if the shader is compiled with multi-view support.142bool needs_view_mask_buffer() const {143return flags & NEEDS_VIEW_MASK_BUFFER;144}145146void set_needs_view_mask_buffer(bool p_value) {147if (p_value) {148flags |= NEEDS_VIEW_MASK_BUFFER;149} else {150flags &= ~NEEDS_VIEW_MASK_BUFFER;151}152}153154/// @brief Returns `true` if the shader was compiled with argument buffer support.155bool uses_argument_buffers() const {156return flags & USES_ARGUMENT_BUFFERS;157}158159void set_uses_argument_buffers(bool p_value) {160if (p_value) {161flags |= USES_ARGUMENT_BUFFERS;162} else {163flags &= ~USES_ARGUMENT_BUFFERS;164}165}166};167168struct StageData {169uint32_t vertex_input_binding_mask = 0;170uint32_t is_position_invariant = 0; ///< <c>true</c> if the position output is invariant171uint32_t supports_fast_math = 0;172SHA256Digest hash; ///< SHA 256 hash of the shader code173uint32_t source_size = 0; ///< size of the source code in the returned bytes174uint32_t library_size = 0; ///< size of the compiled library in the returned bytes, 0 if it is not compiled175uint32_t push_constant_binding = UINT32_MAX; ///< Metal binding slot for the push constant data176};177178struct BindingInfoData {179uint32_t shader_stage = UINT32_MAX; ///< The shader stage this binding is used in, or UINT32_MAX if not used.180uint32_t data_type = 0; // MTLDataTypeNone181uint32_t index = 0;182uint32_t access = 0; // MTLBindingAccessReadOnly183uint32_t usage = 0; // MTLResourceUsage (none)184uint32_t texture_type = 2; // MTLTextureType2D185uint32_t image_format = 0;186uint32_t array_length = 0;187uint32_t is_multisampled = 0;188};189190struct UniformData {191/// Specifies the index into the `bindings` array for the shader stage.192///193/// For example, a vertex and fragment shader use slots 0 and 1 of the bindings and bindings_secondary arrays.194static constexpr uint32_t STAGE_INDEX[RenderingDeviceCommons::SHADER_STAGE_MAX] = {1950, // SHADER_STAGE_VERTEX1961, // SHADER_STAGE_FRAGMENT1970, // SHADER_STAGE_TESSELATION_CONTROL1981, // SHADER_STAGE_TESSELATION_EVALUATION1990, // SHADER_STAGE_COMPUTE200};201202/// Specifies the stages the uniform data is203/// used by the Metal shader.204uint32_t active_stages = 0;205/// The primary binding information for the uniform data.206///207/// A maximum of two stages is expected for any given pipeline, such as a vertex and fragment, so208/// the array size is fixed to 2.209BindingInfoData bindings[2];210/// The secondary binding information for the uniform data.211///212/// This is typically a sampler for an image-sampler uniform213BindingInfoData bindings_secondary[2];214215_FORCE_INLINE_ constexpr uint32_t get_index_for_stage(RenderingDeviceCommons::ShaderStage p_stage) const {216return STAGE_INDEX[p_stage];217}218219_FORCE_INLINE_ BindingInfoData &get_binding_for_stage(RenderingDeviceCommons::ShaderStage p_stage) {220BindingInfoData &info = bindings[get_index_for_stage(p_stage)];221DEV_ASSERT(info.shader_stage == UINT32_MAX || info.shader_stage == p_stage); // make sure this uniform isn't used in the other stage222info.shader_stage = p_stage;223return info;224}225226_FORCE_INLINE_ BindingInfoData &get_secondary_binding_for_stage(RenderingDeviceCommons::ShaderStage p_stage) {227BindingInfoData &info = bindings_secondary[get_index_for_stage(p_stage)];228DEV_ASSERT(info.shader_stage == UINT32_MAX || info.shader_stage == p_stage); // make sure this uniform isn't used in the other stage229info.shader_stage = p_stage;230return info;231}232};233234struct SpecializationData {235uint32_t used_stages = 0;236};237238HeaderData mtl_reflection_data; // compliment to reflection_data239Vector<StageData> mtl_shaders; // compliment to shaders240241private:242struct ToolchainProperties {243MinOsVersion os_version_min_required;244uint32_t metal_version = UINT32_MAX;245246_FORCE_INLINE_ bool is_null() const { return os_version_min_required.is_null() || metal_version == UINT32_MAX; }247_FORCE_INLINE_ bool is_valid() const { return !is_null(); }248};249250ToolchainProperties compiler_props;251252void _initialize_toolchain_properties();253254private:255const MetalDeviceProfile *device_profile = nullptr;256bool export_mode = false;257MinOsVersion min_os_version;258259Vector<UniformData> mtl_reflection_binding_set_uniforms_data; // compliment to reflection_binding_set_uniforms_data260Vector<SpecializationData> mtl_reflection_specialization_data; // compliment to reflection_specialization_data261262Error compile_metal_source(const char *p_source, const StageData &p_stage_data, Vector<uint8_t> &r_binary_data);263264public:265static constexpr uint32_t FORMAT_VERSION = 1;266267void set_export_mode(bool p_export_mode) { export_mode = p_export_mode; }268void set_device_profile(const MetalDeviceProfile *p_device_profile) { device_profile = p_device_profile; }269void set_min_os_version(const MinOsVersion p_min_os_version) { min_os_version = p_min_os_version; }270271struct MetalShaderReflection {272Vector<Vector<UniformData>> uniform_sets;273Vector<SpecializationData> specialization_constants;274};275276MetalShaderReflection get_metal_shader_reflection() const;277278protected:279virtual uint32_t _from_bytes_reflection_extra_data(const uint8_t *p_bytes) override;280virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) override;281virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;282virtual uint32_t _from_bytes_reflection_specialization_extra_data_start(const uint8_t *p_bytes) override;283virtual uint32_t _from_bytes_reflection_specialization_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;284virtual uint32_t _from_bytes_shader_extra_data_start(const uint8_t *p_bytes) override;285virtual uint32_t _from_bytes_shader_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;286287virtual uint32_t _to_bytes_reflection_extra_data(uint8_t *p_bytes) const override;288virtual uint32_t _to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;289virtual uint32_t _to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;290virtual uint32_t _to_bytes_shader_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;291292virtual uint32_t _format() const override;293virtual uint32_t _format_version() const override;294virtual bool _set_code_from_spirv(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv) override;295};296297class RenderingShaderContainerFormatMetal : public RenderingShaderContainerFormat {298bool export_mode = false;299MinOsVersion min_os_version;300301const MetalDeviceProfile *device_profile = nullptr;302303public:304virtual Ref<RenderingShaderContainer> create_container() const override;305virtual ShaderLanguageVersion get_shader_language_version() const override;306virtual ShaderSpirvVersion get_shader_spirv_version() const override;307RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export = false, const MinOsVersion p_min_os_version = MinOsVersion());308virtual ~RenderingShaderContainerFormatMetal() = default;309};310311312