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