Add Metal support for macOS (arm64) and iOS
This commit is contained in:
committed by
Rémi Verschelde
parent
826de7976a
commit
2d0165574d
39
drivers/metal/README.md
Normal file
39
drivers/metal/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Metal Rendering Device
|
||||
|
||||
This document aims to describe the Metal rendering device implementation in Godot.
|
||||
|
||||
## Future work / ideas
|
||||
|
||||
* Use placement heaps
|
||||
* Explicit hazard tracking
|
||||
* [MetalFX] upscaling support?
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
The Metal rendering owes a lot to the work of the [MoltenVK] project, which is a Vulkan implementation on top of Metal.
|
||||
In accordance with the Apache 2.0 license, the following copyright notices have been included where applicable:
|
||||
|
||||
```
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); */
|
||||
/* you may not use this file except in compliance with the License. */
|
||||
/* You may obtain a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
|
||||
/* implied. See the License for the specific language governing */
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
```
|
||||
|
||||
[MoltenVK]: https://github.com/KhronosGroup/MoltenVK
|
||||
[MetalFX]: https://developer.apple.com/documentation/metalfx?language=objc
|
||||
49
drivers/metal/SCsub
Normal file
49
drivers/metal/SCsub
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env_metal = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
thirdparty_dir = "#thirdparty/spirv-cross/"
|
||||
thirdparty_sources = [
|
||||
"spirv_cfg.cpp",
|
||||
"spirv_cross_util.cpp",
|
||||
"spirv_cross.cpp",
|
||||
"spirv_parser.cpp",
|
||||
"spirv_msl.cpp",
|
||||
"spirv_reflect.cpp",
|
||||
"spirv_glsl.cpp",
|
||||
"spirv_cross_parsed_ir.cpp",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_metal.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"])
|
||||
|
||||
# Must enable exceptions for SPIRV-Cross; otherwise, it will abort the process on errors.
|
||||
if "-fno-exceptions" in env_metal["CXXFLAGS"]:
|
||||
env_metal["CXXFLAGS"].remove("-fno-exceptions")
|
||||
env_metal.Append(CXXFLAGS=["-fexceptions"])
|
||||
|
||||
env_thirdparty = env_metal.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env_metal.drivers_sources += thirdparty_obj
|
||||
|
||||
# Enable C++20 for the Objective-C++ Metal code, which uses C++20 concepts.
|
||||
if "-std=gnu++17" in env_metal["CXXFLAGS"]:
|
||||
env_metal["CXXFLAGS"].remove("-std=gnu++17")
|
||||
env_metal.Append(CXXFLAGS=["-std=c++20"])
|
||||
|
||||
# Driver source files
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_metal.add_source_files(driver_obj, "*.mm")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
# Needed to force rebuilding the driver files when the thirdparty library is updated.
|
||||
env.Depends(driver_obj, thirdparty_obj)
|
||||
141
drivers/metal/metal_device_properties.h
Normal file
141
drivers/metal/metal_device_properties.h
Normal file
@ -0,0 +1,141 @@
|
||||
/**************************************************************************/
|
||||
/* metal_device_properties.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); */
|
||||
/* you may not use this file except in compliance with the License. */
|
||||
/* You may obtain a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
|
||||
/* implied. See the License for the specific language governing */
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef METAL_DEVICE_PROPERTIES_H
|
||||
#define METAL_DEVICE_PROPERTIES_H
|
||||
|
||||
#import "servers/rendering/rendering_device.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
|
||||
/** The buffer index to use for vertex content. */
|
||||
const static uint32_t VERT_CONTENT_BUFFER_INDEX = 0;
|
||||
const static uint32_t MAX_COLOR_ATTACHMENT_COUNT = 8;
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, SampleCount) {
|
||||
SampleCount1 = (1UL << 0),
|
||||
SampleCount2 = (1UL << 1),
|
||||
SampleCount4 = (1UL << 2),
|
||||
SampleCount8 = (1UL << 3),
|
||||
SampleCount16 = (1UL << 4),
|
||||
SampleCount32 = (1UL << 5),
|
||||
SampleCount64 = (1UL << 6),
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) MetalFeatures {
|
||||
uint32_t mslVersion;
|
||||
MTLGPUFamily highestFamily;
|
||||
MTLLanguageVersion mslVersionEnum;
|
||||
SampleCount supportedSampleCounts;
|
||||
long hostMemoryPageSize;
|
||||
bool layeredRendering;
|
||||
bool multisampleLayeredRendering;
|
||||
bool quadPermute; /**< If true, quadgroup permutation functions (vote, ballot, shuffle) are supported in shaders. */
|
||||
bool simdPermute; /**< If true, SIMD-group permutation functions (vote, ballot, shuffle) are supported in shaders. */
|
||||
bool simdReduction; /**< If true, SIMD-group reduction functions (arithmetic) are supported in shaders. */
|
||||
bool tessellationShader; /**< If true, tessellation shaders are supported. */
|
||||
bool imageCubeArray; /**< If true, image cube arrays are supported. */
|
||||
};
|
||||
|
||||
struct MetalLimits {
|
||||
uint64_t maxImageArrayLayers;
|
||||
uint64_t maxFramebufferHeight;
|
||||
uint64_t maxFramebufferWidth;
|
||||
uint64_t maxImageDimension1D;
|
||||
uint64_t maxImageDimension2D;
|
||||
uint64_t maxImageDimension3D;
|
||||
uint64_t maxImageDimensionCube;
|
||||
uint64_t maxViewportDimensionX;
|
||||
uint64_t maxViewportDimensionY;
|
||||
MTLSize maxThreadsPerThreadGroup;
|
||||
MTLSize maxComputeWorkGroupCount;
|
||||
uint64_t maxBoundDescriptorSets;
|
||||
uint64_t maxColorAttachments;
|
||||
uint64_t maxTexturesPerArgumentBuffer;
|
||||
uint64_t maxSamplersPerArgumentBuffer;
|
||||
uint64_t maxBuffersPerArgumentBuffer;
|
||||
uint64_t maxBufferLength;
|
||||
uint64_t minUniformBufferOffsetAlignment;
|
||||
uint64_t maxVertexDescriptorLayoutStride;
|
||||
uint16_t maxViewports;
|
||||
uint32_t maxPerStageBufferCount; /**< The total number of per-stage Metal buffers available for shader uniform content and attributes. */
|
||||
uint32_t maxPerStageTextureCount; /**< The total number of per-stage Metal textures available for shader uniform content. */
|
||||
uint32_t maxPerStageSamplerCount; /**< The total number of per-stage Metal samplers available for shader uniform content. */
|
||||
uint32_t maxVertexInputAttributes;
|
||||
uint32_t maxVertexInputBindings;
|
||||
uint32_t maxVertexInputBindingStride;
|
||||
uint32_t maxDrawIndexedIndexValue;
|
||||
|
||||
uint32_t minSubgroupSize; /**< The minimum number of threads in a SIMD-group. */
|
||||
uint32_t maxSubgroupSize; /**< The maximum number of threads in a SIMD-group. */
|
||||
BitField<RDD::ShaderStage> subgroupSupportedShaderStages;
|
||||
BitField<RD::SubgroupOperations> subgroupSupportedOperations; /**< The subgroup operations supported by the device. */
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MetalDeviceProperties {
|
||||
private:
|
||||
void init_features(id<MTLDevice> p_device);
|
||||
void init_limits(id<MTLDevice> p_device);
|
||||
|
||||
public:
|
||||
MetalFeatures features;
|
||||
MetalLimits limits;
|
||||
|
||||
SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
|
||||
|
||||
MetalDeviceProperties(id<MTLDevice> p_device);
|
||||
~MetalDeviceProperties();
|
||||
|
||||
private:
|
||||
static const SampleCount sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX];
|
||||
};
|
||||
|
||||
#endif // METAL_DEVICE_PROPERTIES_H
|
||||
327
drivers/metal/metal_device_properties.mm
Normal file
327
drivers/metal/metal_device_properties.mm
Normal file
@ -0,0 +1,327 @@
|
||||
/**************************************************************************/
|
||||
/* metal_device_properties.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); */
|
||||
/* you may not use this file except in compliance with the License. */
|
||||
/* You may obtain a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
|
||||
/* implied. See the License for the specific language governing */
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "metal_device_properties.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <spirv_cross.hpp>
|
||||
#import <spirv_msl.hpp>
|
||||
|
||||
// Common scaling multipliers.
|
||||
#define KIBI (1024)
|
||||
#define MEBI (KIBI * KIBI)
|
||||
|
||||
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
|
||||
#define MTLGPUFamilyApple9 (MTLGPUFamily)1009
|
||||
#endif
|
||||
|
||||
API_AVAILABLE(macos(11.0), ios(14.0))
|
||||
MTLGPUFamily &operator--(MTLGPUFamily &p_family) {
|
||||
p_family = static_cast<MTLGPUFamily>(static_cast<int>(p_family) - 1);
|
||||
if (p_family < MTLGPUFamilyApple1) {
|
||||
p_family = MTLGPUFamilyApple9;
|
||||
}
|
||||
|
||||
return p_family;
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
|
||||
features = {};
|
||||
|
||||
features.highestFamily = MTLGPUFamilyApple1;
|
||||
for (MTLGPUFamily family = MTLGPUFamilyApple9; family >= MTLGPUFamilyApple1; --family) {
|
||||
if ([p_device supportsFamily:family]) {
|
||||
features.highestFamily = family;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
features.hostMemoryPageSize = sysconf(_SC_PAGESIZE);
|
||||
|
||||
for (SampleCount sc = SampleCount1; sc <= SampleCount64; sc <<= 1) {
|
||||
if ([p_device supportsTextureSampleCount:sc]) {
|
||||
features.supportedSampleCounts |= sc;
|
||||
}
|
||||
}
|
||||
|
||||
features.layeredRendering = [p_device supportsFamily:MTLGPUFamilyApple5];
|
||||
features.multisampleLayeredRendering = [p_device supportsFamily:MTLGPUFamilyApple7];
|
||||
features.tessellationShader = [p_device supportsFamily:MTLGPUFamilyApple3];
|
||||
features.imageCubeArray = [p_device supportsFamily:MTLGPUFamilyApple3];
|
||||
features.quadPermute = [p_device supportsFamily:MTLGPUFamilyApple4];
|
||||
features.simdPermute = [p_device supportsFamily:MTLGPUFamilyApple6];
|
||||
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];
|
||||
|
||||
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
|
||||
case MTLLanguageVersion3_2:
|
||||
setMSLVersion(3, 2);
|
||||
break;
|
||||
#endif
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 || __IPHONE_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
|
||||
case MTLLanguageVersion1_0:
|
||||
setMSLVersion(1, 0);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
// FST: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
|
||||
// FST: Maximum number of layers per 1D texture array, 2D texture array, or 3D texture.
|
||||
limits.maxImageArrayLayers = 2048;
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple3]) {
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxFramebufferWidth = 16384;
|
||||
limits.maxFramebufferHeight = 16384;
|
||||
limits.maxViewportDimensionX = 16384;
|
||||
limits.maxViewportDimensionY = 16384;
|
||||
// FST: Maximum 1D texture width.
|
||||
limits.maxImageDimension1D = 16384;
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxImageDimension2D = 16384;
|
||||
// FST: Maximum cube map texture width and height.
|
||||
limits.maxImageDimensionCube = 16384;
|
||||
} else {
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxFramebufferWidth = 8192;
|
||||
limits.maxFramebufferHeight = 8192;
|
||||
limits.maxViewportDimensionX = 8192;
|
||||
limits.maxViewportDimensionY = 8192;
|
||||
// FST: Maximum 1D texture width.
|
||||
limits.maxImageDimension1D = 8192;
|
||||
// FST: Maximum 2D texture width and height.
|
||||
limits.maxImageDimension2D = 8192;
|
||||
// FST: Maximum cube map texture width and height.
|
||||
limits.maxImageDimensionCube = 8192;
|
||||
}
|
||||
// FST: Maximum 3D texture width, height, and depth.
|
||||
limits.maxImageDimension3D = 2048;
|
||||
|
||||
limits.maxThreadsPerThreadGroup = p_device.maxThreadsPerThreadgroup;
|
||||
// No effective limits.
|
||||
limits.maxComputeWorkGroupCount = { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() };
|
||||
// https://github.com/KhronosGroup/MoltenVK/blob/568cc3acc0e2299931fdaecaaa1fc3ec5b4af281/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h#L85
|
||||
limits.maxBoundDescriptorSets = SPIRV_CROSS_NAMESPACE::kMaxArgumentBuffers;
|
||||
// FST: Maximum number of color render targets per render pass descriptor.
|
||||
limits.maxColorAttachments = 8;
|
||||
|
||||
// Maximum number of textures the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxTexturesPerArgumentBuffer = 1'000'000;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
limits.maxTexturesPerArgumentBuffer = 96;
|
||||
} else {
|
||||
limits.maxTexturesPerArgumentBuffer = 31;
|
||||
}
|
||||
|
||||
// Maximum number of samplers the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxSamplersPerArgumentBuffer = 1024;
|
||||
} else {
|
||||
limits.maxSamplersPerArgumentBuffer = 16;
|
||||
}
|
||||
|
||||
// Maximum number of buffers the device can access, per stage, from an argument buffer.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxBuffersPerArgumentBuffer = std::numeric_limits<uint64_t>::max();
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
limits.maxBuffersPerArgumentBuffer = 96;
|
||||
} else {
|
||||
limits.maxBuffersPerArgumentBuffer = 31;
|
||||
}
|
||||
|
||||
limits.minSubgroupSize = limits.maxSubgroupSize = 1;
|
||||
// These values were taken from MoltenVK.
|
||||
if (features.simdPermute) {
|
||||
limits.minSubgroupSize = 4;
|
||||
limits.maxSubgroupSize = 32;
|
||||
} else if (features.quadPermute) {
|
||||
limits.minSubgroupSize = limits.maxSubgroupSize = 4;
|
||||
}
|
||||
|
||||
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_COMPUTE_BIT);
|
||||
if (features.tessellationShader) {
|
||||
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT);
|
||||
}
|
||||
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BASIC_BIT);
|
||||
if (features.simdPermute || features.quadPermute) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_VOTE_BIT);
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BALLOT_BIT);
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_BIT);
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT);
|
||||
}
|
||||
|
||||
if (features.simdReduction) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT);
|
||||
}
|
||||
|
||||
if (features.quadPermute) {
|
||||
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_QUAD_BIT);
|
||||
}
|
||||
|
||||
limits.maxBufferLength = p_device.maxBufferLength;
|
||||
|
||||
// FST: Maximum size of vertex descriptor layout stride.
|
||||
limits.maxVertexDescriptorLayoutStride = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
// Maximum number of viewports.
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple5]) {
|
||||
limits.maxViewports = 16;
|
||||
} else {
|
||||
limits.maxViewports = 1;
|
||||
}
|
||||
|
||||
limits.maxPerStageBufferCount = 31;
|
||||
limits.maxPerStageSamplerCount = 16;
|
||||
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
|
||||
limits.maxPerStageTextureCount = 128;
|
||||
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
|
||||
limits.maxPerStageTextureCount = 96;
|
||||
} else {
|
||||
limits.maxPerStageTextureCount = 31;
|
||||
}
|
||||
|
||||
limits.maxVertexInputAttributes = 31;
|
||||
limits.maxVertexInputBindings = 31;
|
||||
limits.maxVertexInputBindingStride = (2 * KIBI);
|
||||
|
||||
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
||||
limits.minUniformBufferOffsetAlignment = 64;
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
// This is Apple Silicon specific.
|
||||
limits.minUniformBufferOffsetAlignment = 16;
|
||||
#endif
|
||||
|
||||
limits.maxDrawIndexedIndexValue = std::numeric_limits<uint32_t>::max() - 1;
|
||||
}
|
||||
|
||||
MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
|
||||
init_features(p_device);
|
||||
init_limits(p_device);
|
||||
}
|
||||
|
||||
MetalDeviceProperties::~MetalDeviceProperties() {
|
||||
}
|
||||
|
||||
SampleCount MetalDeviceProperties::find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const {
|
||||
SampleCount supported = features.supportedSampleCounts;
|
||||
if (supported & sample_count[p_samples]) {
|
||||
return sample_count[p_samples];
|
||||
}
|
||||
|
||||
SampleCount requested_sample_count = sample_count[p_samples];
|
||||
// Find the nearest supported sample count.
|
||||
while (requested_sample_count > SampleCount1) {
|
||||
if (supported & requested_sample_count) {
|
||||
return requested_sample_count;
|
||||
}
|
||||
requested_sample_count = (SampleCount)(requested_sample_count >> 1);
|
||||
}
|
||||
|
||||
return SampleCount1;
|
||||
}
|
||||
|
||||
// region static members
|
||||
|
||||
const SampleCount MetalDeviceProperties::sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX] = {
|
||||
SampleCount1,
|
||||
SampleCount2,
|
||||
SampleCount4,
|
||||
SampleCount8,
|
||||
SampleCount16,
|
||||
SampleCount32,
|
||||
SampleCount64,
|
||||
};
|
||||
|
||||
// endregion
|
||||
838
drivers/metal/metal_objects.h
Normal file
838
drivers/metal/metal_objects.h
Normal file
@ -0,0 +1,838 @@
|
||||
/**************************************************************************/
|
||||
/* metal_objects.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); */
|
||||
/* you may not use this file except in compliance with the License. */
|
||||
/* You may obtain a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
|
||||
/* implied. See the License for the specific language governing */
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef METAL_OBJECTS_H
|
||||
#define METAL_OBJECTS_H
|
||||
|
||||
#import "metal_device_properties.h"
|
||||
#import "metal_utils.h"
|
||||
#import "pixel_formats.h"
|
||||
|
||||
#import "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#import <simd/simd.h>
|
||||
#import <initializer_list>
|
||||
#import <optional>
|
||||
#import <spirv.hpp>
|
||||
|
||||
// These types can be used in Vector and other containers that use
|
||||
// pointer operations not supported by ARC.
|
||||
namespace MTL {
|
||||
#define MTL_CLASS(name) \
|
||||
class name { \
|
||||
public: \
|
||||
name(id<MTL##name> obj = nil) : m_obj(obj) {} \
|
||||
operator id<MTL##name>() const { return m_obj; } \
|
||||
id<MTL##name> m_obj; \
|
||||
};
|
||||
|
||||
MTL_CLASS(Texture)
|
||||
|
||||
} //namespace MTL
|
||||
|
||||
enum ShaderStageUsage : uint32_t {
|
||||
None = 0,
|
||||
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
|
||||
Fragment = RDD::SHADER_STAGE_FRAGMENT_BIT,
|
||||
TesselationControl = RDD::SHADER_STAGE_TESSELATION_CONTROL_BIT,
|
||||
TesselationEvaluation = RDD::SHADER_STAGE_TESSELATION_EVALUATION_BIT,
|
||||
Compute = RDD::SHADER_STAGE_COMPUTE_BIT,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
|
||||
p_a = ShaderStageUsage(uint32_t(p_a) | uint32_t(p_b));
|
||||
return p_a;
|
||||
}
|
||||
|
||||
enum class MDCommandBufferStateType {
|
||||
None,
|
||||
Render,
|
||||
Compute,
|
||||
Blit,
|
||||
};
|
||||
|
||||
enum class MDPipelineType {
|
||||
None,
|
||||
Render,
|
||||
Compute,
|
||||
};
|
||||
|
||||
class MDRenderPass;
|
||||
class MDPipeline;
|
||||
class MDRenderPipeline;
|
||||
class MDComputePipeline;
|
||||
class MDFrameBuffer;
|
||||
class RenderingDeviceDriverMetal;
|
||||
class MDUniformSet;
|
||||
class MDShader;
|
||||
|
||||
#pragma mark - Resource Factory
|
||||
|
||||
struct ClearAttKey {
|
||||
const static uint32_t COLOR_COUNT = MAX_COLOR_ATTACHMENT_COUNT;
|
||||
const static uint32_t DEPTH_INDEX = COLOR_COUNT;
|
||||
const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1;
|
||||
const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1;
|
||||
|
||||
uint16_t sample_count = 0;
|
||||
uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 };
|
||||
|
||||
_FORCE_INLINE_ void set_color_format(uint32_t p_idx, MTLPixelFormat p_fmt) { pixel_formats[p_idx] = p_fmt; }
|
||||
_FORCE_INLINE_ void set_depth_format(MTLPixelFormat p_fmt) { pixel_formats[DEPTH_INDEX] = p_fmt; }
|
||||
_FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; }
|
||||
_FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; }
|
||||
_FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; }
|
||||
|
||||
_FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; }
|
||||
_FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; }
|
||||
_FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; }
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const {
|
||||
return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0;
|
||||
}
|
||||
|
||||
uint32_t hash() const {
|
||||
uint32_t h = hash_murmur3_one_32(sample_count);
|
||||
h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceFactory {
|
||||
private:
|
||||
RenderingDeviceDriverMetal *device_driver;
|
||||
|
||||
id<MTLFunction> new_func(NSString *p_source, NSString *p_name, NSError **p_error);
|
||||
id<MTLFunction> new_clear_vert_func(ClearAttKey &p_key);
|
||||
id<MTLFunction> new_clear_frag_func(ClearAttKey &p_key);
|
||||
NSString *get_format_type_string(MTLPixelFormat p_fmt);
|
||||
|
||||
public:
|
||||
id<MTLRenderPipelineState> new_clear_pipeline_state(ClearAttKey &p_key, NSError **p_error);
|
||||
id<MTLDepthStencilState> new_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
|
||||
|
||||
MDResourceFactory(RenderingDeviceDriverMetal *p_device_driver) :
|
||||
device_driver(p_device_driver) {}
|
||||
~MDResourceFactory() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDResourceCache {
|
||||
private:
|
||||
typedef HashMap<ClearAttKey, id<MTLRenderPipelineState>, HashableHasher<ClearAttKey>> HashMap;
|
||||
std::unique_ptr<MDResourceFactory> resource_factory;
|
||||
HashMap clear_states;
|
||||
|
||||
struct {
|
||||
id<MTLDepthStencilState> all;
|
||||
id<MTLDepthStencilState> depth_only;
|
||||
id<MTLDepthStencilState> stencil_only;
|
||||
id<MTLDepthStencilState> none;
|
||||
} clear_depth_stencil_state;
|
||||
|
||||
public:
|
||||
id<MTLRenderPipelineState> get_clear_render_pipeline_state(ClearAttKey &p_key, NSError **p_error);
|
||||
id<MTLDepthStencilState> get_depth_stencil_state(bool p_use_depth, bool p_use_stencil);
|
||||
|
||||
explicit MDResourceCache(RenderingDeviceDriverMetal *p_device_driver) :
|
||||
resource_factory(new MDResourceFactory(p_device_driver)) {}
|
||||
~MDResourceCache() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
|
||||
private:
|
||||
RenderingDeviceDriverMetal *device_driver = nullptr;
|
||||
id<MTLCommandQueue> queue = nil;
|
||||
id<MTLCommandBuffer> commandBuffer = nil;
|
||||
|
||||
void _end_compute_dispatch();
|
||||
void _end_blit();
|
||||
|
||||
#pragma mark - Render
|
||||
|
||||
void _render_set_dirty_state();
|
||||
void _render_bind_uniform_sets();
|
||||
|
||||
static void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
|
||||
static uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
|
||||
void _end_render_pass();
|
||||
void _render_clear_render_area();
|
||||
|
||||
public:
|
||||
MDCommandBufferStateType type = MDCommandBufferStateType::None;
|
||||
|
||||
struct RenderState {
|
||||
MDRenderPass *pass = nullptr;
|
||||
MDFrameBuffer *frameBuffer = nullptr;
|
||||
MDRenderPipeline *pipeline = nullptr;
|
||||
LocalVector<RDD::RenderPassClearValue> clear_values;
|
||||
LocalVector<MTLViewport> viewports;
|
||||
LocalVector<MTLScissorRect> scissors;
|
||||
std::optional<Color> blend_constants;
|
||||
uint32_t current_subpass = UINT32_MAX;
|
||||
Rect2i render_area = {};
|
||||
bool is_rendering_entire_area = false;
|
||||
MTLRenderPassDescriptor *desc = nil;
|
||||
id<MTLRenderCommandEncoder> encoder = nil;
|
||||
id<MTLBuffer> __unsafe_unretained index_buffer = nil; // Buffer is owned by RDD.
|
||||
MTLIndexType index_type = MTLIndexTypeUInt16;
|
||||
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
|
||||
LocalVector<NSUInteger> vertex_offsets;
|
||||
// clang-format off
|
||||
enum DirtyFlag: uint8_t {
|
||||
DIRTY_NONE = 0b0000'0000,
|
||||
DIRTY_PIPELINE = 0b0000'0001, //! pipeline state
|
||||
DIRTY_UNIFORMS = 0b0000'0010, //! uniform sets
|
||||
DIRTY_DEPTH = 0b0000'0100, //! depth / stenci state
|
||||
DIRTY_VERTEX = 0b0000'1000, //! vertex buffers
|
||||
DIRTY_VIEWPORT = 0b0001'0000, //! viewport rectangles
|
||||
DIRTY_SCISSOR = 0b0010'0000, //! scissor rectangles
|
||||
DIRTY_BLEND = 0b0100'0000, //! blend state
|
||||
DIRTY_RASTER = 0b1000'0000, //! encoder state like cull mode
|
||||
|
||||
DIRTY_ALL = 0xff,
|
||||
};
|
||||
// clang-format on
|
||||
BitField<DirtyFlag> dirty = DIRTY_NONE;
|
||||
|
||||
LocalVector<MDUniformSet *> uniform_sets;
|
||||
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
|
||||
uint64_t uniform_set_mask = 0;
|
||||
|
||||
_FORCE_INLINE_ void reset() {
|
||||
pass = nil;
|
||||
frameBuffer = nil;
|
||||
pipeline = nil;
|
||||
current_subpass = UINT32_MAX;
|
||||
render_area = {};
|
||||
is_rendering_entire_area = false;
|
||||
desc = nil;
|
||||
encoder = nil;
|
||||
index_buffer = nil;
|
||||
index_type = MTLIndexTypeUInt16;
|
||||
dirty = DIRTY_NONE;
|
||||
uniform_sets.clear();
|
||||
uniform_set_mask = 0;
|
||||
clear_values.clear();
|
||||
viewports.clear();
|
||||
scissors.clear();
|
||||
blend_constants.reset();
|
||||
vertex_buffers.clear();
|
||||
vertex_offsets.clear();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_viewport_dirty() {
|
||||
if (viewports.is_empty()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_VIEWPORT);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_scissors_dirty() {
|
||||
if (scissors.is_empty()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_SCISSOR);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_vertex_dirty() {
|
||||
if (vertex_buffers.is_empty()) {
|
||||
return;
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_VERTEX);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_uniforms_dirty(std::initializer_list<uint32_t> l) {
|
||||
if (uniform_sets.is_empty()) {
|
||||
return;
|
||||
}
|
||||
for (uint32_t i : l) {
|
||||
if (i < uniform_sets.size() && uniform_sets[i] != nullptr) {
|
||||
uniform_set_mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void mark_uniforms_dirty(void) {
|
||||
if (uniform_sets.is_empty()) {
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < uniform_sets.size(); i++) {
|
||||
if (uniform_sets[i] != nullptr) {
|
||||
uniform_set_mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
dirty.set_flag(DirtyFlag::DIRTY_UNIFORMS);
|
||||
}
|
||||
|
||||
MTLScissorRect clip_to_render_area(MTLScissorRect p_rect) const {
|
||||
uint32_t raLeft = render_area.position.x;
|
||||
uint32_t raRight = raLeft + render_area.size.width;
|
||||
uint32_t raBottom = render_area.position.y;
|
||||
uint32_t raTop = raBottom + render_area.size.height;
|
||||
|
||||
p_rect.x = CLAMP(p_rect.x, raLeft, MAX(raRight - 1, raLeft));
|
||||
p_rect.y = CLAMP(p_rect.y, raBottom, MAX(raTop - 1, raBottom));
|
||||
p_rect.width = MIN(p_rect.width, raRight - p_rect.x);
|
||||
p_rect.height = MIN(p_rect.height, raTop - p_rect.y);
|
||||
|
||||
return p_rect;
|
||||
}
|
||||
|
||||
Rect2i clip_to_render_area(Rect2i p_rect) const {
|
||||
int32_t raLeft = render_area.position.x;
|
||||
int32_t raRight = raLeft + render_area.size.width;
|
||||
int32_t raBottom = render_area.position.y;
|
||||
int32_t raTop = raBottom + render_area.size.height;
|
||||
|
||||
p_rect.position.x = CLAMP(p_rect.position.x, raLeft, MAX(raRight - 1, raLeft));
|
||||
p_rect.position.y = CLAMP(p_rect.position.y, raBottom, MAX(raTop - 1, raBottom));
|
||||
p_rect.size.width = MIN(p_rect.size.width, raRight - p_rect.position.x);
|
||||
p_rect.size.height = MIN(p_rect.size.height, raTop - p_rect.position.y);
|
||||
|
||||
return p_rect;
|
||||
}
|
||||
|
||||
} render;
|
||||
|
||||
// State specific for a compute pass.
|
||||
struct {
|
||||
MDComputePipeline *pipeline = nullptr;
|
||||
id<MTLComputeCommandEncoder> encoder = nil;
|
||||
_FORCE_INLINE_ void reset() {
|
||||
pipeline = nil;
|
||||
encoder = nil;
|
||||
}
|
||||
} compute;
|
||||
|
||||
// State specific to a blit pass.
|
||||
struct {
|
||||
id<MTLBlitCommandEncoder> encoder = nil;
|
||||
_FORCE_INLINE_ void reset() {
|
||||
encoder = nil;
|
||||
}
|
||||
} blit;
|
||||
|
||||
_FORCE_INLINE_ id<MTLCommandBuffer> get_command_buffer() const {
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void begin();
|
||||
void commit();
|
||||
void end();
|
||||
|
||||
id<MTLBlitCommandEncoder> blit_command_encoder();
|
||||
void encodeRenderCommandEncoderWithDescriptor(MTLRenderPassDescriptor *p_desc, NSString *p_label);
|
||||
|
||||
void bind_pipeline(RDD::PipelineID p_pipeline);
|
||||
|
||||
#pragma mark - Render Commands
|
||||
|
||||
void render_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
|
||||
void render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects);
|
||||
void render_set_viewport(VectorView<Rect2i> p_viewports);
|
||||
void render_set_scissor(VectorView<Rect2i> p_scissors);
|
||||
void render_set_blend_constants(const Color &p_constants);
|
||||
void render_begin_pass(RDD::RenderPassID p_render_pass,
|
||||
RDD::FramebufferID p_frameBuffer,
|
||||
RDD::CommandBufferType p_cmd_buffer_type,
|
||||
const Rect2i &p_rect,
|
||||
VectorView<RDD::RenderPassClearValue> p_clear_values);
|
||||
void render_next_subpass();
|
||||
void render_draw(uint32_t p_vertex_count,
|
||||
uint32_t p_instance_count,
|
||||
uint32_t p_base_vertex,
|
||||
uint32_t p_first_instance);
|
||||
void render_bind_vertex_buffers(uint32_t p_binding_count, const RDD::BufferID *p_buffers, const uint64_t *p_offsets);
|
||||
void render_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint64_t p_offset);
|
||||
|
||||
void render_draw_indexed(uint32_t p_index_count,
|
||||
uint32_t p_instance_count,
|
||||
uint32_t p_first_index,
|
||||
int32_t p_vertex_offset,
|
||||
uint32_t p_first_instance);
|
||||
|
||||
void render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
|
||||
void render_draw_indexed_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
|
||||
void render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride);
|
||||
void render_draw_indirect_count(RDD::BufferID p_indirect_buffer, uint64_t p_offset, RDD::BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride);
|
||||
|
||||
void render_end_pass();
|
||||
|
||||
#pragma mark - Compute Commands
|
||||
|
||||
void compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index);
|
||||
void compute_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups);
|
||||
void compute_dispatch_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset);
|
||||
|
||||
MDCommandBuffer(id<MTLCommandQueue> p_queue, RenderingDeviceDriverMetal *p_device_driver) :
|
||||
device_driver(p_device_driver), queue(p_queue) {
|
||||
type = MDCommandBufferStateType::None;
|
||||
}
|
||||
|
||||
MDCommandBuffer() = default;
|
||||
};
|
||||
|
||||
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
|
||||
#define MTLBindingAccess MTLArgumentAccess
|
||||
#define MTLBindingAccessReadOnly MTLArgumentAccessReadOnly
|
||||
#define MTLBindingAccessReadWrite MTLArgumentAccessReadWrite
|
||||
#define MTLBindingAccessWriteOnly MTLArgumentAccessWriteOnly
|
||||
#endif
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) BindingInfo {
|
||||
MTLDataType dataType = MTLDataTypeNone;
|
||||
uint32_t index = 0;
|
||||
MTLBindingAccess access = MTLBindingAccessReadOnly;
|
||||
MTLResourceUsage usage = 0;
|
||||
MTLTextureType textureType = MTLTextureType2D;
|
||||
spv::ImageFormat imageFormat = spv::ImageFormatUnknown;
|
||||
uint32_t arrayLength = 0;
|
||||
bool isMultisampled = false;
|
||||
|
||||
inline MTLArgumentDescriptor *new_argument_descriptor() const {
|
||||
MTLArgumentDescriptor *desc = MTLArgumentDescriptor.argumentDescriptor;
|
||||
desc.dataType = dataType;
|
||||
desc.index = index;
|
||||
desc.access = access;
|
||||
desc.textureType = textureType;
|
||||
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;
|
||||
|
||||
typedef API_AVAILABLE(macos(11.0), ios(14.0)) HashMap<RDC::ShaderStage, BindingInfo> BindingInfoMap;
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) UniformInfo {
|
||||
uint32_t binding;
|
||||
ShaderStageUsage active_stages = None;
|
||||
BindingInfoMap bindings;
|
||||
BindingInfoMap bindings_secondary;
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) UniformSet {
|
||||
LocalVector<UniformInfo> uniforms;
|
||||
uint32_t buffer_size = 0;
|
||||
HashMap<RDC::ShaderStage, uint32_t> offsets;
|
||||
HashMap<RDC::ShaderStage, id<MTLArgumentEncoder>> encoders;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDShader {
|
||||
public:
|
||||
CharString name;
|
||||
Vector<UniformSet> sets;
|
||||
|
||||
virtual void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) = 0;
|
||||
|
||||
MDShader(CharString p_name, Vector<UniformSet> p_sets) :
|
||||
name(p_name), sets(p_sets) {}
|
||||
virtual ~MDShader() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputeShader final : public MDShader {
|
||||
public:
|
||||
struct {
|
||||
uint32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} push_constants;
|
||||
MTLSize local = {};
|
||||
|
||||
id<MTLLibrary> kernel;
|
||||
#if DEV_ENABLED
|
||||
CharString kernel_source;
|
||||
#endif
|
||||
|
||||
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
|
||||
|
||||
MDComputeShader(CharString p_name, Vector<UniformSet> p_sets, id<MTLLibrary> p_kernel);
|
||||
~MDComputeShader() override = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderShader final : public MDShader {
|
||||
public:
|
||||
struct {
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} vert;
|
||||
struct {
|
||||
int32_t binding = -1;
|
||||
uint32_t size = 0;
|
||||
} frag;
|
||||
} push_constants;
|
||||
|
||||
id<MTLLibrary> vert;
|
||||
id<MTLLibrary> 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;
|
||||
|
||||
MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, id<MTLLibrary> p_vert, id<MTLLibrary> p_frag);
|
||||
~MDRenderShader() override = default;
|
||||
};
|
||||
|
||||
enum StageResourceUsage : uint32_t {
|
||||
VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
|
||||
FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
|
||||
TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
|
||||
TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
|
||||
ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
|
||||
p_a = StageResourceUsage(uint32_t(p_a) | p_b);
|
||||
return p_a;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ StageResourceUsage stage_resource_usage(RDC::ShaderStage p_stage, MTLResourceUsage p_usage) {
|
||||
return StageResourceUsage(p_usage << (p_stage * 2));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ MTLResourceUsage resource_usage_for_stage(StageResourceUsage p_usage, RDC::ShaderStage p_stage) {
|
||||
return MTLResourceUsage((p_usage >> (p_stage * 2)) & 0b11);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct HashMapComparatorDefault<RDD::ShaderID> {
|
||||
static bool compare(const RDD::ShaderID &p_lhs, const RDD::ShaderID &p_rhs) {
|
||||
return p_lhs.id == p_rhs.id;
|
||||
}
|
||||
};
|
||||
|
||||
struct BoundUniformSet {
|
||||
id<MTLBuffer> buffer;
|
||||
HashMap<id<MTLResource>, StageResourceUsage> bound_resources;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
|
||||
public:
|
||||
uint32_t index;
|
||||
LocalVector<RDD::BoundUniform> uniforms;
|
||||
HashMap<MDShader *, BoundUniformSet> bound_uniforms;
|
||||
|
||||
BoundUniformSet &boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device);
|
||||
};
|
||||
|
||||
enum class MDAttachmentType : uint8_t {
|
||||
None = 0,
|
||||
Color = 1 << 0,
|
||||
Depth = 1 << 1,
|
||||
Stencil = 1 << 2,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
|
||||
flags::set(p_a, p_b);
|
||||
return p_a;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
|
||||
return uint8_t(p_a) & uint8_t(p_b);
|
||||
}
|
||||
|
||||
struct MDSubpass {
|
||||
uint32_t subpass_index = 0;
|
||||
LocalVector<RDD::AttachmentReference> input_references;
|
||||
LocalVector<RDD::AttachmentReference> color_references;
|
||||
RDD::AttachmentReference depth_stencil_reference;
|
||||
LocalVector<RDD::AttachmentReference> resolve_references;
|
||||
|
||||
MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
|
||||
};
|
||||
|
||||
struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
|
||||
private:
|
||||
uint32_t index = 0;
|
||||
uint32_t firstUseSubpassIndex = 0;
|
||||
uint32_t lastUseSubpassIndex = 0;
|
||||
|
||||
public:
|
||||
MTLPixelFormat format = MTLPixelFormatInvalid;
|
||||
MDAttachmentType type = MDAttachmentType::None;
|
||||
MTLLoadAction loadAction = MTLLoadActionDontCare;
|
||||
MTLStoreAction storeAction = MTLStoreActionDontCare;
|
||||
MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
|
||||
MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
|
||||
uint32_t samples = 1;
|
||||
|
||||
/*!
|
||||
* @brief Returns true if this attachment is first used in the given subpass.
|
||||
* @param p_subpass
|
||||
* @return
|
||||
*/
|
||||
_FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
|
||||
return p_subpass.subpass_index == firstUseSubpassIndex;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns true if this attachment is last used in the given subpass.
|
||||
* @param p_subpass
|
||||
* @return
|
||||
*/
|
||||
_FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
|
||||
return p_subpass.subpass_index == lastUseSubpassIndex;
|
||||
}
|
||||
|
||||
void linkToSubpass(MDRenderPass const &p_pass);
|
||||
|
||||
MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
|
||||
bool p_is_rendering_entire_area,
|
||||
bool p_has_resolve,
|
||||
bool p_can_resolve,
|
||||
bool p_is_stencil) const;
|
||||
bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
|
||||
PixelFormats &p_pf,
|
||||
MDSubpass const &p_subpass,
|
||||
id<MTLTexture> p_attachment,
|
||||
bool p_is_rendering_entire_area,
|
||||
bool p_has_resolve,
|
||||
bool p_can_resolve,
|
||||
bool p_is_stencil) const;
|
||||
/** Returns whether this attachment should be cleared in the subpass. */
|
||||
bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
|
||||
public:
|
||||
Vector<MDAttachment> attachments;
|
||||
Vector<MDSubpass> subpasses;
|
||||
|
||||
uint32_t get_sample_count() const {
|
||||
return attachments.is_empty() ? 1 : attachments[0].samples;
|
||||
}
|
||||
|
||||
MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDPipeline {
|
||||
public:
|
||||
MDPipelineType type;
|
||||
|
||||
explicit MDPipeline(MDPipelineType p_type) :
|
||||
type(p_type) {}
|
||||
virtual ~MDPipeline() = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPipeline final : public MDPipeline {
|
||||
public:
|
||||
id<MTLRenderPipelineState> state = nil;
|
||||
id<MTLDepthStencilState> depth_stencil = nil;
|
||||
uint32_t push_constant_size = 0;
|
||||
uint32_t push_constant_stages_mask = 0;
|
||||
SampleCount sample_count = SampleCount1;
|
||||
|
||||
struct {
|
||||
MTLCullMode cull_mode = MTLCullModeNone;
|
||||
MTLTriangleFillMode fill_mode = MTLTriangleFillModeFill;
|
||||
MTLDepthClipMode clip_mode = MTLDepthClipModeClip;
|
||||
MTLWinding winding = MTLWindingClockwise;
|
||||
MTLPrimitiveType render_primitive = MTLPrimitiveTypePoint;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
} depth_test;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float depth_bias = 0.0;
|
||||
float slope_scale = 0.0;
|
||||
float clamp = 0.0;
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
[p_enc setDepthBias:depth_bias slopeScale:slope_scale clamp:clamp];
|
||||
}
|
||||
} depth_bias;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
uint32_t front_reference = 0;
|
||||
uint32_t back_reference = 0;
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
if (!enabled)
|
||||
return;
|
||||
[p_enc setStencilFrontReferenceValue:front_reference backReferenceValue:back_reference];
|
||||
};
|
||||
} stencil;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float r = 0.0;
|
||||
float g = 0.0;
|
||||
float b = 0.0;
|
||||
float a = 0.0;
|
||||
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
//if (!enabled)
|
||||
// return;
|
||||
[p_enc setBlendColorRed:r green:g blue:b alpha:a];
|
||||
};
|
||||
} blend;
|
||||
|
||||
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
|
||||
[p_enc setCullMode:cull_mode];
|
||||
[p_enc setTriangleFillMode:fill_mode];
|
||||
[p_enc setDepthClipMode:clip_mode];
|
||||
[p_enc setFrontFacingWinding:winding];
|
||||
depth_bias.apply(p_enc);
|
||||
stencil.apply(p_enc);
|
||||
blend.apply(p_enc);
|
||||
}
|
||||
|
||||
} raster_state;
|
||||
|
||||
MDRenderShader *shader = nil;
|
||||
|
||||
MDRenderPipeline() :
|
||||
MDPipeline(MDPipelineType::Render) {}
|
||||
~MDRenderPipeline() final = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDComputePipeline final : public MDPipeline {
|
||||
public:
|
||||
id<MTLComputePipelineState> state = nil;
|
||||
struct {
|
||||
MTLSize local = {};
|
||||
} compute_state;
|
||||
|
||||
MDComputeShader *shader = nil;
|
||||
|
||||
explicit MDComputePipeline(id<MTLComputePipelineState> p_state) :
|
||||
MDPipeline(MDPipelineType::Compute), state(p_state) {}
|
||||
~MDComputePipeline() final = default;
|
||||
};
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) MDFrameBuffer {
|
||||
public:
|
||||
Vector<MTL::Texture> textures;
|
||||
Size2i size;
|
||||
MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
|
||||
textures(p_textures), size(p_size) {}
|
||||
MDFrameBuffer() {}
|
||||
|
||||
virtual ~MDFrameBuffer() = default;
|
||||
};
|
||||
|
||||
// These functions are used to convert between Objective-C objects and
|
||||
// the RIDs used by Godot, respecting automatic reference counting.
|
||||
namespace rid {
|
||||
|
||||
// Converts an Objective-C object to a pointer, and incrementing the
|
||||
// reference count.
|
||||
_FORCE_INLINE_
|
||||
void *owned(id p_id) {
|
||||
return (__bridge_retained void *)p_id;
|
||||
}
|
||||
|
||||
#define MAKE_ID(FROM, TO) \
|
||||
_FORCE_INLINE_ TO make(FROM p_obj) { return TO(owned(p_obj)); }
|
||||
|
||||
MAKE_ID(id<MTLTexture>, RDD::TextureID)
|
||||
MAKE_ID(id<MTLBuffer>, RDD::BufferID)
|
||||
MAKE_ID(id<MTLSamplerState>, RDD::SamplerID)
|
||||
MAKE_ID(MTLVertexDescriptor *, RDD::VertexFormatID)
|
||||
MAKE_ID(id<MTLCommandQueue>, RDD::CommandPoolID)
|
||||
|
||||
// Converts a pointer to an Objective-C object without changing the reference count.
|
||||
_FORCE_INLINE_
|
||||
auto get(RDD::ID p_id) {
|
||||
return (p_id.id) ? (__bridge ::id)(void *)p_id.id : nil;
|
||||
}
|
||||
|
||||
// Converts a pointer to an Objective-C object, and decrements the reference count.
|
||||
_FORCE_INLINE_
|
||||
auto release(RDD::ID p_id) {
|
||||
return (__bridge_transfer ::id)(void *)p_id.id;
|
||||
}
|
||||
|
||||
} // namespace rid
|
||||
|
||||
#endif // METAL_OBJECTS_H
|
||||
1380
drivers/metal/metal_objects.mm
Normal file
1380
drivers/metal/metal_objects.mm
Normal file
File diff suppressed because it is too large
Load Diff
81
drivers/metal/metal_utils.h
Normal file
81
drivers/metal/metal_utils.h
Normal file
@ -0,0 +1,81 @@
|
||||
/**************************************************************************/
|
||||
/* metal_utils.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 METAL_UTILS_H
|
||||
#define METAL_UTILS_H
|
||||
|
||||
#pragma mark - Boolean flags
|
||||
|
||||
namespace flags {
|
||||
|
||||
/*! Sets the flags within the value parameter specified by the mask parameter. */
|
||||
template <typename Tv, typename Tm>
|
||||
void set(Tv &p_value, Tm p_mask) {
|
||||
using T = std::underlying_type_t<Tv>;
|
||||
p_value = static_cast<Tv>(static_cast<T>(p_value) | static_cast<T>(p_mask));
|
||||
}
|
||||
|
||||
/*! Clears the flags within the value parameter specified by the mask parameter. */
|
||||
template <typename Tv, typename Tm>
|
||||
void clear(Tv &p_value, Tm p_mask) {
|
||||
using T = std::underlying_type_t<Tv>;
|
||||
p_value = static_cast<Tv>(static_cast<T>(p_value) & ~static_cast<T>(p_mask));
|
||||
}
|
||||
|
||||
/*! Returns whether the specified value has any of the bits specified in mask set to 1. */
|
||||
template <typename Tv, typename Tm>
|
||||
static constexpr bool any(Tv p_value, const Tm p_mask) { return ((p_value & p_mask) != 0); }
|
||||
|
||||
/*! Returns whether the specified value has all of the bits specified in mask set to 1. */
|
||||
template <typename Tv, typename Tm>
|
||||
static constexpr bool all(Tv p_value, const Tm p_mask) { return ((p_value & p_mask) == p_mask); }
|
||||
|
||||
} //namespace flags
|
||||
|
||||
#pragma mark - Alignment and Offsets
|
||||
|
||||
static constexpr bool is_power_of_two(uint64_t p_value) {
|
||||
return p_value && ((p_value & (p_value - 1)) == 0);
|
||||
}
|
||||
|
||||
static constexpr uint64_t round_up_to_alignment(uint64_t p_value, uint64_t p_alignment) {
|
||||
DEV_ASSERT(is_power_of_two(p_alignment));
|
||||
|
||||
if (p_alignment == 0) {
|
||||
return p_value;
|
||||
}
|
||||
|
||||
uint64_t mask = p_alignment - 1;
|
||||
uint64_t aligned_value = (p_value + mask) & ~mask;
|
||||
|
||||
return aligned_value;
|
||||
}
|
||||
|
||||
#endif // METAL_UTILS_H
|
||||
416
drivers/metal/pixel_formats.h
Normal file
416
drivers/metal/pixel_formats.h
Normal file
@ -0,0 +1,416 @@
|
||||
/**************************************************************************/
|
||||
/* pixel_formats.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* Portions of this code were derived from MoltenVK. */
|
||||
/* */
|
||||
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
|
||||
/* (http://www.brenwill.com) */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); */
|
||||
/* you may not use this file except in compliance with the License. */
|
||||
/* You may obtain a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
|
||||
/* implied. See the License for the specific language governing */
|
||||
/* permissions and limitations under the License. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PIXEL_FORMATS_H
|
||||
#define PIXEL_FORMATS_H
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#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
|
||||
|
||||
typedef enum : uint16_t {
|
||||
|
||||
kMTLFmtCapsNone = 0,
|
||||
/*! The format can be used in a shader read operation. */
|
||||
kMTLFmtCapsRead = (1 << 0),
|
||||
/*! The format can be used in a shader filter operation during sampling. */
|
||||
kMTLFmtCapsFilter = (1 << 1),
|
||||
/*! The format can be used in a shader write operation. */
|
||||
kMTLFmtCapsWrite = (1 << 2),
|
||||
/*! The format can be used with atomic operations. */
|
||||
kMTLFmtCapsAtomic = (1 << 3),
|
||||
/*! The format can be used as a color attachment. */
|
||||
kMTLFmtCapsColorAtt = (1 << 4),
|
||||
/*! The format can be used as a depth-stencil attachment. */
|
||||
kMTLFmtCapsDSAtt = (1 << 5),
|
||||
/*! The format can be used with blend operations. */
|
||||
kMTLFmtCapsBlend = (1 << 6),
|
||||
/*! The format can be used as a destination for multisample antialias (MSAA) data. */
|
||||
kMTLFmtCapsMSAA = (1 << 7),
|
||||
/*! The format can be used as a resolve attachment. */
|
||||
kMTLFmtCapsResolve = (1 << 8),
|
||||
kMTLFmtCapsVertex = (1 << 9),
|
||||
|
||||
kMTLFmtCapsRF = (kMTLFmtCapsRead | kMTLFmtCapsFilter),
|
||||
kMTLFmtCapsRC = (kMTLFmtCapsRead | kMTLFmtCapsColorAtt),
|
||||
kMTLFmtCapsRCB = (kMTLFmtCapsRC | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRCM = (kMTLFmtCapsRC | kMTLFmtCapsMSAA),
|
||||
kMTLFmtCapsRCMB = (kMTLFmtCapsRCM | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRWC = (kMTLFmtCapsRC | kMTLFmtCapsWrite),
|
||||
kMTLFmtCapsRWCB = (kMTLFmtCapsRWC | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRWCM = (kMTLFmtCapsRWC | kMTLFmtCapsMSAA),
|
||||
kMTLFmtCapsRWCMB = (kMTLFmtCapsRWCM | kMTLFmtCapsBlend),
|
||||
kMTLFmtCapsRFCMRB = (kMTLFmtCapsRCMB | kMTLFmtCapsFilter | kMTLFmtCapsResolve),
|
||||
kMTLFmtCapsRFWCMB = (kMTLFmtCapsRWCMB | kMTLFmtCapsFilter),
|
||||
kMTLFmtCapsAll = (kMTLFmtCapsRFWCMB | kMTLFmtCapsResolve),
|
||||
|
||||
kMTLFmtCapsDRM = (kMTLFmtCapsDSAtt | kMTLFmtCapsRead | kMTLFmtCapsMSAA),
|
||||
kMTLFmtCapsDRFM = (kMTLFmtCapsDRM | kMTLFmtCapsFilter),
|
||||
kMTLFmtCapsDRMR = (kMTLFmtCapsDRM | kMTLFmtCapsResolve),
|
||||
kMTLFmtCapsDRFMR = (kMTLFmtCapsDRFM | kMTLFmtCapsResolve),
|
||||
|
||||
kMTLFmtCapsChromaSubsampling = kMTLFmtCapsRF,
|
||||
kMTLFmtCapsMultiPlanar = kMTLFmtCapsChromaSubsampling,
|
||||
} MTLFmtCaps;
|
||||
|
||||
inline MTLFmtCaps operator|(MTLFmtCaps p_left, MTLFmtCaps p_right) {
|
||||
return static_cast<MTLFmtCaps>(static_cast<uint32_t>(p_left) | p_right);
|
||||
}
|
||||
|
||||
inline MTLFmtCaps &operator|=(MTLFmtCaps &p_left, MTLFmtCaps p_right) {
|
||||
return (p_left = p_left | p_right);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Metal view classes
|
||||
|
||||
enum class MTLViewClass : uint8_t {
|
||||
None,
|
||||
Color8,
|
||||
Color16,
|
||||
Color32,
|
||||
Color64,
|
||||
Color128,
|
||||
PVRTC_RGB_2BPP,
|
||||
PVRTC_RGB_4BPP,
|
||||
PVRTC_RGBA_2BPP,
|
||||
PVRTC_RGBA_4BPP,
|
||||
EAC_R11,
|
||||
EAC_RG11,
|
||||
EAC_RGBA8,
|
||||
ETC2_RGB8,
|
||||
ETC2_RGB8A1,
|
||||
ASTC_4x4,
|
||||
ASTC_5x4,
|
||||
ASTC_5x5,
|
||||
ASTC_6x5,
|
||||
ASTC_6x6,
|
||||
ASTC_8x5,
|
||||
ASTC_8x6,
|
||||
ASTC_8x8,
|
||||
ASTC_10x5,
|
||||
ASTC_10x6,
|
||||
ASTC_10x8,
|
||||
ASTC_10x10,
|
||||
ASTC_12x10,
|
||||
ASTC_12x12,
|
||||
BC1_RGBA,
|
||||
BC2_RGBA,
|
||||
BC3_RGBA,
|
||||
BC4_R,
|
||||
BC5_RG,
|
||||
BC6H_RGB,
|
||||
BC7_RGBA,
|
||||
Depth24_Stencil8,
|
||||
Depth32_Stencil8,
|
||||
BGRA10_XR,
|
||||
BGR10_XR
|
||||
};
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Format descriptors
|
||||
|
||||
/** Enumerates the data type of a format. */
|
||||
enum class MTLFormatType {
|
||||
None, /**< Format type is unknown. */
|
||||
ColorHalf, /**< A 16-bit floating point color. */
|
||||
ColorFloat, /**< A 32-bit floating point color. */
|
||||
ColorInt8, /**< A signed 8-bit integer color. */
|
||||
ColorUInt8, /**< An unsigned 8-bit integer color. */
|
||||
ColorInt16, /**< A signed 16-bit integer color. */
|
||||
ColorUInt16, /**< An unsigned 16-bit integer color. */
|
||||
ColorInt32, /**< A signed 32-bit integer color. */
|
||||
ColorUInt32, /**< An unsigned 32-bit integer color. */
|
||||
DepthStencil, /**< A depth and stencil value. */
|
||||
Compressed, /**< A block-compressed color. */
|
||||
};
|
||||
|
||||
typedef struct Extent2D {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} Extent2D;
|
||||
|
||||
/** Describes the properties of a DataFormat, including the corresponding Metal pixel and vertex format. */
|
||||
typedef struct DataFormatDesc {
|
||||
RD::DataFormat dataFormat;
|
||||
MTLPixelFormat mtlPixelFormat;
|
||||
MTLPixelFormat mtlPixelFormatSubstitute;
|
||||
MTLVertexFormat mtlVertexFormat;
|
||||
MTLVertexFormat mtlVertexFormatSubstitute;
|
||||
uint8_t chromaSubsamplingPlaneCount;
|
||||
uint8_t chromaSubsamplingComponentBits;
|
||||
Extent2D blockTexelSize;
|
||||
uint32_t bytesPerBlock;
|
||||
MTLFormatType formatType;
|
||||
const char *name;
|
||||
bool hasReportedSubstitution;
|
||||
|
||||
inline double bytesPerTexel() const { return (double)bytesPerBlock / (double)(blockTexelSize.width * blockTexelSize.height); }
|
||||
|
||||
inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid || chromaSubsamplingPlaneCount > 1); }
|
||||
inline bool isSupportedOrSubstitutable() const { return isSupported() || (mtlPixelFormatSubstitute != MTLPixelFormatInvalid); }
|
||||
|
||||
inline bool vertexIsSupported() const { return (mtlVertexFormat != MTLVertexFormatInvalid); }
|
||||
inline bool vertexIsSupportedOrSubstitutable() const { return vertexIsSupported() || (mtlVertexFormatSubstitute != MTLVertexFormatInvalid); }
|
||||
} DataFormatDesc;
|
||||
|
||||
/** Describes the properties of a MTLPixelFormat or MTLVertexFormat. */
|
||||
typedef struct MTLFormatDesc {
|
||||
union {
|
||||
MTLPixelFormat mtlPixelFormat;
|
||||
MTLVertexFormat mtlVertexFormat;
|
||||
};
|
||||
RD::DataFormat dataFormat;
|
||||
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 {
|
||||
using DataFormat = RD::DataFormat;
|
||||
|
||||
public:
|
||||
/** Returns whether the DataFormat is supported by the GPU bound to this instance. */
|
||||
bool isSupported(DataFormat p_format);
|
||||
|
||||
/** Returns whether the DataFormat is supported by this implementation, or can be substituted by one that is. */
|
||||
bool isSupportedOrSubstitutable(DataFormat p_format);
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat can be used as a depth format. */
|
||||
_FORCE_INLINE_ bool isDepthFormat(MTLPixelFormat p_format) {
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatDepth32Float:
|
||||
case MTLPixelFormatDepth16Unorm:
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
#if TARGET_OS_OSX
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
#endif
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat can be used as a stencil format. */
|
||||
_FORCE_INLINE_ bool isStencilFormat(MTLPixelFormat p_format) {
|
||||
switch (p_format) {
|
||||
case MTLPixelFormatStencil8:
|
||||
#if TARGET_OS_OSX
|
||||
case MTLPixelFormatDepth24Unorm_Stencil8:
|
||||
case MTLPixelFormatX24_Stencil8:
|
||||
#endif
|
||||
case MTLPixelFormatDepth32Float_Stencil8:
|
||||
case MTLPixelFormatX32_Stencil8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the specified Metal MTLPixelFormat is a PVRTC format. */
|
||||
bool isPVRTCFormat(MTLPixelFormat p_format);
|
||||
|
||||
/** Returns the format type corresponding to the specified Godot pixel format, */
|
||||
MTLFormatType getFormatType(DataFormat p_format);
|
||||
|
||||
/** Returns the format type corresponding to the specified Metal MTLPixelFormat, */
|
||||
MTLFormatType getFormatType(MTLPixelFormat p_formt);
|
||||
|
||||
/**
|
||||
* Returns the Metal MTLPixelFormat corresponding to the specified Godot pixel
|
||||
* or returns MTLPixelFormatInvalid if no corresponding MTLPixelFormat exists.
|
||||
*/
|
||||
MTLPixelFormat getMTLPixelFormat(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the DataFormat corresponding to the specified Metal MTLPixelFormat,
|
||||
* or returns DATA_FORMAT_MAX if no corresponding DataFormat exists.
|
||||
*/
|
||||
DataFormat getDataFormat(MTLPixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel block of the specified Godot pixel.
|
||||
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
|
||||
*/
|
||||
uint32_t getBytesPerBlock(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel block of the specified Metal format.
|
||||
* For uncompressed formats, the returned value corresponds to the size in bytes of a single texel.
|
||||
*/
|
||||
uint32_t getBytesPerBlock(MTLPixelFormat p_format);
|
||||
|
||||
/** Returns the number of planes of the specified chroma-subsampling (YCbCr) DataFormat */
|
||||
uint8_t getChromaSubsamplingPlaneCount(DataFormat p_format);
|
||||
|
||||
/** Returns the number of bits per channel of the specified chroma-subsampling (YCbCr) DataFormat */
|
||||
uint8_t getChromaSubsamplingComponentBits(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel of the specified Godot format.
|
||||
* The returned value may be fractional for certain compressed formats.
|
||||
*/
|
||||
float getBytesPerTexel(DataFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texel of the specified Metal format.
|
||||
* The returned value may be fractional for certain compressed formats.
|
||||
*/
|
||||
float getBytesPerTexel(MTLPixelFormat p_format);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a row of texels of the specified Godot pixel format.
|
||||
*
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and p_texels_per_row should specify the width in texels, not blocks. The result is rounded
|
||||
* up if p_texels_per_row is not an integer multiple of the compression block width.
|
||||
*/
|
||||
size_t getBytesPerRow(DataFormat p_format, uint32_t p_texels_per_row);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a row of texels of the specified Metal format.
|
||||
*
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and texelsPerRow should specify the width in texels, not blocks. The result is rounded
|
||||
* up if texelsPerRow is not an integer multiple of the compression block width.
|
||||
*/
|
||||
size_t getBytesPerRow(MTLPixelFormat p_format, uint32_t p_texels_per_row);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texture layer of the specified Godot pixel format.
|
||||
*
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
|
||||
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
|
||||
*/
|
||||
size_t getBytesPerLayer(DataFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
|
||||
|
||||
/**
|
||||
* Returns the size, in bytes, of a texture layer of the specified Metal format.
|
||||
* For compressed formats, this takes into consideration the compression block size,
|
||||
* and p_texel_rows_per_layer should specify the height in texels, not blocks. The result is
|
||||
* rounded up if p_texel_rows_per_layer is not an integer multiple of the compression block height.
|
||||
*/
|
||||
size_t getBytesPerLayer(MTLPixelFormat p_format, size_t p_bytes_per_row, uint32_t p_texel_rows_per_layer);
|
||||
|
||||
/** Returns the Metal format capabilities supported by the specified Godot format, without substitution. */
|
||||
MTLFmtCaps getCapabilities(DataFormat p_format, bool p_extended = false);
|
||||
|
||||
/** Returns the Metal format capabilities supported by the specified Metal format. */
|
||||
MTLFmtCaps getCapabilities(MTLPixelFormat p_format, bool p_extended = false);
|
||||
|
||||
/**
|
||||
* Returns the Metal MTLVertexFormat corresponding to the specified
|
||||
* DataFormat as used as a vertex attribute format.
|
||||
*/
|
||||
MTLVertexFormat getMTLVertexFormat(DataFormat p_format);
|
||||
|
||||
#pragma mark Construction
|
||||
|
||||
explicit PixelFormats(id<MTLDevice> p_device);
|
||||
|
||||
protected:
|
||||
id<MTLDevice> device;
|
||||
|
||||
DataFormatDesc &getDataFormatDesc(DataFormat p_format);
|
||||
DataFormatDesc &getDataFormatDesc(MTLPixelFormat p_format);
|
||||
MTLFormatDesc &getMTLPixelFormatDesc(MTLPixelFormat p_format);
|
||||
MTLFormatDesc &getMTLVertexFormatDesc(MTLVertexFormat p_format);
|
||||
void initDataFormatCapabilities();
|
||||
void initMTLPixelFormatCapabilities();
|
||||
void initMTLVertexFormatCapabilities();
|
||||
void buildMTLFormatMaps();
|
||||
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];
|
||||
};
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#endif // PIXEL_FORMATS_H
|
||||
1298
drivers/metal/pixel_formats.mm
Normal file
1298
drivers/metal/pixel_formats.mm
Normal file
File diff suppressed because it is too large
Load Diff
206
drivers/metal/rendering_context_driver_metal.h
Normal file
206
drivers/metal/rendering_context_driver_metal.h
Normal file
@ -0,0 +1,206 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERING_CONTEXT_DRIVER_METAL_H
|
||||
#define RENDERING_CONTEXT_DRIVER_METAL_H
|
||||
|
||||
#ifdef METAL_ENABLED
|
||||
|
||||
#import "rendering_device_driver_metal.h"
|
||||
|
||||
#import "servers/rendering/rendering_context_driver.h"
|
||||
|
||||
#import <CoreGraphics/CGGeometry.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CALayer.h>
|
||||
|
||||
@class CAMetalLayer;
|
||||
@protocol CAMetalDrawable;
|
||||
class PixelFormats;
|
||||
class MDResourceCache;
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingContextDriverMetal : public RenderingContextDriver {
|
||||
protected:
|
||||
id<MTLDevice> metal_device = nil;
|
||||
Device device; // There is only one device on Apple Silicon.
|
||||
|
||||
public:
|
||||
Error initialize() final override;
|
||||
const Device &device_get(uint32_t p_device_index) const final override;
|
||||
uint32_t device_get_count() const final override;
|
||||
bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const final override { return true; }
|
||||
RenderingDeviceDriver *driver_create() final override;
|
||||
void driver_free(RenderingDeviceDriver *p_driver) final override;
|
||||
SurfaceID surface_create(const void *p_platform_data) final override;
|
||||
void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) final override;
|
||||
void surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) final override;
|
||||
DisplayServer::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const final override;
|
||||
uint32_t surface_get_width(SurfaceID p_surface) const final override;
|
||||
uint32_t surface_get_height(SurfaceID p_surface) const final override;
|
||||
void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) final override;
|
||||
bool surface_get_needs_resize(SurfaceID p_surface) const final override;
|
||||
void surface_destroy(SurfaceID p_surface) final override;
|
||||
bool is_debug_utils_enabled() const final override { return true; }
|
||||
|
||||
#pragma mark - Metal-specific methods
|
||||
|
||||
// Platform-specific data for the Windows embedded in this driver.
|
||||
struct WindowPlatformData {
|
||||
CAMetalLayer *__unsafe_unretained layer;
|
||||
};
|
||||
|
||||
class Surface {
|
||||
protected:
|
||||
id<MTLDevice> device;
|
||||
|
||||
public:
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
|
||||
bool needs_resize = false;
|
||||
|
||||
Surface(id<MTLDevice> p_device) :
|
||||
device(p_device) {}
|
||||
virtual ~Surface() = default;
|
||||
|
||||
MTLPixelFormat get_pixel_format() const { return MTLPixelFormatBGRA8Unorm; }
|
||||
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
|
||||
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
|
||||
virtual void present(MDCommandBuffer *p_cmd_buffer) = 0;
|
||||
};
|
||||
|
||||
class SurfaceLayer : public Surface {
|
||||
CAMetalLayer *__unsafe_unretained layer = nil;
|
||||
LocalVector<MDFrameBuffer> frame_buffers;
|
||||
LocalVector<id<MTLDrawable>> drawables;
|
||||
uint32_t rear = -1;
|
||||
uint32_t front = 0;
|
||||
uint32_t count = 0;
|
||||
|
||||
public:
|
||||
SurfaceLayer(CAMetalLayer *p_layer, id<MTLDevice> p_device) :
|
||||
Surface(p_device), layer(p_layer) {
|
||||
layer.allowsNextDrawableTimeout = YES;
|
||||
layer.framebufferOnly = YES;
|
||||
layer.opaque = OS::get_singleton()->is_layered_allowed() ? NO : YES;
|
||||
layer.pixelFormat = get_pixel_format();
|
||||
layer.device = p_device;
|
||||
}
|
||||
|
||||
~SurfaceLayer() override {
|
||||
layer = nil;
|
||||
}
|
||||
|
||||
Error resize(uint32_t p_desired_framebuffer_count) override final {
|
||||
if (width == 0 || height == 0) {
|
||||
// Very likely the window is minimized, don't create a swap chain.
|
||||
return ERR_SKIP;
|
||||
}
|
||||
|
||||
CGSize drawableSize = CGSizeMake(width, height);
|
||||
CGSize current = layer.drawableSize;
|
||||
if (!CGSizeEqualToSize(current, drawableSize)) {
|
||||
layer.drawableSize = drawableSize;
|
||||
}
|
||||
|
||||
// Metal supports a maximum of 3 drawables.
|
||||
p_desired_framebuffer_count = MIN(3U, p_desired_framebuffer_count);
|
||||
layer.maximumDrawableCount = p_desired_framebuffer_count;
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
// Display sync is only supported on macOS.
|
||||
switch (vsync_mode) {
|
||||
case DisplayServer::VSYNC_MAILBOX:
|
||||
case DisplayServer::VSYNC_ADAPTIVE:
|
||||
case DisplayServer::VSYNC_ENABLED:
|
||||
layer.displaySyncEnabled = YES;
|
||||
break;
|
||||
case DisplayServer::VSYNC_DISABLED:
|
||||
layer.displaySyncEnabled = NO;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
drawables.resize(p_desired_framebuffer_count);
|
||||
frame_buffers.resize(p_desired_framebuffer_count);
|
||||
for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) {
|
||||
// Reserve space for the drawable texture.
|
||||
frame_buffers[i].textures.resize(1);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
RDD::FramebufferID acquire_next_frame_buffer() override final {
|
||||
if (count == frame_buffers.size()) {
|
||||
return RDD::FramebufferID();
|
||||
}
|
||||
|
||||
rear = (rear + 1) % frame_buffers.size();
|
||||
count++;
|
||||
|
||||
MDFrameBuffer &frame_buffer = frame_buffers[rear];
|
||||
frame_buffer.size = Size2i(width, height);
|
||||
|
||||
id<CAMetalDrawable> drawable = layer.nextDrawable;
|
||||
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
|
||||
drawables[rear] = drawable;
|
||||
frame_buffer.textures.write[0] = drawable.texture;
|
||||
|
||||
return RDD::FramebufferID(&frame_buffer);
|
||||
}
|
||||
|
||||
void present(MDCommandBuffer *p_cmd_buffer) override final {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Release texture and drawable.
|
||||
frame_buffers[front].textures.write[0] = nil;
|
||||
id<MTLDrawable> drawable = drawables[front];
|
||||
drawables[front] = nil;
|
||||
|
||||
count--;
|
||||
front = (front + 1) % frame_buffers.size();
|
||||
|
||||
[p_cmd_buffer->get_command_buffer() presentDrawable:drawable];
|
||||
}
|
||||
};
|
||||
|
||||
id<MTLDevice> get_metal_device() const { return metal_device; }
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
RenderingContextDriverMetal();
|
||||
~RenderingContextDriverMetal() override;
|
||||
};
|
||||
|
||||
#endif // METAL_ENABLED
|
||||
|
||||
#endif // RENDERING_CONTEXT_DRIVER_METAL_H
|
||||
134
drivers/metal/rendering_context_driver_metal.mm
Normal file
134
drivers/metal/rendering_context_driver_metal.mm
Normal file
@ -0,0 +1,134 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_context_driver_metal.h"
|
||||
|
||||
@protocol MTLDeviceEx <MTLDevice>
|
||||
#if TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 130300
|
||||
- (void)setShouldMaximizeConcurrentCompilation:(BOOL)v;
|
||||
#endif
|
||||
@end
|
||||
|
||||
RenderingContextDriverMetal::RenderingContextDriverMetal() {
|
||||
}
|
||||
|
||||
RenderingContextDriverMetal::~RenderingContextDriverMetal() {
|
||||
}
|
||||
|
||||
Error RenderingContextDriverMetal::initialize() {
|
||||
metal_device = MTLCreateSystemDefaultDevice();
|
||||
#if TARGET_OS_OSX
|
||||
if (@available(macOS 13.3, *)) {
|
||||
[id<MTLDeviceEx>(metal_device) setShouldMaximizeConcurrentCompilation:YES];
|
||||
}
|
||||
#endif
|
||||
device.type = DEVICE_TYPE_INTEGRATED_GPU;
|
||||
device.vendor = VENDOR_APPLE;
|
||||
device.workarounds = Workarounds();
|
||||
|
||||
MetalDeviceProperties props(metal_device);
|
||||
int version = (int)props.features.highestFamily - (int)MTLGPUFamilyApple1 + 1;
|
||||
device.name = vformat("%s (Apple%d)", metal_device.name.UTF8String, version);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
const RenderingContextDriver::Device &RenderingContextDriverMetal::device_get(uint32_t p_device_index) const {
|
||||
DEV_ASSERT(p_device_index < 1);
|
||||
return device;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverMetal::device_get_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
RenderingDeviceDriver *RenderingContextDriverMetal::driver_create() {
|
||||
return memnew(RenderingDeviceDriverMetal(this));
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::driver_free(RenderingDeviceDriver *p_driver) {
|
||||
memdelete(p_driver);
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverMetal::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
Surface *surface = memnew(SurfaceLayer(wpd->layer, metal_device));
|
||||
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
if (surface->width == p_width && surface->height == p_height) {
|
||||
return;
|
||||
}
|
||||
surface->width = p_width;
|
||||
surface->height = p_height;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_set_vsync_mode(SurfaceID p_surface, DisplayServer::VSyncMode p_vsync_mode) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
if (surface->vsync_mode == p_vsync_mode) {
|
||||
return;
|
||||
}
|
||||
surface->vsync_mode = p_vsync_mode;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->vsync_mode;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverMetal::surface_get_width(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->width;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverMetal::surface_get_height(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->height;
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->needs_resize = p_needs_resize;
|
||||
}
|
||||
|
||||
bool RenderingContextDriverMetal::surface_get_needs_resize(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->needs_resize;
|
||||
}
|
||||
|
||||
void RenderingContextDriverMetal::surface_destroy(SurfaceID p_surface) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
memdelete(surface);
|
||||
}
|
||||
417
drivers/metal/rendering_device_driver_metal.h
Normal file
417
drivers/metal/rendering_device_driver_metal.h
Normal file
@ -0,0 +1,417 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_device_driver_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RENDERING_DEVICE_DRIVER_METAL_H
|
||||
#define RENDERING_DEVICE_DRIVER_METAL_H
|
||||
|
||||
#import "metal_objects.h"
|
||||
|
||||
#import "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <spirv.hpp>
|
||||
#import <variant>
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#ifndef _DEBUG
|
||||
#define _DEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class RenderingContextDriverMetal;
|
||||
|
||||
class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingDeviceDriverMetal : public RenderingDeviceDriver {
|
||||
template <typename T>
|
||||
using Result = std::variant<T, Error>;
|
||||
|
||||
#pragma mark - Generic
|
||||
|
||||
RenderingContextDriverMetal *context_driver = nullptr;
|
||||
RenderingContextDriver::Device context_device;
|
||||
id<MTLDevice> device = nil;
|
||||
|
||||
uint32_t version_major = 2;
|
||||
uint32_t version_minor = 0;
|
||||
MetalDeviceProperties *metal_device_properties = nullptr;
|
||||
PixelFormats *pixel_formats = nullptr;
|
||||
std::unique_ptr<MDResourceCache> resource_cache;
|
||||
|
||||
RDD::Capabilities capabilities;
|
||||
RDD::MultiviewCapabilities multiview_capabilities;
|
||||
|
||||
id<MTLBinaryArchive> archive = nil;
|
||||
uint32_t archive_count = 0;
|
||||
|
||||
id<MTLCommandQueue> device_queue = nil;
|
||||
id<MTLCaptureScope> device_scope = nil;
|
||||
|
||||
String pipeline_cache_id;
|
||||
|
||||
Error _create_device();
|
||||
Error _check_capabilities();
|
||||
|
||||
public:
|
||||
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final;
|
||||
|
||||
#pragma mark - Memory
|
||||
|
||||
#pragma mark - Buffers
|
||||
|
||||
public:
|
||||
virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) override final;
|
||||
virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final;
|
||||
virtual void buffer_free(BufferID p_buffer) override final;
|
||||
virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final;
|
||||
virtual uint8_t *buffer_map(BufferID p_buffer) override final;
|
||||
virtual void buffer_unmap(BufferID p_buffer) override final;
|
||||
|
||||
#pragma mark - Texture
|
||||
|
||||
private:
|
||||
// Returns true if the texture is a valid linear format.
|
||||
Result<bool> is_valid_linear(TextureFormat const &p_format) const;
|
||||
void _get_sub_resource(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) const;
|
||||
|
||||
public:
|
||||
virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final;
|
||||
virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil) override final;
|
||||
virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final;
|
||||
virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final;
|
||||
virtual void texture_free(TextureID p_texture) override final;
|
||||
virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final;
|
||||
virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final;
|
||||
virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final;
|
||||
virtual void texture_unmap(TextureID p_texture) override final;
|
||||
virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;
|
||||
virtual bool texture_can_make_shared_with_format(TextureID p_texture, DataFormat p_format, bool &r_raw_reinterpretation) override final;
|
||||
|
||||
#pragma mark - Sampler
|
||||
|
||||
public:
|
||||
virtual SamplerID sampler_create(const SamplerState &p_state) final override;
|
||||
virtual void sampler_free(SamplerID p_sampler) final override;
|
||||
virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final;
|
||||
|
||||
#pragma mark - Vertex Array
|
||||
|
||||
private:
|
||||
public:
|
||||
virtual VertexFormatID vertex_format_create(VectorView<VertexAttribute> p_vertex_attribs) override final;
|
||||
virtual void vertex_format_free(VertexFormatID p_vertex_format) override final;
|
||||
|
||||
#pragma mark - Barriers
|
||||
|
||||
virtual void command_pipeline_barrier(
|
||||
CommandBufferID p_cmd_buffer,
|
||||
BitField<PipelineStageBits> p_src_stages,
|
||||
BitField<PipelineStageBits> p_dst_stages,
|
||||
VectorView<MemoryBarrier> p_memory_barriers,
|
||||
VectorView<BufferBarrier> p_buffer_barriers,
|
||||
VectorView<TextureBarrier> p_texture_barriers) override final;
|
||||
|
||||
#pragma mark - Fences
|
||||
|
||||
private:
|
||||
struct Fence {
|
||||
dispatch_semaphore_t semaphore;
|
||||
Fence() :
|
||||
semaphore(dispatch_semaphore_create(0)) {}
|
||||
};
|
||||
|
||||
public:
|
||||
virtual FenceID fence_create() override final;
|
||||
virtual Error fence_wait(FenceID p_fence) override final;
|
||||
virtual void fence_free(FenceID p_fence) override final;
|
||||
|
||||
#pragma mark - Semaphores
|
||||
|
||||
public:
|
||||
virtual SemaphoreID semaphore_create() override final;
|
||||
virtual void semaphore_free(SemaphoreID p_semaphore) override final;
|
||||
|
||||
#pragma mark - Commands
|
||||
// ----- QUEUE FAMILY -----
|
||||
|
||||
virtual CommandQueueFamilyID command_queue_family_get(BitField<CommandQueueFamilyBits> p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override final;
|
||||
|
||||
// ----- QUEUE -----
|
||||
public:
|
||||
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override final;
|
||||
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override final;
|
||||
virtual void command_queue_free(CommandQueueID p_cmd_queue) override final;
|
||||
|
||||
// ----- POOL -----
|
||||
|
||||
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
|
||||
|
||||
// ----- BUFFER -----
|
||||
|
||||
private:
|
||||
// Used to maintain references.
|
||||
Vector<MDCommandBuffer *> command_buffers;
|
||||
|
||||
public:
|
||||
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
|
||||
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
|
||||
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) override final;
|
||||
|
||||
#pragma mark - Swapchain
|
||||
|
||||
private:
|
||||
struct SwapChain {
|
||||
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
|
||||
RenderPassID render_pass;
|
||||
RDD::DataFormat data_format = DATA_FORMAT_MAX;
|
||||
SwapChain() :
|
||||
render_pass(nullptr) {}
|
||||
};
|
||||
|
||||
void _swap_chain_release(SwapChain *p_swap_chain);
|
||||
void _swap_chain_release_buffers(SwapChain *p_swap_chain);
|
||||
|
||||
public:
|
||||
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override final;
|
||||
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override final;
|
||||
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
|
||||
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
|
||||
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
|
||||
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
|
||||
|
||||
#pragma mark - Frame Buffer
|
||||
|
||||
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
|
||||
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
|
||||
|
||||
#pragma mark - Shader
|
||||
|
||||
private:
|
||||
// Serialization types need access to private state.
|
||||
|
||||
friend struct ShaderStageData;
|
||||
friend struct SpecializationConstantData;
|
||||
friend struct UniformData;
|
||||
friend struct ShaderBinaryData;
|
||||
friend struct PushConstantData;
|
||||
|
||||
private:
|
||||
Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection);
|
||||
|
||||
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) override final;
|
||||
virtual void shader_free(ShaderID p_shader) override final;
|
||||
|
||||
#pragma mark - Uniform Set
|
||||
|
||||
public:
|
||||
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
virtual void uniform_set_free(UniformSetID p_uniform_set) override final;
|
||||
|
||||
#pragma mark - Commands
|
||||
|
||||
virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
#pragma mark Transfer
|
||||
|
||||
private:
|
||||
enum class CopySource {
|
||||
Buffer,
|
||||
Texture,
|
||||
};
|
||||
void _copy_texture_buffer(CommandBufferID p_cmd_buffer,
|
||||
CopySource p_source,
|
||||
TextureID p_texture,
|
||||
BufferID p_buffer,
|
||||
VectorView<BufferTextureCopyRegion> p_regions);
|
||||
|
||||
public:
|
||||
virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final;
|
||||
virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final;
|
||||
|
||||
virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final;
|
||||
virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final;
|
||||
virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final;
|
||||
|
||||
virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final;
|
||||
virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final;
|
||||
|
||||
#pragma mark Pipeline
|
||||
|
||||
private:
|
||||
Result<id<MTLFunction>> _create_function(id<MTLLibrary> p_library, NSString *p_name, VectorView<PipelineSpecializationConstant> &p_specialization_constants);
|
||||
|
||||
public:
|
||||
virtual void pipeline_free(PipelineID p_pipeline_id) override final;
|
||||
|
||||
// ----- BINDING -----
|
||||
|
||||
virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_first_index, VectorView<uint32_t> p_data) override final;
|
||||
|
||||
// ----- CACHE -----
|
||||
private:
|
||||
String _pipeline_get_cache_path() const;
|
||||
|
||||
public:
|
||||
virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final;
|
||||
virtual void pipeline_cache_free() override final;
|
||||
virtual size_t pipeline_cache_query_size() override final;
|
||||
virtual Vector<uint8_t> pipeline_cache_serialize() override final;
|
||||
|
||||
#pragma mark Rendering
|
||||
|
||||
// ----- SUBPASS -----
|
||||
|
||||
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count) override final;
|
||||
virtual void render_pass_free(RenderPassID p_render_pass) override final;
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
public:
|
||||
virtual void command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) override final;
|
||||
virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final;
|
||||
virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final;
|
||||
virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final;
|
||||
|
||||
// Binding.
|
||||
virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
|
||||
virtual void command_bind_render_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
// Drawing.
|
||||
virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final;
|
||||
virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final;
|
||||
virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
|
||||
|
||||
// Buffer binding.
|
||||
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets) override final;
|
||||
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
|
||||
|
||||
// Dynamic state.
|
||||
virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final;
|
||||
virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
virtual PipelineID render_pipeline_create(
|
||||
ShaderID p_shader,
|
||||
VertexFormatID p_vertex_format,
|
||||
RenderPrimitive p_render_primitive,
|
||||
PipelineRasterizationState p_rasterization_state,
|
||||
PipelineMultisampleState p_multisample_state,
|
||||
PipelineDepthStencilState p_depth_stencil_state,
|
||||
PipelineColorBlendState p_blend_state,
|
||||
VectorView<int32_t> p_color_attachments,
|
||||
BitField<PipelineDynamicStateFlags> p_dynamic_state,
|
||||
RenderPassID p_render_pass,
|
||||
uint32_t p_render_subpass,
|
||||
VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
|
||||
#pragma mark - Compute
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
// Binding.
|
||||
virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
|
||||
virtual void command_bind_compute_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
// Dispatching.
|
||||
virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
|
||||
virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
|
||||
#pragma mark - Queries
|
||||
|
||||
// ----- TIMESTAMP -----
|
||||
|
||||
// Basic.
|
||||
virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final;
|
||||
virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final;
|
||||
virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final;
|
||||
virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final;
|
||||
|
||||
// Commands.
|
||||
virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final;
|
||||
virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final;
|
||||
|
||||
#pragma mark - Labels
|
||||
|
||||
virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
|
||||
virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
|
||||
|
||||
#pragma mark - Submission
|
||||
|
||||
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
|
||||
virtual void end_segment() override final;
|
||||
|
||||
#pragma mark - Miscellaneous
|
||||
|
||||
virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final;
|
||||
virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final;
|
||||
virtual uint64_t get_total_memory_used() override final;
|
||||
virtual uint64_t limit_get(Limit p_limit) override final;
|
||||
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
|
||||
virtual bool has_feature(Features p_feature) override final;
|
||||
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
|
||||
virtual String get_api_name() const override final { return "Metal"; };
|
||||
virtual String get_api_version() const override final;
|
||||
virtual String get_pipeline_cache_uuid() const override final;
|
||||
virtual const Capabilities &get_capabilities() const override final;
|
||||
virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
|
||||
|
||||
// Metal-specific.
|
||||
id<MTLDevice> get_device() const { return device; }
|
||||
PixelFormats &get_pixel_formats() const { return *pixel_formats; }
|
||||
MDResourceCache &get_resource_cache() const { return *resource_cache; }
|
||||
MetalDeviceProperties const &get_device_properties() const { return *metal_device_properties; }
|
||||
|
||||
_FORCE_INLINE_ uint32_t get_metal_buffer_index_for_vertex_attribute_binding(uint32_t p_binding) {
|
||||
return (metal_device_properties->limits.maxPerStageBufferCount - 1) - p_binding;
|
||||
}
|
||||
|
||||
size_t get_texel_buffer_alignment_for_format(RDD::DataFormat p_format) const;
|
||||
size_t get_texel_buffer_alignment_for_format(MTLPixelFormat p_format) const;
|
||||
|
||||
/******************/
|
||||
RenderingDeviceDriverMetal(RenderingContextDriverMetal *p_context_driver);
|
||||
~RenderingDeviceDriverMetal();
|
||||
};
|
||||
|
||||
#endif // RENDERING_DEVICE_DRIVER_METAL_H
|
||||
3883
drivers/metal/rendering_device_driver_metal.mm
Normal file
3883
drivers/metal/rendering_device_driver_metal.mm
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user