Add shader baker to project exporter.
Metal Support contributed by Migeran (https://migeran.com) and Stuart Carnie. Co-authored-by: Stuart Carnie <stuart.carnie@gmail.com> Co-authored-by: Gergely Kis <gergely.kis@migeran.com>
This commit is contained in:
@ -70,7 +70,8 @@ typedef NS_OPTIONS(NSUInteger, SampleCount) {
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
|
||||
uint32_t mslVersion = 0;
|
||||
uint32_t mslVersionMajor = 0;
|
||||
uint32_t mslVersionMinor = 0;
|
||||
MTLGPUFamily highestFamily = MTLGPUFamilyApple4;
|
||||
bool supportsBCTextureCompression = false;
|
||||
bool supportsDepth24Stencil8 = false;
|
||||
|
||||
@ -137,51 +137,8 @@ void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
|
||||
|
||||
MTLCompileOptions *opts = [MTLCompileOptions new];
|
||||
features.mslVersionEnum = opts.languageVersion; // By default, Metal uses the most recent language version.
|
||||
|
||||
#define setMSLVersion(m_maj, m_min) \
|
||||
features.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(m_maj, m_min)
|
||||
|
||||
switch (features.mslVersionEnum) {
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || __TV_OS_VERSION_MAX_ALLOWED >= 180000 || __VISION_OS_VERSION_MAX_ALLOWED >= 20000
|
||||
case MTLLanguageVersion3_2:
|
||||
setMSLVersion(3, 2);
|
||||
break;
|
||||
#endif
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 || __TV_OS_VERSION_MAX_ALLOWED >= 170000
|
||||
case MTLLanguageVersion3_1:
|
||||
setMSLVersion(3, 1);
|
||||
break;
|
||||
#endif
|
||||
case MTLLanguageVersion3_0:
|
||||
setMSLVersion(3, 0);
|
||||
break;
|
||||
case MTLLanguageVersion2_4:
|
||||
setMSLVersion(2, 4);
|
||||
break;
|
||||
case MTLLanguageVersion2_3:
|
||||
setMSLVersion(2, 3);
|
||||
break;
|
||||
case MTLLanguageVersion2_2:
|
||||
setMSLVersion(2, 2);
|
||||
break;
|
||||
case MTLLanguageVersion2_1:
|
||||
setMSLVersion(2, 1);
|
||||
break;
|
||||
case MTLLanguageVersion2_0:
|
||||
setMSLVersion(2, 0);
|
||||
break;
|
||||
case MTLLanguageVersion1_2:
|
||||
setMSLVersion(1, 2);
|
||||
break;
|
||||
case MTLLanguageVersion1_1:
|
||||
setMSLVersion(1, 1);
|
||||
break;
|
||||
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST && !TARGET_OS_VISION
|
||||
case MTLLanguageVersion1_0:
|
||||
setMSLVersion(1, 0);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
features.mslVersionMajor = (opts.languageVersion >> 0x10) & 0xff;
|
||||
features.mslVersionMinor = (opts.languageVersion >> 0x00) & 0xff;
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
#import "metal_device_properties.h"
|
||||
#import "metal_utils.h"
|
||||
#import "pixel_formats.h"
|
||||
#import "sha256_digest.h"
|
||||
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
@ -82,9 +83,6 @@ MTL_CLASS(Texture)
|
||||
|
||||
} //namespace MTL
|
||||
|
||||
/// Metal buffer index for the view mask when rendering multi-view.
|
||||
const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
|
||||
|
||||
enum ShaderStageUsage : uint32_t {
|
||||
None = 0,
|
||||
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
|
||||
@ -574,34 +572,6 @@ struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) BindingInfo {
|
||||
desc.arrayLength = arrayLength;
|
||||
return desc;
|
||||
}
|
||||
|
||||
size_t serialize_size() const {
|
||||
return sizeof(uint32_t) * 8 /* 8 uint32_t fields */;
|
||||
}
|
||||
|
||||
template <typename W>
|
||||
void serialize(W &p_writer) const {
|
||||
p_writer.write((uint32_t)dataType);
|
||||
p_writer.write(index);
|
||||
p_writer.write((uint32_t)access);
|
||||
p_writer.write((uint32_t)usage);
|
||||
p_writer.write((uint32_t)textureType);
|
||||
p_writer.write(imageFormat);
|
||||
p_writer.write(arrayLength);
|
||||
p_writer.write(isMultisampled);
|
||||
}
|
||||
|
||||
template <typename R>
|
||||
void deserialize(R &p_reader) {
|
||||
p_reader.read((uint32_t &)dataType);
|
||||
p_reader.read(index);
|
||||
p_reader.read((uint32_t &)access);
|
||||
p_reader.read((uint32_t &)usage);
|
||||
p_reader.read((uint32_t &)textureType);
|
||||
p_reader.read((uint32_t &)imageFormat);
|
||||
p_reader.read(arrayLength);
|
||||
p_reader.read(isMultisampled);
|
||||
}
|
||||
};
|
||||
|
||||
using RDC = RenderingDeviceCommons;
|
||||
@ -635,39 +605,29 @@ enum class ShaderLoadStrategy {
|
||||
/// A Metal shader library.
|
||||
@interface MDLibrary : NSObject {
|
||||
ShaderCacheEntry *_entry;
|
||||
NSString *_original_source;
|
||||
};
|
||||
- (id<MTLLibrary>)library;
|
||||
- (NSError *)error;
|
||||
- (void)setLabel:(NSString *)label;
|
||||
#ifdef DEV_ENABLED
|
||||
- (NSString *)originalSource;
|
||||
#endif
|
||||
|
||||
+ (instancetype)newLibraryWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
device:(id<MTLDevice>)device
|
||||
source:(NSString *)source
|
||||
options:(MTLCompileOptions *)options
|
||||
strategy:(ShaderLoadStrategy)strategy;
|
||||
|
||||
+ (instancetype)newLibraryWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
device:(id<MTLDevice>)device
|
||||
#ifdef DEV_ENABLED
|
||||
source:(NSString *)source
|
||||
#endif
|
||||
data:(dispatch_data_t)data;
|
||||
@end
|
||||
|
||||
struct SHA256Digest {
|
||||
unsigned char data[CC_SHA256_DIGEST_LENGTH];
|
||||
|
||||
uint32_t hash() const {
|
||||
uint32_t c = crc32(0, data, CC_SHA256_DIGEST_LENGTH);
|
||||
return c;
|
||||
}
|
||||
|
||||
SHA256Digest() {
|
||||
bzero(data, CC_SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
SHA256Digest(const char *p_data, size_t p_length) {
|
||||
CC_SHA256(p_data, (CC_LONG)p_length, data);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t short_sha() const {
|
||||
return __builtin_bswap32(*(uint32_t *)&data[0]);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<SHA256Digest> {
|
||||
static bool compare(const SHA256Digest &p_lhs, const SHA256Digest &p_rhs) {
|
||||
@ -717,9 +677,6 @@ public:
|
||||
MTLSize local = {};
|
||||
|
||||
MDLibrary *kernel;
|
||||
#if DEV_ENABLED
|
||||
CharString kernel_source;
|
||||
#endif
|
||||
|
||||
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
|
||||
|
||||
@ -742,10 +699,6 @@ public:
|
||||
|
||||
MDLibrary *vert;
|
||||
MDLibrary *frag;
|
||||
#if DEV_ENABLED
|
||||
CharString vert_source;
|
||||
CharString frag_source;
|
||||
#endif
|
||||
|
||||
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
#import "metal_utils.h"
|
||||
#import "pixel_formats.h"
|
||||
#import "rendering_device_driver_metal.h"
|
||||
#import "rendering_shader_container_metal.h"
|
||||
|
||||
#import <os/signpost.h>
|
||||
|
||||
@ -1941,7 +1942,11 @@ void ShaderCacheEntry::notify_free() const {
|
||||
}
|
||||
|
||||
@interface MDLibrary ()
|
||||
- (instancetype)initWithCacheEntry:(ShaderCacheEntry *)entry;
|
||||
- (instancetype)initWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
#ifdef DEV_ENABLED
|
||||
source:(NSString *)source;
|
||||
#endif
|
||||
;
|
||||
@end
|
||||
|
||||
/// Loads the MTLLibrary when the library is first accessed.
|
||||
@ -1975,6 +1980,18 @@ void ShaderCacheEntry::notify_free() const {
|
||||
options:(MTLCompileOptions *)options;
|
||||
@end
|
||||
|
||||
@interface MDBinaryLibrary : MDLibrary {
|
||||
id<MTLLibrary> _library;
|
||||
NSError *_error;
|
||||
}
|
||||
- (instancetype)initWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
device:(id<MTLDevice>)device
|
||||
#ifdef DEV_ENABLED
|
||||
source:(NSString *)source
|
||||
#endif
|
||||
data:(dispatch_data_t)data;
|
||||
@end
|
||||
|
||||
@implementation MDLibrary
|
||||
|
||||
+ (instancetype)newLibraryWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
@ -1992,6 +2009,26 @@ void ShaderCacheEntry::notify_free() const {
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)newLibraryWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
device:(id<MTLDevice>)device
|
||||
#ifdef DEV_ENABLED
|
||||
source:(NSString *)source
|
||||
#endif
|
||||
data:(dispatch_data_t)data {
|
||||
return [[MDBinaryLibrary alloc] initWithCacheEntry:entry
|
||||
device:device
|
||||
#ifdef DEV_ENABLED
|
||||
source:source
|
||||
#endif
|
||||
data:data];
|
||||
}
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
- (NSString *)originalSource {
|
||||
return _original_source;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id<MTLLibrary>)library {
|
||||
CRASH_NOW_MSG("Not implemented");
|
||||
return nil;
|
||||
@ -2005,10 +2042,17 @@ void ShaderCacheEntry::notify_free() const {
|
||||
- (void)setLabel:(NSString *)label {
|
||||
}
|
||||
|
||||
- (instancetype)initWithCacheEntry:(ShaderCacheEntry *)entry {
|
||||
- (instancetype)initWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
#ifdef DEV_ENABLED
|
||||
source:(NSString *)source
|
||||
#endif
|
||||
{
|
||||
self = [super init];
|
||||
_entry = entry;
|
||||
_entry->library = self;
|
||||
#ifdef DEV_ENABLED
|
||||
_original_source = source;
|
||||
#endif
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -2024,7 +2068,11 @@ void ShaderCacheEntry::notify_free() const {
|
||||
device:(id<MTLDevice>)device
|
||||
source:(NSString *)source
|
||||
options:(MTLCompileOptions *)options {
|
||||
self = [super initWithCacheEntry:entry];
|
||||
self = [super initWithCacheEntry:entry
|
||||
#ifdef DEV_ENABLED
|
||||
source:source
|
||||
#endif
|
||||
];
|
||||
_complete = false;
|
||||
_ready = false;
|
||||
|
||||
@ -2076,7 +2124,11 @@ void ShaderCacheEntry::notify_free() const {
|
||||
device:(id<MTLDevice>)device
|
||||
source:(NSString *)source
|
||||
options:(MTLCompileOptions *)options {
|
||||
self = [super initWithCacheEntry:entry];
|
||||
self = [super initWithCacheEntry:entry
|
||||
#ifdef DEV_ENABLED
|
||||
source:source
|
||||
#endif
|
||||
];
|
||||
_device = device;
|
||||
_source = source;
|
||||
_options = options;
|
||||
@ -2121,3 +2173,36 @@ void ShaderCacheEntry::notify_free() const {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MDBinaryLibrary
|
||||
|
||||
- (instancetype)initWithCacheEntry:(ShaderCacheEntry *)entry
|
||||
device:(id<MTLDevice>)device
|
||||
#ifdef DEV_ENABLED
|
||||
source:(NSString *)source
|
||||
#endif
|
||||
data:(dispatch_data_t)data {
|
||||
self = [super initWithCacheEntry:entry
|
||||
#ifdef DEV_ENABLED
|
||||
source:source
|
||||
#endif
|
||||
];
|
||||
NSError *error = nil;
|
||||
_library = [device newLibraryWithData:data error:&error];
|
||||
if (error != nil) {
|
||||
_error = error;
|
||||
NSString *desc = [error description];
|
||||
ERR_PRINT(vformat("Unable to load shader library: %s", desc.UTF8String));
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<MTLLibrary>)library {
|
||||
return _library;
|
||||
}
|
||||
|
||||
- (NSError *)error {
|
||||
return _error;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#pragma once
|
||||
|
||||
#import "metal_objects.h"
|
||||
#import "rendering_shader_container_metal.h"
|
||||
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
@ -57,9 +58,9 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet
|
||||
RenderingContextDriver::Device context_device;
|
||||
id<MTLDevice> device = nil;
|
||||
|
||||
uint32_t version_major = 2;
|
||||
uint32_t version_minor = 0;
|
||||
MetalDeviceProperties *device_properties = nullptr;
|
||||
MetalDeviceProfile device_profile;
|
||||
RenderingShaderContainerFormatMetal *shader_container_format = nullptr;
|
||||
PixelFormats *pixel_formats = nullptr;
|
||||
std::unique_ptr<MDResourceCache> resource_cache;
|
||||
|
||||
@ -77,7 +78,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) RenderingDeviceDriverMet
|
||||
String pipeline_cache_id;
|
||||
|
||||
Error _create_device();
|
||||
Error _check_capabilities();
|
||||
void _check_capabilities();
|
||||
|
||||
#pragma mark - Shader Cache
|
||||
|
||||
@ -241,21 +242,11 @@ private:
|
||||
friend struct ShaderBinaryData;
|
||||
friend struct PushConstantData;
|
||||
|
||||
private:
|
||||
/// Contains additional metadata about the shader.
|
||||
struct ShaderMeta {
|
||||
/// Indicates whether the shader uses multiview.
|
||||
bool has_multiview = false;
|
||||
};
|
||||
|
||||
Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection, ShaderMeta &r_shader_meta);
|
||||
|
||||
public:
|
||||
virtual String shader_get_binary_cache_key() override final;
|
||||
virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final;
|
||||
virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name, const Vector<ImmutableSampler> &p_immutable_samplers) override final;
|
||||
virtual ShaderID shader_create_from_container(const Ref<RenderingShaderContainer> &p_shader_container, const Vector<ImmutableSampler> &p_immutable_samplers) override final;
|
||||
virtual void shader_free(ShaderID p_shader) override final;
|
||||
virtual void shader_destroy_modules(ShaderID p_shader) override final;
|
||||
virtual const RenderingShaderContainerFormat &get_shader_container_format() const override final;
|
||||
|
||||
#pragma mark - Uniform Set
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
265
drivers/metal/rendering_shader_container_metal.h
Normal file
265
drivers/metal/rendering_shader_container_metal.h
Normal file
@ -0,0 +1,265 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_shader_container_metal.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "sha256_digest.h"
|
||||
|
||||
#import "servers/rendering/rendering_device_driver.h"
|
||||
#import "servers/rendering/rendering_shader_container.h"
|
||||
|
||||
constexpr uint32_t R32UI_ALIGNMENT_CONSTANT_ID = 65535;
|
||||
/// Metal buffer index for the view mask when rendering multi-view.
|
||||
const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
|
||||
|
||||
class RenderingShaderContainerFormatMetal;
|
||||
|
||||
/// @brief A minimal structure that defines a device profile for Metal.
|
||||
///
|
||||
/// This structure is used by the `RenderingShaderContainerMetal` class to
|
||||
/// determine options for compiling SPIR-V to Metal source. It currently only
|
||||
/// contains the minimum properties required to transform shaders from SPIR-V to Metal
|
||||
/// and potentially compile to a `.metallib`.
|
||||
struct MetalDeviceProfile {
|
||||
enum class Platform : uint32_t {
|
||||
macOS = 0,
|
||||
iOS = 1,
|
||||
};
|
||||
|
||||
/// @brief The GPU family.
|
||||
enum class GPU : uint32_t {
|
||||
Apple1,
|
||||
Apple2,
|
||||
Apple3,
|
||||
Apple4,
|
||||
Apple5,
|
||||
Apple6,
|
||||
Apple7,
|
||||
Apple8,
|
||||
Apple9,
|
||||
};
|
||||
|
||||
enum class ArgumentBuffersTier : uint32_t {
|
||||
Tier1 = 0,
|
||||
Tier2 = 1,
|
||||
};
|
||||
|
||||
struct Features {
|
||||
uint32_t mslVersionMajor = 0;
|
||||
uint32_t mslVersionMinor = 0;
|
||||
ArgumentBuffersTier argument_buffers_tier = ArgumentBuffersTier::Tier1;
|
||||
bool simdPermute = false;
|
||||
};
|
||||
|
||||
Platform platform = Platform::macOS;
|
||||
GPU gpu = GPU::Apple4;
|
||||
Features features;
|
||||
|
||||
static const MetalDeviceProfile *get_profile(Platform p_platform, GPU p_gpu);
|
||||
|
||||
MetalDeviceProfile() = default;
|
||||
|
||||
private:
|
||||
static Mutex profiles_lock; ///< Mutex to protect access to the profiles map.
|
||||
static HashMap<uint32_t, MetalDeviceProfile> profiles;
|
||||
};
|
||||
|
||||
class RenderingShaderContainerMetal : public RenderingShaderContainer {
|
||||
GDSOFTCLASS(RenderingShaderContainerMetal, RenderingShaderContainer);
|
||||
|
||||
public:
|
||||
struct HeaderData {
|
||||
enum Flags : uint32_t {
|
||||
NONE = 0,
|
||||
NEEDS_VIEW_MASK_BUFFER = 1 << 0,
|
||||
USES_ARGUMENT_BUFFERS = 1 << 1,
|
||||
};
|
||||
|
||||
/// The base profile that was used to generate this shader.
|
||||
MetalDeviceProfile profile;
|
||||
|
||||
/// The Metal language version specified when compiling SPIR-V to MSL.
|
||||
/// Format is major * 10000 + minor * 100 + patch.
|
||||
uint32_t msl_version = UINT32_MAX;
|
||||
uint32_t flags = NONE;
|
||||
|
||||
/// @brief Returns `true` if the shader is compiled with multi-view support.
|
||||
bool needs_view_mask_buffer() const {
|
||||
return flags & NEEDS_VIEW_MASK_BUFFER;
|
||||
}
|
||||
|
||||
void set_needs_view_mask_buffer(bool p_value) {
|
||||
if (p_value) {
|
||||
flags |= NEEDS_VIEW_MASK_BUFFER;
|
||||
} else {
|
||||
flags &= ~NEEDS_VIEW_MASK_BUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Returns `true` if the shader was compiled with argument buffer support.
|
||||
bool uses_argument_buffers() const {
|
||||
return flags & USES_ARGUMENT_BUFFERS;
|
||||
}
|
||||
|
||||
void set_uses_argument_buffers(bool p_value) {
|
||||
if (p_value) {
|
||||
flags |= USES_ARGUMENT_BUFFERS;
|
||||
} else {
|
||||
flags &= ~USES_ARGUMENT_BUFFERS;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct StageData {
|
||||
uint32_t vertex_input_binding_mask = 0;
|
||||
uint32_t is_position_invariant = 0; ///< <c>true</c> if the position output is invariant
|
||||
uint32_t supports_fast_math = 0;
|
||||
SHA256Digest hash; ///< SHA 256 hash of the shader code
|
||||
uint32_t source_size = 0; ///< size of the source code in the returned bytes
|
||||
uint32_t library_size = 0; ///< size of the compiled library in the returned bytes, 0 if it is not compiled
|
||||
uint32_t push_constant_binding = UINT32_MAX; ///< Metal binding slot for the push constant data
|
||||
};
|
||||
|
||||
struct BindingInfoData {
|
||||
uint32_t shader_stage = UINT32_MAX; ///< The shader stage this binding is used in, or UINT32_MAX if not used.
|
||||
uint32_t data_type = 0; // MTLDataTypeNone
|
||||
uint32_t index = 0;
|
||||
uint32_t access = 0; // MTLBindingAccessReadOnly
|
||||
uint32_t usage = 0; // MTLResourceUsage (none)
|
||||
uint32_t texture_type = 2; // MTLTextureType2D
|
||||
uint32_t image_format = 0;
|
||||
uint32_t array_length = 0;
|
||||
uint32_t is_multisampled = 0;
|
||||
};
|
||||
|
||||
struct UniformData {
|
||||
/// Specifies the index into the `bindings` array for the shader stage.
|
||||
///
|
||||
/// For example, a vertex and fragment shader use slots 0 and 1 of the bindings and bindings_secondary arrays.
|
||||
static constexpr uint32_t STAGE_INDEX[RenderingDeviceCommons::SHADER_STAGE_MAX] = {
|
||||
0, // SHADER_STAGE_VERTEX
|
||||
1, // SHADER_STAGE_FRAGMENT
|
||||
0, // SHADER_STAGE_TESSELATION_CONTROL
|
||||
1, // SHADER_STAGE_TESSELATION_EVALUATION
|
||||
0, // SHADER_STAGE_COMPUTE
|
||||
};
|
||||
|
||||
/// Specifies the stages the uniform data is
|
||||
/// used by the Metal shader.
|
||||
uint32_t active_stages = 0;
|
||||
/// The primary binding information for the uniform data.
|
||||
///
|
||||
/// A maximum of two stages is expected for any given pipeline, such as a vertex and fragment, so
|
||||
/// the array size is fixed to 2.
|
||||
BindingInfoData bindings[2];
|
||||
/// The secondary binding information for the uniform data.
|
||||
///
|
||||
/// This is typically a sampler for an image-sampler uniform
|
||||
BindingInfoData bindings_secondary[2];
|
||||
|
||||
_FORCE_INLINE_ constexpr uint32_t get_index_for_stage(RenderingDeviceCommons::ShaderStage p_stage) const {
|
||||
return STAGE_INDEX[p_stage];
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ BindingInfoData &get_binding_for_stage(RenderingDeviceCommons::ShaderStage p_stage) {
|
||||
BindingInfoData &info = bindings[get_index_for_stage(p_stage)];
|
||||
DEV_ASSERT(info.shader_stage == UINT32_MAX || info.shader_stage == p_stage); // make sure this uniform isn't used in the other stage
|
||||
info.shader_stage = p_stage;
|
||||
return info;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ BindingInfoData &get_secondary_binding_for_stage(RenderingDeviceCommons::ShaderStage p_stage) {
|
||||
BindingInfoData &info = bindings_secondary[get_index_for_stage(p_stage)];
|
||||
DEV_ASSERT(info.shader_stage == UINT32_MAX || info.shader_stage == p_stage); // make sure this uniform isn't used in the other stage
|
||||
info.shader_stage = p_stage;
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
struct SpecializationData {
|
||||
uint32_t used_stages = 0;
|
||||
};
|
||||
|
||||
HeaderData mtl_reflection_data; // compliment to reflection_data
|
||||
Vector<StageData> mtl_shaders; // compliment to shaders
|
||||
|
||||
private:
|
||||
const MetalDeviceProfile *device_profile = nullptr;
|
||||
bool export_mode = false;
|
||||
|
||||
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
|
||||
|
||||
Error compile_metal_source(const char *p_source, const StageData &p_stage_data, Vector<uint8_t> &r_binary_data);
|
||||
|
||||
public:
|
||||
static constexpr uint32_t FORMAT_VERSION = 1;
|
||||
|
||||
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; }
|
||||
|
||||
struct MetalShaderReflection {
|
||||
Vector<Vector<UniformData>> uniform_sets;
|
||||
Vector<SpecializationData> specialization_constants;
|
||||
};
|
||||
|
||||
MetalShaderReflection get_metal_shader_reflection() const;
|
||||
|
||||
protected:
|
||||
virtual uint32_t _from_bytes_reflection_extra_data(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
|
||||
virtual uint32_t _from_bytes_reflection_specialization_extra_data_start(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_reflection_specialization_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
|
||||
virtual uint32_t _from_bytes_shader_extra_data_start(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_shader_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
|
||||
|
||||
virtual uint32_t _to_bytes_reflection_extra_data(uint8_t *p_bytes) const override;
|
||||
virtual uint32_t _to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
|
||||
virtual uint32_t _to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
|
||||
virtual uint32_t _to_bytes_shader_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
|
||||
|
||||
virtual uint32_t _format() const override;
|
||||
virtual uint32_t _format_version() const override;
|
||||
virtual bool _set_code_from_spirv(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv) override;
|
||||
};
|
||||
|
||||
class RenderingShaderContainerFormatMetal : public RenderingShaderContainerFormat {
|
||||
bool export_mode = false;
|
||||
|
||||
const MetalDeviceProfile *device_profile = nullptr;
|
||||
|
||||
public:
|
||||
virtual Ref<RenderingShaderContainer> create_container() const override;
|
||||
virtual ShaderLanguageVersion get_shader_language_version() const override;
|
||||
virtual ShaderSpirvVersion get_shader_spirv_version() const override;
|
||||
RenderingShaderContainerFormatMetal(const MetalDeviceProfile *p_device_profile, bool p_export = false);
|
||||
virtual ~RenderingShaderContainerFormatMetal() = default;
|
||||
};
|
||||
699
drivers/metal/rendering_shader_container_metal.mm
Normal file
699
drivers/metal/rendering_shader_container_metal.mm
Normal file
@ -0,0 +1,699 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_shader_container_metal.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "rendering_shader_container_metal.h"
|
||||
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#import "core/io/marshalls.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <spirv.hpp>
|
||||
#import <spirv_msl.hpp>
|
||||
#import <spirv_parser.hpp>
|
||||
|
||||
Mutex MetalDeviceProfile::profiles_lock;
|
||||
HashMap<uint32_t, MetalDeviceProfile> MetalDeviceProfile::profiles;
|
||||
|
||||
const MetalDeviceProfile *MetalDeviceProfile::get_profile(MetalDeviceProfile::Platform p_platform, MetalDeviceProfile::GPU p_gpu) {
|
||||
DEV_ASSERT(p_platform == Platform::macOS || p_platform == Platform::iOS);
|
||||
|
||||
MutexLock lock(profiles_lock);
|
||||
|
||||
uint32_t key = (uint32_t)p_platform << 16 | (uint32_t)p_gpu;
|
||||
if (MetalDeviceProfile *profile = profiles.getptr(key)) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
MetalDeviceProfile res;
|
||||
res.platform = p_platform;
|
||||
res.gpu = p_gpu;
|
||||
if (p_platform == Platform::macOS) {
|
||||
res.features.mslVersionMajor = 3;
|
||||
res.features.mslVersionMinor = 2;
|
||||
res.features.argument_buffers_tier = ArgumentBuffersTier::Tier2;
|
||||
res.features.simdPermute = true;
|
||||
} else if (p_platform == Platform::iOS) {
|
||||
switch (p_gpu) {
|
||||
case GPU::Apple1:
|
||||
case GPU::Apple2:
|
||||
case GPU::Apple3:
|
||||
case GPU::Apple4:
|
||||
case GPU::Apple5: {
|
||||
res.features.simdPermute = false;
|
||||
res.features.argument_buffers_tier = ArgumentBuffersTier::Tier1;
|
||||
} break;
|
||||
case GPU::Apple6:
|
||||
case GPU::Apple7:
|
||||
case GPU::Apple8:
|
||||
case GPU::Apple9: {
|
||||
res.features.argument_buffers_tier = ArgumentBuffersTier::Tier2;
|
||||
res.features.simdPermute = true;
|
||||
} break;
|
||||
}
|
||||
res.features.mslVersionMajor = 3;
|
||||
res.features.mslVersionMinor = 2;
|
||||
}
|
||||
|
||||
return &profiles.insert(key, res)->value;
|
||||
}
|
||||
|
||||
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());
|
||||
if (name.contains_char(':')) {
|
||||
name = name.replace_char(':', '_');
|
||||
}
|
||||
Error r_error;
|
||||
Ref<FileAccess> source_file = FileAccess::create_temp(FileAccess::ModeFlags::READ_WRITE,
|
||||
name + "_" + itos(p_stage_data.hash.short_sha()),
|
||||
"metal", false, &r_error);
|
||||
ERR_FAIL_COND_V_MSG(r_error != OK, r_error, "Unable to create temporary source file.");
|
||||
if (!source_file->store_buffer((const uint8_t *)p_source, strlen(p_source))) {
|
||||
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Unable to write temporary source file");
|
||||
}
|
||||
source_file->flush();
|
||||
Ref<FileAccess> result_file = FileAccess::create_temp(FileAccess::ModeFlags::READ_WRITE,
|
||||
name + "_" + itos(p_stage_data.hash.short_sha()),
|
||||
"metallib", false, &r_error);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(r_error != OK, r_error, "Unable to create temporary target file");
|
||||
|
||||
String sdk;
|
||||
switch (device_profile->platform) {
|
||||
case MetalDeviceProfile::Platform::macOS:
|
||||
sdk = "macosx";
|
||||
break;
|
||||
case MetalDeviceProfile::Platform::iOS:
|
||||
sdk = "iphoneos";
|
||||
break;
|
||||
}
|
||||
|
||||
// Build the metallib binary.
|
||||
{
|
||||
List<String> args{ "-sdk", sdk, "metal", "-O3" };
|
||||
if (p_stage_data.is_position_invariant) {
|
||||
args.push_back("-fpreserve-invariance");
|
||||
}
|
||||
args.push_back("-fmetal-math-mode=fast");
|
||||
args.push_back(source_file->get_path_absolute());
|
||||
args.push_back("-o");
|
||||
args.push_back(result_file->get_path_absolute());
|
||||
String r_pipe;
|
||||
int exit_code;
|
||||
Error err = OS::get_singleton()->execute("/usr/bin/xcrun", args, &r_pipe, &exit_code, true);
|
||||
if (!r_pipe.is_empty()) {
|
||||
print_line(r_pipe);
|
||||
}
|
||||
if (err != OK) {
|
||||
ERR_PRINT(vformat("Metal compiler returned error code: %d", err));
|
||||
}
|
||||
|
||||
if (exit_code != 0) {
|
||||
ERR_PRINT(vformat("Metal compiler exited with error code: %d", exit_code));
|
||||
}
|
||||
int len = result_file->get_length();
|
||||
ERR_FAIL_COND_V_MSG(len == 0, ERR_CANT_CREATE, "Metal compiler created empty library");
|
||||
}
|
||||
|
||||
// Strip the source from the binary.
|
||||
{
|
||||
List<String> args{ "-sdk", sdk, "metal-dsymutil", "--remove-source", result_file->get_path_absolute() };
|
||||
String r_pipe;
|
||||
int exit_code;
|
||||
Error err = OS::get_singleton()->execute("/usr/bin/xcrun", args, &r_pipe, &exit_code, true);
|
||||
if (!r_pipe.is_empty()) {
|
||||
print_line(r_pipe);
|
||||
}
|
||||
if (err != OK) {
|
||||
ERR_PRINT(vformat("metal-dsymutil tool returned error code: %d", err));
|
||||
}
|
||||
|
||||
if (exit_code != 0) {
|
||||
ERR_PRINT(vformat("metal-dsymutil Compiler exited with error code: %d", exit_code));
|
||||
}
|
||||
int len = result_file->get_length();
|
||||
ERR_FAIL_COND_V_MSG(len == 0, ERR_CANT_CREATE, "metal-dsymutil tool created empty library");
|
||||
}
|
||||
|
||||
r_binary_data = result_file->get_buffer(result_file->get_length());
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
|
||||
bool RenderingShaderContainerMetal::_set_code_from_spirv(const Vector<RenderingDeviceCommons::ShaderStageSPIRVData> &p_spirv) {
|
||||
using namespace spirv_cross;
|
||||
using spirv_cross::CompilerMSL;
|
||||
using spirv_cross::Resource;
|
||||
|
||||
// initialize Metal-specific reflection data
|
||||
shaders.resize(p_spirv.size());
|
||||
mtl_shaders.resize(p_spirv.size());
|
||||
mtl_reflection_binding_set_uniforms_data.resize(reflection_binding_set_uniforms_data.size());
|
||||
mtl_reflection_specialization_data.resize(reflection_specialization_data.size());
|
||||
|
||||
mtl_reflection_data.set_needs_view_mask_buffer(reflection_data.has_multiview);
|
||||
|
||||
// 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.
|
||||
LocalVector<uint32_t> set_indexes;
|
||||
uint32_t set_indexes_size = reflection_binding_set_uniforms_count.size() + 1;
|
||||
{
|
||||
// calculate the starting offsets of each descriptor set in the binding set uniforms data
|
||||
uint32_t size = reflection_binding_set_uniforms_count.size();
|
||||
set_indexes.resize(set_indexes_size);
|
||||
uint32_t offset = 0;
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
set_indexes[i] = offset;
|
||||
offset += reflection_binding_set_uniforms_count.get(i);
|
||||
}
|
||||
set_indexes[set_indexes_size - 1] = offset;
|
||||
}
|
||||
CompilerMSL::Options msl_options{};
|
||||
msl_options.set_msl_version(device_profile->features.mslVersionMajor, device_profile->features.mslVersionMinor);
|
||||
mtl_reflection_data.msl_version = msl_options.msl_version;
|
||||
msl_options.platform = device_profile->platform == MetalDeviceProfile::Platform::macOS ? CompilerMSL::Options::macOS : CompilerMSL::Options::iOS;
|
||||
|
||||
if (device_profile->platform == MetalDeviceProfile::Platform::iOS) {
|
||||
msl_options.ios_use_simdgroup_functions = device_profile->features.simdPermute;
|
||||
msl_options.ios_support_base_vertex_instance = true;
|
||||
}
|
||||
|
||||
bool disable_argument_buffers = false;
|
||||
if (String v = OS::get_singleton()->get_environment(U"GODOT_DISABLE_ARGUMENT_BUFFERS"); v == U"1") {
|
||||
disable_argument_buffers = true;
|
||||
}
|
||||
|
||||
if (device_profile->features.argument_buffers_tier >= MetalDeviceProfile::ArgumentBuffersTier::Tier2 && !disable_argument_buffers) {
|
||||
msl_options.argument_buffers_tier = CompilerMSL::Options::ArgumentBuffersTier::Tier2;
|
||||
msl_options.argument_buffers = true;
|
||||
mtl_reflection_data.set_uses_argument_buffers(true);
|
||||
} else {
|
||||
msl_options.argument_buffers_tier = CompilerMSL::Options::ArgumentBuffersTier::Tier1;
|
||||
// Tier 1 argument buffers don't support writable textures, so we disable them completely.
|
||||
msl_options.argument_buffers = false;
|
||||
mtl_reflection_data.set_uses_argument_buffers(false);
|
||||
}
|
||||
msl_options.force_active_argument_buffer_resources = true;
|
||||
// We can't use this, as we have to add the descriptor sets via compiler.add_msl_resource_binding.
|
||||
// msl_options.pad_argument_buffer_resources = true;
|
||||
msl_options.texture_buffer_native = true; // Enable texture buffer support.
|
||||
msl_options.use_framebuffer_fetch_subpasses = false;
|
||||
msl_options.pad_fragment_output_components = true;
|
||||
msl_options.r32ui_alignment_constant_id = R32UI_ALIGNMENT_CONSTANT_ID;
|
||||
msl_options.agx_manual_cube_grad_fixup = true;
|
||||
if (reflection_data.has_multiview) {
|
||||
msl_options.multiview = true;
|
||||
msl_options.multiview_layered_rendering = true;
|
||||
msl_options.view_mask_buffer_index = VIEW_MASK_BUFFER_INDEX;
|
||||
}
|
||||
|
||||
CompilerGLSL::Options options{};
|
||||
options.vertex.flip_vert_y = true;
|
||||
#if DEV_ENABLED
|
||||
options.emit_line_directives = true;
|
||||
#endif
|
||||
|
||||
for (uint32_t i = 0; i < p_spirv.size(); i++) {
|
||||
StageData &stage_data = mtl_shaders.write[i];
|
||||
RD::ShaderStageSPIRVData const &v = p_spirv[i];
|
||||
RD::ShaderStage stage = v.shader_stage;
|
||||
char const *stage_name = RD::SHADER_STAGE_NAMES[stage];
|
||||
uint32_t const *const ir = reinterpret_cast<uint32_t const *const>(v.spirv.ptr());
|
||||
size_t word_count = v.spirv.size() / sizeof(uint32_t);
|
||||
Parser parser(ir, word_count);
|
||||
try {
|
||||
parser.parse();
|
||||
} catch (CompilerError &e) {
|
||||
ERR_FAIL_V_MSG(false, "Failed to parse IR at stage " + String(RD::SHADER_STAGE_NAMES[stage]) + ": " + e.what());
|
||||
}
|
||||
|
||||
CompilerMSL compiler(std::move(parser.get_parsed_ir()));
|
||||
compiler.set_msl_options(msl_options);
|
||||
compiler.set_common_options(options);
|
||||
|
||||
std::unordered_set<VariableID> active = compiler.get_active_interface_variables();
|
||||
ShaderResources resources = compiler.get_shader_resources();
|
||||
|
||||
std::string source;
|
||||
try {
|
||||
source = compiler.compile();
|
||||
} catch (CompilerError &e) {
|
||||
ERR_FAIL_V_MSG(false, "Failed to compile stage " + String(RD::SHADER_STAGE_NAMES[stage]) + ": " + e.what());
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(compiler.get_entry_points_and_stages().size() != 1, false, "Expected a single entry point and stage.");
|
||||
|
||||
SmallVector<EntryPoint> entry_pts_stages = compiler.get_entry_points_and_stages();
|
||||
EntryPoint &entry_point_stage = entry_pts_stages.front();
|
||||
SPIREntryPoint &entry_point = compiler.get_entry_point(entry_point_stage.name, entry_point_stage.execution_model);
|
||||
|
||||
// Process specialization constants.
|
||||
if (!compiler.get_specialization_constants().empty()) {
|
||||
uint32_t size = reflection_specialization_data.size();
|
||||
for (SpecializationConstant const &constant : compiler.get_specialization_constants()) {
|
||||
uint32_t j = 0;
|
||||
while (j < size) {
|
||||
const ReflectionSpecializationData &res = reflection_specialization_data.ptr()[j];
|
||||
if (res.constant_id == constant.constant_id) {
|
||||
mtl_reflection_specialization_data.ptrw()[j].used_stages |= 1 << stage;
|
||||
// emulate labeled for loop and continue
|
||||
goto outer_continue;
|
||||
}
|
||||
++j;
|
||||
}
|
||||
if (j == size) {
|
||||
WARN_PRINT(String(stage_name) + ": unable to find constant_id: " + itos(constant.constant_id));
|
||||
}
|
||||
outer_continue:;
|
||||
}
|
||||
}
|
||||
|
||||
// Process bindings.
|
||||
uint32_t uniform_sets_size = reflection_binding_set_uniforms_count.size();
|
||||
using BT = SPIRType::BaseType;
|
||||
|
||||
// Always clearer than a boolean.
|
||||
enum class Writable {
|
||||
No,
|
||||
Maybe,
|
||||
};
|
||||
|
||||
// Returns a std::optional containing the value of the
|
||||
// decoration, if it exists.
|
||||
auto get_decoration = [&compiler](spirv_cross::ID id, spv::Decoration decoration) {
|
||||
uint32_t res = -1;
|
||||
if (compiler.has_decoration(id, decoration)) {
|
||||
res = compiler.get_decoration(id, decoration);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
auto descriptor_bindings = [&compiler, &active, this, &set_indexes, uniform_sets_size, stage, &get_decoration](SmallVector<Resource> &p_resources, Writable p_writable) {
|
||||
for (Resource const &res : p_resources) {
|
||||
uint32_t dset = get_decoration(res.id, spv::DecorationDescriptorSet);
|
||||
uint32_t dbin = get_decoration(res.id, spv::DecorationBinding);
|
||||
UniformData *found = nullptr;
|
||||
if (dset != (uint32_t)-1 && dbin != (uint32_t)-1 && dset < uniform_sets_size) {
|
||||
uint32_t begin = set_indexes[dset];
|
||||
uint32_t end = set_indexes[dset + 1];
|
||||
for (uint32_t j = begin; j < end; j++) {
|
||||
const ReflectionBindingData &ref_bind = reflection_binding_set_uniforms_data[j];
|
||||
if (dbin == ref_bind.binding) {
|
||||
found = &mtl_reflection_binding_set_uniforms_data.write[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V_MSG(found, ERR_CANT_CREATE, "UniformData not found");
|
||||
|
||||
bool is_active = active.find(res.id) != active.end();
|
||||
if (is_active) {
|
||||
found->active_stages |= 1 << stage;
|
||||
}
|
||||
|
||||
BindingInfoData &primary = found->get_binding_for_stage(stage);
|
||||
|
||||
SPIRType const &a_type = compiler.get_type(res.type_id);
|
||||
BT basetype = a_type.basetype;
|
||||
|
||||
switch (basetype) {
|
||||
case BT::Struct: {
|
||||
primary.data_type = MTLDataTypePointer;
|
||||
} break;
|
||||
|
||||
case BT::Image:
|
||||
case BT::SampledImage: {
|
||||
primary.data_type = MTLDataTypeTexture;
|
||||
} break;
|
||||
|
||||
case BT::Sampler: {
|
||||
primary.data_type = MTLDataTypeSampler;
|
||||
primary.array_length = 1;
|
||||
for (uint32_t const &a : a_type.array) {
|
||||
primary.array_length *= a;
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Unexpected BaseType");
|
||||
} break;
|
||||
}
|
||||
|
||||
// Find array length of image.
|
||||
if (basetype == BT::Image || basetype == BT::SampledImage) {
|
||||
primary.array_length = 1;
|
||||
for (uint32_t const &a : a_type.array) {
|
||||
primary.array_length *= a;
|
||||
}
|
||||
primary.is_multisampled = a_type.image.ms;
|
||||
|
||||
SPIRType::ImageType const &image = a_type.image;
|
||||
primary.image_format = image.format;
|
||||
|
||||
switch (image.dim) {
|
||||
case spv::Dim1D: {
|
||||
if (image.arrayed) {
|
||||
primary.texture_type = MTLTextureType1DArray;
|
||||
} else {
|
||||
primary.texture_type = MTLTextureType1D;
|
||||
}
|
||||
} break;
|
||||
case spv::DimSubpassData: {
|
||||
[[fallthrough]];
|
||||
}
|
||||
case spv::Dim2D: {
|
||||
if (image.arrayed && image.ms) {
|
||||
primary.texture_type = MTLTextureType2DMultisampleArray;
|
||||
} else if (image.arrayed) {
|
||||
primary.texture_type = MTLTextureType2DArray;
|
||||
} else if (image.ms) {
|
||||
primary.texture_type = MTLTextureType2DMultisample;
|
||||
} else {
|
||||
primary.texture_type = MTLTextureType2D;
|
||||
}
|
||||
} break;
|
||||
case spv::Dim3D: {
|
||||
primary.texture_type = MTLTextureType3D;
|
||||
} break;
|
||||
case spv::DimCube: {
|
||||
if (image.arrayed) {
|
||||
primary.texture_type = MTLTextureTypeCube;
|
||||
}
|
||||
} break;
|
||||
case spv::DimRect: {
|
||||
} break;
|
||||
case spv::DimBuffer: {
|
||||
// VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
|
||||
primary.texture_type = MTLTextureTypeTextureBuffer;
|
||||
} break;
|
||||
case spv::DimMax: {
|
||||
// Add all enumerations to silence the compiler warning
|
||||
// and generate future warnings, should a new one be added.
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update writable.
|
||||
if (p_writable == Writable::Maybe) {
|
||||
if (basetype == BT::Struct) {
|
||||
Bitset flags = compiler.get_buffer_block_flags(res.id);
|
||||
if (!flags.get(spv::DecorationNonWritable)) {
|
||||
if (flags.get(spv::DecorationNonReadable)) {
|
||||
primary.access = MTLBindingAccessWriteOnly;
|
||||
} else {
|
||||
primary.access = MTLBindingAccessReadWrite;
|
||||
}
|
||||
}
|
||||
} else if (basetype == BT::Image) {
|
||||
switch (a_type.image.access) {
|
||||
case spv::AccessQualifierWriteOnly:
|
||||
primary.access = MTLBindingAccessWriteOnly;
|
||||
break;
|
||||
case spv::AccessQualifierReadWrite:
|
||||
primary.access = MTLBindingAccessReadWrite;
|
||||
break;
|
||||
case spv::AccessQualifierReadOnly:
|
||||
break;
|
||||
case spv::AccessQualifierMax:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
if (!compiler.has_decoration(res.id, spv::DecorationNonWritable)) {
|
||||
if (compiler.has_decoration(res.id, spv::DecorationNonReadable)) {
|
||||
primary.access = MTLBindingAccessWriteOnly;
|
||||
} else {
|
||||
primary.access = MTLBindingAccessReadWrite;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (primary.access) {
|
||||
case MTLBindingAccessReadOnly:
|
||||
primary.usage = MTLResourceUsageRead;
|
||||
break;
|
||||
case MTLBindingAccessWriteOnly:
|
||||
primary.usage = MTLResourceUsageWrite;
|
||||
break;
|
||||
case MTLBindingAccessReadWrite:
|
||||
primary.usage = MTLResourceUsageRead | MTLResourceUsageWrite;
|
||||
break;
|
||||
}
|
||||
|
||||
primary.index = compiler.get_automatic_msl_resource_binding(res.id);
|
||||
|
||||
// A sampled image contains two bindings, the primary
|
||||
// is to the image, and the secondary is to the associated sampler.
|
||||
if (basetype == BT::SampledImage) {
|
||||
uint32_t binding = compiler.get_automatic_msl_resource_binding_secondary(res.id);
|
||||
if (binding != (uint32_t)-1) {
|
||||
BindingInfoData &secondary = found->get_secondary_binding_for_stage(stage);
|
||||
secondary.data_type = MTLDataTypeSampler;
|
||||
secondary.index = binding;
|
||||
secondary.access = MTLBindingAccessReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
// An image may have a secondary binding if it is used
|
||||
// for atomic operations.
|
||||
if (basetype == BT::Image) {
|
||||
uint32_t binding = compiler.get_automatic_msl_resource_binding_secondary(res.id);
|
||||
if (binding != (uint32_t)-1) {
|
||||
BindingInfoData &secondary = found->get_secondary_binding_for_stage(stage);
|
||||
secondary.data_type = MTLDataTypePointer;
|
||||
secondary.index = binding;
|
||||
secondary.access = MTLBindingAccessReadWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Error::OK;
|
||||
};
|
||||
|
||||
if (!resources.uniform_buffers.empty()) {
|
||||
Error err = descriptor_bindings(resources.uniform_buffers, Writable::No);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
if (!resources.storage_buffers.empty()) {
|
||||
Error err = descriptor_bindings(resources.storage_buffers, Writable::Maybe);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
if (!resources.storage_images.empty()) {
|
||||
Error err = descriptor_bindings(resources.storage_images, Writable::Maybe);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
if (!resources.sampled_images.empty()) {
|
||||
Error err = descriptor_bindings(resources.sampled_images, Writable::No);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
if (!resources.separate_images.empty()) {
|
||||
Error err = descriptor_bindings(resources.separate_images, Writable::No);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
if (!resources.separate_samplers.empty()) {
|
||||
Error err = descriptor_bindings(resources.separate_samplers, Writable::No);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
if (!resources.subpass_inputs.empty()) {
|
||||
Error err = descriptor_bindings(resources.subpass_inputs, Writable::No);
|
||||
ERR_FAIL_COND_V(err != OK, false);
|
||||
}
|
||||
|
||||
if (!resources.push_constant_buffers.empty()) {
|
||||
for (Resource const &res : resources.push_constant_buffers) {
|
||||
uint32_t binding = compiler.get_automatic_msl_resource_binding(res.id);
|
||||
if (binding != (uint32_t)-1) {
|
||||
stage_data.push_constant_binding = binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!resources.atomic_counters.empty(), false, "Atomic counters not supported");
|
||||
ERR_FAIL_COND_V_MSG(!resources.acceleration_structures.empty(), false, "Acceleration structures not supported");
|
||||
ERR_FAIL_COND_V_MSG(!resources.shader_record_buffers.empty(), false, "Shader record buffers not supported");
|
||||
|
||||
if (!resources.stage_inputs.empty()) {
|
||||
for (Resource const &res : resources.stage_inputs) {
|
||||
uint32_t binding = compiler.get_automatic_msl_resource_binding(res.id);
|
||||
if (binding != (uint32_t)-1) {
|
||||
stage_data.vertex_input_binding_mask |= 1 << binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage_data.is_position_invariant = compiler.is_position_invariant();
|
||||
stage_data.supports_fast_math = !entry_point.flags.get(spv::ExecutionModeSignedZeroInfNanPreserve);
|
||||
stage_data.hash = SHA256Digest(source.c_str(), source.length());
|
||||
stage_data.source_size = source.length();
|
||||
::Vector<uint8_t> binary_data;
|
||||
binary_data.resize(stage_data.source_size);
|
||||
memcpy(binary_data.ptrw(), source.c_str(), stage_data.source_size);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t binary_data_size = binary_data.size();
|
||||
Shader &shader = shaders.write[i];
|
||||
shader.shader_stage = stage;
|
||||
shader.code_decompressed_size = binary_data_size;
|
||||
shader.code_compressed_bytes.resize(binary_data_size);
|
||||
|
||||
uint32_t compressed_size = 0;
|
||||
bool compressed = compress_code(binary_data.ptr(), binary_data_size, shader.code_compressed_bytes.ptrw(), &compressed_size, &shader.code_compression_flags);
|
||||
ERR_FAIL_COND_V_MSG(!compressed, false, vformat("Failed to compress native code to native for SPIR-V #%d.", i));
|
||||
|
||||
shader.code_compressed_bytes.resize(compressed_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_to_bytes_reflection_extra_data(uint8_t *p_bytes) const {
|
||||
if (p_bytes != nullptr) {
|
||||
*(HeaderData *)p_bytes = mtl_reflection_data;
|
||||
}
|
||||
return sizeof(HeaderData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const {
|
||||
if (p_bytes != nullptr) {
|
||||
*(UniformData *)p_bytes = mtl_reflection_binding_set_uniforms_data[p_index];
|
||||
}
|
||||
return sizeof(UniformData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const {
|
||||
if (p_bytes != nullptr) {
|
||||
*(SpecializationData *)p_bytes = mtl_reflection_specialization_data[p_index];
|
||||
}
|
||||
return sizeof(SpecializationData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_to_bytes_shader_extra_data(uint8_t *p_bytes, uint32_t p_index) const {
|
||||
if (p_bytes != nullptr) {
|
||||
*(StageData *)p_bytes = mtl_shaders[p_index];
|
||||
}
|
||||
return sizeof(StageData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_extra_data(const uint8_t *p_bytes) {
|
||||
mtl_reflection_data = *(HeaderData *)p_bytes;
|
||||
return sizeof(HeaderData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) {
|
||||
mtl_reflection_binding_set_uniforms_data.resize(reflection_binding_set_uniforms_data.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) {
|
||||
mtl_reflection_binding_set_uniforms_data.ptrw()[p_index] = *(UniformData *)p_bytes;
|
||||
return sizeof(UniformData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_specialization_extra_data_start(const uint8_t *p_bytes) {
|
||||
mtl_reflection_specialization_data.resize(reflection_specialization_data.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_reflection_specialization_extra_data(const uint8_t *p_bytes, uint32_t p_index) {
|
||||
mtl_reflection_specialization_data.ptrw()[p_index] = *(SpecializationData *)p_bytes;
|
||||
return sizeof(SpecializationData);
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_shader_extra_data_start(const uint8_t *p_bytes) {
|
||||
mtl_shaders.resize(shaders.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_from_bytes_shader_extra_data(const uint8_t *p_bytes, uint32_t p_index) {
|
||||
mtl_shaders.ptrw()[p_index] = *(StageData *)p_bytes;
|
||||
return sizeof(StageData);
|
||||
}
|
||||
|
||||
RenderingShaderContainerMetal::MetalShaderReflection RenderingShaderContainerMetal::get_metal_shader_reflection() const {
|
||||
MetalShaderReflection res;
|
||||
|
||||
res.specialization_constants = mtl_reflection_specialization_data;
|
||||
uint32_t uniform_set_count = reflection_binding_set_uniforms_count.size();
|
||||
uint32_t start = 0;
|
||||
res.uniform_sets.resize(uniform_set_count);
|
||||
for (uint32_t i = 0; i < uniform_set_count; i++) {
|
||||
Vector<UniformData> &set = res.uniform_sets.ptrw()[i];
|
||||
uint32_t count = reflection_binding_set_uniforms_count.get(i);
|
||||
set.resize(count);
|
||||
memcpy(set.ptrw(), &mtl_reflection_binding_set_uniforms_data.ptr()[start], count * sizeof(UniformData));
|
||||
start += count;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_format() const {
|
||||
return 0x42424242;
|
||||
}
|
||||
|
||||
uint32_t RenderingShaderContainerMetal::_format_version() const {
|
||||
return FORMAT_VERSION;
|
||||
}
|
||||
|
||||
Ref<RenderingShaderContainer> RenderingShaderContainerFormatMetal::create_container() const {
|
||||
Ref<RenderingShaderContainerMetal> result;
|
||||
result.instantiate();
|
||||
result->set_export_mode(export_mode);
|
||||
result->set_device_profile(device_profile);
|
||||
return result;
|
||||
}
|
||||
|
||||
RenderingDeviceCommons::ShaderLanguageVersion RenderingShaderContainerFormatMetal::get_shader_language_version() const {
|
||||
return SHADER_LANGUAGE_VULKAN_VERSION_1_1;
|
||||
}
|
||||
|
||||
RenderingDeviceCommons::ShaderSpirvVersion RenderingShaderContainerFormatMetal::get_shader_spirv_version() const {
|
||||
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) {
|
||||
}
|
||||
75
drivers/metal/sha256_digest.h
Normal file
75
drivers/metal/sha256_digest.h
Normal file
@ -0,0 +1,75 @@
|
||||
/**************************************************************************/
|
||||
/* sha256_digest.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <simd/simd.h>
|
||||
#import <zlib.h>
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
struct SHA256Digest {
|
||||
unsigned char data[CC_SHA256_DIGEST_LENGTH];
|
||||
|
||||
static constexpr size_t serialized_size() { return CC_SHA256_DIGEST_LENGTH; }
|
||||
|
||||
uint32_t hash() const {
|
||||
uint32_t c = crc32(0, data, CC_SHA256_DIGEST_LENGTH);
|
||||
return c;
|
||||
}
|
||||
|
||||
SHA256Digest() {
|
||||
bzero(data, CC_SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
SHA256Digest(const char *p_hash) {
|
||||
memcpy(data, p_hash, CC_SHA256_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
SHA256Digest(const char *p_data, size_t p_length) {
|
||||
CC_SHA256(p_data, (CC_LONG)p_length, data);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t short_sha() const {
|
||||
return __builtin_bswap32(*(uint32_t *)&data[0]);
|
||||
}
|
||||
|
||||
LocalVector<uint8_t> serialize() const {
|
||||
LocalVector<uint8_t> result;
|
||||
result.resize(CC_SHA256_DIGEST_LENGTH);
|
||||
memcpy(result.ptr(), data, CC_SHA256_DIGEST_LENGTH);
|
||||
return result;
|
||||
}
|
||||
|
||||
static SHA256Digest deserialize(LocalVector<uint8_t> p_ser) {
|
||||
return SHA256Digest((const char *)p_ser.ptr());
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user