From 11dc4f2e5eb13e7a6c4f5269d709b1ca5f03b38d Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sun, 24 Nov 2024 09:11:43 +1100 Subject: [PATCH] Metal: Add MetalFX upscaling support Co-authored-by: Hugo Locurcio --- doc/classes/ProjectSettings.xml | 6 + doc/classes/RenderingDevice.xml | 8 + doc/classes/RenderingServer.xml | 10 +- doc/classes/Viewport.xml | 16 +- drivers/metal/metal_device_properties.h | 5 + drivers/metal/metal_device_properties.mm | 15 ++ .../metal/rendering_device_driver_metal.mm | 8 + platform/ios/detect.py | 1 + platform/macos/detect.py | 1 + scene/main/viewport.cpp | 4 +- scene/main/viewport.h | 2 + servers/rendering/renderer_rd/effects/SCsub | 2 + servers/rendering/renderer_rd/effects/fsr.cpp | 2 +- servers/rendering/renderer_rd/effects/fsr.h | 10 +- .../rendering/renderer_rd/effects/metal_fx.h | 182 ++++++++++++++ .../rendering/renderer_rd/effects/metal_fx.mm | 225 ++++++++++++++++++ .../effects/motion_vectors_store.cpp | 101 ++++++++ .../effects/motion_vectors_store.h | 62 +++++ .../renderer_rd/effects/spatial_upscaler.h | 48 ++++ .../render_forward_clustered.cpp | 121 +++++++++- .../render_forward_clustered.h | 17 ++ .../renderer_rd/renderer_scene_render_rd.cpp | 35 ++- .../renderer_rd/renderer_scene_render_rd.h | 6 + .../shaders/effects/motion_vectors_store.glsl | 32 +++ .../storage_rd/render_scene_buffers_rd.cpp | 27 ++- .../storage_rd/render_scene_buffers_rd.h | 14 +- servers/rendering/renderer_viewport.cpp | 53 ++++- servers/rendering/rendering_device.cpp | 75 ++++++ servers/rendering/rendering_device.h | 42 ++++ servers/rendering/rendering_device_commons.h | 6 +- servers/rendering/rendering_device_driver.h | 6 + servers/rendering/rendering_device_graph.cpp | 36 ++- servers/rendering/rendering_device_graph.h | 10 +- .../storage/render_scene_buffers.cpp | 2 +- servers/rendering_server.cpp | 20 +- servers/rendering_server.h | 18 ++ 36 files changed, 1180 insertions(+), 48 deletions(-) create mode 100644 servers/rendering/renderer_rd/effects/metal_fx.h create mode 100644 servers/rendering/renderer_rd/effects/metal_fx.mm create mode 100644 servers/rendering/renderer_rd/effects/motion_vectors_store.cpp create mode 100644 servers/rendering/renderer_rd/effects/motion_vectors_store.h create mode 100644 servers/rendering/renderer_rd/effects/spatial_upscaler.h create mode 100644 servers/rendering/renderer_rd/shaders/effects/motion_vectors_store.glsl diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 45ff4ff74f2a..3b1b8a2cdfcb 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -3024,6 +3024,12 @@ Sets the scaling 3D mode. Bilinear scaling renders at different resolution to either undersample or supersample the viewport. FidelityFX Super Resolution 1.0, abbreviated to FSR, is an upscaling technology that produces high quality images at fast framerates by using a spatially-aware upscaling algorithm. FSR is slightly more expensive than bilinear, but it produces significantly higher image quality. On particularly low-end GPUs, the added cost of FSR may not be worth it (compared to using bilinear scaling with a slightly higher resolution scale to match performance). [b]Note:[/b] FSR is only effective when using the Forward+ rendering method, not Mobile or Compatibility. If using an incompatible rendering method, FSR will fall back to bilinear scaling. + + iOS override for [member rendering/scaling_3d/mode]. This allows selecting the MetalFX spatial and MetalFX temporal scaling modes, which are exclusive to platforms where the Metal rendering driver is used. + + + macOS override for [member rendering/scaling_3d/mode]. This allows selecting the MetalFX spatial and MetalFX temporal scaling modes, which are exclusive to platforms where the Metal rendering driver is used. + Scales the 3D render buffer based on the viewport size uses an image filter specified in [member rendering/scaling_3d/mode] to scale the output image to the full viewport size. Values lower than [code]1.0[/code] can be used to speed up 3D rendering at the cost of quality (undersampling). Values greater than [code]1.0[/code] are only valid for bilinear mode and can be used to improve 3D rendering quality at a high performance cost (supersampling). See also [member rendering/anti_aliasing/quality/msaa_3d] for multi-sample antialiasing, which is significantly cheaper but only smooths the edges of polygons. diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index b7f95587cda6..29a2f4723f72 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -2529,6 +2529,14 @@ Maximum viewport height (in pixels). + + Returns the smallest value for [member ProjectSettings.rendering/scaling_3d/scale] when using the MetalFX temporal upscaler. + [b]Note:[/b] The returned value is multiplied by a factor of [code]1000000[/code] to preserve 6 digits of precision. It must be divided by [code]1000000.0[/code] to convert the value to a floating point number. + + + Returns the largest value for [member ProjectSettings.rendering/scaling_3d/scale] when using the MetalFX temporal upscaler. + [b]Note:[/b] The returned value is multiplied by a factor of [code]1000000[/code] to preserve 6 digits of precision. It must be divided by [code]1000000.0[/code] to convert the value to a floating point number. + Memory taken by textures. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 50d7b397b6d0..f05f431dcf36 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4986,7 +4986,15 @@ 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. - + + 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. + [b]Note:[/b] Only supported when the Metal rendering driver is in use, which limits this scaling mode to macOS and iOS. + + + 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. + [b]Note:[/b] Only supported when the Metal rendering driver is in use, which limits this scaling mode to macOS and iOS. + + Represents the size of the [enum ViewportScaling3DMode] enum. diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 98457b5e90b3..e4fb3ee9abe8 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -503,7 +503,21 @@ 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. - + + Use the [url=https://developer.apple.com/documentation/metalfx/mtlfxspatialscaler#overview]MetalFX spatial upscaler[/url] 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. + More information: [url=https://developer.apple.com/documentation/metalfx]MetalFX[/url]. + [b]Note:[/b] Only supported when the Metal rendering driver is in use, which limits this scaling mode to macOS and iOS. + + + Use the [url=https://developer.apple.com/documentation/metalfx/mtlfxtemporalscaler#overview]MetalFX temporal upscaler[/url] for the viewport's 3D buffer. + The amount of scaling can be set using [member scaling_3d_scale]. To determine the minimum input scale, use the [method RenderingDevice.limit_get] method with [constant RenderingDevice.LIMIT_METALFX_TEMPORAL_SCALER_MIN_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. + More information: [url=https://developer.apple.com/documentation/metalfx]MetalFX[/url]. + [b]Note:[/b] Only supported when the Metal rendering driver is in use, which limits this scaling mode to macOS and iOS. + + Represents the size of the [enum Scaling3DMode] enum. diff --git a/drivers/metal/metal_device_properties.h b/drivers/metal/metal_device_properties.h index af7746b9ac54..3bb5d4965db2 100644 --- a/drivers/metal/metal_device_properties.h +++ b/drivers/metal/metal_device_properties.h @@ -84,6 +84,8 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) MetalFeatures { bool tessellationShader = false; /**< If true, tessellation shaders are supported. */ bool imageCubeArray = false; /**< If true, image cube arrays are supported. */ MTLArgumentBuffersTier argument_buffers_tier = MTLArgumentBuffersTier1; + 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 { @@ -115,6 +117,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 subgroupSupportedShaderStages; diff --git a/drivers/metal/metal_device_properties.mm b/drivers/metal/metal_device_properties.mm index 2c3f99dd50c2..c2acddc68b08 100644 --- a/drivers/metal/metal_device_properties.mm +++ b/drivers/metal/metal_device_properties.mm @@ -51,6 +51,7 @@ #import "metal_device_properties.h" #import +#import #import #import @@ -100,6 +101,11 @@ features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7]; features.argument_buffers_tier = p_device.argumentBuffersSupport; + 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. @@ -285,6 +291,15 @@ #endif limits.maxDrawIndexedIndexValue = std::numeric_limits::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 p_device) { diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index 1d16ac964da3..a647a2223232 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -3982,6 +3982,10 @@ bool isArrayTexture(MTLTextureType p_type) { return (uint64_t)limits.subgroupSupportedShaderStages; case LIMIT_SUBGROUP_OPERATIONS: return (uint64_t)limits.subgroupSupportedOperations; + case LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE: + return (uint64_t)((1.0 / limits.temporalScalerInputContentMaxScale) * 1000'000); + case LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE: + return (uint64_t)((1.0 / limits.temporalScalerInputContentMinScale) * 1000'000); UNKNOWN(LIMIT_VRS_TEXEL_WIDTH); UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT); UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH); @@ -4017,6 +4021,10 @@ bool isArrayTexture(MTLTextureType p_type) { return false; case SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS: return true; + case SUPPORTS_METALFX_SPATIAL: + return device_properties->features.metal_fx_spatial; + case SUPPORTS_METALFX_TEMPORAL: + return device_properties->features.metal_fx_temporal; default: return false; } diff --git a/platform/ios/detect.py b/platform/ios/detect.py index 0f7f938852d9..d6aabe02b500 100644 --- a/platform/ios/detect.py +++ b/platform/ios/detect.py @@ -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", ] ) diff --git a/platform/macos/detect.py b/platform/macos/detect.py index 61a9f0d8c85a..1ac8780ca904 100644 --- a/platform/macos/detect.py +++ b/platform/macos/detect.py @@ -243,6 +243,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"]: diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 81778f62827f..6a898bd5ca2b 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -4899,7 +4899,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::INT, "anisotropic_filtering_level", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Faster),4× (Fast),8× (Average),16x (Slow)")), "set_anisotropic_filtering_level", "get_anisotropic_filtering_level"); @@ -4954,6 +4954,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); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index c308286d94be..a6a6e8ed4464 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -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 }; diff --git a/servers/rendering/renderer_rd/effects/SCsub b/servers/rendering/renderer_rd/effects/SCsub index 9f330c9f0f60..25edf51835ed 100644 --- a/servers/rendering/renderer_rd/effects/SCsub +++ b/servers/rendering/renderer_rd/effects/SCsub @@ -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. diff --git a/servers/rendering/renderer_rd/effects/fsr.cpp b/servers/rendering/renderer_rd/effects/fsr.cpp index d8ceb64c3f88..634d2d9dab27 100644 --- a/servers/rendering/renderer_rd/effects/fsr.cpp +++ b/servers/rendering/renderer_rd/effects/fsr.cpp @@ -52,7 +52,7 @@ FSR::~FSR() { fsr_shader.version_free(shader_version); } -void FSR::fsr_upscale(Ref p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) { +void FSR::process(Ref p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) { UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); diff --git a/servers/rendering/renderer_rd/effects/fsr.h b/servers/rendering/renderer_rd/effects/fsr.h index 675b82ffb306..a6962db8fd5c 100644 --- a/servers/rendering/renderer_rd/effects/fsr.h +++ b/servers/rendering/renderer_rd/effects/fsr.h @@ -31,17 +31,23 @@ #ifndef FSR_RD_H #define FSR_RD_H +#include "spatial_upscaler.h" + #include "../storage_rd/render_scene_buffers_rd.h" #include "servers/rendering/renderer_rd/shaders/effects/fsr_upscale.glsl.gen.h" namespace RendererRD { -class FSR { +class FSR : public SpatialUpscaler { + String name = "FSR 1.0 Upscale"; + public: FSR(); ~FSR(); - void fsr_upscale(Ref p_render_buffers, RID p_source_rd_texture, RID p_destination_texture); + virtual String get_label() const final { return name; } + virtual void ensure_context(Ref p_render_buffers) final {} + virtual void process(Ref p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) final; private: enum FSRUpscalePass { diff --git a/servers/rendering/renderer_rd/effects/metal_fx.h b/servers/rendering/renderer_rd/effects/metal_fx.h new file mode 100644 index 000000000000..31a54850b1a6 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/metal_fx.h @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* 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 "spatial_upscaler.h" + +#include "core/templates/paged_allocator.h" +#include "servers/rendering/renderer_scene_render.h" + +#ifdef __OBJC__ +@protocol MTLFXSpatialScaler; +@protocol MTLFXTemporalScaler; +#endif + +namespace RendererRD { + +struct MFXSpatialContext { +#ifdef __OBJC__ + id scaler = nullptr; +#else + void *scaler = nullptr; +#endif + MFXSpatialContext() = default; + ~MFXSpatialContext(); +}; + +class MFXSpatialEffect : public SpatialUpscaler { + struct CallbackArgs { + MFXSpatialEffect *owner; + 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) {} + + static void free(CallbackArgs **p_args) { + (*p_args)->owner->args_allocator.free(*p_args); + *p_args = nullptr; + } + }; + + PagedAllocator args_allocator; + static void callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata); + String name = "MetalFX Spatial Upscale"; + +public: + virtual String get_label() const final { return name; } + virtual void ensure_context(Ref p_render_buffers) final; + virtual void process(Ref p_render_buffers, RID p_src, RID p_dst) final; + + struct CreateParams { + Vector2i input_size; + Vector2i output_size; + RD::DataFormat input_format; + RD::DataFormat output_format; + }; + + MFXSpatialContext *create_context(CreateParams p_params) const; + + MFXSpatialEffect(); + ~MFXSpatialEffect(); +}; + +struct MFXTemporalContext { +#ifdef __OBJC__ + id scaler = nullptr; +#else + void *scaler = nullptr; +#endif + MFXTemporalContext() = default; + ~MFXTemporalContext(); +}; + +class MFXTemporalEffect { + struct CallbackArgs { + MFXTemporalEffect *owner; + RDD::TextureID src; + RDD::TextureID depth; + RDD::TextureID motion; + RDD::TextureID exposure; + Vector2 jitter_offset; + RDD::TextureID dst; + MFXTemporalContext ctx; + bool reset = false; + + CallbackArgs( + MFXTemporalEffect *p_owner, + RDD::TextureID p_src, + RDD::TextureID p_depth, + RDD::TextureID p_motion, + RDD::TextureID p_exposure, + Vector2 p_jitter_offset, + RDD::TextureID p_dst, + MFXTemporalContext p_ctx, + bool p_reset) : + owner(p_owner), + 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) { + (*p_args)->owner->args_allocator.free(*p_args); + *p_args = nullptr; + } + }; + + PagedAllocator args_allocator; + + 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 diff --git a/servers/rendering/renderer_rd/effects/metal_fx.mm b/servers/rendering/renderer_rd/effects/metal_fx.mm new file mode 100644 index 000000000000..e2273d446395 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/metal_fx.mm @@ -0,0 +1,225 @@ +/**************************************************************************/ +/* metal_fx.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 "metal_fx.h" + +#import "../storage_rd/render_scene_buffers_rd.h" +#import "drivers/metal/pixel_formats.h" +#import "drivers/metal/rendering_device_driver_metal.h" + +#import +#import + +using namespace RendererRD; + +#pragma mark - Spatial Scaler + +MFXSpatialContext::~MFXSpatialContext() { +} + +MFXSpatialEffect::MFXSpatialEffect() { +} + +MFXSpatialEffect::~MFXSpatialEffect() { +} + +void MFXSpatialEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + + MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id); + obj->end(); + + id src_texture = rid::get(p_userdata->src); + id dst_texture = rid::get(p_userdata->dst); + + __block id 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 _Nonnull) { + // This block retains a reference to the scaler until the command buffer. + // completes. + scaler = nil; + }]; + + CallbackArgs::free(&p_userdata); + +#pragma clang diagnostic pop +} + +void MFXSpatialEffect::ensure_context(Ref p_render_buffers) { + p_render_buffers->ensure_mfx(this); +} + +void MFXSpatialEffect::process(Ref p_render_buffers, RID p_src, RID p_dst) { + MFXSpatialContext *ctx = p_render_buffers->get_mfx_spatial_context(); + DEV_ASSERT(ctx); // this should have been done by the caller via ensure_context + + CallbackArgs *userdata = args_allocator.alloc( + this, + RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_src)), + RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_dst)), + *ctx); + RD::CallbackResource res[2] = { + { .rid = p_src, .usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE }, + { .rid = p_dst, .usage = RD::CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE } + }; + RD::get_singleton()->driver_callback_add((RDD::DriverCallback)MFXSpatialEffect::callback, userdata, VectorView(res, 2)); +} + +MFXSpatialContext *MFXSpatialEffect::create_context(CreateParams p_params) const { + DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL)); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + + RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver(); + PixelFormats &pf = rdd->get_pixel_formats(); + id dev = rdd->get_device(); + + MTLFXSpatialScalerDescriptor *desc = [MTLFXSpatialScalerDescriptor new]; + desc.inputWidth = (NSUInteger)p_params.input_size.width; + desc.inputHeight = (NSUInteger)p_params.input_size.height; + + desc.outputWidth = (NSUInteger)p_params.output_size.width; + desc.outputHeight = (NSUInteger)p_params.output_size.height; + + desc.colorTextureFormat = pf.getMTLPixelFormat(p_params.input_format); + desc.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format); + desc.colorProcessingMode = MTLFXSpatialScalerColorProcessingModeLinear; + id scaler = [desc newSpatialScalerWithDevice:dev]; + MFXSpatialContext *context = memnew(MFXSpatialContext); + context->scaler = scaler; + +#pragma clang diagnostic pop + + return context; +} + +#pragma mark - Temporal Scaler + +MFXTemporalContext::~MFXTemporalContext() {} + +MFXTemporalEffect::MFXTemporalEffect() {} +MFXTemporalEffect::~MFXTemporalEffect() {} + +MFXTemporalContext *MFXTemporalEffect::create_context(CreateParams p_params) const { + DEV_ASSERT(RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL)); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + + RenderingDeviceDriverMetal *rdd = (RenderingDeviceDriverMetal *)RD::get_singleton()->get_device_driver(); + PixelFormats &pf = rdd->get_pixel_formats(); + id dev = rdd->get_device(); + + MTLFXTemporalScalerDescriptor *desc = [MTLFXTemporalScalerDescriptor new]; + desc.inputWidth = (NSUInteger)p_params.input_size.width; + desc.inputHeight = (NSUInteger)p_params.input_size.height; + + desc.outputWidth = (NSUInteger)p_params.output_size.width; + desc.outputHeight = (NSUInteger)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.outputTextureFormat = pf.getMTLPixelFormat(p_params.output_format); + + id 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 + +#pragma clang diagnostic pop + + return context; +} + +void MFXTemporalEffect::process(RendererRD::MFXTemporalContext *p_ctx, RendererRD::MFXTemporalEffect::Params p_params) { + CallbackArgs *userdata = args_allocator.alloc( + this, + RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.src)), + RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.depth)), + RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.motion)), + p_params.exposure.is_valid() ? RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.exposure)) : RDD::TextureID(), + p_params.jitter_offset, + RDD::TextureID(RD::get_singleton()->get_driver_resource(RDC::DRIVER_RESOURCE_TEXTURE, p_params.dst)), + *p_ctx, + p_params.reset); + RD::CallbackResource res[3] = { + { .rid = p_params.src, .usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE }, + { .rid = p_params.depth, .usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE }, + { .rid = p_params.dst, .usage = RD::CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE }, + }; + RD::get_singleton()->driver_callback_add((RDD::DriverCallback)MFXTemporalEffect::callback, userdata, VectorView(res, 3)); +} + +void MFXTemporalEffect::callback(RDD *p_driver, RDD::CommandBufferID p_command_buffer, CallbackArgs *p_userdata) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + + MDCommandBuffer *obj = (MDCommandBuffer *)(p_command_buffer.id); + obj->end(); + + id src_texture = rid::get(p_userdata->src); + id depth = rid::get(p_userdata->depth); + id motion = rid::get(p_userdata->motion); + id exposure = rid::get(p_userdata->exposure); + + id dst_texture = rid::get(p_userdata->dst); + + __block id 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 _Nonnull) { + // This block retains a reference to the scaler until the command buffer. + // completes. + scaler = nil; + }]; + + CallbackArgs::free(&p_userdata); + +#pragma clang diagnostic pop +} diff --git a/servers/rendering/renderer_rd/effects/motion_vectors_store.cpp b/servers/rendering/renderer_rd/effects/motion_vectors_store.cpp new file mode 100644 index 000000000000..6413b86fb41a --- /dev/null +++ b/servers/rendering/renderer_rd/effects/motion_vectors_store.cpp @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* motion_vectors_store.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "motion_vectors_store.h" + +#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" + +namespace RendererRD { + +MotionVectorsStore::MotionVectorsStore() { + Vector modes; + modes.push_back(""); + + motion_shader.initialize(modes); + shader_version = motion_shader.version_create(); + + pipeline = RD::get_singleton()->compute_pipeline_create(motion_shader.version_get_shader(shader_version, 0)); +} + +MotionVectorsStore::~MotionVectorsStore() { + motion_shader.version_free(shader_version); +} + +void MotionVectorsStore::process(Ref p_render_buffers, + const Projection &p_current_projection, const Transform3D &p_current_transform, + const Projection &p_previous_projection, const Transform3D &p_previous_transform) { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + + uint32_t view_count = p_render_buffers->get_view_count(); + Size2i internal_size = p_render_buffers->get_internal_size(); + + PushConstant push_constant; + { + push_constant.resolution[0] = internal_size.width; + push_constant.resolution[1] = internal_size.height; + + Projection correction; + correction.set_depth_correction(true, true, false); + Projection reprojection = (correction * p_previous_projection) * p_previous_transform.affine_inverse() * p_current_transform * (correction * p_current_projection).inverse(); + RendererRD::MaterialStorage::store_camera(reprojection, push_constant.reprojection_matrix); + } + + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + + RD::get_singleton()->draw_command_begin_label("Motion Vector Store"); + + RID shader = motion_shader.version_get_shader(shader_version, 0); + ERR_FAIL_COND(shader.is_null()); + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipeline); + + for (uint32_t v = 0; v < view_count; v++) { + RID velocity = p_render_buffers->get_velocity_buffer(false, v); + RID depth = p_render_buffers->get_depth_texture(v); + RD::Uniform u_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, depth })); + RD::Uniform u_velocity(RD::UNIFORM_TYPE_IMAGE, 1, velocity); + + RID uniform_set = uniform_set_cache->get_cache(shader, 0, u_depth, u_velocity); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, internal_size.width, internal_size.height, 1); + } + + RD::get_singleton()->compute_list_end(); + + RD::get_singleton()->draw_command_end_label(); +} + +} //namespace RendererRD diff --git a/servers/rendering/renderer_rd/effects/motion_vectors_store.h b/servers/rendering/renderer_rd/effects/motion_vectors_store.h new file mode 100644 index 000000000000..19b3360ae4f7 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/motion_vectors_store.h @@ -0,0 +1,62 @@ +/**************************************************************************/ +/* motion_vectors_store.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 MOTION_VECTORS_STORE_RD_H +#define MOTION_VECTORS_STORE_RD_H + +#include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/motion_vectors_store.glsl.gen.h" +#include "servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h" +#include "servers/rendering/renderer_scene_render.h" +#include "servers/rendering_server.h" + +namespace RendererRD { +class MotionVectorsStore { + struct PushConstant { + float reprojection_matrix[16]; + float resolution[2]; + uint32_t pad[2]; + }; + + MotionVectorsStoreShaderRD motion_shader; + RID shader_version; + RID pipeline; + +public: + MotionVectorsStore(); + ~MotionVectorsStore(); + + void process(Ref p_render_buffers, + const Projection &p_current_projection, const Transform3D &p_current_transform, + const Projection &p_previous_projection, const Transform3D &p_previous_transform); +}; +} //namespace RendererRD + +#endif // MOTION_VECTORS_STORE_RD_H diff --git a/servers/rendering/renderer_rd/effects/spatial_upscaler.h b/servers/rendering/renderer_rd/effects/spatial_upscaler.h new file mode 100644 index 000000000000..c472e63bf174 --- /dev/null +++ b/servers/rendering/renderer_rd/effects/spatial_upscaler.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* spatial_upscaler.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 SPATIAL_UPSCALER_RD_H +#define SPATIAL_UPSCALER_RD_H + +#include "core/object/ref_counted.h" + +class RenderSceneBuffersRD; + +class SpatialUpscaler { +public: + virtual String get_label() const = 0; + virtual void ensure_context(Ref p_render_buffers) = 0; + virtual void process(Ref p_render_buffers, RID p_source_rd_texture, RID p_destination_texture) = 0; + + SpatialUpscaler() = default; + virtual ~SpatialUpscaler() = default; +}; + +#endif // SPATIAL_UPSCALER_RD_H diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 31de59f990e9..99acf211821c 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -88,6 +88,25 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_fsr2(Rende } } +#ifdef METAL_ENABLED +bool RenderForwardClustered::RenderBufferDataForwardClustered::ensure_mfx_temporal(RendererRD::MFXTemporalEffect *p_effect) { + if (mfx_temporal_context == nullptr) { + RendererRD::MFXTemporalEffect::CreateParams params; + params.input_size = render_buffers->get_internal_size(); + params.output_size = render_buffers->get_target_size(); + params.input_format = render_buffers->get_base_data_format(); + params.depth_format = render_buffers->get_depth_format(false, false, render_buffers->get_can_be_storage()); + params.motion_format = render_buffers->get_velocity_format(); + params.reactive_format = render_buffers->get_base_data_format(); // Reactive is derived from input. + params.output_format = render_buffers->get_base_data_format(); + params.motion_vector_scale = render_buffers->get_internal_size(); + mfx_temporal_context = p_effect->create_context(params); + return true; + } + return false; +} +#endif + void RenderForwardClustered::RenderBufferDataForwardClustered::free_data() { // JIC, should already have been cleared if (render_buffers) { @@ -108,6 +127,13 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::free_data() { fsr2_context = nullptr; } +#ifdef METAL_ENABLED + if (mfx_temporal_context) { + memdelete(mfx_temporal_context); + mfx_temporal_context = nullptr; + } +#endif + if (!render_sdfgi_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_sdfgi_uniform_set)) { RD::get_singleton()->free(render_sdfgi_uniform_set); } @@ -1706,7 +1732,29 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool using_debug_mvs = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS; bool using_taa = rb->get_use_taa(); - bool using_fsr2 = rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR2; + + enum { + SCALE_NONE, + SCALE_FSR2, + SCALE_MFX, + } scale_type = SCALE_NONE; + + switch (rb->get_scaling_3d_mode()) { + case RS::VIEWPORT_SCALING_3D_MODE_FSR2: + scale_type = SCALE_FSR2; + break; + case RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL: +#ifdef METAL_ENABLED + scale_type = SCALE_MFX; +#else + scale_type = SCALE_NONE; +#endif + break; + default: + break; + } + + bool using_upscaling = scale_type != SCALE_NONE; // check if we need motion vectors bool motion_vectors_required; @@ -1716,7 +1764,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co motion_vectors_required = true; } else if (!is_reflection_probe && using_taa) { motion_vectors_required = true; - } else if (!is_reflection_probe && using_fsr2) { + } else if (!is_reflection_probe && using_upscaling) { motion_vectors_required = true; } else { motion_vectors_required = false; @@ -1742,7 +1790,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool using_voxelgi = false; bool reverse_cull = p_render_data->scene_data->cam_transform.basis.determinant() < 0; bool using_ssil = !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssil_enabled(p_render_data->environment); - bool using_motion_pass = rb_data.is_valid() && using_fsr2; + bool using_motion_pass = rb_data.is_valid() && using_upscaling; if (is_reflection_probe) { uint32_t resolution = light_storage->reflection_probe_instance_get_resolution(p_render_data->reflection_probe); @@ -2111,10 +2159,16 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_end_label(); if (using_motion_pass) { - Vector motion_vector_clear_colors; - motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0)); - RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::DRAW_CLEAR_ALL, motion_vector_clear_colors); - RD::get_singleton()->draw_list_end(); + if (scale_type == SCALE_MFX) { + motion_vectors_store->process(rb, + p_render_data->scene_data->cam_projection, p_render_data->scene_data->cam_transform, + p_render_data->scene_data->prev_cam_projection, p_render_data->scene_data->prev_cam_transform); + } else { + Vector motion_vector_clear_colors; + motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0)); + RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::DRAW_CLEAR_ALL, motion_vector_clear_colors); + RD::get_singleton()->draw_list_end(); + } } if (render_motion_pass) { @@ -2244,7 +2298,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_list_end(); } - if (rb_data.is_valid() && using_fsr2) { + if (rb_data.is_valid() && using_upscaling) { // Make sure the upscaled texture is initialized, but not necessarily filled, before running screen copies // so it properly detect if a dedicated copy texture should be used. rb->ensure_upscaled(); @@ -2314,7 +2368,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_command_begin_label("Resolve"); if (rb_data.is_valid() && use_msaa) { - bool resolve_velocity_buffer = (using_taa || using_fsr2 || ce_needs_motion_vectors) && rb->has_velocity_buffer(true); + bool resolve_velocity_buffer = (using_taa || using_upscaling || ce_needs_motion_vectors) && rb->has_velocity_buffer(true); for (uint32_t v = 0; v < rb->get_view_count(); v++) { RD::get_singleton()->texture_resolve_multisample(rb->get_color_msaa(v), rb->get_internal_texture(v)); resolve_effects->resolve_depth(rb->get_depth_msaa(v), rb->get_depth_texture(v), rb->get_internal_size(), texture_multisamples[msaa]); @@ -2339,8 +2393,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } RD::get_singleton()->draw_command_end_label(); - if (rb_data.is_valid() && (using_fsr2 || using_taa)) { - if (using_fsr2) { + if (rb_data.is_valid() && (using_upscaling || using_taa)) { + if (scale_type == SCALE_FSR2) { rb_data->ensure_fsr2(fsr2_effect); RID exposure; @@ -2386,6 +2440,35 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } RD::get_singleton()->draw_command_end_label(); + } else if (scale_type == SCALE_MFX) { +#ifdef METAL_ENABLED + bool reset = rb_data->ensure_mfx_temporal(mfx_temporal_effect); + + RID exposure; + if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { + exposure = luminance->get_current_luminance_buffer(rb); + } + + RD::get_singleton()->draw_command_begin_label("MetalFX Temporal"); + // Scale to ±0.5. + Vector2 jitter = p_render_data->scene_data->taa_jitter * 0.5f; + jitter *= Vector2(1.0, -1.0); // Flip y-axis as bottom left is origin. + + for (uint32_t v = 0; v < rb->get_view_count(); v++) { + RendererRD::MFXTemporalEffect::Params params; + params.src = rb->get_internal_texture(v); + params.depth = rb->get_depth_texture(v); + params.motion = rb->get_velocity_buffer(false, v); + params.exposure = exposure; + params.dst = rb->get_upscaled_texture(v); + params.jitter_offset = jitter; + params.reset = reset; + + mfx_temporal_effect->process(rb_data->get_mfx_temporal_context(), params); + } + + RD::get_singleton()->draw_command_end_label(); +#endif } else if (using_taa) { RD::get_singleton()->draw_command_begin_label("TAA"); RENDER_TIMESTAMP("TAA"); @@ -4846,6 +4929,10 @@ RenderForwardClustered::RenderForwardClustered() { taa = memnew(RendererRD::TAA); fsr2_effect = memnew(RendererRD::FSR2Effect); ss_effects = memnew(RendererRD::SSEffects); +#ifdef METAL_ENABLED + motion_vectors_store = memnew(RendererRD::MotionVectorsStore); + mfx_temporal_effect = memnew(RendererRD::MFXTemporalEffect); +#endif } RenderForwardClustered::~RenderForwardClustered() { @@ -4864,6 +4951,18 @@ RenderForwardClustered::~RenderForwardClustered() { fsr2_effect = nullptr; } +#ifdef METAL_ENABLED + if (mfx_temporal_effect) { + memdelete(mfx_temporal_effect); + mfx_temporal_effect = nullptr; + } + + if (motion_vectors_store) { + memdelete(motion_vectors_store); + motion_vectors_store = nullptr; + } +#endif + if (resolve_effects != nullptr) { memdelete(resolve_effects); resolve_effects = nullptr; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 90569e11c757..42c1da1a9711 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -34,6 +34,10 @@ #include "core/templates/paged_allocator.h" #include "servers/rendering/renderer_rd/cluster_builder_rd.h" #include "servers/rendering/renderer_rd/effects/fsr2.h" +#ifdef METAL_ENABLED +#include "servers/rendering/renderer_rd/effects/metal_fx.h" +#endif +#include "servers/rendering/renderer_rd/effects/motion_vectors_store.h" #include "servers/rendering/renderer_rd/effects/resolve.h" #include "servers/rendering/renderer_rd/effects/ss_effects.h" #include "servers/rendering/renderer_rd/effects/taa.h" @@ -91,6 +95,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { private: RenderSceneBuffersRD *render_buffers = nullptr; RendererRD::FSR2Context *fsr2_context = nullptr; +#ifdef METAL_ENABLED + RendererRD::MFXTemporalContext *mfx_temporal_context = nullptr; +#endif public: ClusterBuilderRD *cluster_builder = nullptr; @@ -134,6 +141,11 @@ class RenderForwardClustered : public RendererSceneRenderRD { void ensure_fsr2(RendererRD::FSR2Effect *p_effect); RendererRD::FSR2Context *get_fsr2_context() const { return fsr2_context; } +#ifdef METAL_ENABLED + bool ensure_mfx_temporal(RendererRD::MFXTemporalEffect *p_effect); + RendererRD::MFXTemporalContext *get_mfx_temporal_context() const { return mfx_temporal_context; } +#endif + RID get_color_only_fb(); RID get_color_pass_fb(uint32_t p_color_pass_flags); RID get_depth_fb(DepthFrameBufferType p_type = DEPTH_FB); @@ -634,6 +646,11 @@ class RenderForwardClustered : public RendererSceneRenderRD { RendererRD::FSR2Effect *fsr2_effect = nullptr; RendererRD::SSEffects *ss_effects = nullptr; +#ifdef METAL_ENABLED + RendererRD::MFXTemporalEffect *mfx_temporal_effect = nullptr; +#endif + RendererRD::MotionVectorsStore *motion_vectors_store = nullptr; + /* Cluster builder */ ClusterBuilderSharedDataRD cluster_builder_shared; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 7defef816b64..c0af62a267ee 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -429,8 +429,18 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende can_use_effects &= _debug_draw_can_use_effects(debug_draw); bool can_use_storage = _render_buffers_can_be_storage(); - bool use_fsr = fsr && can_use_effects && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR; - bool use_upscaled_texture = rb->has_upscaled_texture() && rb->get_scaling_3d_mode() == RS::VIEWPORT_SCALING_3D_MODE_FSR2; + RS::ViewportScaling3DMode scale_mode = rb->get_scaling_3d_mode(); + bool use_upscaled_texture = rb->has_upscaled_texture() && (scale_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2 || scale_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL); + SpatialUpscaler *spatial_upscaler = nullptr; + if (can_use_effects) { + if (scale_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) { + spatial_upscaler = fsr; + } else if (scale_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL) { +#if METAL_ENABLED + spatial_upscaler = mfx_spatial; +#endif + } + } RID render_target = rb->get_render_target(); RID color_texture = use_upscaled_texture ? rb->get_upscaled_texture() : rb->get_internal_texture(); @@ -644,9 +654,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende tonemap.convert_to_srgb = !texture_storage->render_target_is_using_hdr(render_target); RID dest_fb; - bool use_intermediate_fb = use_fsr; - if (use_intermediate_fb) { - // If we use FSR to upscale we need to write our result into an intermediate buffer. + if (spatial_upscaler != nullptr) { + // If we use a spatial upscaler to upscale we need to write our result into an intermediate buffer. // Note that this is cached so we only create the texture the first time. RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true); dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture); @@ -668,14 +677,16 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RD::get_singleton()->draw_command_end_label(); } - if (use_fsr) { - RD::get_singleton()->draw_command_begin_label("FSR 1.0 Upscale"); + if (rb.is_valid() && spatial_upscaler) { + spatial_upscaler->ensure_context(rb); + + RD::get_singleton()->draw_command_begin_label(spatial_upscaler->get_label()); for (uint32_t v = 0; v < rb->get_view_count(); v++) { RID source_texture = rb->get_texture_slice(SNAME("Tonemapper"), SNAME("destination"), v, 0); RID dest_texture = texture_storage->render_target_get_rd_texture_slice(render_target, v); - fsr->fsr_upscale(rb, source_texture, dest_texture); + spatial_upscaler->process(rb, source_texture, dest_texture); } if (dest_is_msaa_2d) { @@ -1520,6 +1531,9 @@ void RendererSceneRenderRD::init() { if (can_use_storage) { fsr = memnew(RendererRD::FSR); } +#ifdef METAL_ENABLED + mfx_spatial = memnew(RendererRD::MFXSpatialEffect); +#endif } RendererSceneRenderRD::~RendererSceneRenderRD() { @@ -1548,6 +1562,11 @@ RendererSceneRenderRD::~RendererSceneRenderRD() { if (fsr) { memdelete(fsr); } +#ifdef METAL_ENABLED + if (mfx_spatial) { + memdelete(mfx_spatial); + } +#endif if (sky.sky_scene_state.uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(sky.sky_scene_state.uniform_set)) { RD::get_singleton()->free(sky.sky_scene_state.uniform_set); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 57a25f069529..ff06efa95673 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -37,6 +37,9 @@ #include "servers/rendering/renderer_rd/effects/debug_effects.h" #include "servers/rendering/renderer_rd/effects/fsr.h" #include "servers/rendering/renderer_rd/effects/luminance.h" +#ifdef METAL_ENABLED +#include "servers/rendering/renderer_rd/effects/metal_fx.h" +#endif #include "servers/rendering/renderer_rd/effects/tone_mapper.h" #include "servers/rendering/renderer_rd/effects/vrs.h" #include "servers/rendering/renderer_rd/environment/gi.h" @@ -61,6 +64,9 @@ class RendererSceneRenderRD : public RendererSceneRender { RendererRD::ToneMapper *tone_mapper = nullptr; RendererRD::FSR *fsr = nullptr; RendererRD::VRS *vrs = nullptr; +#ifdef METAL_ENABLED + RendererRD::MFXSpatialEffect *mfx_spatial = nullptr; +#endif double time = 0.0; double time_step = 0.0; diff --git a/servers/rendering/renderer_rd/shaders/effects/motion_vectors_store.glsl b/servers/rendering/renderer_rd/shaders/effects/motion_vectors_store.glsl new file mode 100644 index 000000000000..d2f4dad90dcc --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/effects/motion_vectors_store.glsl @@ -0,0 +1,32 @@ +#[compute] + +#version 450 + +#VERSION_DEFINES + +#include "motion_vector_inc.glsl" + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +layout(set = 0, binding = 0) uniform sampler2D depth_buffer; +layout(rg16f, set = 0, binding = 1) uniform restrict writeonly image2D velocity_buffer; + +layout(push_constant, std430) uniform Params { + highp mat4 reprojection_matrix; + vec2 resolution; + uint pad[2]; +} +params; + +void main() { + // Out of bounds check. + if (any(greaterThanEqual(vec2(gl_GlobalInvocationID.xy), params.resolution))) { + return; + } + + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + + float depth = texelFetch(depth_buffer, pos, 0).x; + vec2 uv = (vec2(pos) + 0.5f) / params.resolution; + vec2 velocity = derive_motion_vector(uv, depth, params.reprojection_matrix); + imageStore(velocity_buffer, pos, vec4(velocity, 0.0f, 0.0f)); +} diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 9c886b460ff5..26dec52d24f5 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -102,7 +102,7 @@ void RenderSceneBuffersRD::free_named_texture(NamedTexture &p_named_texture) { void RenderSceneBuffersRD::update_samplers() { float computed_mipmap_bias = texture_mipmap_bias; - if (use_taa || (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2)) { + if (use_taa || (RS::scaling_3d_mode_type(scaling_3d_mode) == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL)) { // Use negative mipmap LOD bias when TAA or FSR2 is enabled to compensate for loss of sharpness. // This restores sharpness in still images to be roughly at the same level as without TAA, // but moving scenes will still be blurrier. @@ -139,6 +139,13 @@ void RenderSceneBuffersRD::cleanup() { weight_buffer.weight = RID(); } } + +#ifdef METAL_ENABLED + if (mfx_spatial_context) { + memdelete(mfx_spatial_context); + mfx_spatial_context = nullptr; + } +#endif } void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_config) { @@ -242,6 +249,22 @@ void RenderSceneBuffersRD::set_use_debanding(bool p_use_debanding) { use_debanding = p_use_debanding; } +#ifdef METAL_ENABLED +void RenderSceneBuffersRD::ensure_mfx(RendererRD::MFXSpatialEffect *p_effect) { + if (mfx_spatial_context) { + return; + } + RendererRD::MFXSpatialEffect::CreateParams params = { + .input_size = internal_size, + .output_size = target_size, + .input_format = base_data_format, + .output_format = RD::DATA_FORMAT_R8G8B8A8_UNORM, + }; + + mfx_spatial_context = p_effect->create_context(params); +} +#endif + // Named textures bool RenderSceneBuffersRD::has_texture(const StringName &p_context, const StringName &p_texture_name) const { @@ -481,7 +504,7 @@ void RenderSceneBuffersRD::allocate_blur_textures() { } Size2i blur_size = internal_size; - if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2) { + if (RS::scaling_3d_mode_type(scaling_3d_mode) == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL) { // The blur texture should be as big as the target size when using an upscaler. blur_size = target_size; } diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index b1c1470e6bc0..74e68f7e47af 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -31,6 +31,9 @@ #ifndef RENDER_SCENE_BUFFERS_RD_H #define RENDER_SCENE_BUFFERS_RD_H +#ifdef METAL_ENABLED +#include "../effects/metal_fx.h" +#endif #include "../effects/vrs.h" #include "core/templates/hash_map.h" #include "material_storage.h" @@ -80,7 +83,11 @@ class RenderSceneBuffersRD : public RenderSceneBuffers { float texture_mipmap_bias = 0.0f; RS::ViewportAnisotropicFiltering anisotropic_filtering_level = RS::VIEWPORT_ANISOTROPY_4X; - // Aliassing settings +#ifdef METAL_ENABLED + RendererRD::MFXSpatialContext *mfx_spatial_context = nullptr; +#endif + + // Aliasing settings RS::ViewportMSAA msaa_3d = RS::VIEWPORT_MSAA_DISABLED; RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; bool use_taa = false; @@ -191,6 +198,11 @@ class RenderSceneBuffersRD : public RenderSceneBuffers { virtual void set_anisotropic_filtering_level(RS::ViewportAnisotropicFiltering p_anisotropic_filtering_level) override; virtual void set_use_debanding(bool p_use_debanding) override; +#ifdef METAL_ENABLED + void ensure_mfx(RendererRD::MFXSpatialEffect *p_effect); + _FORCE_INLINE_ RendererRD::MFXSpatialContext *get_mfx_spatial_context() const { return mfx_spatial_context; } +#endif + // Named Textures bool has_texture(const StringName &p_context, const StringName &p_texture_name) const; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index a254647c71aa..69952ca42bd5 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -130,37 +130,63 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { float scaling_3d_scale = p_viewport->scaling_3d_scale; RS::ViewportScaling3DMode scaling_3d_mode = p_viewport->scaling_3d_mode; bool upscaler_available = p_viewport->fsr_enabled; + RS::ViewportScaling3DType scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode); - if ((!upscaler_available || scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_BILINEAR || scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) && scaling_3d_scale >= (1.0 - EPSILON) && scaling_3d_scale <= (1.0 + EPSILON)) { - // No 3D scaling on bilinear or FSR? Ignore scaling mode, this just introduces overhead. + if ((!upscaler_available || (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_SPATIAL)) && scaling_3d_scale >= (1.0 - EPSILON) && scaling_3d_scale <= (1.0 + EPSILON)) { + // No 3D scaling for spatial modes? Ignore scaling mode, this just introduces overhead. // - Mobile can't perform optimal path // - FSR does an extra pass (or 2 extra passes if 2D-MSAA is enabled) - // Scaling = 1.0 on FSR2 has benefits + // Scaling = 1.0 on FSR2 and MetalFX temporal has benefits scaling_3d_scale = 1.0; scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF; } - bool scaling_3d_is_fsr = (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR) || (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2); + // Verify MetalFX upscaling support. + if ( + (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL)) || + (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL))) { + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; + WARN_PRINT_ONCE("MetalFX upscaling is not supported in the current renderer. Falling back to bilinear 3D resolution scaling."); + } + + RS::ViewportMSAA msaa_3d = p_viewport->msaa_3d; + + // If MetalFX Temporal upscaling is supported, verify limits. + if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL) { + double min_scale = (double)RD::get_singleton()->limit_get(RD::LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE) / 1000'000.0; + double max_scale = (double)RD::get_singleton()->limit_get(RD::LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE) / 1000'000.0; + if ((double)scaling_3d_scale < min_scale || (double)scaling_3d_scale > max_scale) { + scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; + WARN_PRINT_ONCE(vformat("MetalFX temporal upscaling scale is outside limits; scale must be between %f and %f. Falling back to bilinear 3D resolution scaling.", min_scale, max_scale)); + } + + if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) { + WARN_PRINT_ONCE("MetalFX temporal upscaling does not support 3D MSAA. Disabling 3D MSAA internally."); + msaa_3d = RS::VIEWPORT_MSAA_DISABLED; + } + } + + bool scaling_3d_is_not_bilinear = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; bool use_taa = p_viewport->use_taa; - if (scaling_3d_is_fsr && (scaling_3d_scale >= (1.0 + EPSILON))) { + if (scaling_3d_is_not_bilinear && (scaling_3d_scale >= (1.0 + EPSILON))) { // FSR is not designed for downsampling. // Fall back to bilinear scaling. WARN_PRINT_ONCE("FSR 3D resolution scaling is not designed for downsampling. Falling back to bilinear 3D resolution scaling."); scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; } - if (scaling_3d_is_fsr && !upscaler_available) { + if (scaling_3d_is_not_bilinear && !upscaler_available) { // FSR is not actually available. // Fall back to bilinear scaling. WARN_PRINT_ONCE("FSR 3D resolution scaling is not available. Falling back to bilinear 3D resolution scaling."); scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; } - if (use_taa && scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2) { + if (use_taa && (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL)) { // FSR2 can't be used with TAA. // Turn it off and prefer using FSR2. - WARN_PRINT_ONCE("FSR 2 is not compatible with TAA. Disabling TAA internally."); + WARN_PRINT_ONCE("FSR 2 or MetalFX Temporal is not compatible with TAA. Disabling TAA internally."); use_taa = false; } @@ -178,6 +204,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { render_width = CLAMP(target_width * scaling_3d_scale, 1, 16384); render_height = CLAMP(target_height * scaling_3d_scale, 1, 16384); break; + case RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL: + case RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL: case RS::VIEWPORT_SCALING_3D_MODE_FSR: case RS::VIEWPORT_SCALING_3D_MODE_FSR2: target_width = p_viewport->size.width; @@ -204,8 +232,9 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { } uint32_t jitter_phase_count = 0; - if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_FSR2) { + if (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL) { // Implementation has been copied from ffxFsr2GetJitterPhaseCount. + // Also used for MetalFX Temporal scaling. jitter_phase_count = uint32_t(8.0f * pow(float(target_width) / render_width, 2.0f)); } else if (use_taa) { // Default jitter count for TAA. @@ -225,7 +254,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { rb_config.set_target_size(Size2(target_width, target_height)); rb_config.set_view_count(p_viewport->view_count); rb_config.set_scaling_3d_mode(scaling_3d_mode); - rb_config.set_msaa_3d(p_viewport->msaa_3d); + rb_config.set_msaa_3d(msaa_3d); rb_config.set_screen_space_aa(p_viewport->screen_space_aa); rb_config.set_fsr_sharpness(p_viewport->fsr_sharpness); rb_config.set_texture_mipmap_bias(texture_mipmap_bias); @@ -1011,7 +1040,9 @@ void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int } bool RendererViewport::_viewport_requires_motion_vectors(Viewport *p_viewport) { - return p_viewport->use_taa || p_viewport->scaling_3d_mode == RenderingServer::VIEWPORT_SCALING_3D_MODE_FSR2 || p_viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS || p_viewport->force_motion_vectors; + return p_viewport->use_taa || + RS::scaling_3d_mode_type(p_viewport->scaling_3d_mode) == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL || + p_viewport->debug_draw == RenderingServer::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS || p_viewport->force_motion_vectors; } void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index b62314ae3001..374b79f7ce8d 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -561,6 +561,59 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p return OK; } +Error RenderingDevice::driver_callback_add(RDD::DriverCallback p_callback, void *p_userdata, VectorView p_resources) { + ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE); + + ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, + "Driver callback is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, + "Driver callback is forbidden during creation of a compute list"); + + thread_local LocalVector trackers; + thread_local LocalVector usages; + + uint32_t resource_count = p_resources.size(); + trackers.resize(resource_count); + usages.resize(resource_count); + + if (resource_count > 0) { + for (uint32_t i = 0; i < p_resources.size(); i++) { + const CallbackResource &cr = p_resources[i]; + switch (cr.type) { + case CALLBACK_RESOURCE_TYPE_BUFFER: { + Buffer *buffer = _get_buffer_from_owner(cr.rid); + if (!buffer) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("Argument %d is not a valid buffer of any type.", i)); + } + if (_buffer_make_mutable(buffer, cr.rid)) { + draw_graph.add_synchronization(); + } + trackers[i] = buffer->draw_tracker; + usages[i] = (RDG::ResourceUsage)cr.usage; + } break; + case CALLBACK_RESOURCE_TYPE_TEXTURE: { + Texture *texture = texture_owner.get_or_null(cr.rid); + if (!texture) { + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("Argument %d is not a valid texture.", i)); + } + if (_texture_make_mutable(texture, cr.rid)) { + draw_graph.add_synchronization(); + } + trackers[i] = texture->draw_tracker; + usages[i] = (RDG::ResourceUsage)cr.usage; + } break; + default: { + CRASH_NOW_MSG("Invalid callback resource type."); + } break; + } + } + } + + draw_graph.add_driver_callback(p_callback, p_userdata, trackers, usages); + + return OK; +} + String RenderingDevice::get_perf_report() const { return perf_report_text; } @@ -7855,6 +7908,8 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z); BIND_ENUM_CONSTANT(LIMIT_MAX_VIEWPORT_DIMENSIONS_X); BIND_ENUM_CONSTANT(LIMIT_MAX_VIEWPORT_DIMENSIONS_Y); + BIND_ENUM_CONSTANT(LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE); + BIND_ENUM_CONSTANT(LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE); BIND_ENUM_CONSTANT(MEMORY_TEXTURES); BIND_ENUM_CONSTANT(MEMORY_BUFFERS); @@ -8201,3 +8256,23 @@ void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, cons ERR_FAIL_COND(p_data_size > (uint32_t)p_data.size()); compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size); } + +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_NONE, RDG::RESOURCE_USAGE_NONE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_COPY_FROM, RDG::RESOURCE_USAGE_COPY_FROM)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_COPY_TO, RDG::RESOURCE_USAGE_COPY_TO)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_RESOLVE_FROM, RDG::RESOURCE_USAGE_RESOLVE_FROM)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_RESOLVE_TO, RDG::RESOURCE_USAGE_RESOLVE_TO)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_UNIFORM_BUFFER_READ, RDG::RESOURCE_USAGE_UNIFORM_BUFFER_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_INDIRECT_BUFFER_READ, RDG::RESOURCE_USAGE_INDIRECT_BUFFER_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_TEXTURE_BUFFER_READ, RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE, RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_STORAGE_BUFFER_READ, RDG::RESOURCE_USAGE_STORAGE_BUFFER_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE, RDG::RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_VERTEX_BUFFER_READ, RDG::RESOURCE_USAGE_VERTEX_BUFFER_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_INDEX_BUFFER_READ, RDG::RESOURCE_USAGE_INDEX_BUFFER_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE, RDG::RESOURCE_USAGE_TEXTURE_SAMPLE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ, RDG::RESOURCE_USAGE_STORAGE_IMAGE_READ)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE, RDG::RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE, RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE, RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE)); +static_assert(ENUM_MEMBERS_EQUAL(RD::CALLBACK_RESOURCE_USAGE_MAX, RDG::RESOURCE_USAGE_MAX)); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index ea268460c034..a4aa4df660a2 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -223,6 +223,47 @@ class RenderingDevice : public RenderingDeviceCommons { Vector buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); // This causes stall, only use to retrieve large buffers for saving. Error buffer_get_data_async(RID p_buffer, const Callable &p_callback, uint32_t p_offset = 0, uint32_t p_size = 0); +private: + /******************/ + /**** CALLBACK ****/ + /******************/ + +public: + enum CallbackResourceType { + CALLBACK_RESOURCE_TYPE_TEXTURE, + CALLBACK_RESOURCE_TYPE_BUFFER, + }; + + enum CallbackResourceUsage { + CALLBACK_RESOURCE_USAGE_NONE, + CALLBACK_RESOURCE_USAGE_COPY_FROM, + CALLBACK_RESOURCE_USAGE_COPY_TO, + CALLBACK_RESOURCE_USAGE_RESOLVE_FROM, + CALLBACK_RESOURCE_USAGE_RESOLVE_TO, + CALLBACK_RESOURCE_USAGE_UNIFORM_BUFFER_READ, + CALLBACK_RESOURCE_USAGE_INDIRECT_BUFFER_READ, + CALLBACK_RESOURCE_USAGE_TEXTURE_BUFFER_READ, + CALLBACK_RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE, + CALLBACK_RESOURCE_USAGE_STORAGE_BUFFER_READ, + CALLBACK_RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE, + CALLBACK_RESOURCE_USAGE_VERTEX_BUFFER_READ, + CALLBACK_RESOURCE_USAGE_INDEX_BUFFER_READ, + CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE, + CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ, + CALLBACK_RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE, + CALLBACK_RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE, + CALLBACK_RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE, + CALLBACK_RESOURCE_USAGE_MAX + }; + + struct CallbackResource { + RID rid; + CallbackResourceType type = CALLBACK_RESOURCE_TYPE_TEXTURE; + CallbackResourceUsage usage = CALLBACK_RESOURCE_USAGE_NONE; + }; + + Error driver_callback_add(RDD::DriverCallback p_callback, void *p_userdata, VectorView p_resources); + /*****************/ /**** TEXTURE ****/ /*****************/ @@ -855,6 +896,7 @@ class RenderingDevice : public RenderingDeviceCommons { #endif public: + RenderingDeviceDriver *get_device_driver() const { return driver; } RenderingContextDriver *get_context_driver() const { return context; } const RDD::Capabilities &get_device_capabilities() const { return driver->get_capabilities(); } diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h index 284337fe6bc1..6bade4bff95c 100644 --- a/servers/rendering/rendering_device_commons.h +++ b/servers/rendering/rendering_device_commons.h @@ -873,13 +873,17 @@ class RenderingDeviceCommons : public Object { LIMIT_VRS_TEXEL_HEIGHT, LIMIT_VRS_MAX_FRAGMENT_WIDTH, LIMIT_VRS_MAX_FRAGMENT_HEIGHT, + LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE, + LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE, }; enum Features { SUPPORTS_MULTIVIEW, SUPPORTS_FSR_HALF_FLOAT, SUPPORTS_ATTACHMENT_VRS, - // If not supported, a fragment shader with only side effets (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver. + SUPPORTS_METALFX_SPATIAL, + SUPPORTS_METALFX_TEMPORAL, + // If not supported, a fragment shader with only side effects (i.e., writes to buffers, but doesn't output to attachments), may be optimized down to no-op by the GPU driver. SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS, }; diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index 5beba6eff432..2f6ea79316b3 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -722,6 +722,12 @@ class RenderingDeviceDriver : public RenderingDeviceCommons { virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView p_specialization_constants) = 0; + /******************/ + /**** CALLBACK ****/ + /******************/ + + typedef void (*DriverCallback)(RenderingDeviceDriver *p_driver, CommandBufferID p_command_buffer, void *p_userdata); + /*****************/ /**** QUERIES ****/ /*****************/ diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index 905c7ecb7974..ae91e1b3e4a0 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -994,6 +994,10 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC driver->command_copy_buffer(r_command_buffer, command_buffer_copies[j].source, buffer_update_command->destination, command_buffer_copies[j].region); } } break; + case RecordedCommand::TYPE_DRIVER_CALLBACK: { + const RecordedDriverCallbackCommand *driver_callback_command = reinterpret_cast(command); + driver_callback_command->callback(driver, r_command_buffer, driver_callback_command->userdata); + } break; case RecordedCommand::TYPE_COMPUTE_LIST: { if (device.workarounds.avoid_compute_after_draw && workarounds_state.draw_list_found) { // Avoid compute after draw workaround. Refer to the comment that enables this in the Vulkan driver for more information. @@ -1132,6 +1136,7 @@ void RenderingDeviceGraph::_run_label_command_change(RDD::CommandBufferID p_comm bool copy_commands = false; bool compute_commands = false; bool draw_commands = false; + bool custom_commands = false; for (uint32_t i = 0; i < p_sorted_commands_count; i++) { const uint32_t command_index = p_sorted_commands[i].index; const uint32_t command_data_offset = command_data_offsets[command_index]; @@ -1158,27 +1163,33 @@ void RenderingDeviceGraph::_run_label_command_change(RDD::CommandBufferID p_comm case RecordedCommand::TYPE_DRAW_LIST: { draw_commands = true; } break; + case RecordedCommand::TYPE_DRIVER_CALLBACK: { + custom_commands = true; + } break; default: { // Ignore command. } break; } - if (copy_commands && compute_commands && draw_commands) { + if (copy_commands && compute_commands && draw_commands && custom_commands) { // There's no more command types to find. break; } } - if (copy_commands || compute_commands || draw_commands) { + if (copy_commands || compute_commands || draw_commands || custom_commands) { // Add the operations to the name. - bool plus_after_copy = copy_commands && (compute_commands || draw_commands); - bool plus_after_compute = compute_commands && draw_commands; + bool plus_after_copy = copy_commands && (compute_commands || draw_commands || custom_commands); + bool plus_after_compute = compute_commands && (draw_commands || custom_commands); + bool plus_after_draw = draw_commands && custom_commands; label_name += " ("; label_name += copy_commands ? "Copy" : ""; label_name += plus_after_copy ? "+" : ""; label_name += compute_commands ? "Compute" : ""; label_name += plus_after_compute ? "+" : ""; label_name += draw_commands ? "Draw" : ""; + label_name += plus_after_draw ? "+" : ""; + label_name += custom_commands ? "Custom" : ""; label_name += ")"; } } @@ -1328,6 +1339,9 @@ void RenderingDeviceGraph::_print_render_commands(const RecordedCommandSort *p_s const RecordedBufferUpdateCommand *buffer_update_command = reinterpret_cast(command); print_line(command_index, "LEVEL", command_level, "BUFFER UPDATE DESTINATION", itos(buffer_update_command->destination.id), "COPIES", buffer_update_command->buffer_copies_count); } break; + case RecordedCommand::TYPE_DRIVER_CALLBACK: { + print_line(command_index, "LEVEL", command_level, "DRIVER CALLBACK"); + } break; case RecordedCommand::TYPE_COMPUTE_LIST: { const RecordedComputeListCommand *compute_list_command = reinterpret_cast(command); print_line(command_index, "LEVEL", command_level, "COMPUTE LIST SIZE", compute_list_command->instruction_data_size); @@ -1658,6 +1672,17 @@ void RenderingDeviceGraph::add_buffer_update(RDD::BufferID p_dst, ResourceTracke _add_command_to_graph(&p_dst_tracker, &buffer_usage, 1, command_index, command); } +void RenderingDeviceGraph::add_driver_callback(RDD::DriverCallback p_callback, void *p_userdata, VectorView p_trackers, VectorView p_usages) { + DEV_ASSERT(p_trackers.size() == p_usages.size()); + + int32_t command_index; + RecordedDriverCallbackCommand *command = static_cast(_allocate_command(sizeof(RecordedDriverCallbackCommand), command_index)); + command->type = RecordedCommand::TYPE_DRIVER_CALLBACK; + command->callback = p_callback; + command->userdata = p_userdata; + _add_command_to_graph((ResourceTracker **)p_trackers.ptr(), (ResourceUsage *)p_usages.ptr(), p_trackers.size(), command_index, command); +} + void RenderingDeviceGraph::add_compute_list_begin(RDD::BreadcrumbMarker p_phase, uint32_t p_breadcrumb_data) { compute_instruction_list.clear(); #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) @@ -2271,7 +2296,8 @@ void RenderingDeviceGraph::end(bool p_reorder_commands, bool p_full_barriers, RD 2, // TYPE_TEXTURE_GET_DATA 2, // TYPE_TEXTURE_RESOLVE 2, // TYPE_TEXTURE_UPDATE - 2, // TYPE_INSERT_BREADCRUMB + 2, // TYPE_CAPTURE_TIMESTAMP + 5, // TYPE_DRIVER_CALLBACK }; commands_sorted.clear(); diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h index 97412a331728..567893e6fe8b 100644 --- a/servers/rendering/rendering_device_graph.h +++ b/servers/rendering/rendering_device_graph.h @@ -99,6 +99,7 @@ class RenderingDeviceGraph { TYPE_TEXTURE_RESOLVE, TYPE_TEXTURE_UPDATE, TYPE_CAPTURE_TIMESTAMP, + TYPE_DRIVER_CALLBACK, TYPE_MAX }; @@ -147,7 +148,8 @@ class RenderingDeviceGraph { RESOURCE_USAGE_STORAGE_IMAGE_READ, RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE, RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE, - RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE + RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE, + RESOURCE_USAGE_MAX }; struct ResourceTracker { @@ -336,6 +338,11 @@ class RenderingDeviceGraph { } }; + struct RecordedDriverCallbackCommand : RecordedCommand { + RDD::DriverCallback callback; + void *userdata = nullptr; + }; + struct RecordedComputeListCommand : RecordedCommand { uint32_t instruction_data_size = 0; uint32_t breadcrumb = 0; @@ -766,6 +773,7 @@ class RenderingDeviceGraph { void add_buffer_copy(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, RDD::BufferCopyRegion p_region); void add_buffer_get_data(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, RDD::BufferCopyRegion p_region); void add_buffer_update(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, VectorView p_buffer_copies); + void add_driver_callback(RDD::DriverCallback p_callback, void *p_userdata, VectorView p_trackers, VectorView p_usages); void add_compute_list_begin(RDD::BreadcrumbMarker p_phase = RDD::BreadcrumbMarker::NONE, uint32_t p_breadcrumb_data = 0); void add_compute_list_bind_pipeline(RDD::PipelineID p_pipeline); void add_compute_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); diff --git a/servers/rendering/storage/render_scene_buffers.cpp b/servers/rendering/storage/render_scene_buffers.cpp index acd18795ed1f..4bf0365b72df 100644 --- a/servers/rendering/storage/render_scene_buffers.cpp +++ b/servers/rendering/storage/render_scene_buffers.cpp @@ -49,7 +49,7 @@ void RenderSceneBuffersConfiguration::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &RenderSceneBuffersConfiguration::get_scaling_3d_mode); ClassDB::bind_method(D_METHOD("set_scaling_3d_mode", "scaling_3d_mode"), &RenderSceneBuffersConfiguration::set_scaling_3d_mode); - 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"); // TODO VIEWPORT_SCALING_3D_MODE_OFF is possible here too, but we can't specify an enum string for it. + 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"); // TODO VIEWPORT_SCALING_3D_MODE_OFF is possible here too, but we can't specify an enum string for it. ClassDB::bind_method(D_METHOD("get_msaa_3d"), &RenderSceneBuffersConfiguration::get_msaa_3d); ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa_3d"), &RenderSceneBuffersConfiguration::set_msaa_3d); diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 96c49e505b82..b68974d3cea1 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2867,6 +2867,8 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_BILINEAR); BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_FSR); BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_FSR2); + BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL); + BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL); BIND_ENUM_CONSTANT(VIEWPORT_SCALING_3D_MODE_MAX); BIND_ENUM_CONSTANT(VIEWPORT_UPDATE_DISABLED); @@ -3662,7 +3664,23 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01"), 0.25); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/anti_aliasing/screen_space_roughness_limiter/limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"), 0.18); - GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/scaling_3d/mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow)"), 0); + { + String mode_hints; + String mode_hints_metal; + { + Vector mode_hints_arr = { "Bilinear (Fastest)", "FSR 1.0 (Fast)", "FSR 2.2 (Slow)" }; + mode_hints = String(",").join(mode_hints_arr); +#ifdef METAL_ENABLED + mode_hints_arr.push_back("MetalFX (Spatial)"); + mode_hints_arr.push_back("MetalFX (Temporal)"); +#endif + mode_hints_metal = String(",").join(mode_hints_arr); + } + + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/scaling_3d/mode", PROPERTY_HINT_ENUM, mode_hints), 0); + GLOBAL_DEF_NOVAL(PropertyInfo(Variant::INT, "rendering/scaling_3d/mode.ios", PROPERTY_HINT_ENUM, mode_hints_metal), 0); + GLOBAL_DEF_NOVAL(PropertyInfo(Variant::INT, "rendering/scaling_3d/mode.macos", PROPERTY_HINT_ENUM, mode_hints_metal), 0); + } GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/scaling_3d/scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), 1.0); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/scaling_3d/fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), 0.2f); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/textures/default_filters/texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), 0.0f); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 67b6dc915cab..ceee68b85e36 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -918,6 +918,8 @@ class RenderingServer : public Object { VIEWPORT_SCALING_3D_MODE_BILINEAR, VIEWPORT_SCALING_3D_MODE_FSR, VIEWPORT_SCALING_3D_MODE_FSR2, + VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL, + VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL, VIEWPORT_SCALING_3D_MODE_MAX, VIEWPORT_SCALING_3D_MODE_OFF = 255, // for internal use only }; @@ -931,6 +933,22 @@ class RenderingServer : public Object { VIEWPORT_ANISOTROPY_MAX }; + enum ViewportScaling3DType { + VIEWPORT_SCALING_3D_TYPE_NONE, + VIEWPORT_SCALING_3D_TYPE_TEMPORAL, + VIEWPORT_SCALING_3D_TYPE_SPATIAL, + VIEWPORT_SCALING_3D_TYPE_MAX, + }; + + _ALWAYS_INLINE_ static ViewportScaling3DType scaling_3d_mode_type(ViewportScaling3DMode p_mode) { + if (p_mode == VIEWPORT_SCALING_3D_MODE_BILINEAR || p_mode == VIEWPORT_SCALING_3D_MODE_FSR || p_mode == VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL) { + return VIEWPORT_SCALING_3D_TYPE_SPATIAL; + } else if (p_mode == VIEWPORT_SCALING_3D_MODE_FSR2 || p_mode == VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL) { + return VIEWPORT_SCALING_3D_TYPE_TEMPORAL; + } + return VIEWPORT_SCALING_3D_TYPE_NONE; + } + virtual void viewport_set_use_xr(RID p_viewport, bool p_use_xr) = 0; virtual void viewport_set_size(RID p_viewport, int p_width, int p_height) = 0; virtual void viewport_set_active(RID p_viewport, bool p_active) = 0;