Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metal: Pixel format mapping is shared for all platforms #100851

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions drivers/metal/inflection_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**************************************************************************/
/* inflection_map.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. */
/**************************************************************************/

#ifndef INFLECTION_MAP_H
#define INFLECTION_MAP_H

#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"

/// An unordered map that splits elements between a fast-access vector of LinearCount consecutively
/// indexed elements, and a slower-access map holding sparse indexes larger than LinearCount.
///
/// \tparam KeyType is used to lookup values, and must be a type that is convertible to an unsigned integer.
/// \tparam ValueType must have an empty constructor (default or otherwise).
/// \tparam LinearCount
/// \tparam IndexType must be a type that is convertible to an unsigned integer (eg. uint8_t...uint64_t), and which is large enough to represent the number of values in this map.
template <typename KeyType, typename ValueType, size_t LinearCount, typename IndexType = uint16_t>
class InflectionMap {
public:
using value_type = ValueType;
class Iterator {
InflectionMap *map;
IndexType index;

public:
using iterator_category = std::forward_iterator_tag;
using value_type = ValueType;
using pointer = value_type *;
using reference = value_type &;

Iterator() :
map(nullptr), index(0) {}
Iterator(InflectionMap &m, const IndexType i) :
map(&m), index(i) {}

Iterator &operator=(const Iterator &it) {
map = it.map;
index = it.index;
return *this;
}

ValueType *operator->() { return &map->_values[index]; }
ValueType &operator*() { return map->_values[index]; }
operator ValueType *() { return &map->_values[index]; }

bool operator==(const Iterator &it) const { return map == it.map && index == it.index; }
bool operator!=(const Iterator &it) const { return map != it.map || index != it.index; }

Iterator &operator++() {
index++;
return *this;
}
Iterator operator++(int) {
auto t = *this;
index++;
return t;
}

bool is_valid() const { return index < map->_values.size(); }
};

const ValueType &operator[](const KeyType p_idx) const { return get_value(p_idx); }
ValueType &operator[](const KeyType p_idx) { return get_value(p_idx); }

Iterator begin() { return Iterator(*this, 0); }
Iterator end() { return Iterator(*this, _values.size()); }

bool is_empty() { return _values.is_empty(); }
size_t size() { return _values.size(); }
void reserve(const size_t p_new_cap) { _values.reserve(p_new_cap); }

protected:
static constexpr IndexType INVALID = std::numeric_limits<IndexType>::max();
typedef struct IndexValue {
IndexType value = INVALID;
} IndexValue;

// Returns a reference to the value at the index.
// If the index has not been initialized, add an empty element at
// the end of the values array, and set the index to its position.
ValueType &get_value(KeyType p_idx) {
IndexValue *valIdx = p_idx < LinearCount ? &_linearIndexes[p_idx] : _inflectionIndexes.getptr(p_idx);
if (valIdx == nullptr || valIdx->value == INVALID) {
_values.push_back({});
if (valIdx == nullptr) {
valIdx = &_inflectionIndexes.insert(p_idx, {})->value;
}
valIdx->value = _values.size() - 1;
}
return _values[valIdx->value];
}

TightLocalVector<ValueType> _values;
HashMap<KeyType, IndexValue> _inflectionIndexes;
IndexValue _linearIndexes[LinearCount];
};

#endif // INFLECTION_MAP_H
7 changes: 7 additions & 0 deletions drivers/metal/metal_device_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ typedef NS_OPTIONS(NSUInteger, SampleCount) {
struct API_AVAILABLE(macos(11.0), ios(14.0)) MetalFeatures {
uint32_t mslVersion = 0;
MTLGPUFamily highestFamily = MTLGPUFamilyApple4;
bool supportsBCTextureCompression = false;
bool supportsDepth24Stencil8 = false;
bool supports32BitFloatFiltering = false;
bool supports32BitMSAA = false;
bool supportsMac = TARGET_OS_OSX;
MTLLanguageVersion mslVersionEnum = MTLLanguageVersion1_2;
SampleCount supportedSampleCounts = SampleCount1;
long hostMemoryPageSize = 0;
Expand All @@ -84,6 +89,8 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) MetalFeatures {
bool tessellationShader = false; /**< If true, tessellation shaders are supported. */
bool imageCubeArray = false; /**< If true, image cube arrays are supported. */
MTLArgumentBuffersTier argument_buffers_tier = MTLArgumentBuffersTier1;
/// If true, argument encoders are required to encode arguments into an argument buffer.
bool needs_arg_encoders = true;
};

struct MetalLimits {
Expand Down
19 changes: 19 additions & 0 deletions drivers/metal/metal_device_properties.mm
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@
}
}

if (@available(macOS 11, iOS 16.4, tvOS 16.4, *)) {
features.supportsBCTextureCompression = p_device.supportsBCTextureCompression;
} else {
features.supportsBCTextureCompression = false;
}

#if TARGET_OS_OSX
features.supportsDepth24Stencil8 = p_device.isDepth24Stencil8PixelFormatSupported;
#endif

if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
features.supports32BitFloatFiltering = p_device.supports32BitFloatFiltering;
features.supports32BitMSAA = p_device.supports32BitMSAA;
}

features.hostMemoryPageSize = sysconf(_SC_PAGESIZE);

for (SampleCount sc = SampleCount1; sc <= SampleCount64; sc <<= 1) {
Expand All @@ -100,6 +115,10 @@
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];
features.argument_buffers_tier = p_device.argumentBuffersSupport;

if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.needs_arg_encoders = !([p_device supportsFamily:MTLGPUFamilyMetal3] && features.argument_buffers_tier == MTLArgumentBuffersTier2);
}

MTLCompileOptions *opts = [MTLCompileOptions new];
features.mslVersionEnum = opts.languageVersion; // By default, Metal uses the most recent language version.

Expand Down
88 changes: 43 additions & 45 deletions drivers/metal/pixel_formats.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

#import "inflection_map.h"
#import "metal_device_properties.h"

#import "core/templates/local_vector.h"
#import "servers/rendering/rendering_device.h"

#import <Metal/Metal.h>

static const uint32_t _mtlPixelFormatCount = 256;
static const uint32_t _mtlPixelFormatCoreCount = MTLPixelFormatX32_Stencil8 + 2; // The actual last enum value is not available on iOS.
static const uint32_t _mtlVertexFormatCount = MTLVertexFormatHalf + 1;

#pragma mark -
#pragma mark Metal format capabilities

Expand Down Expand Up @@ -182,13 +182,20 @@ enum class MTLFormatType {
Compressed, /**< A block-compressed color. */
};

typedef struct Extent2D {
struct Extent2D {
uint32_t width;
uint32_t height;
} Extent2D;
};

struct ComponentMapping {
RD::TextureSwizzle r = RD::TEXTURE_SWIZZLE_IDENTITY;
RD::TextureSwizzle g = RD::TEXTURE_SWIZZLE_IDENTITY;
RD::TextureSwizzle b = RD::TEXTURE_SWIZZLE_IDENTITY;
RD::TextureSwizzle a = RD::TEXTURE_SWIZZLE_IDENTITY;
};

/** Describes the properties of a DataFormat, including the corresponding Metal pixel and vertex format. */
typedef struct DataFormatDesc {
struct DataFormatDesc {
RD::DataFormat dataFormat;
MTLPixelFormat mtlPixelFormat;
MTLPixelFormat mtlPixelFormatSubstitute;
Expand All @@ -199,6 +206,7 @@ typedef struct DataFormatDesc {
Extent2D blockTexelSize;
uint32_t bytesPerBlock;
MTLFormatType formatType;
ComponentMapping componentMapping;
const char *name;
bool hasReportedSubstitution;

Expand All @@ -209,24 +217,31 @@ typedef struct DataFormatDesc {

inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }
} DataFormatDesc;

bool needsSwizzle() const {
return (componentMapping.r != RD::TEXTURE_SWIZZLE_IDENTITY ||
componentMapping.g != RD::TEXTURE_SWIZZLE_IDENTITY ||
componentMapping.b != RD::TEXTURE_SWIZZLE_IDENTITY ||
componentMapping.a != RD::TEXTURE_SWIZZLE_IDENTITY);
}
};

/** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */
typedef struct MTLFormatDesc {
struct MTLFormatDesc {
union {
MTLPixelFormat mtlPixelFormat;
MTLVertexFormat mtlVertexFormat;
};
RD::DataFormat dataFormat;
RD::DataFormat dataFormat = RD::DATA_FORMAT_MAX;
MTLFmtCaps mtlFmtCaps;
MTLViewClass mtlViewClass;
MTLPixelFormat mtlPixelFormatLinear;
const char *name = nullptr;

inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid) && (mtlFmtCaps != kMTLFmtCapsNone); }
} MTLFormatDesc;
};

class API_AVAILABLE(macos(11.0), ios(14.0)) PixelFormats {
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) PixelFormats {
using DataFormat = RD::DataFormat;

public:
Expand Down Expand Up @@ -353,6 +368,9 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) PixelFormats {
*/
size_t getBytesPerLayer(MTLPixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);

/** Returns whether or not the specified Godot format requires swizzling to use with Metal. */
bool needsSwizzle(DataFormat p_format);

/** Returns the Metal format capabilities supported by the specified Godot format, without substitution. */
MTLFmtCaps getCapabilities(DataFormat p_format, bool p_extended = false);

Expand All @@ -367,48 +385,28 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) PixelFormats {

#pragma mark Construction

explicit PixelFormats(id<MTLDevice> p_device);
explicit PixelFormats(id<MTLDevice> p_device, const MetalFeatures &p_feat);

protected:
id<MTLDevice> device;

DataFormatDesc &getDataFormatDesc(DataFormat p_format);
DataFormatDesc &getDataFormatDesc(MTLPixelFormat p_format);
MTLFormatDesc &getMTLPixelFormatDesc(MTLPixelFormat p_format);
MTLFmtCaps &getMTLPixelFormatCapsIf(MTLPixelFormat mtlPixFmt, bool cond);
MTLFormatDesc &getMTLVertexFormatDesc(MTLVertexFormat p_format);

void initDataFormatCapabilities();
void initMTLPixelFormatCapabilities();
void initMTLVertexFormatCapabilities();
void buildMTLFormatMaps();
void initMTLVertexFormatCapabilities(const MetalFeatures &p_feat);
void modifyMTLFormatCapabilities(const MetalFeatures &p_feat);
void buildDFFormatMaps();
void modifyMTLFormatCapabilities();
void modifyMTLFormatCapabilities(id<MTLDevice> p_device);
void addMTLPixelFormatCapabilities(id<MTLDevice> p_device,
MTLFeatureSet p_feature_set,
MTLPixelFormat p_format,
MTLFmtCaps p_caps);
void addMTLPixelFormatCapabilities(id<MTLDevice> p_device,
MTLGPUFamily p_family,
MTLPixelFormat p_format,
MTLFmtCaps p_caps);
void disableMTLPixelFormatCapabilities(MTLPixelFormat p_format,
MTLFmtCaps p_caps);
void disableAllMTLPixelFormatCapabilities(MTLPixelFormat p_format);
void addMTLVertexFormatCapabilities(id<MTLDevice> p_device,
MTLFeatureSet p_feature_set,
MTLVertexFormat p_format,
MTLFmtCaps p_caps);

DataFormatDesc _dataFormatDescriptions[RD::DATA_FORMAT_MAX];
MTLFormatDesc _mtlPixelFormatDescriptions[_mtlPixelFormatCount];
MTLFormatDesc _mtlVertexFormatDescriptions[_mtlVertexFormatCount];

// Most Metal formats have small values and are mapped by simple lookup array.
// Outliers are mapped by a map.
uint16_t _mtlFormatDescIndicesByMTLPixelFormatsCore[_mtlPixelFormatCoreCount];
HashMap<uint32_t, uint32_t> _mtlFormatDescIndicesByMTLPixelFormatsExt;

uint16_t _mtlFormatDescIndicesByMTLVertexFormats[_mtlVertexFormatCount];
void addMTLPixelFormatDescImpl(MTLPixelFormat p_pix_fmt, MTLPixelFormat p_pix_fmt_linear,
MTLViewClass p_view_class, MTLFmtCaps p_fmt_caps, const char *p_name);
void addMTLVertexFormatDescImpl(MTLVertexFormat p_vert_fmt, MTLFmtCaps p_vert_caps, const char *name);

id<MTLDevice> device;
InflectionMap<DataFormat, DataFormatDesc, RD::DATA_FORMAT_MAX> _data_format_descs;
InflectionMap<uint16_t, MTLFormatDesc, MTLPixelFormatX32_Stencil8 + 2> _mtl_pixel_format_descs; // The actual last enum value is not available on iOS
TightLocalVector<MTLFormatDesc> _mtl_vertex_format_descs;
};

#pragma clang diagnostic pop
Expand Down
Loading