diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 5b6dcbb567b..3ed90147761 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -149,6 +149,11 @@ Comment: Temporal Anti-Aliasing resolve implementation
Copyright: 2016, Panos Karabelas
License: Expat
+Files: ./thirdparty/amd-antilag2/
+Comment: AMD Anti-Lag 2
+Copyright: 2024, Advanced Micro Devices, Inc.
+License: Expat
+
Files: ./thirdparty/amd-fsr/
Comment: AMD FidelityFX Super Resolution
Copyright: 2021, Advanced Micro Devices, Inc.
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 32f36e01f9a..2e3132d429b 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1545,6 +1545,7 @@ ProjectSettings::ProjectSettings() {
custom_prop_info["rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame", PROPERTY_HINT_RANGE, "256,2048");
GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", 512);
custom_prop_info["rendering/rendering_device/d3d12/max_misc_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", PROPERTY_HINT_RANGE, "32,4096");
+ GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/d3d12/amd_antilag2_enabled"), true);
// The default value must match the minor part of the Agility SDK version
// installed by the scripts provided in the repository
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index b205b862a3f..e77ff82048e 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2761,6 +2761,9 @@
Version code of the [url=https://devblogs.microsoft.com/directx/directx12agility/]Direct3D 12 Agility SDK[/url] to use ([code]D3D12SDKVersion[/code]). This must match the [i]minor[/i] version that is installed next to the editor binary and in the export templates directory for the current editor version. For example, if you have [code]1.613.3[/code] installed, you need to input [code]613[/code] here.
+
+ If [code]true[/code], enables AMD AntiLag 2.
+
The number of entries in the miscellaneous descriptors heap the Direct3D 12 rendering driver uses each frame, used for various operations like clearing a texture.
Depending on the complexity of scenes, this value may be lowered or may need to be raised.
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index a445006058c..69ef2cb3f0c 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "servers/rendering/rendering_device.h"
+#include "thirdparty/amd-antilag2/ffx_antilag2_dx12.h"
#include "thirdparty/zlib/zlib.h"
#include "d3d12_godot_nir_bridge.h"
@@ -6292,6 +6293,7 @@ RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(RenderingContextDriverD3D
RenderingDeviceDriverD3D12::~RenderingDeviceDriverD3D12() {
glsl_type_singleton_decref();
+ AMD::AntiLag2DX12::DeInitialize(&amd_antilag_ctx);
}
bool RenderingDeviceDriverD3D12::is_in_developer_mode() {
@@ -6389,6 +6391,9 @@ Error RenderingDeviceDriverD3D12::_initialize_device() {
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
}
+ amd_antilag_present = AMD::AntiLag2DX12::Initialize(&amd_antilag_ctx, device.Get()) == S_OK;
+ amd_antilag_enabled = GLOBAL_GET("rendering/rendering_device/d3d12/amd_antilag2_enabled");
+
return OK;
}
@@ -6723,3 +6728,9 @@ Error RenderingDeviceDriverD3D12::initialize(uint32_t p_device_index, uint32_t p
return OK;
}
+
+void RenderingDeviceDriverD3D12::pre_input_hook(unsigned max_fps) {
+ if (amd_antilag_present) {
+ AMD::AntiLag2DX12::Update(&amd_antilag_ctx, amd_antilag_enabled, max_fps);
+ }
+}
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index d8381279ec8..c6d1e6a13d1 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -62,6 +62,8 @@
#define D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
#include "D3D12MemAlloc.h"
+#include "thirdparty/amd-antilag2/ffx_antilag2_dx12.h"
+
#include
#if defined(_MSC_VER) && defined(MemoryBarrier)
@@ -1041,6 +1043,19 @@ private:
public:
RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver);
virtual ~RenderingDeviceDriverD3D12();
+
+private:
+ /***************/
+ /**** INPUT ****/
+ /***************/
+
+ AMD::AntiLag2DX12::Context amd_antilag_ctx = {};
+ bool amd_antilag_present;
+ bool amd_antilag_enabled;
+
+ /***************/
+public:
+ virtual void pre_input_hook(unsigned max_fps) override;
};
#endif // RENDERING_DEVICE_DRIVER_D3D12_H
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 5bb520bd737..92fe5a30c7b 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -499,6 +499,9 @@ bool DisplayServerAndroid::can_any_window_draw() const {
}
void DisplayServerAndroid::process_events() {
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
Input::get_singleton()->flush_buffered_events();
}
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 5a027e01964..86b80002bbd 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -229,6 +229,9 @@ void DisplayServerIOS::window_set_drop_files_callback(const Callable &p_callable
}
void DisplayServerIOS::process_events() {
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
Input::get_singleton()->flush_buffered_events();
}
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index 2074d7228db..a171b65e4bd 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1134,6 +1134,10 @@ void DisplayServerWayland::try_suspend() {
}
void DisplayServerWayland::process_events() {
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
+
wayland_thread.mutex.lock();
while (wayland_thread.has_message()) {
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 840cadace3e..203ed01f847 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4327,6 +4327,10 @@ bool DisplayServerX11::_window_focus_check() {
void DisplayServerX11::process_events() {
ERR_FAIL_COND(!Thread::is_main_thread());
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
+
_THREAD_SAFE_LOCK_
#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 52dc51bc960..02eaacffccf 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -2998,6 +2998,10 @@ Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const {
void DisplayServerMacOS::process_events() {
ERR_FAIL_COND(!Thread::is_main_thread());
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
+
while (true) {
NSEvent *event = [NSApp
nextEventMatchingMask:NSEventMaskAny
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 4e55cc137a3..bd9a4b54508 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -1356,6 +1356,11 @@ DisplayServer::VSyncMode DisplayServerWeb::window_get_vsync_mode(WindowID p_vsyn
}
void DisplayServerWeb::process_events() {
+ RenderingDevice *rendering_device = RenderingDevice::get_singleton();
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
+
process_keys();
Input::get_singleton()->flush_buffered_events();
if (godot_js_input_gamepad_sample() == OK) {
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 50ebe7077fd..ae058d6e05a 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3166,6 +3166,10 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
void DisplayServerWindows::process_events() {
ERR_FAIL_COND(!Thread::is_main_thread());
+ if (rendering_device) {
+ rendering_device->pre_input_hook(Engine::get_singleton()->get_max_fps());
+ }
+
if (!drop_events) {
joypad->process_joypads();
}
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index e322bba7687..4039c924c2d 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -6921,3 +6921,7 @@ 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);
}
+
+void RenderingDevice::pre_input_hook(unsigned max_fps) {
+ driver->pre_input_hook(max_fps);
+}
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 1405f585b24..0666af5cc6a 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1472,6 +1472,13 @@ private:
void _draw_list_set_push_constant(DrawListID p_list, const Vector &p_data, uint32_t p_data_size);
void _compute_list_set_push_constant(ComputeListID p_list, const Vector &p_data, uint32_t p_data_size);
+
+public:
+ /***************/
+ /**** INPUT ****/
+ /***************/
+
+ void pre_input_hook(unsigned max_fps);
};
VARIANT_ENUM_CAST(RenderingDevice::DeviceType)
diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h
index 97c84c9d05f..1855a5642a1 100644
--- a/servers/rendering/rendering_device_driver.h
+++ b/servers/rendering/rendering_device_driver.h
@@ -478,6 +478,12 @@ public:
virtual void shader_free(ShaderID p_shader) = 0;
virtual void shader_destroy_modules(ShaderID p_shader) = 0;
+ /***************/
+ /**** INPUT ****/
+ /***************/
+
+ virtual void pre_input_hook(unsigned max_fps) {}
+
protected:
// An optional service to implementations.
Error _reflect_spirv(VectorView p_spirv, ShaderReflection &r_reflection);
diff --git a/thirdparty/amd-antilag2/LICENSE.txt b/thirdparty/amd-antilag2/LICENSE.txt
new file mode 100644
index 00000000000..2559e2a00cd
--- /dev/null
+++ b/thirdparty/amd-antilag2/LICENSE.txt
@@ -0,0 +1,23 @@
+This file is part of the Anti-Lag 2 SDK.
+
+Copyright (C) 2024 Advanced Micro Devices, Inc.
+
+SPDX License Identifier: MIT
+
+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.
diff --git a/thirdparty/amd-antilag2/ffx_antilag2_dx12.h b/thirdparty/amd-antilag2/ffx_antilag2_dx12.h
new file mode 100644
index 00000000000..6af93272fbe
--- /dev/null
+++ b/thirdparty/amd-antilag2/ffx_antilag2_dx12.h
@@ -0,0 +1,249 @@
+// This file is part of the Anti-Lag 2.0 SDK.
+//
+// Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// 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.
+
+#pragma once
+
+namespace AMD {
+namespace AntiLag2DX12 {
+
+ struct Context;
+
+ // Initialize function - call this once before the Update function.
+ // context - Declare a persistent Context variable in your game code. Ensure the contents are zero'ed, and pass the address in to initialize it.
+ // Be sure to use the *same* context object everywhere when calling the Anti-Lag 2.0 SDK functions.
+ // device - The game's D3D12 device.
+ // A return value of S_OK indicates that Anti-Lag 2.0 is available on the system.
+ HRESULT Initialize( Context* context, ID3D12Device* device );
+
+ // DeInitialize function - call this before destroying the device.
+ // context - address of the game's context object.
+ // The return value is the reference count of the internal API. It should be 0.
+ ULONG DeInitialize( Context* context );
+
+ // Update function - call this just before the input to the game is polled.
+ // context - address of the game's context object.
+ // enable - enables or disables Anti-Lag 2.0.
+ // maxFPS - sets a framerate limit. Zero will disable the limiter.
+ HRESULT Update( Context* context, bool enable, unsigned int maxFPS );
+
+ // Call this on the game render thread once the game's main rendering workload has been submitted in an ExecuteCommandLists call.
+ // Call before the FSR 3 Present call is made.
+ // This is only required if frame generation is enabled, but calling it anyway is harmless.
+ // context - address of the game's context object.
+ HRESULT MarkEndOfFrameRendering( Context* context );
+
+ // Call this on the presentation thread just before the Present call.
+ // This is only required if frame generation is enabled, but calling it anyway is harmless.
+ // context - address of the game's context object.
+ // bInterpolatedFrame - whether the frame about to be presented is interpolated.
+ HRESULT SetFrameGenFrameType( Context* context, bool bInterpolatedFrame );
+
+ //
+ // End of public API section.
+ // Private implementation details below.
+ //
+
+ // Forward declaration of the Anti-Lag interface into the DX12 driver
+ MIDL_INTERFACE("44085fbe-e839-40c5-bf38-0ebc5ab4d0a6")
+ IAmdExtAntiLagApi: public IUnknown
+ {
+ public:
+ virtual HRESULT UpdateAntiLagState(VOID* pData) = 0;
+ };
+
+ // Context structure for the SDK. Declare a persistent object of this type *once* in your game code.
+ // Ensure the contents are initialized to zero before calling Initialize() but do not modify these members directly after that.
+ struct Context
+ {
+ IAmdExtAntiLagApi* m_pAntiLagAPI = nullptr;
+ bool m_enabled = false;
+ unsigned int m_maxFPS = 0;
+ };
+
+ // Structure version 1 for Anti-Lag 2.0:
+ struct APIData_v1
+ {
+ unsigned int uiSize;
+ unsigned int uiVersion;
+ unsigned int eMode;
+ const char* sControlStr;
+ unsigned int uiControlStrLength;
+ unsigned int maxFPS;
+ };
+ static_assert(sizeof(APIData_v1) == 32, "Check structure packing compiler settings.");
+
+ // Structure version 2 for Anti-Lag 2.0:
+ struct APIData_v2
+ {
+ unsigned int uiSize;
+ unsigned int uiVersion;
+ struct Flags
+ {
+ unsigned int unused0 : 1;
+ unsigned int unused1 : 1;
+
+ unsigned int signalFgFrameType : 1;
+ unsigned int isInterpolatedFrame : 1;
+
+ unsigned int signalGetUserInputIdx : 1;
+ unsigned int signalEndOfFrameIdx : 1;
+
+ unsigned int reserved :26;
+ } flags;
+ unsigned __int64 iiFrameIdx;
+ unsigned __int64 uiiReserved[19];
+ };
+ static_assert(sizeof(APIData_v2) == 176, "Check structure packing compiler settings.");
+
+ inline HRESULT Initialize( Context* context, ID3D12Device* device )
+ {
+ HRESULT hr = E_INVALIDARG;
+ if ( context && device && context->m_pAntiLagAPI == nullptr )
+ {
+ HMODULE hModule = GetModuleHandleA("amdxc64.dll");
+ if ( hModule )
+ {
+ typedef HRESULT(__cdecl* PFNAmdExtD3DCreateInterface)( IUnknown* pOuter, REFIID riid, void** ppvObject );
+ PFNAmdExtD3DCreateInterface AmdExtD3DCreateInterface = reinterpret_cast( (VOID*)GetProcAddress(hModule, "AmdExtD3DCreateInterface") );
+ if ( AmdExtD3DCreateInterface )
+ {
+ hr = AmdExtD3DCreateInterface( device, __uuidof(IAmdExtAntiLagApi), (void**)&context->m_pAntiLagAPI );
+ if ( hr == S_OK && context->m_pAntiLagAPI )
+ {
+ APIData_v1 data = {};
+ data.uiSize = sizeof(data);
+ data.uiVersion = 1;
+ data.eMode = 2; // Anti-Lag 2.0 is disabled during initialization
+ data.sControlStr = nullptr;
+ data.uiControlStrLength = 0;
+ data.maxFPS = 0;
+
+ hr = context->m_pAntiLagAPI->UpdateAntiLagState( &data );
+ }
+
+ if ( hr != S_OK )
+ {
+ DeInitialize( context );
+ }
+ }
+ }
+ else
+ {
+ hr = E_HANDLE;
+ }
+ }
+ return hr;
+ }
+
+ inline ULONG DeInitialize( Context* context )
+ {
+ ULONG refCount = 0;
+ if ( context )
+ {
+ if ( context->m_pAntiLagAPI )
+ {
+ refCount = context->m_pAntiLagAPI->Release();
+ context->m_pAntiLagAPI = nullptr;
+ }
+ context->m_enabled = false;
+ }
+
+ return refCount;
+ }
+
+ inline HRESULT Update( Context* context, bool enabled, unsigned int maxFPS )
+ {
+ // This function needs to be called once per frame, before the user input
+ // is sampled - or optionally also when the UI settings are modified.
+ if ( context && context->m_pAntiLagAPI )
+ {
+ // Update the Anti-Lag 2.0 internal state only when necessary:
+ if ( context->m_enabled != enabled || context->m_maxFPS != maxFPS )
+ {
+ context->m_enabled = enabled;
+ context->m_maxFPS = maxFPS;
+
+ APIData_v1 data = {};
+ data.uiSize = sizeof(data);
+ data.uiVersion = 1;
+ data.eMode = enabled ? 1 : 2;
+ data.sControlStr = nullptr;
+ data.uiControlStrLength = 0;
+ data.maxFPS = maxFPS;
+
+ // Only call the function with non-null arguments when setting state.
+ // Make sure not to set the state every frame.
+ context->m_pAntiLagAPI->UpdateAntiLagState( &data );
+ }
+
+ // Call the function with a nullptr to insert the latency-reducing delay.
+ // (if the state has not been set to 'enabled' this call will have no effect)
+ HRESULT hr = context->m_pAntiLagAPI->UpdateAntiLagState( nullptr );
+ if ( hr == S_OK || hr == S_FALSE )
+ {
+ return S_OK;
+ }
+ else
+ {
+ return hr;
+ }
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ }
+
+ // Internal helper function
+ inline HRESULT SetFrameGenParamsInternal( Context* context, APIData_v2::Flags flags )
+ {
+ if ( context && context->m_pAntiLagAPI )
+ {
+ APIData_v2 data = {};
+ data.uiSize = sizeof(data);
+ data.uiVersion = 2;
+ data.flags = flags;
+ data.iiFrameIdx = 0;
+
+ return context->m_pAntiLagAPI->UpdateAntiLagState( &data );
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ }
+
+ inline HRESULT MarkEndOfFrameRendering( Context* context )
+ {
+ APIData_v2::Flags flags = {};
+ flags.signalEndOfFrameIdx = 1;
+ return SetFrameGenParamsInternal( context, flags );
+ }
+
+ inline HRESULT SetFrameGenFrameType( Context* context, bool bInterpolatedFrame )
+ {
+ APIData_v2::Flags flags = {};
+ flags.signalFgFrameType = 1;
+ flags.isInterpolatedFrame = bInterpolatedFrame ? 1 : 0;
+ return SetFrameGenParamsInternal( context, flags );
+ }
+} // namespace AntiLag2DX12
+} // namespace AMD