Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Metal support for macOS (arm64) and iOS #88199

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/godot-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ inputs:
tests:
description: Unit tests.
default: false
required: false
platform:
description: Target platform.
required: false
sconsflags:
description: Additional SCons flags.
default: ""
required: false
scons-cache:
description: The SCons cache path.
default: "${{ github.workspace }}/.scons-cache/"
Expand Down
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,11 @@ Comment: RVO2
Copyright: 2016, University of North Carolina at Chapel Hill
License: Apache-2.0

Files: ./thirdparty/spirv-cross/
Comment: SPIRV-Cross
Copyright: 2015-2021, Arm Limited
License: Apache-2.0 or Expat
AThousandShips marked this conversation as resolved.
Show resolved Hide resolved

Files: ./thirdparty/spirv-reflect/
Comment: SPIRV-Reflect
Copyright: 2017-2022, Google Inc.
Expand Down
1 change: 1 addition & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver", False))
opts.Add(BoolVariable("metal", "Enable the Metal rendering driver (Apple arm64 only)", False))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider enabling metal by default sooner than later IMO, as it should probably be our preferred RenderingDevice implementation for macOS and iOS.

d3d12 above defaults to False because it adds some build dependencies and we also used to have to ship a proprietary DLL for it (now solved), but I guess for Metal that may not be a concern, all deps should be part of the macOS and iOS SDKs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm given later considerations on the lack of support for metal on non-Apple GPUs, this should probably stay opt-in for now indeed.

I wonder whether it will work for official universal builds with arm64 and x86_64 lipo'd together?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akien-mga yes, it should work with lipo'd builds, as they have no dependencies on each other.

Perhaps we can enable dynamically at build time, such that ARM builds have Metal by default?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO ARM builds should have metal onby default while X86 builds should have it off by default

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree – best way to start getting it tested 👍🏻

opts.Add(BoolVariable("openxr", "Enable the OpenXR driver", True))
opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
Expand Down
1 change: 1 addition & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ void OS::_bind_methods() {
BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN);
BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3);
BIND_ENUM_CONSTANT(RENDERING_DRIVER_D3D12);
BIND_ENUM_CONSTANT(RENDERING_DRIVER_METAL);

BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP);
BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM);
Expand Down
1 change: 1 addition & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class OS : public Object {
RENDERING_DRIVER_VULKAN,
RENDERING_DRIVER_OPENGL3,
RENDERING_DRIVER_D3D12,
RENDERING_DRIVER_METAL,
};

PackedByteArray get_entropy(int p_bytes);
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/OS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,9 @@
<constant name="RENDERING_DRIVER_D3D12" value="2" enum="RenderingDriver">
The Direct3D 12 rendering driver.
</constant>
<constant name="RENDERING_DRIVER_METAL" value="3" enum="RenderingDriver">
The Metal rendering driver.
</constant>
Comment on lines +805 to +807
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this to be consistent with RENDERING_DRIVER_D3D12, but I don't see how this is used anywhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't seem used anywhere indeed, we could flag this enum as deprecated.

It used to be used in 3.x for OS::get_current_video_driver() (7a79eee), and was refactored for 4.x in f82deaa, but the API that used it no longer exists. I'm not sure what replaced it in 4.x, CC @Calinou.

We also have this piece of dead code to remove eventually:

editor/plugins/node_3d_editor_plugin.cpp
5339:   //if (OS::get_singleton()->get_current_video_driver() == OS::RENDERING_DRIVER_OPENGL3) {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what replaced it in 4.x, CC @Calinou.

We seem to have:

core/os/os.h:
        String get_current_rendering_driver_name() const { return _current_rendering_driver_name; }
        String get_current_rendering_method() const { return _current_rendering_method; }

but not exposed to scripting.

Copy link
Member

@Calinou Calinou Aug 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a PR to expose these: #85430

It's awaiting review (specifically about moving it from OS to Engine). It's not exposed currently, so moving it doesn't break compatibility.

<constant name="SYSTEM_DIR_DESKTOP" value="0" enum="SystemDir">
Refers to the Desktop directory path.
</constant>
Expand Down
2 changes: 2 additions & 0 deletions drivers/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ if env["opengl3"]:
SConscript("gl_context/SCsub")
SConscript("gles3/SCsub")
SConscript("egl/SCsub")
if env["metal"]:
SConscript("metal/SCsub")

# Core dependencies
SConscript("png/SCsub")
Expand Down
39 changes: 39 additions & 0 deletions drivers/metal/README.md
Original file line number Diff line number Diff line change
@@ -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.
akien-mga marked this conversation as resolved.
Show resolved Hide resolved
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 changes: 49 additions & 0 deletions drivers/metal/SCsub
Original file line number Diff line number Diff line change
@@ -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 changes: 141 additions & 0 deletions drivers/metal/metal_device_properties.h
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading