Skip to content

Commit

Permalink
Metal: Add MetalFX upscaling support
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartcarnie committed Nov 26, 2024
1 parent 0c45ace commit 09231d1
Show file tree
Hide file tree
Showing 32 changed files with 989 additions and 47 deletions.
19 changes: 19 additions & 0 deletions core/typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define TYPEDEFS_H

#include <stddef.h>
#include <string.h>

/**
* Basic definitions and simple functions to be used everywhere.
Expand All @@ -43,6 +44,7 @@
// Should be available everywhere.
#include "core/error/error_list.h"
#include <cstdint>
#include <type_traits>

// Ensure that C++ standard is at least C++17. If on MSVC, also ensures that the `Zc:__cplusplus` flag is present.
static_assert(__cplusplus >= 201703L);
Expand Down Expand Up @@ -127,6 +129,23 @@ constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) {
return m_a < m_min ? m_min : (m_a > m_max ? m_max : m_a);
}

template <class To, class From>
std::enable_if_t<
sizeof(To) == sizeof(From) &&
std::is_trivially_copyable_v<From> &&
std::is_trivially_copyable_v<To>,
To>
// constexpr support needs compiler magic
bit_cast(const From &src) noexcept {
static_assert(std::is_trivially_constructible_v<To>,
"This implementation additionally requires "
"destination type to be trivially constructible");

To dst;
memcpy(&dst, &src, sizeof(To));
return dst;
}

// Generic swap template.
#ifndef SWAP
#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y))
Expand Down
8 changes: 7 additions & 1 deletion doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4917,7 +4917,13 @@
<constant name="VIEWPORT_SCALING_3D_MODE_FSR2" value="2" enum="ViewportScaling3DMode">
Use AMD FidelityFX Super Resolution 2.2 upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using FSR2. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] will use FSR2 at native resolution as a TAA solution.
</constant>
<constant name="VIEWPORT_SCALING_3D_MODE_MAX" value="3" enum="ViewportScaling3DMode">
<constant name="VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL" value="3" enum="ViewportScaling3DMode">
Use MetalFX spatial upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using MetalFX. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] disables scaling.
</constant>
<constant name="VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL" value="4" enum="ViewportScaling3DMode">
Use MetalFX temporal upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using MetalFX. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] will use MetalFX at native resolution as a TAA solution.
</constant>
<constant name="VIEWPORT_SCALING_3D_MODE_MAX" value="5" enum="ViewportScaling3DMode">
Represents the size of the [enum ViewportScaling3DMode] enum.
</constant>
<constant name="VIEWPORT_UPDATE_DISABLED" value="0" enum="ViewportUpdateMode">
Expand Down
8 changes: 7 additions & 1 deletion doc/classes/Viewport.xml
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,13 @@
<constant name="SCALING_3D_MODE_FSR2" value="2" enum="Scaling3DMode">
Use AMD FidelityFX Super Resolution 2.2 upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member Viewport.scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using FSR2. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] will use FSR2 at native resolution as a TAA solution.
</constant>
<constant name="SCALING_3D_MODE_MAX" value="3" enum="Scaling3DMode">
<constant name="SCALING_3D_MODE_METALFX_SPATIAL" value="3" enum="ViewportScaling3DMode">
Use MetalFX spatial upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using MetalFX. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] disables scaling.
</constant>
<constant name="SCALING_3D_MODE_METALFX_TEMPORAL" value="4" enum="ViewportScaling3DMode">
Use MetalFX temporal upscaling for the viewport's 3D buffer. The amount of scaling can be set using [member scaling_3d_scale]. Values less than [code]1.0[/code] will be result in the viewport being upscaled using MetalFX. Values greater than [code]1.0[/code] are not supported and bilinear downsampling will be used instead. A value of [code]1.0[/code] will use MetalFX at native resolution as a TAA solution.
</constant>
<constant name="SCALING_3D_MODE_MAX" value="5" enum="Scaling3DMode">
Represents the size of the [enum Scaling3DMode] enum.
</constant>
<constant name="MSAA_DISABLED" value="0" enum="MSAA">
Expand Down
29 changes: 17 additions & 12 deletions drivers/metal/metal_device_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,20 @@ typedef NS_OPTIONS(NSUInteger, SampleCount) {
};

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. */
uint32_t mslVersion = 0;
MTLGPUFamily highestFamily = MTLGPUFamilyApple6;
MTLLanguageVersion mslVersionEnum = MTLLanguageVersion1_2;
SampleCount supportedSampleCounts = SampleCount1;
long hostMemoryPageSize = 0;
bool layeredRendering = false;
bool multisampleLayeredRendering = false;
bool quadPermute = false; /**< If true, quadgroup permutation functions (vote, ballot, shuffle) are supported in shaders. */
bool simdPermute = false; /**< If true, SIMD-group permutation functions (vote, ballot, shuffle) are supported in shaders. */
bool simdReduction = false; /**< If true, SIMD-group reduction functions (arithmetic) are supported in shaders. */
bool tessellationShader = false; /**< If true, tessellation shaders are supported. */
bool imageCubeArray = false; /**< If true, image cube arrays are supported. */
bool metal_fx_spatial = false; /**< If true, Metal FX spatial functions are supported. */
bool metal_fx_temporal = false; /**< If true, Metal FX temporal functions are supported. */
};

struct MetalLimits {
Expand Down Expand Up @@ -114,6 +116,9 @@ struct MetalLimits {
uint32_t maxVertexInputBindingStride;
uint32_t maxDrawIndexedIndexValue;

double temporalScalerInputContentMinScale;
double temporalScalerInputContentMaxScale;

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;
Expand Down
15 changes: 15 additions & 0 deletions drivers/metal/metal_device_properties.mm
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#import "metal_device_properties.h"

#import <Metal/Metal.h>
#import <MetalFX/MetalFX.h>
#import <spirv_cross.hpp>
#import <spirv_msl.hpp>

Expand Down Expand Up @@ -99,6 +100,11 @@
features.simdPermute = [p_device supportsFamily:MTLGPUFamilyApple6];
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];

if (@available(macOS 13.0, iOS 16.0, *)) {
features.metal_fx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:p_device];
features.metal_fx_temporal = [MTLFXTemporalScalerDescriptor supportsDevice:p_device];
}

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

Expand Down Expand Up @@ -284,6 +290,15 @@
#endif

limits.maxDrawIndexedIndexValue = std::numeric_limits<uint32_t>::max() - 1;

if (@available(macOS 14.0, iOS 17.0, *)) {
limits.temporalScalerInputContentMinScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMinScaleForDevice:p_device];
limits.temporalScalerInputContentMaxScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMaxScaleForDevice:p_device];
} else {
// defaults taken from macOS 14+
limits.temporalScalerInputContentMinScale = 1.0;
limits.temporalScalerInputContentMaxScale = 3.0;
}
}

MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
Expand Down
14 changes: 12 additions & 2 deletions drivers/metal/rendering_device_driver_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3869,11 +3869,17 @@ bool isArrayTexture(MTLTextureType p_type) {
case LIMIT_SUBGROUP_MAX_SIZE:
return limits.maxSubgroupSize;
case LIMIT_SUBGROUP_IN_SHADERS:
return (int64_t)limits.subgroupSupportedShaderStages;
return (uint64_t)limits.subgroupSupportedShaderStages;
case LIMIT_SUBGROUP_OPERATIONS:
return (int64_t)limits.subgroupSupportedOperations;
return (uint64_t)limits.subgroupSupportedOperations;
case LIMIT_METALFX_TEMPORAL_SCALER_MIN_INPUT_SCALE:
return bit_cast<uint64_t, double>(limits.temporalScalerInputContentMinScale);
case LIMIT_METALFX_TEMPORAL_SCALER_MAX_INPUT_SCALE:
return bit_cast<uint64_t, double>(limits.temporalScalerInputContentMaxScale);
UNKNOWN(LIMIT_VRS_TEXEL_WIDTH);
UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT);
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH);
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_HEIGHT);
default:
ERR_FAIL_V(0);
}
Expand Down Expand Up @@ -3905,6 +3911,10 @@ bool isArrayTexture(MTLTextureType p_type) {
return false;
case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS:
return true;
case SUPPORTS_METALFX_SPATIAL:
return metal_device_properties->features.metal_fx_spatial;
case SUPPORTS_METALFX_TEMPORAL:
return metal_device_properties->features.metal_fx_temporal;
default:
return false;
}
Expand Down
1 change: 1 addition & 0 deletions platform/ios/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def configure(env: "SConsEnvironment"):
env.Prepend(
CPPPATH=[
"$IOS_SDK_PATH/System/Library/Frameworks/Metal.framework/Headers",
"$IOS_SDK_PATH/System/Library/Frameworks/MetalFX.framework/Headers",
"$IOS_SDK_PATH/System/Library/Frameworks/QuartzCore.framework/Headers",
]
)
Expand Down
1 change: 1 addition & 0 deletions platform/macos/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def configure(env: "SConsEnvironment"):
env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
extra_frameworks.add("Metal")
extra_frameworks.add("MetalKit")
extra_frameworks.add("MetalFX")
env.Prepend(CPPPATH=["#thirdparty/spirv-cross"])

if env["vulkan"]:
Expand Down
4 changes: 3 additions & 1 deletion scene/main/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4821,7 +4821,7 @@ void Viewport::_bind_methods() {

#ifndef _3D_DISABLED
ADD_GROUP("Scaling 3D", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow),MetalFX (Spatial),MetalFX (Temporal)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
Expand Down Expand Up @@ -4875,6 +4875,8 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(SCALING_3D_MODE_BILINEAR);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR2);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_METALFX_SPATIAL);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_METALFX_TEMPORAL);
BIND_ENUM_CONSTANT(SCALING_3D_MODE_MAX);

BIND_ENUM_CONSTANT(MSAA_DISABLED);
Expand Down
2 changes: 2 additions & 0 deletions scene/main/viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class Viewport : public Node {
SCALING_3D_MODE_BILINEAR,
SCALING_3D_MODE_FSR,
SCALING_3D_MODE_FSR2,
SCALING_3D_MODE_METALFX_SPATIAL,
SCALING_3D_MODE_METALFX_TEMPORAL,
SCALING_3D_MODE_MAX
};

Expand Down
2 changes: 2 additions & 0 deletions servers/rendering/renderer_rd/effects/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ env.servers_sources += thirdparty_obj
module_obj = []

env_effects.add_source_files(module_obj, "*.cpp")
if env["metal"]:
env_effects.add_source_files(module_obj, "metal_fx.mm")
env.servers_sources += module_obj

# Needed to force rebuilding the module files when the thirdparty library is updated.
Expand Down
145 changes: 145 additions & 0 deletions servers/rendering/renderer_rd/effects/metal_fx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**************************************************************************/
/* metal_fx.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_FX_RD_H
#define METAL_FX_RD_H

#ifdef METAL_ENABLED

#include "servers/rendering/renderer_scene_render.h"

#ifdef __OBJC__
@protocol MTLFXSpatialScaler;
@protocol MTLFXTemporalScaler;
#endif

namespace RendererRD {

struct MFXSpatialContext {
#ifdef __OBJC__
id<MTLFXSpatialScaler> scaler = nullptr;
#else
void *scaler = nullptr;
#endif
MFXSpatialContext() = default;
~MFXSpatialContext();
};

class MFXSpatialEffect {
static const size_t RID_COUNT = 3;

struct CallbackArgs {
RDD::TextureID src;
RDD::TextureID dst;
MFXSpatialContext ctx;
};

uint32_t i = 0;
CallbackArgs args[RID_COUNT];
static void callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata);

public:
MFXSpatialEffect();
~MFXSpatialEffect();

struct CreateParams {
Vector2i input_size;
Vector2i output_size;
RD::DataFormat input_format;
RD::DataFormat output_format;
};

MFXSpatialContext *create_context(CreateParams p_params) const;
void process(MFXSpatialContext *p_ctx, RID p_src, RID p_dst);
};

struct MFXTemporalContext {
#ifdef __OBJC__
id<MTLFXTemporalScaler> scaler = nullptr;
#else
void *scaler = nullptr;
#endif
MFXTemporalContext() = default;
~MFXTemporalContext();
};

class MFXTemporalEffect {
static const size_t RID_COUNT = 3;

struct CallbackArgs {
RDD::TextureID src;
RDD::TextureID depth;
RDD::TextureID motion;
RDD::TextureID exposure;
Vector2 jitter_offset;
RDD::TextureID dst;
MFXTemporalContext ctx;
bool reset = false;
};

uint32_t i = 0;
CallbackArgs args[RID_COUNT];
static void callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata);

public:
MFXTemporalEffect();
~MFXTemporalEffect();

struct CreateParams {
Vector2i input_size;
Vector2i output_size;
RD::DataFormat input_format;
RD::DataFormat depth_format;
RD::DataFormat motion_format;
RD::DataFormat reactive_format;
RD::DataFormat output_format;
Vector2 motion_vector_scale;
};

MFXTemporalContext *create_context(CreateParams p_params) const;

struct Params {
RID src;
RID depth;
RID motion;
RID exposure;
RID dst;
Vector2 jitter_offset;
bool reset = false;
};

void process(MFXTemporalContext *p_ctx, Params p_params);
};

} //namespace RendererRD

#endif // METAL_ENABLED

#endif // METAL_FX_RD_H
Loading

0 comments on commit 09231d1

Please sign in to comment.