Metal: Refactor for future Metal 4, switch to C++; fix dynamic uniforms

This commit is contained in:
Stuart Carnie
2026-01-12 10:12:14 +11:00
parent 31d79ae53e
commit b815c88dec
40 changed files with 6433 additions and 5674 deletions

View File

@@ -7,6 +7,10 @@ Import("env")
env_effects = env.Clone()
# metal-cpp headers for Metal FX
if env["metal"]:
env_effects.Prepend(CPPPATH=["#thirdparty/metal-cpp"])
# Thirdparty source files
thirdparty_obj = []
@@ -69,8 +73,6 @@ 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.

View File

@@ -1,5 +1,5 @@
/**************************************************************************/
/* metal_fx.mm */
/* metal_fx.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,20 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "metal_fx.h"
#ifdef METAL_ENABLED
#import "../storage_rd/render_scene_buffers_rd.h"
#import "drivers/metal/pixel_formats.h"
#import "drivers/metal/rendering_device_driver_metal.h"
#include "metal_fx.h"
#import <Metal/Metal.h>
#import <MetalFX/MetalFX.h>
#include "../storage_rd/render_scene_buffers_rd.h"
#include "drivers/metal/pixel_formats.h"
#include "drivers/metal/rendering_device_driver_metal3.h"
#include <MetalFX/MetalFX.hpp>
using namespace RendererRD;
#pragma mark - Spatial Scaler
MFXSpatialContext::~MFXSpatialContext() {
if (scaler) {
scaler->release();
}
}
MFXSpatialEffect::MFXSpatialEffect() {
@@ -51,28 +55,21 @@ MFXSpatialEffect::~MFXSpatialEffect() {
}
void MFXSpatialEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) {
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id);
MDCommandBufferBase *obj = (MDCommandBufferBase *)(p_command_buffer.id);
obj->end();
id<MTLTexture> src_texture = rid::get(p_userdata->src);
id<MTLTexture> dst_texture = rid::get(p_userdata->dst);
MTL::Texture *src_texture = reinterpret_cast<MTL::Texture *>(p_userdata->src.id);
MTL::Texture *dst_texture = reinterpret_cast<MTL::Texture *>(p_userdata->dst.id);
__block id<MTLFXSpatialScaler> scaler = p_userdata->ctx.scaler;
scaler.colorTexture = src_texture;
scaler.outputTexture = dst_texture;
[scaler encodeToCommandBuffer:obj->get_command_buffer()];
// TODO(sgc): add API to retain objects until the command buffer completes
[obj->get_command_buffer() addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
// This block retains a reference to the scaler until the command buffer.
// completes.
scaler = nil;
}];
MTLFX::SpatialScalerBase *scaler = p_userdata->scaler;
scaler->setColorTexture(src_texture);
scaler->setOutputTexture(dst_texture);
MTLFX::SpatialScaler *s = static_cast<MTLFX::SpatialScaler *>(scaler);
MTL3::MDCommandBuffer *cmd = (MTL3::MDCommandBuffer *)(p_command_buffer.id);
s->encodeToCommandBuffer(cmd->get_command_buffer());
obj->retain_resource(scaler);
CallbackArgs::free(&p_userdata);
GODOT_CLANG_WARNING_POP
}
void MFXSpatialEffect::ensure_context(Ref<RenderSceneBuffersRD> p_render_buffers) {
@@ -98,27 +95,23 @@ void MFXSpatialEffect::process(Ref<RenderSceneBuffersRD> p_render_buffers, RID p
MFXSpatialContext *MFXSpatialEffect::create_context(CreateParams p_params) const {
DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL));
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver();
PixelFormats &pf = rdd->get_pixel_formats();
id<MTLDevice> dev = rdd->get_device();
MTL::Device *dev = rdd->get_device();
MTLFXSpatialScalerDescriptor *desc = [MTLFXSpatialScalerDescriptor new];
desc.inputWidth = (NSUInteger)p_params.input_size.width;
desc.inputHeight = (NSUInteger)p_params.input_size.height;
NS::SharedPtr<MTLFX::SpatialScalerDescriptor> desc = NS::TransferPtr(MTLFX::SpatialScalerDescriptor::alloc()->init());
desc->setInputWidth((NS::UInteger)p_params.input_size.width);
desc->setInputHeight((NS::UInteger)p_params.input_size.height);
desc.outputWidth = (NSUInteger)p_params.output_size.width;
desc.outputHeight = (NSUInteger)p_params.output_size.height;
desc->setOutputWidth((NS::UInteger)p_params.output_size.width);
desc->setOutputHeight((NS::UInteger)p_params.output_size.height);
desc->setColorTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.input_format));
desc->setOutputTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.output_format));
desc->setColorProcessingMode(MTLFX::SpatialScalerColorProcessingModeLinear);
desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format);
desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format);
desc.colorProcessingMode = MTLFXSpatialScalerColorProcessingModeLinear;
id<MTLFXSpatialScaler> scaler = [desc newSpatialScalerWithDevice:dev];
MFXSpatialContext *context = memnew(MFXSpatialContext);
context->scaler = scaler;
GODOT_CLANG_WARNING_POP
context->scaler = desc->newSpatialScaler(dev);
return context;
}
@@ -127,7 +120,11 @@ MFXSpatialContext *MFXSpatialEffect::create_context(CreateParams p_params) const
#pragma mark - Temporal Scaler
MFXTemporalContext::~MFXTemporalContext() {}
MFXTemporalContext::~MFXTemporalContext() {
if (scaler) {
scaler->release();
}
}
MFXTemporalEffect::MFXTemporalEffect() {}
MFXTemporalEffect::~MFXTemporalEffect() {}
@@ -135,35 +132,29 @@ MFXTemporalEffect::~MFXTemporalEffect() {}
MFXTemporalContext *MFXTemporalEffect::create_context(CreateParams p_params) const {
DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL));
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver();
PixelFormats &pf = rdd->get_pixel_formats();
id<MTLDevice> dev = rdd->get_device();
MTL::Device *dev = rdd->get_device();
MTLFXTemporalScalerDescriptor *desc = [MTLFXTemporalScalerDescriptor new];
desc.inputWidth = (NSUInteger)p_params.input_size.width;
desc.inputHeight = (NSUInteger)p_params.input_size.height;
NS::SharedPtr<MTLFX::TemporalScalerDescriptor> desc = NS::TransferPtr(MTLFX::TemporalScalerDescriptor::alloc()->init());
desc->setInputWidth((NS::UInteger)p_params.input_size.width);
desc->setInputHeight((NS::UInteger)p_params.input_size.height);
desc.outputWidth = (NSUInteger)p_params.output_size.width;
desc.outputHeight = (NSUInteger)p_params.output_size.height;
desc->setOutputWidth((NS::UInteger)p_params.output_size.width);
desc->setOutputHeight((NS::UInteger)p_params.output_size.height);
desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format);
desc.depthTextureFormat = pf.getMTLPixelFormat(p_params.depth_format);
desc.motionTextureFormat = pf.getMTLPixelFormat(p_params.motion_format);
desc.autoExposureEnabled = NO;
desc->setColorTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.input_format));
desc->setDepthTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.depth_format));
desc->setMotionTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.motion_format));
desc->setAutoExposureEnabled(false);
desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format);
desc->setOutputTextureFormat((MTL::PixelFormat)pf.getMTLPixelFormat(p_params.output_format));
id<MTLFXTemporalScaler> scaler = [desc newTemporalScalerWithDevice:dev];
MFXTemporalContext *context = memnew(MFXTemporalContext);
context->scaler = scaler;
scaler.motionVectorScaleX = p_params.motion_vector_scale.x;
scaler.motionVectorScaleY = p_params.motion_vector_scale.y;
scaler.depthReversed = true; // Godot uses reverse Z per https://github.com/godotengine/godot/pull/88328
GODOT_CLANG_WARNING_POP
context->scaler = desc->newTemporalScaler(dev);
context->scaler->setMotionVectorScaleX(p_params.motion_vector_scale.x);
context->scaler->setMotionVectorScaleY(p_params.motion_vector_scale.y);
context->scaler->setDepthReversed(true); // Godot uses reverse Z per https://github.com/godotengine/godot/pull/88328
return context;
}
@@ -188,38 +179,33 @@ void MFXTemporalEffect::process(RendererRD::MFXTemporalContext *p_ctx, RendererR
}
void MFXTemporalEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) {
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id);
MDCommandBufferBase *obj = (MDCommandBufferBase *)(p_command_buffer.id);
obj->end();
id<MTLTexture> src_texture = rid::get(p_userdata->src);
id<MTLTexture> depth = rid::get(p_userdata->depth);
id<MTLTexture> motion = rid::get(p_userdata->motion);
id<MTLTexture> exposure = rid::get(p_userdata->exposure);
MTL::Texture *src_texture = reinterpret_cast<MTL::Texture *>(p_userdata->src.id);
MTL::Texture *depth = reinterpret_cast<MTL::Texture *>(p_userdata->depth.id);
MTL::Texture *motion = reinterpret_cast<MTL::Texture *>(p_userdata->motion.id);
MTL::Texture *exposure = reinterpret_cast<MTL::Texture *>(p_userdata->exposure.id);
id<MTLTexture> dst_texture = rid::get(p_userdata->dst);
MTL::Texture *dst_texture = reinterpret_cast<MTL::Texture *>(p_userdata->dst.id);
__block id<MTLFXTemporalScaler> scaler = p_userdata->ctx.scaler;
scaler.reset = p_userdata->reset;
scaler.colorTexture = src_texture;
scaler.depthTexture = depth;
scaler.motionTexture = motion;
scaler.exposureTexture = exposure;
scaler.jitterOffsetX = p_userdata->jitter_offset.x;
scaler.jitterOffsetY = p_userdata->jitter_offset.y;
scaler.outputTexture = dst_texture;
[scaler encodeToCommandBuffer:obj->get_command_buffer()];
// TODO(sgc): add API to retain objects until the command buffer completes
[obj->get_command_buffer() addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull) {
// This block retains a reference to the scaler until the command buffer.
// completes.
scaler = nil;
}];
MTLFX::TemporalScalerBase *scaler = p_userdata->scaler;
scaler->setReset(p_userdata->reset);
scaler->setColorTexture(src_texture);
scaler->setDepthTexture(depth);
scaler->setMotionTexture(motion);
scaler->setExposureTexture(exposure);
scaler->setJitterOffsetX(p_userdata->jitter_offset.x);
scaler->setJitterOffsetY(p_userdata->jitter_offset.y);
scaler->setOutputTexture(dst_texture);
MTLFX::TemporalScaler *s = static_cast<MTLFX::TemporalScaler *>(scaler);
MTL3::MDCommandBuffer *cmd = (MTL3::MDCommandBuffer *)(p_command_buffer.id);
s->encodeToCommandBuffer(cmd->get_command_buffer());
obj->retain_resource(scaler);
CallbackArgs::free(&p_userdata);
GODOT_CLANG_WARNING_POP
}
#endif
#endif

View File

@@ -41,32 +41,28 @@
#include "core/templates/paged_allocator.h"
#include "servers/rendering/renderer_scene_render.h"
#ifdef __OBJC__
@protocol MTLFXSpatialScaler;
@protocol MTLFXTemporalScaler;
#endif
namespace MTLFX {
class SpatialScalerBase;
class TemporalScalerBase;
} //namespace MTLFX
namespace RendererRD {
struct MFXSpatialContext {
#ifdef __OBJC__
id<MTLFXSpatialScaler> scaler = nullptr;
#else
void *scaler = nullptr;
#endif
MTLFX::SpatialScalerBase *scaler = nullptr;
MFXSpatialContext() = default;
~MFXSpatialContext();
};
class MFXSpatialEffect : public SpatialUpscaler {
struct CallbackArgs {
MFXSpatialEffect *owner;
MFXSpatialEffect *owner = nullptr;
MTLFX::SpatialScalerBase *scaler = nullptr;
RDD::TextureID src;
RDD::TextureID dst;
MFXSpatialContext ctx;
CallbackArgs(MFXSpatialEffect *p_owner, RDD::TextureID p_src, RDD::TextureID p_dst, MFXSpatialContext p_ctx) :
owner(p_owner), src(p_src), dst(p_dst), ctx(p_ctx) {}
CallbackArgs(MFXSpatialEffect *p_owner, RDD::TextureID p_src, RDD::TextureID p_dst, const MFXSpatialContext &p_ctx) :
owner(p_owner), scaler(p_ctx.scaler), src(p_src), dst(p_dst) {}
static void free(CallbackArgs **p_args) {
(*p_args)->owner->args_allocator.free(*p_args);
@@ -98,25 +94,21 @@ public:
#ifdef METAL_MFXTEMPORAL_ENABLED
struct MFXTemporalContext {
#ifdef __OBJC__
id<MTLFXTemporalScaler> scaler = nullptr;
#else
void *scaler = nullptr;
#endif
MTLFX::TemporalScalerBase *scaler = nullptr;
MFXTemporalContext() = default;
~MFXTemporalContext();
};
class MFXTemporalEffect {
struct CallbackArgs {
MFXTemporalEffect *owner;
MFXTemporalEffect *owner = nullptr;
MTLFX::TemporalScalerBase *scaler = nullptr;
RDD::TextureID src;
RDD::TextureID depth;
RDD::TextureID motion;
RDD::TextureID exposure;
Vector2 jitter_offset;
RDD::TextureID dst;
MFXTemporalContext ctx;
bool reset = false;
CallbackArgs(
@@ -127,16 +119,16 @@ class MFXTemporalEffect {
RDD::TextureID p_exposure,
Vector2 p_jitter_offset,
RDD::TextureID p_dst,
MFXTemporalContext p_ctx,
const MFXTemporalContext &p_ctx,
bool p_reset) :
owner(p_owner),
scaler(p_ctx.scaler),
src(p_src),
depth(p_depth),
motion(p_motion),
exposure(p_exposure),
jitter_offset(p_jitter_offset),
dst(p_dst),
ctx(p_ctx),
reset(p_reset) {}
static void free(CallbackArgs **p_args) {