From 06b2c3a4b3c3e03714dc6c879fc05a32750c4faa Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 9 Oct 2026 03:44:43 +0200 Subject: [PATCH] NVIDIA Reflex integration. (#4029) * Integrated NVIDIA Reflex. * Generated bindings and replaced bools with enum. * Merged upstream master. * Addressed review feedback. --- bindings/bf/bgfx.bf | 52 ++++-- bindings/c3/bgfx.c3 | 53 ++++-- bindings/cs/bgfx.cs | 52 ++++-- bindings/d/package.d | 50 ++++-- bindings/py/bgfx.py | 38 +++-- bindings/py/bgfx.pyi | 48 ++++-- bindings/zig/bgfx.zig | 46 +++-- docs/bgfx.rst | 4 + include/bgfx/bgfx.h | 13 ++ include/bgfx/c99/bgfx.h | 13 ++ include/bgfx/defines.h | 34 ++-- scripts/bgfx.idl | 22 ++- src/bgfx.cpp | 105 ++++++++++++ src/bgfx_p.h | 91 +++++++++- src/nvapi.cpp | 252 +++++++++++++++++++++++++++- src/nvapi.h | 23 +++ src/renderer.h | 18 ++ src/renderer_d3d11.cpp | 39 +++++ src/renderer_d3d12.cpp | 39 +++++ src/renderer_vk.cpp | 364 ++++++++++++++++++++++++++++++++++++++++ src/renderer_vk.h | 9 + 21 files changed, 1247 insertions(+), 118 deletions(-) diff --git a/bindings/bf/bgfx.bf b/bindings/bf/bgfx.bf index 25cac3a3f..61054e2f2 100644 --- a/bindings/bf/bgfx.bf +++ b/bindings/bf/bgfx.bf @@ -968,6 +968,18 @@ public static class bgfx /// Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. /// TransparentBackbuffer = 0x00100000, + + /// + /// Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + /// + LowLatencyOn = 0x00000400, + + /// + /// Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + /// + LowLatencyBoost = 0x00000800, + LowLatencyShift = 10, + LowLatencyMask = 0x00000c00, FullscreenShift = 0, FullscreenMask = 0x00000001, ReservedShift = 31, @@ -1085,70 +1097,75 @@ public static class bgfx /// Index32 = 0x0000000000000200, + /// + /// Low latency mode (NVIDIA Reflex) is supported. + /// + LowLatency = 0x0000000000000400, + /// /// PrimitiveID is available in fragment shader. /// - PrimitiveId = 0x0000000000000400, + PrimitiveId = 0x0000000000000800, /// /// Renderer is on separate thread. /// - RendererMultithreaded = 0x0000000000000800, + RendererMultithreaded = 0x0000000000001000, /// /// 16-bit floats are supported in shaders. /// - ShaderF16 = 0x0000000000001000, + ShaderF16 = 0x0000000000002000, /// /// Multiple windows are supported. /// - SwapChain = 0x0000000000002000, + SwapChain = 0x0000000000004000, /// /// Cubemap texture array is supported. /// - TextureCubeArray = 0x0000000000004000, + TextureCubeArray = 0x0000000000008000, /// /// CPU direct access to GPU texture memory. /// - TextureDirectAccess = 0x0000000000008000, + TextureDirectAccess = 0x0000000000010000, /// /// External texture is supported. /// - TextureExternal = 0x0000000000010000, + TextureExternal = 0x0000000000020000, /// /// External shared texture is supported. /// - TextureExternalShared = 0x0000000000020000, + TextureExternalShared = 0x0000000000040000, /// /// Transparent back buffer supported. /// - TransparentBackbuffer = 0x0000000000040000, + TransparentBackbuffer = 0x0000000000080000, /// /// Variable Rate Shading /// - VariableRateShading = 0x0000000000080000, + VariableRateShading = 0x0000000000100000, /// /// Vertex attribute 10_10_10_2 is supported. /// - VertexAttribUint10 = 0x0000000000100000, + VertexAttribUint10 = 0x0000000000200000, /// /// Hardware video decode is supported. /// - VideoDecode = 0x0000000000200000, + VideoDecode = 0x0000000000400000, /// /// Viewport layer is available in vertex shader. /// - ViewportLayerArray = 0x0000000000400000, + ViewportLayerArray = 0x0000000000800000, } [AllowDuplicates] @@ -2893,6 +2910,13 @@ public static class bgfx public int64 gpuTimerFreq; public int64 waitRender; public int64 waitSubmit; + public int64 latencySleep; + public int64 latencyTotal; + public int64 latencySimulation; + public int64 latencyRenderSubmit; + public int64 latencyPresent; + public int64 latencyQueue; + public int64 latencyGpu; public uint32 numDraw; public uint32 numCompute; public uint32 numBlit; @@ -3253,7 +3277,7 @@ public static class bgfx /// /// /// - /// See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. + /// See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or `BGFX_RESET_LOW_LATENCY_BOOST`, not both. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. /// Main window swap chain. When `NULL` the main window is left untouched and only the device and frame globals above are applied, which is what an application driving its own swap chains wants. Otherwise the main window takes on this description: resize it, change its format, or change its per-surface flags. Fields left neutral keep their current value, and `nwh`/`ndt` are ignored -- main's are bgfx's own. Must be `NULL` when `bgfx::init` created no main window. /// [LinkName("bgfx_reset")] diff --git a/bindings/c3/bgfx.c3 b/bindings/c3/bgfx.c3 index 1f4c4415c..1e6857c50 100644 --- a/bindings/c3/bgfx.c3 +++ b/bindings/c3/bgfx.c3 @@ -618,6 +618,14 @@ constdef ResetFlags : inline uint // Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. TRANSPARENTBACKBUFFER = 0x00100000, + + // Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + LOWLATENCYON = 0x00000400, + + // Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + LOWLATENCYBOOST = 0x00000800, + LOWLATENCYSHIFT = 10, + LOWLATENCYMASK = 0x00000c00, FULLSCREENSHIFT = 0, FULLSCREENMASK = 0x00000001, RESERVEDSHIFT = 31, @@ -693,44 +701,47 @@ constdef CapsFlags : inline ulong // 32-bit indices are supported. INDEX32 = 0x0000000000000200, + // Low latency mode (NVIDIA Reflex) is supported. + LOWLATENCY = 0x0000000000000400, + // PrimitiveID is available in fragment shader. - PRIMITIVEID = 0x0000000000000400, + PRIMITIVEID = 0x0000000000000800, // Renderer is on separate thread. - RENDERERMULTITHREADED = 0x0000000000000800, + RENDERERMULTITHREADED = 0x0000000000001000, // 16-bit floats are supported in shaders. - SHADERF16 = 0x0000000000001000, + SHADERF16 = 0x0000000000002000, // Multiple windows are supported. - SWAPCHAIN = 0x0000000000002000, + SWAPCHAIN = 0x0000000000004000, // Cubemap texture array is supported. - TEXTURECUBEARRAY = 0x0000000000004000, + TEXTURECUBEARRAY = 0x0000000000008000, // CPU direct access to GPU texture memory. - TEXTUREDIRECTACCESS = 0x0000000000008000, + TEXTUREDIRECTACCESS = 0x0000000000010000, // External texture is supported. - TEXTUREEXTERNAL = 0x0000000000010000, + TEXTUREEXTERNAL = 0x0000000000020000, // External shared texture is supported. - TEXTUREEXTERNALSHARED = 0x0000000000020000, + TEXTUREEXTERNALSHARED = 0x0000000000040000, // Transparent back buffer supported. - TRANSPARENTBACKBUFFER = 0x0000000000040000, + TRANSPARENTBACKBUFFER = 0x0000000000080000, // Variable Rate Shading - VARIABLERATESHADING = 0x0000000000080000, + VARIABLERATESHADING = 0x0000000000100000, // Vertex attribute 10_10_10_2 is supported. - VERTEXATTRIBUINT10 = 0x0000000000100000, + VERTEXATTRIBUINT10 = 0x0000000000200000, // Hardware video decode is supported. - VIDEODECODE = 0x0000000000200000, + VIDEODECODE = 0x0000000000400000, // Viewport layer is available in vertex shader. - VIEWPORTLAYERARRAY = 0x0000000000400000, + VIEWPORTLAYERARRAY = 0x0000000000800000, } constdef CapsFormatFlags : inline uint @@ -2236,6 +2247,20 @@ struct Stats long waitRender; // Time spent waiting for submit thread to advance to next frame. long waitSubmit; + // Time `bgfx::frame` slept in low latency mode. + long latencySleep; + // Time from simulation start until GPU finished the frame. + long latencyTotal; + // Simulation, API thread time between `bgfx::frame` calls. + long latencySimulation; + // Render thread time issuing the frame to the graphics API. + long latencyRenderSubmit; + // Time spent in present. + long latencyPresent; + // Time between present and GPU starting the frame. + long latencyQueue; + // GPU time from start to end of the frame. + long latencyGpu; // Number of draw calls submitted. uint numDraw; // Number of compute calls submitted. @@ -2513,7 +2538,7 @@ extern fn void shutdown() @cname("bgfx_shutdown"); // @attention This call doesn’t change the window size, it just resizes // the back-buffer. Your windowing code controls the window size. // -// _flags : `See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here.` +// _flags : `See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or `BGFX_RESET_LOW_LATENCY_BOOST`, not both. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here.` // _swapChain : `Main window swap chain. When `NULL` the main window is left untouched and only the device and frame globals above are applied, which is what an application driving its own swap chains wants. Otherwise the main window takes on this description: resize it, change its format, or change its per-surface flags. Fields left neutral keep their current value, and `nwh`/`ndt` are ignored -- main's are bgfx's own. Must be `NULL` when `bgfx::init` created no main window.` extern fn void reset(uint _flags, SwapChain* _swapChain) @cname("bgfx_reset"); diff --git a/bindings/cs/bgfx.cs b/bindings/cs/bgfx.cs index 0f470a8de..4dd4c890f 100644 --- a/bindings/cs/bgfx.cs +++ b/bindings/cs/bgfx.cs @@ -967,6 +967,18 @@ public static partial class bgfx /// Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. /// TransparentBackbuffer = 0x00100000, + + /// + /// Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + /// + LowLatencyOn = 0x00000400, + + /// + /// Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + /// + LowLatencyBoost = 0x00000800, + LowLatencyShift = 10, + LowLatencyMask = 0x00000c00, FullscreenShift = 0, FullscreenMask = 0x00000001, ReservedShift = 31, @@ -1084,70 +1096,75 @@ public static partial class bgfx /// Index32 = 0x0000000000000200, + /// + /// Low latency mode (NVIDIA Reflex) is supported. + /// + LowLatency = 0x0000000000000400, + /// /// PrimitiveID is available in fragment shader. /// - PrimitiveId = 0x0000000000000400, + PrimitiveId = 0x0000000000000800, /// /// Renderer is on separate thread. /// - RendererMultithreaded = 0x0000000000000800, + RendererMultithreaded = 0x0000000000001000, /// /// 16-bit floats are supported in shaders. /// - ShaderF16 = 0x0000000000001000, + ShaderF16 = 0x0000000000002000, /// /// Multiple windows are supported. /// - SwapChain = 0x0000000000002000, + SwapChain = 0x0000000000004000, /// /// Cubemap texture array is supported. /// - TextureCubeArray = 0x0000000000004000, + TextureCubeArray = 0x0000000000008000, /// /// CPU direct access to GPU texture memory. /// - TextureDirectAccess = 0x0000000000008000, + TextureDirectAccess = 0x0000000000010000, /// /// External texture is supported. /// - TextureExternal = 0x0000000000010000, + TextureExternal = 0x0000000000020000, /// /// External shared texture is supported. /// - TextureExternalShared = 0x0000000000020000, + TextureExternalShared = 0x0000000000040000, /// /// Transparent back buffer supported. /// - TransparentBackbuffer = 0x0000000000040000, + TransparentBackbuffer = 0x0000000000080000, /// /// Variable Rate Shading /// - VariableRateShading = 0x0000000000080000, + VariableRateShading = 0x0000000000100000, /// /// Vertex attribute 10_10_10_2 is supported. /// - VertexAttribUint10 = 0x0000000000100000, + VertexAttribUint10 = 0x0000000000200000, /// /// Hardware video decode is supported. /// - VideoDecode = 0x0000000000200000, + VideoDecode = 0x0000000000400000, /// /// Viewport layer is available in vertex shader. /// - ViewportLayerArray = 0x0000000000400000, + ViewportLayerArray = 0x0000000000800000, } [Flags] @@ -2850,6 +2867,13 @@ public static partial class bgfx public long gpuTimerFreq; public long waitRender; public long waitSubmit; + public long latencySleep; + public long latencyTotal; + public long latencySimulation; + public long latencyRenderSubmit; + public long latencyPresent; + public long latencyQueue; + public long latencyGpu; public uint numDraw; public uint numCompute; public uint numBlit; @@ -3196,7 +3220,7 @@ public static partial class bgfx /// /// /// - /// See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. + /// See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or `BGFX_RESET_LOW_LATENCY_BOOST`, not both. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. /// Main window swap chain. When `NULL` the main window is left untouched and only the device and frame globals above are applied, which is what an application driving its own swap chains wants. Otherwise the main window takes on this description: resize it, change its format, or change its per-surface flags. Fields left neutral keep their current value, and `nwh`/`ndt` are ignored -- main's are bgfx's own. Must be `NULL` when `bgfx::init` created no main window. /// [DllImport(DllName, EntryPoint="bgfx_reset", CallingConvention = CallingConvention.Cdecl)] diff --git a/bindings/d/package.d b/bindings/d/package.d index 20d1f3643..4abfa4877 100644 --- a/bindings/d/package.d +++ b/bindings/d/package.d @@ -9,7 +9,7 @@ import bindbc.common.types: c_int64, c_uint64, va_list; import bindbc.bgfx.config; static import bgfx.impl; -enum uint apiVersion = 165; +enum uint apiVersion = 166; alias ViewID = ushort; @@ -489,6 +489,14 @@ enum Reset: Reset_{ transparentBackbuffer = 0x0010_0000, ///Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. } +alias ResetLowLatency_ = uint; +enum ResetLowLatency: ResetLowLatency_{ + on = 0x0000_0400, ///Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + boost = 0x0000_0800, ///Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + shift = 10, ///Low latency mode bit shift + mask = 0x0000_0C00, ///Low latency mode bit mask +} + alias ResetFullscreen_ = uint; enum ResetFullscreen: ResetFullscreen_{ shift = 0, @@ -539,19 +547,20 @@ enum CapFlags: CapFlags_{ hdr10 = 0x0000_0000_0000_0080, ///HDR10 rendering is supported. imageRW = 0x0000_0000_0000_0100, ///Image Read/Write is supported. index32 = 0x0000_0000_0000_0200, ///32-bit indices are supported. - primitiveID = 0x0000_0000_0000_0400, ///PrimitiveID is available in fragment shader. - rendererMultithreaded = 0x0000_0000_0000_0800, ///Renderer is on separate thread. - shaderF16 = 0x0000_0000_0000_1000, ///16-bit floats are supported in shaders. - swapChain = 0x0000_0000_0000_2000, ///Multiple windows are supported. - textureCubeArray = 0x0000_0000_0000_4000, ///Cubemap texture array is supported. - textureDirectAccess = 0x0000_0000_0000_8000, ///CPU direct access to GPU texture memory. - textureExternal = 0x0000_0000_0001_0000, ///External texture is supported. - textureExternalShared = 0x0000_0000_0002_0000, ///External shared texture is supported. - transparentBackbuffer = 0x0000_0000_0004_0000, ///Transparent back buffer supported. - variableRateShading = 0x0000_0000_0008_0000, ///Variable Rate Shading - vertexAttribUint10 = 0x0000_0000_0010_0000, ///Vertex attribute 10_10_10_2 is supported. - videoDecode = 0x0000_0000_0020_0000, ///Hardware video decode is supported. - viewportLayerArray = 0x0000_0000_0040_0000, ///Viewport layer is available in vertex shader. + lowLatency = 0x0000_0000_0000_0400, ///Low latency mode (NVIDIA Reflex) is supported. + primitiveID = 0x0000_0000_0000_0800, ///PrimitiveID is available in fragment shader. + rendererMultithreaded = 0x0000_0000_0000_1000, ///Renderer is on separate thread. + shaderF16 = 0x0000_0000_0000_2000, ///16-bit floats are supported in shaders. + swapChain = 0x0000_0000_0000_4000, ///Multiple windows are supported. + textureCubeArray = 0x0000_0000_0000_8000, ///Cubemap texture array is supported. + textureDirectAccess = 0x0000_0000_0001_0000, ///CPU direct access to GPU texture memory. + textureExternal = 0x0000_0000_0002_0000, ///External texture is supported. + textureExternalShared = 0x0000_0000_0004_0000, ///External shared texture is supported. + transparentBackbuffer = 0x0000_0000_0008_0000, ///Transparent back buffer supported. + variableRateShading = 0x0000_0000_0010_0000, ///Variable Rate Shading + vertexAttribUint10 = 0x0000_0000_0020_0000, ///Vertex attribute 10_10_10_2 is supported. + videoDecode = 0x0000_0000_0040_0000, ///Hardware video decode is supported. + viewportLayerArray = 0x0000_0000_0080_0000, ///Viewport layer is available in vertex shader. } alias CapsFormat_ = uint; @@ -1764,6 +1773,13 @@ extern(C++, "bgfx") struct Stats{ c_int64 gpuTimerFreq; ///GPU timer frequency. c_int64 waitRender; ///Time spent waiting for render backend thread to finish issuing draw commands to underlying graphics API. c_int64 waitSubmit; ///Time spent waiting for submit thread to advance to next frame. + c_int64 latencySleep; ///Time `bgfx::frame` slept in low latency mode. + c_int64 latencyTotal; ///Time from simulation start until GPU finished the frame. + c_int64 latencySimulation; ///Simulation, API thread time between `bgfx::frame` calls. + c_int64 latencyRenderSubmit; ///Render thread time issuing the frame to the graphics API. + c_int64 latencyPresent; ///Time spent in present. + c_int64 latencyQueue; ///Time between present and GPU starting the frame. + c_int64 latencyGpu; ///GPU time from start to end of the frame. uint numDraw; ///Number of draw calls submitted. uint numCompute; ///Number of compute calls submitted. uint numBlit; ///Number of blit calls submitted. @@ -2638,6 +2654,12 @@ mixin(joinFnBinds((){ - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. + - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). + Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU + clocks high even when GPU is mostly idle. Availability depends on: + `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or + `BGFX_RESET_LOW_LATENCY_BOOST`, not both. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. swapChain = Main window swap chain. When `NULL` the main window is left diff --git a/bindings/py/bgfx.py b/bindings/py/bgfx.py index b5ce54d20..29a4cce96 100644 --- a/bindings/py/bgfx.py +++ b/bindings/py/bgfx.py @@ -498,6 +498,10 @@ class ResetFlags(enum.IntFlag): Hidpi = 0x20000 Suspend = 0x80000 TransparentBackbuffer = 0x100000 + LowLatencyOn = 0x400 + LowLatencyBoost = 0x800 + LowLatencyShift = 0xa + LowLatencyMask = 0xc00 FullscreenShift = 0x0 FullscreenMask = 0x1 ReservedShift = 0x1f @@ -570,19 +574,20 @@ class CapsFlags(enum.IntFlag): Hdr10 = 0x80 ImageRw = 0x100 Index32 = 0x200 - PrimitiveId = 0x400 - RendererMultithreaded = 0x800 - ShaderF16 = 0x1000 - SwapChain = 0x2000 - TextureCubeArray = 0x4000 - TextureDirectAccess = 0x8000 - TextureExternal = 0x10000 - TextureExternalShared = 0x20000 - TransparentBackbuffer = 0x40000 - VariableRateShading = 0x80000 - VertexAttribUint10 = 0x100000 - VideoDecode = 0x200000 - ViewportLayerArray = 0x400000 + LowLatency = 0x400 + PrimitiveId = 0x800 + RendererMultithreaded = 0x1000 + ShaderF16 = 0x2000 + SwapChain = 0x4000 + TextureCubeArray = 0x8000 + TextureDirectAccess = 0x10000 + TextureExternal = 0x20000 + TextureExternalShared = 0x40000 + TransparentBackbuffer = 0x80000 + VariableRateShading = 0x100000 + VertexAttribUint10 = 0x200000 + VideoDecode = 0x400000 + ViewportLayerArray = 0x800000 class CapsFormatFlags(enum.IntFlag): TextureNone = 0x0 @@ -1062,6 +1067,13 @@ Stats._fields_ = [ ("gpuTimerFreq", ctypes.c_int64), ("waitRender", ctypes.c_int64), ("waitSubmit", ctypes.c_int64), + ("latencySleep", ctypes.c_int64), + ("latencyTotal", ctypes.c_int64), + ("latencySimulation", ctypes.c_int64), + ("latencyRenderSubmit", ctypes.c_int64), + ("latencyPresent", ctypes.c_int64), + ("latencyQueue", ctypes.c_int64), + ("latencyGpu", ctypes.c_int64), ("numDraw", ctypes.c_uint32), ("numCompute", ctypes.c_uint32), ("numBlit", ctypes.c_uint32), diff --git a/bindings/py/bgfx.pyi b/bindings/py/bgfx.pyi index 36ea5f897..aab7848c1 100644 --- a/bindings/py/bgfx.pyi +++ b/bindings/py/bgfx.pyi @@ -931,6 +931,12 @@ class ResetFlags(enum.IntFlag): Suspend = 0x80000 # Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. TransparentBackbuffer = 0x100000 + # Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + LowLatencyOn = 0x400 + # Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + LowLatencyBoost = 0x800 + LowLatencyShift = 0xa + LowLatencyMask = 0xc00 FullscreenShift = 0x0 FullscreenMask = 0x1 ReservedShift = 0x1f @@ -1052,32 +1058,34 @@ class CapsFlags(enum.IntFlag): ImageRw = 0x100 # 32-bit indices are supported. Index32 = 0x200 + # Low latency mode (NVIDIA Reflex) is supported. + LowLatency = 0x400 # PrimitiveID is available in fragment shader. - PrimitiveId = 0x400 + PrimitiveId = 0x800 # Renderer is on separate thread. - RendererMultithreaded = 0x800 + RendererMultithreaded = 0x1000 # 16-bit floats are supported in shaders. - ShaderF16 = 0x1000 + ShaderF16 = 0x2000 # Multiple windows are supported. - SwapChain = 0x2000 + SwapChain = 0x4000 # Cubemap texture array is supported. - TextureCubeArray = 0x4000 + TextureCubeArray = 0x8000 # CPU direct access to GPU texture memory. - TextureDirectAccess = 0x8000 + TextureDirectAccess = 0x10000 # External texture is supported. - TextureExternal = 0x10000 + TextureExternal = 0x20000 # External shared texture is supported. - TextureExternalShared = 0x20000 + TextureExternalShared = 0x40000 # Transparent back buffer supported. - TransparentBackbuffer = 0x40000 + TransparentBackbuffer = 0x80000 # Variable Rate Shading - VariableRateShading = 0x80000 + VariableRateShading = 0x100000 # Vertex attribute 10_10_10_2 is supported. - VertexAttribUint10 = 0x100000 + VertexAttribUint10 = 0x200000 # Hardware video decode is supported. - VideoDecode = 0x200000 + VideoDecode = 0x400000 # Viewport layer is available in vertex shader. - ViewportLayerArray = 0x400000 + ViewportLayerArray = 0x800000 class CapsFormatFlags(enum.IntFlag): # Texture format is not supported. @@ -1759,6 +1767,20 @@ class Stats(ctypes.Structure): waitRender: int # Time spent waiting for submit thread to advance to next frame. waitSubmit: int + # Time `bgfx::frame` slept in low latency mode. + latencySleep: int + # Time from simulation start until GPU finished the frame. + latencyTotal: int + # Simulation, API thread time between `bgfx::frame` calls. + latencySimulation: int + # Render thread time issuing the frame to the graphics API. + latencyRenderSubmit: int + # Time spent in present. + latencyPresent: int + # Time between present and GPU starting the frame. + latencyQueue: int + # GPU time from start to end of the frame. + latencyGpu: int # Number of draw calls submitted. numDraw: int # Number of compute calls submitted. diff --git a/bindings/zig/bgfx.zig b/bindings/zig/bgfx.zig index 7c40b8457..556a2c903 100644 --- a/bindings/zig/bgfx.zig +++ b/bindings/zig/bgfx.zig @@ -602,6 +602,14 @@ pub const ResetFlags_Suspend: ResetFlags = 0x00080000; /// Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. pub const ResetFlags_TransparentBackbuffer: ResetFlags = 0x00100000; + +/// Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. +pub const ResetFlags_LowLatencyOn: ResetFlags = 0x00000400; + +/// Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. +pub const ResetFlags_LowLatencyBoost: ResetFlags = 0x00000800; +pub const ResetFlags_LowLatencyShift: ResetFlags = 10; +pub const ResetFlags_LowLatencyMask: ResetFlags = 0x00000c00; pub const ResetFlags_FullscreenShift: ResetFlags = 0; pub const ResetFlags_FullscreenMask: ResetFlags = 0x00000001; pub const ResetFlags_ReservedShift: ResetFlags = 31; @@ -673,44 +681,47 @@ pub const CapsFlags_ImageRw: CapsFlags = 0x0000000000000100; /// 32-bit indices are supported. pub const CapsFlags_Index32: CapsFlags = 0x0000000000000200; +/// Low latency mode (NVIDIA Reflex) is supported. +pub const CapsFlags_LowLatency: CapsFlags = 0x0000000000000400; + /// PrimitiveID is available in fragment shader. -pub const CapsFlags_PrimitiveId: CapsFlags = 0x0000000000000400; +pub const CapsFlags_PrimitiveId: CapsFlags = 0x0000000000000800; /// Renderer is on separate thread. -pub const CapsFlags_RendererMultithreaded: CapsFlags = 0x0000000000000800; +pub const CapsFlags_RendererMultithreaded: CapsFlags = 0x0000000000001000; /// 16-bit floats are supported in shaders. -pub const CapsFlags_ShaderF16: CapsFlags = 0x0000000000001000; +pub const CapsFlags_ShaderF16: CapsFlags = 0x0000000000002000; /// Multiple windows are supported. -pub const CapsFlags_SwapChain: CapsFlags = 0x0000000000002000; +pub const CapsFlags_SwapChain: CapsFlags = 0x0000000000004000; /// Cubemap texture array is supported. -pub const CapsFlags_TextureCubeArray: CapsFlags = 0x0000000000004000; +pub const CapsFlags_TextureCubeArray: CapsFlags = 0x0000000000008000; /// CPU direct access to GPU texture memory. -pub const CapsFlags_TextureDirectAccess: CapsFlags = 0x0000000000008000; +pub const CapsFlags_TextureDirectAccess: CapsFlags = 0x0000000000010000; /// External texture is supported. -pub const CapsFlags_TextureExternal: CapsFlags = 0x0000000000010000; +pub const CapsFlags_TextureExternal: CapsFlags = 0x0000000000020000; /// External shared texture is supported. -pub const CapsFlags_TextureExternalShared: CapsFlags = 0x0000000000020000; +pub const CapsFlags_TextureExternalShared: CapsFlags = 0x0000000000040000; /// Transparent back buffer supported. -pub const CapsFlags_TransparentBackbuffer: CapsFlags = 0x0000000000040000; +pub const CapsFlags_TransparentBackbuffer: CapsFlags = 0x0000000000080000; /// Variable Rate Shading -pub const CapsFlags_VariableRateShading: CapsFlags = 0x0000000000080000; +pub const CapsFlags_VariableRateShading: CapsFlags = 0x0000000000100000; /// Vertex attribute 10_10_10_2 is supported. -pub const CapsFlags_VertexAttribUint10: CapsFlags = 0x0000000000100000; +pub const CapsFlags_VertexAttribUint10: CapsFlags = 0x0000000000200000; /// Hardware video decode is supported. -pub const CapsFlags_VideoDecode: CapsFlags = 0x0000000000200000; +pub const CapsFlags_VideoDecode: CapsFlags = 0x0000000000400000; /// Viewport layer is available in vertex shader. -pub const CapsFlags_ViewportLayerArray: CapsFlags = 0x0000000000400000; +pub const CapsFlags_ViewportLayerArray: CapsFlags = 0x0000000000800000; pub const CapsFormatFlags = u32; /// Texture format is not supported. @@ -1869,6 +1880,13 @@ pub const Init = extern struct { gpuTimerFreq: i64, waitRender: i64, waitSubmit: i64, + latencySleep: i64, + latencyTotal: i64, + latencySimulation: i64, + latencyRenderSubmit: i64, + latencyPresent: i64, + latencyQueue: i64, + latencyGpu: i64, numDraw: u32, numCompute: u32, numBlit: u32, @@ -2650,7 +2668,7 @@ extern fn bgfx_shutdown() void; /// @attention This call doesn’t change the window size, it just resizes /// the back-buffer. Your windowing code controls the window size. /// -/// See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. +/// See: `BGFX_RESET_*` for more info. - `BGFX_RESET_NONE` - No reset flags. - `BGFX_RESET_VSYNC` - Enable V-Sync. - `BGFX_RESET_MAXANISOTROPY` - Turn on/off max anisotropy. - `BGFX_RESET_CAPTURE` - Begin screen capture. - `BGFX_RESET_FLUSH_AFTER_RENDER` - Flush rendering after submitting to GPU. - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip occurs. Default behaviour is that flip occurs before rendering new frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or `BGFX_RESET_LOW_LATENCY_BOOST`, not both. Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong on `SwapChain::flags`, and are ignored if passed here. /// Main window swap chain. When `NULL` the main window is left untouched and only the device and frame globals above are applied, which is what an application driving its own swap chains wants. Otherwise the main window takes on this description: resize it, change its format, or change its per-surface flags. Fields left neutral keep their current value, and `nwh`/`ndt` are ignored -- main's are bgfx's own. Must be `NULL` when `bgfx::init` created no main window. pub inline fn reset(_flags: u32, _swapChain: [*c]const SwapChain) void { return bgfx_reset(_flags, _swapChain); diff --git a/docs/bgfx.rst b/docs/bgfx.rst index 273129f6d..20c3b0468 100644 --- a/docs/bgfx.rst +++ b/docs/bgfx.rst @@ -57,6 +57,9 @@ Reset flags control back-buffer resolution, MSAA, vsync, and other global render .. doxygendefine:: BGFX_RESET_SUSPEND .. doxygendefine:: BGFX_RESET_TRANSPARENT_BACKBUFFER +.. doxygendefine:: BGFX_RESET_LOW_LATENCY_ON +.. doxygendefine:: BGFX_RESET_LOW_LATENCY_BOOST + .. doxygenfunction:: bgfx::reset Swap Chain @@ -156,6 +159,7 @@ Individual capability flags. .. doxygendefine:: BGFX_CAPS_HDR10 .. doxygendefine:: BGFX_CAPS_IMAGE_RW .. doxygendefine:: BGFX_CAPS_INDEX32 +.. doxygendefine:: BGFX_CAPS_LOW_LATENCY .. doxygendefine:: BGFX_CAPS_PRIMITIVE_ID .. doxygendefine:: BGFX_CAPS_RENDERER_MULTITHREADED .. doxygendefine:: BGFX_CAPS_SHADER_F16 diff --git a/include/bgfx/bgfx.h b/include/bgfx/bgfx.h index c92bf35da..0307469f0 100644 --- a/include/bgfx/bgfx.h +++ b/include/bgfx/bgfx.h @@ -1146,6 +1146,13 @@ namespace bgfx int64_t gpuTimerFreq; //!< GPU timer frequency. int64_t waitRender; //!< Time spent waiting for render backend thread to finish issuing draw commands to underlying graphics API. int64_t waitSubmit; //!< Time spent waiting for submit thread to advance to next frame. + int64_t latencySleep; //!< Time `bgfx::frame` slept in low latency mode. + int64_t latencyTotal; //!< Time from simulation start until GPU finished the frame. + int64_t latencySimulation; //!< Simulation, API thread time between `bgfx::frame` calls. + int64_t latencyRenderSubmit; //!< Render thread time issuing the frame to the graphics API. + int64_t latencyPresent; //!< Time spent in present. + int64_t latencyQueue; //!< Time between present and GPU starting the frame. + int64_t latencyGpu; //!< GPU time from start to end of the frame. uint32_t numDraw; //!< Number of draw calls submitted. uint32_t numCompute; //!< Number of compute calls submitted. uint32_t numBlit; //!< Number of blit calls submitted. @@ -2572,6 +2579,12 @@ namespace bgfx /// - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip /// occurs. Default behaviour is that flip occurs before rendering new /// frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. + /// - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). + /// Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + /// - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU + /// clocks high even when GPU is mostly idle. Availability depends on: + /// `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or + /// `BGFX_RESET_LOW_LATENCY_BOOST`, not both. /// Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong /// on `SwapChain::flags`, and are ignored if passed here. /// @param[in] _swapChain Main window swap chain. When `NULL` the main window is left diff --git a/include/bgfx/c99/bgfx.h b/include/bgfx/c99/bgfx.h index 11fc966b8..76f8c8eae 100644 --- a/include/bgfx/c99/bgfx.h +++ b/include/bgfx/c99/bgfx.h @@ -1266,6 +1266,13 @@ typedef struct bgfx_stats_s int64_t gpuTimerFreq; /** GPU timer frequency. */ int64_t waitRender; /** Time spent waiting for render backend thread to finish issuing draw commands to underlying graphics API. */ int64_t waitSubmit; /** Time spent waiting for submit thread to advance to next frame. */ + int64_t latencySleep; /** Time `bgfx::frame` slept in low latency mode. */ + int64_t latencyTotal; /** Time from simulation start until GPU finished the frame. */ + int64_t latencySimulation; /** Simulation, API thread time between `bgfx::frame` calls. */ + int64_t latencyRenderSubmit; /** Render thread time issuing the frame to the graphics API. */ + int64_t latencyPresent; /** Time spent in present. */ + int64_t latencyQueue; /** Time between present and GPU starting the frame. */ + int64_t latencyGpu; /** GPU time from start to end of the frame. */ uint32_t numDraw; /** Number of draw calls submitted. */ uint32_t numCompute; /** Number of compute calls submitted. */ uint32_t numBlit; /** Number of blit calls submitted. */ @@ -1617,6 +1624,12 @@ BGFX_C_API void bgfx_shutdown(void); * - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip * occurs. Default behaviour is that flip occurs before rendering new * frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. + * - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). + * Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + * - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU + * clocks high even when GPU is mostly idle. Availability depends on: + * `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or + * `BGFX_RESET_LOW_LATENCY_BOOST`, not both. * Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong * on `SwapChain::flags`, and are ignored if passed here. * @param[in] _swapChain Main window swap chain. When `NULL` the main window is left diff --git a/include/bgfx/defines.h b/include/bgfx/defines.h index 7ac0b2bc5..4a9d70902 100644 --- a/include/bgfx/defines.h +++ b/include/bgfx/defines.h @@ -15,7 +15,7 @@ #ifndef BGFX_DEFINES_H_HEADER_GUARD #define BGFX_DEFINES_H_HEADER_GUARD -#define BGFX_API_VERSION UINT32_C(165) +#define BGFX_API_VERSION UINT32_C(166) /** * Color RGB/alpha/depth write. When it's not specified write will be disabled. @@ -452,6 +452,11 @@ #define BGFX_RESET_SUSPEND UINT32_C(0x00080000) //!< Suspend rendering. #define BGFX_RESET_TRANSPARENT_BACKBUFFER UINT32_C(0x00100000) //!< Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. +#define BGFX_RESET_LOW_LATENCY_ON UINT32_C(0x00000400) //!< Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. +#define BGFX_RESET_LOW_LATENCY_BOOST UINT32_C(0x00000800) //!< Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. +#define BGFX_RESET_LOW_LATENCY_SHIFT 10 //!< Low latency mode bit shift +#define BGFX_RESET_LOW_LATENCY_MASK UINT32_C(0x00000c00) //!< Low latency mode bit mask + #define BGFX_RESET_FULLSCREEN_SHIFT 0 #define BGFX_RESET_FULLSCREEN_MASK UINT32_C(0x00000001) @@ -485,19 +490,20 @@ #define BGFX_CAPS_HDR10 UINT64_C(0x0000000000000080) //!< HDR10 rendering is supported. #define BGFX_CAPS_IMAGE_RW UINT64_C(0x0000000000000100) //!< Image Read/Write is supported. #define BGFX_CAPS_INDEX32 UINT64_C(0x0000000000000200) //!< 32-bit indices are supported. -#define BGFX_CAPS_PRIMITIVE_ID UINT64_C(0x0000000000000400) //!< PrimitiveID is available in fragment shader. -#define BGFX_CAPS_RENDERER_MULTITHREADED UINT64_C(0x0000000000000800) //!< Renderer is on separate thread. -#define BGFX_CAPS_SHADER_F16 UINT64_C(0x0000000000001000) //!< 16-bit floats are supported in shaders. -#define BGFX_CAPS_SWAP_CHAIN UINT64_C(0x0000000000002000) //!< Multiple windows are supported. -#define BGFX_CAPS_TEXTURE_CUBE_ARRAY UINT64_C(0x0000000000004000) //!< Cubemap texture array is supported. -#define BGFX_CAPS_TEXTURE_DIRECT_ACCESS UINT64_C(0x0000000000008000) //!< CPU direct access to GPU texture memory. -#define BGFX_CAPS_TEXTURE_EXTERNAL UINT64_C(0x0000000000010000) //!< External texture is supported. -#define BGFX_CAPS_TEXTURE_EXTERNAL_SHARED UINT64_C(0x0000000000020000) //!< External shared texture is supported. -#define BGFX_CAPS_TRANSPARENT_BACKBUFFER UINT64_C(0x0000000000040000) //!< Transparent back buffer supported. -#define BGFX_CAPS_VARIABLE_RATE_SHADING UINT64_C(0x0000000000080000) //!< Variable Rate Shading -#define BGFX_CAPS_VERTEX_ATTRIB_UINT10 UINT64_C(0x0000000000100000) //!< Vertex attribute 10_10_10_2 is supported. -#define BGFX_CAPS_VIDEO_DECODE UINT64_C(0x0000000000200000) //!< Hardware video decode is supported. -#define BGFX_CAPS_VIEWPORT_LAYER_ARRAY UINT64_C(0x0000000000400000) //!< Viewport layer is available in vertex shader. +#define BGFX_CAPS_LOW_LATENCY UINT64_C(0x0000000000000400) //!< Low latency mode (NVIDIA Reflex) is supported. +#define BGFX_CAPS_PRIMITIVE_ID UINT64_C(0x0000000000000800) //!< PrimitiveID is available in fragment shader. +#define BGFX_CAPS_RENDERER_MULTITHREADED UINT64_C(0x0000000000001000) //!< Renderer is on separate thread. +#define BGFX_CAPS_SHADER_F16 UINT64_C(0x0000000000002000) //!< 16-bit floats are supported in shaders. +#define BGFX_CAPS_SWAP_CHAIN UINT64_C(0x0000000000004000) //!< Multiple windows are supported. +#define BGFX_CAPS_TEXTURE_CUBE_ARRAY UINT64_C(0x0000000000008000) //!< Cubemap texture array is supported. +#define BGFX_CAPS_TEXTURE_DIRECT_ACCESS UINT64_C(0x0000000000010000) //!< CPU direct access to GPU texture memory. +#define BGFX_CAPS_TEXTURE_EXTERNAL UINT64_C(0x0000000000020000) //!< External texture is supported. +#define BGFX_CAPS_TEXTURE_EXTERNAL_SHARED UINT64_C(0x0000000000040000) //!< External shared texture is supported. +#define BGFX_CAPS_TRANSPARENT_BACKBUFFER UINT64_C(0x0000000000080000) //!< Transparent back buffer supported. +#define BGFX_CAPS_VARIABLE_RATE_SHADING UINT64_C(0x0000000000100000) //!< Variable Rate Shading +#define BGFX_CAPS_VERTEX_ATTRIB_UINT10 UINT64_C(0x0000000000200000) //!< Vertex attribute 10_10_10_2 is supported. +#define BGFX_CAPS_VIDEO_DECODE UINT64_C(0x0000000000400000) //!< Hardware video decode is supported. +#define BGFX_CAPS_VIEWPORT_LAYER_ARRAY UINT64_C(0x0000000000800000) //!< Viewport layer is available in vertex shader. #define BGFX_CAPS_FORMAT_TEXTURE_NONE UINT32_C(0x00000000) //!< Texture format is not supported. #define BGFX_CAPS_FORMAT_TEXTURE_2D UINT32_C(0x00000001) //!< Texture format is supported. diff --git a/scripts/bgfx.idl b/scripts/bgfx.idl index e164f9c18..983e78b94 100644 --- a/scripts/bgfx.idl +++ b/scripts/bgfx.idl @@ -1,7 +1,7 @@ -- vim: syntax=lua -- bgfx interface -version(165) +version(166) typedef "bool" typedef "char" @@ -361,6 +361,11 @@ flag.Reset { bits = 32, section = "Reset" } .TransparentBackbuffer (21) --- Transparent backbuffer. Availability depends on: `BGFX_CAPS_TRANSPARENT_BACKBUFFER`. () +flag.ResetLowLatency { bits = 32, shift = 10, range = 2, base = 1, desc = "Low latency mode", section = "Reset" } + .On --- Enable low latency mode (NVIDIA Reflex). Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + .Boost --- Enable low latency mode, and keep GPU clocks high even when GPU is mostly idle. Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + () + flag.ResetFullscreen { bits = 32, shift = 0, range = 1, base = 1 } () @@ -396,6 +401,7 @@ flag.Caps { bits = 64, base = 1, name = "Caps", section = "Available Caps" } .Hdr10 --- HDR10 rendering is supported. .ImageRw --- Image Read/Write is supported. .Index32 --- 32-bit indices are supported. + .LowLatency --- Low latency mode (NVIDIA Reflex) is supported. .PrimitiveId --- PrimitiveID is available in fragment shader. .RendererMultithreaded --- Renderer is on separate thread. .ShaderF16 --- 16-bit floats are supported in shaders. @@ -1263,6 +1269,14 @@ struct.Stats { section = "Statistics" } .waitRender "int64_t" --- Time spent waiting for render backend thread to finish issuing draw commands to underlying graphics API. .waitSubmit "int64_t" --- Time spent waiting for submit thread to advance to next frame. + .latencySleep "int64_t" --- Time `bgfx::frame` slept in low latency mode. + .latencyTotal "int64_t" --- Time from simulation start until GPU finished the frame. + .latencySimulation "int64_t" --- Simulation, API thread time between `bgfx::frame` calls. + .latencyRenderSubmit "int64_t" --- Render thread time issuing the frame to the graphics API. + .latencyPresent "int64_t" --- Time spent in present. + .latencyQueue "int64_t" --- Time between present and GPU starting the frame. + .latencyGpu "int64_t" --- GPU time from start to end of the frame. + .numDraw "uint32_t" --- Number of draw calls submitted. .numCompute "uint32_t" --- Number of compute calls submitted. .numBlit "uint32_t" --- Number of blit calls submitted. @@ -1509,6 +1523,12 @@ func.reset { section = "Reset" } --- - `BGFX_RESET_FLIP_AFTER_RENDER` - This flag specifies where flip --- occurs. Default behaviour is that flip occurs before rendering new --- frame. This flag only has effect when `BGFX_CONFIG_MULTITHREADED=0`. + --- - `BGFX_RESET_LOW_LATENCY_ON` - Enable low latency mode (NVIDIA Reflex). + --- Availability depends on: `BGFX_CAPS_LOW_LATENCY`. + --- - `BGFX_RESET_LOW_LATENCY_BOOST` - Enable low latency mode, and keep GPU + --- clocks high even when GPU is mostly idle. Availability depends on: + --- `BGFX_CAPS_LOW_LATENCY`. Set either `BGFX_RESET_LOW_LATENCY_ON` or + --- `BGFX_RESET_LOW_LATENCY_BOOST`, not both. --- --- Per-surface settings are not here. `BGFX_SWAP_CHAIN_*` flags belong --- on `SwapChain::flags`, and are ignored if passed here. diff --git a/src/bgfx.cpp b/src/bgfx.cpp index 19b900d31..dd16f962c 100644 --- a/src/bgfx.cpp +++ b/src/bgfx.cpp @@ -1894,6 +1894,7 @@ namespace bgfx CAPS_FLAGS(BGFX_CAPS_HDR10), CAPS_FLAGS(BGFX_CAPS_IMAGE_RW), CAPS_FLAGS(BGFX_CAPS_INDEX32), + CAPS_FLAGS(BGFX_CAPS_LOW_LATENCY), CAPS_FLAGS(BGFX_CAPS_PRIMITIVE_ID), CAPS_FLAGS(BGFX_CAPS_RENDERER_MULTITHREADED), CAPS_FLAGS(BGFX_CAPS_SHADER_F16), @@ -2158,6 +2159,8 @@ namespace bgfx BX_TRACE("\t[%c] Flush After Render", 0 != (reset & BGFX_RESET_FLUSH_AFTER_RENDER) ? 'x' : ' '); BX_TRACE("\t[%c] Flip After Render", 0 != (reset & BGFX_RESET_FLIP_AFTER_RENDER) ? 'x' : ' '); BX_TRACE("\t[%c] Suspend", 0 != (reset & BGFX_RESET_SUSPEND) ? 'x' : ' '); + BX_TRACE("\t[%c] Low Latency", 0 != (reset & BGFX_RESET_LOW_LATENCY_MASK) ? 'x' : ' '); + BX_TRACE("\t[%c] Low Latency Boost", 0 != (reset & BGFX_RESET_LOW_LATENCY_BOOST) ? 'x' : ' '); } TextureFormat::Enum getViableTextureFormat(const bimg::ImageContainer& _imageContainer) @@ -2328,6 +2331,8 @@ namespace bgfx return false; } + BX_ASSERT(BGFX_RESET_LOW_LATENCY_MASK != (_init.reset&BGFX_RESET_LOW_LATENCY_MASK), "Set either `BGFX_RESET_LOW_LATENCY_ON` or `BGFX_RESET_LOW_LATENCY_BOOST`, not both!"); + m_init = _init; m_uniformCache.reserve(m_init.limits.minUniformCacheSize); @@ -2486,6 +2491,8 @@ namespace bgfx | (isGraphicsDebuggerPresent() ? BGFX_CAPS_GRAPHICS_DEBUGGER : 0) ; + m_init.reset = checkResetFlags(m_init.reset); + dumpCaps(); m_textVideoMemBlitter.init(); @@ -2507,11 +2514,21 @@ namespace bgfx g_internalData.caps = getCaps(); + { + BGFX_MUTEX_SCOPE(m_latencyLock); + m_lowLatency = 0 != (g_caps.supported & BGFX_CAPS_LOW_LATENCY); + } + return true; } void Context::shutdown() { + { + BGFX_MUTEX_SCOPE(m_latencyLock); + m_lowLatency = false; + } + getCommandBuffer(CommandBuffer::RendererShutdownBegin); frame(); @@ -2822,6 +2839,13 @@ namespace bgfx m_submit->m_capture = 0 != (_flags & BGFX_FRAME_DEBUG_CAPTURE); m_submit->m_flush = 0 != (_flags & BGFX_FRAME_FLUSH); + const bool present = !m_submit->m_flush; + + if (present) + { + latencySimulationEnd(); + } + uint32_t frameNum = m_submit->m_frameNum; BGFX_PROFILER_SCOPE("bgfx/API thread frame", kColorSubmit); @@ -2829,11 +2853,45 @@ namespace bgfx renderSemWait(); frameNoRenderWait(); + if (present) + { + latencySimulationStart(); + } + m_encoder[0].begin(m_submit, 0); return frameNum; } + void Context::latencySimulationEnd() + { + BGFX_MUTEX_SCOPE(m_latencyLock); + + if (m_lowLatency + && 0 != m_latencyFrameId) + { + m_renderCtx->setLatencyMarker(LatencyMarker::SimulationEnd, m_latencyFrameId); + m_submit->m_latencyFrameId = m_latencyFrameId; + } + } + + void Context::latencySimulationStart() + { + BGFX_MUTEX_SCOPE(m_latencyLock); + + if (m_lowLatency) + { + BGFX_PROFILER_SCOPE("bgfx/Low latency sleep", kColorWait); + + const int64_t sleepBegin = bx::getHPCounter(); + m_renderCtx->latencySleep(); + m_latencySleep = bx::getHPCounter() - sleepBegin; + + ++m_latencyFrameId; + m_renderCtx->setLatencyMarker(LatencyMarker::SimulationStart, m_latencyFrameId); + } + } + void Context::frameNoRenderWait() { swap(); @@ -2880,6 +2938,19 @@ namespace bgfx m_submit->m_perfStats.numViews = 0; m_submit->reserveViewStats(0 != (m_debug & BGFX_DEBUG_PROFILER), m_submit->m_numUsedViews); + { + // Renderer writes latency stats only when driver returns a report. + Stats& perfStats = m_submit->m_perfStats; + perfStats.latencySleep = m_latencySleep; + perfStats.latencyTotal = 0; + perfStats.latencySimulation = 0; + perfStats.latencyRenderSubmit = 0; + perfStats.latencyPresent = 0; + perfStats.latencyQueue = 0; + perfStats.latencyGpu = 0; + m_latencySleep = 0; + } + m_uniformCache.frame(m_submit->m_uniformCacheFrame); static_assert(bx::isTriviallyCopyable(), "Must be memcopyiable..."); @@ -2993,11 +3064,30 @@ namespace bgfx if (m_rendererInitialized && !m_flipped) { + const uint64_t latencyFrameId = m_latencyPresentFrameId; + m_latencyPresentFrameId = 0; + + if (0 != latencyFrameId) + { + m_renderCtx->setLatencyMarker(LatencyMarker::PresentStart, latencyFrameId); + } + m_renderCtx->flip(); m_flipped = true; + if (0 != latencyFrameId) + { + m_renderCtx->setLatencyMarker(LatencyMarker::PresentEnd, latencyFrameId); + } + if (m_renderCtx->isDeviceRemoved() ) { + { + BGFX_MUTEX_SCOPE(m_latencyLock); + m_lowLatency = false; + g_caps.supported &= ~BGFX_CAPS_LOW_LATENCY; + } + // Something horribly went wrong, fallback to noop renderer. rendererDestroy(m_renderCtx); @@ -3055,12 +3145,25 @@ namespace bgfx if (m_rendererInitialized) { + const uint64_t latencyFrameId = m_render->m_latencyFrameId; + + if (0 != latencyFrameId) + { + m_renderCtx->setLatencyMarker(LatencyMarker::RenderSubmitStart, latencyFrameId); + } + { BGFX_PROFILER_SCOPE("bgfx/Render submit", kColorSubmit); m_renderCtx->submit(m_render, m_clearQuad, m_mipGen, m_textVideoMemBlitter); m_flipped = false; } + if (0 != latencyFrameId) + { + m_renderCtx->setLatencyMarker(LatencyMarker::RenderSubmitEnd, latencyFrameId); + m_latencyPresentFrameId = latencyFrameId; + } + { BGFX_PROFILER_SCOPE("bgfx/Screenshot", kColorResource); for (uint8_t ii = 0, num = m_render->m_numScreenShots; ii < num; ++ii) @@ -4404,6 +4507,7 @@ namespace bgfx { BGFX_CHECK_API_THREAD(); BX_ASSERT(0 == (_flags&BGFX_RESET_RESERVED_MASK), "Do not set reset reserved flags!"); + BX_ASSERT(BGFX_RESET_LOW_LATENCY_MASK != (_flags&BGFX_RESET_LOW_LATENCY_MASK), "Set either `BGFX_RESET_LOW_LATENCY_ON` or `BGFX_RESET_LOW_LATENCY_BOOST`, not both!"); s_ctx->reset(_flags, _swapChain); } @@ -7344,6 +7448,7 @@ static_assert(FLAGS_MASK_TEST(0 | BGFX_RESET_FLUSH_AFTER_RENDER | BGFX_RESET_FLIP_AFTER_RENDER | BGFX_RESET_SUSPEND + | BGFX_RESET_LOW_LATENCY_MASK | BGFX_RESET_RESERVED_MASK ) ) ); diff --git a/src/bgfx_p.h b/src/bgfx_p.h index d0d4be9de..79b1c3f8b 100644 --- a/src/bgfx_p.h +++ b/src/bgfx_p.h @@ -4118,6 +4118,7 @@ namespace bgfx , m_waitSubmit(0) , m_waitRender(0) , m_frameNum(0) + , m_latencyFrameId(0) , m_capture(false) , m_flush(false) , m_needBindDedup(false) @@ -4130,6 +4131,7 @@ namespace bgfx bx::memSet(m_viewUsed, 0, sizeof(m_viewUsed) ); bx::memSet(m_viewUsedOffset, 0, sizeof(m_viewUsedOffset) ); + bx::memSet(&m_perfStats, 0, sizeof(m_perfStats) ); m_perfStats.viewStats = m_viewStats; } @@ -4361,6 +4363,7 @@ namespace bgfx m_flush = false; m_numScreenShots = 0; m_frameNum = frameNum; + m_latencyFrameId = 0; m_numUsedViews = 0; bx::memSet(m_viewUsed, 0, sizeof(m_viewUsed) ); @@ -4553,6 +4556,7 @@ namespace bgfx int64_t m_waitRender; uint32_t m_frameNum; + uint64_t m_latencyFrameId; bool m_capture; bool m_flush; @@ -5791,6 +5795,54 @@ namespace bgfx uint32_t m_capacity; }; + struct LatencyMarker + { + enum Enum + { + SimulationStart, + SimulationEnd, + RenderSubmitStart, + RenderSubmitEnd, + PresentStart, + PresentEnd, + + Count + }; + }; + + struct LatencyMode + { + enum Enum + { + Off, + On, + Boost, + + Count + }; + }; + + static_assert(LatencyMode::On == BGFX_RESET_LOW_LATENCY_ON >> BGFX_RESET_LOW_LATENCY_SHIFT); + static_assert(LatencyMode::Boost == BGFX_RESET_LOW_LATENCY_BOOST >> BGFX_RESET_LOW_LATENCY_SHIFT); + + inline LatencyMode::Enum getLatencyMode(uint32_t _reset) + { + return LatencyMode::Enum( (_reset & BGFX_RESET_LOW_LATENCY_MASK) >> BGFX_RESET_LOW_LATENCY_SHIFT); + } + + // Driver's latency report, microseconds. + struct LatencyReport + { + uint64_t simulationStart; + uint64_t simulationEnd; + uint64_t renderSubmitStart; + uint64_t renderSubmitEnd; + uint64_t presentStart; + uint64_t presentEnd; + uint64_t gpuRenderStart; + uint64_t gpuRenderEnd; + }; + struct BX_NO_VTABLE RendererContextI { RendererContextI() @@ -5846,6 +5898,15 @@ namespace bgfx virtual void dbgTextRender(TextVideoMemBlitter& _blitter, uint32_t _numIndices) = 0; virtual void dbgTextRenderEnd(TextVideoMemBlitter& _blitter) = 0; + virtual void latencySleep() + { + } + + virtual void setLatencyMarker(LatencyMarker::Enum _marker, uint64_t _frameId) + { + BX_UNUSED(_marker, _frameId); + } + void createUniform(UniformHandle _handle, UniformType::Enum _type, uint16_t _num, const char* _name) { if (NULL != m_uniforms[_handle.idx]) @@ -5920,6 +5981,10 @@ namespace bgfx , m_flipAfterRender(false) , m_singleThreaded(false) , m_flushPrevFrame(false) + , m_lowLatency(false) + , m_latencyFrameId(0) + , m_latencyPresentFrameId(0) + , m_latencySleep(0) { } @@ -5998,7 +6063,7 @@ namespace bgfx , "Per-surface flags passed to `reset` are ignored. They belong on `SwapChain::flags`." ); - const uint32_t resetFlags = _flags & ~kSwapChainFlagMask; + const uint32_t resetFlags = checkResetFlags(_flags & ~kSwapChainFlagMask); SwapChain swapChain = m_init.swapChain; @@ -7977,6 +8042,22 @@ namespace bgfx return flags; } + static uint32_t checkResetFlags(uint32_t _flags) + { + const uint32_t maskFlags = ~(0 + | (0 != (g_caps.supported & BGFX_CAPS_LOW_LATENCY) ? 0 : BGFX_RESET_LOW_LATENCY_MASK) + ); + + const uint32_t flags = _flags & maskFlags; + + BX_WARN(_flags == flags + , "Reset flags `BGFX_RESET_LOW_LATENCY_*` will be ignored, because " + "`BGFX_CAPS_LOW_LATENCY` is not supported." + ); + + return flags; + } + void checkSwapChainDepth(const SwapChain& _desc) { if (!isValid(_desc.depth) ) @@ -8483,6 +8564,8 @@ namespace bgfx void frameNoRenderWait(); void swap(); void collectSubmitViewUsed(); + void latencySimulationEnd(); + void latencySimulationStart(); // render thread void flip(); @@ -8575,6 +8658,7 @@ namespace bgfx bx::Mutex m_encoderApiLock; bx::Mutex m_encoderBeginLock; bx::Mutex m_resourceApiLock; + bx::Mutex m_latencyLock; bx::Thread m_thread; #else void apiSemPost() @@ -8709,6 +8793,11 @@ namespace bgfx bool m_singleThreaded; bool m_flipped; bool m_flushPrevFrame; + bool m_lowLatency; + + uint64_t m_latencyFrameId; + uint64_t m_latencyPresentFrameId; + int64_t m_latencySleep; typedef UpdateBatchT<256> TextureUpdateBatch; BX_ALIGN_DECL_CACHE_LINE(TextureUpdateBatch m_textureUpdateBatch); diff --git a/src/nvapi.cpp b/src/nvapi.cpp index 6f33f487b..dcf84f6f3 100644 --- a/src/nvapi.cpp +++ b/src/nvapi.cpp @@ -47,12 +47,108 @@ namespace bgfx uint32_t curAvailableDedicatedVideoMemory; }; + /* + * NVIDIA Reflex + * + * Reference(s): + * - https://github.com/NVIDIA/nvapi/blob/main/nvapi.h + * - https://developer.nvidia.com/performance-rendering-tools/reflex + */ + + enum NvLatencyMarkerType + { + NV_SIMULATION_START = 0, + NV_SIMULATION_END = 1, + NV_RENDERSUBMIT_START = 2, + NV_RENDERSUBMIT_END = 3, + NV_PRESENT_START = 4, + NV_PRESENT_END = 5, + }; + + static const NvLatencyMarkerType s_nvLatencyMarker[] = + { + NV_SIMULATION_START, + NV_SIMULATION_END, + NV_RENDERSUBMIT_START, + NV_RENDERSUBMIT_END, + NV_PRESENT_START, + NV_PRESENT_END, + }; + + struct NvSetSleepModeParamsV1 + { + NvSetSleepModeParamsV1() + { + bx::memSet(this, 0, sizeof(NvSetSleepModeParamsV1) ); + version = sizeof(NvSetSleepModeParamsV1) | (1 << 16); + } + + uint32_t version; + uint8_t lowLatencyMode; + uint8_t lowLatencyBoost; + uint32_t minimumIntervalUs; + uint8_t useMarkersToOptimize; + uint8_t useMinQueueTime; + uint8_t reserved[30]; + }; + + struct NvLatencyMarkerParamsV1 + { + NvLatencyMarkerParamsV1() + { + bx::memSet(this, 0, sizeof(NvLatencyMarkerParamsV1) ); + version = sizeof(NvLatencyMarkerParamsV1) | (1 << 16); + } + + uint32_t version; + uint64_t frameId; + NvLatencyMarkerType markerType; + uint64_t reserved0; + uint8_t reserved[56]; + }; + + struct NvLatencyFrameReport + { + uint64_t frameId; + uint64_t inputSampleTime; + uint64_t simStartTime; + uint64_t simEndTime; + uint64_t renderSubmitStartTime; + uint64_t renderSubmitEndTime; + uint64_t presentStartTime; + uint64_t presentEndTime; + uint64_t driverStartTime; + uint64_t driverEndTime; + uint64_t osRenderQueueStartTime; + uint64_t osRenderQueueEndTime; + uint64_t gpuRenderStartTime; + uint64_t gpuRenderEndTime; + uint8_t reserved[128]; + }; + + struct NvLatencyResultParamsV1 + { + NvLatencyResultParamsV1() + { + bx::memSet(this, 0, sizeof(NvLatencyResultParamsV1) ); + version = sizeof(NvLatencyResultParamsV1) | (1 << 16); + } + + uint32_t version; + NvLatencyFrameReport frameReport[64]; + uint8_t reserved[32]; + }; + typedef void* (NVAPICALL* PFN_NVAPI_QUERYINTERFACE)(uint32_t _functionOffset); typedef NvApiStatus (NVAPICALL* PFN_NVAPI_INITIALIZE)(); typedef NvApiStatus (NVAPICALL* PFN_NVAPI_UNLOAD)(); typedef NvApiStatus (NVAPICALL* PFN_NVAPI_ENUMPHYSICALGPUS)(NvPhysicalGpuHandle* _handle[NVAPI_MAX_PHYSICAL_GPUS], uint32_t* _gpuCount); typedef NvApiStatus (NVAPICALL* PFN_NVAPI_GPUGETMEMORYINFO)(NvPhysicalGpuHandle* _handle, NvMemoryInfoV2* _memoryInfo); typedef NvApiStatus (NVAPICALL* PFN_NVAPI_GPUGETFULLNAME)(NvPhysicalGpuHandle* _physicalGpu, char _name[64]); + typedef NvApiStatus (NVAPICALL* PFN_NVAPI_D3DSETSLEEPMODE)(IUnknown* _device, NvSetSleepModeParamsV1* _params); + typedef NvApiStatus (NVAPICALL* PFN_NVAPI_D3DSLEEP)(IUnknown* _device); + typedef NvApiStatus (NVAPICALL* PFN_NVAPI_D3DSETLATENCYMARKER)(IUnknown* _device, NvLatencyMarkerParamsV1* _params); + typedef NvApiStatus (NVAPICALL* PFN_NVAPI_D3DGETLATENCY)(IUnknown* _device, NvLatencyResultParamsV1* _params); #define NVAPI_INITIALIZE UINT32_C(0x0150e828) #define NVAPI_UNLOAD UINT32_C(0xd22bdd7e) @@ -61,13 +157,21 @@ namespace bgfx #define NVAPI_GPUGETFULLNAME UINT32_C(0xceee8e9f) #define NVAPI_MULTIDRAWINSTANCEDINDIRECT UINT32_C(0xd4e26bbf) #define NVAPI_MULTIDRAWINDEXEDINSTANCEDINDIRECT UINT32_C(0x59e890f9) +#define NVAPI_D3DSETSLEEPMODE UINT32_C(0xac1ca9e0) +#define NVAPI_D3DSLEEP UINT32_C(0x852cd1d2) +#define NVAPI_D3DSETLATENCYMARKER UINT32_C(0xd9984c05) +#define NVAPI_D3DGETLATENCY UINT32_C(0x1a587f9c) - static PFN_NVAPI_QUERYINTERFACE nvApiQueryInterface; - static PFN_NVAPI_INITIALIZE nvApiInitialize; - static PFN_NVAPI_UNLOAD nvApiUnload; - static PFN_NVAPI_ENUMPHYSICALGPUS nvApiEnumPhysicalGPUs; - static PFN_NVAPI_GPUGETMEMORYINFO nvApiGpuGetMemoryInfo; - static PFN_NVAPI_GPUGETFULLNAME nvApiGpuGetFullName; + static PFN_NVAPI_QUERYINTERFACE nvApiQueryInterface; + static PFN_NVAPI_INITIALIZE nvApiInitialize; + static PFN_NVAPI_UNLOAD nvApiUnload; + static PFN_NVAPI_ENUMPHYSICALGPUS nvApiEnumPhysicalGPUs; + static PFN_NVAPI_GPUGETMEMORYINFO nvApiGpuGetMemoryInfo; + static PFN_NVAPI_GPUGETFULLNAME nvApiGpuGetFullName; + static PFN_NVAPI_D3DSETSLEEPMODE nvApiD3DSetSleepMode; + static PFN_NVAPI_D3DSLEEP nvApiD3DSleep; + static PFN_NVAPI_D3DSETLATENCYMARKER nvApiD3DSetLatencyMarker; + static PFN_NVAPI_D3DGETLATENCY nvApiD3DGetLatency; /* * NVIDIA Aftermath @@ -101,6 +205,8 @@ namespace bgfx , m_nvGpu(NULL) , m_nvAftermathDll(NULL) , m_aftermathHandle(NULL) + , m_reflexDevice(NULL) + , m_latencyResult(NULL) { } @@ -132,6 +238,11 @@ namespace bgfx nvApiD3D11MultiDrawInstancedIndirect = (PFN_NVAPI_MULTIDRAWINDIRECT)nvApiQueryInterface(NVAPI_MULTIDRAWINSTANCEDINDIRECT); nvApiD3D11MultiDrawIndexedInstancedIndirect = (PFN_NVAPI_MULTIDRAWINDIRECT)nvApiQueryInterface(NVAPI_MULTIDRAWINDEXEDINSTANCEDINDIRECT); + nvApiD3DSetSleepMode = (PFN_NVAPI_D3DSETSLEEPMODE )nvApiQueryInterface(NVAPI_D3DSETSLEEPMODE); + nvApiD3DSleep = (PFN_NVAPI_D3DSLEEP )nvApiQueryInterface(NVAPI_D3DSLEEP); + nvApiD3DSetLatencyMarker = (PFN_NVAPI_D3DSETLATENCYMARKER)nvApiQueryInterface(NVAPI_D3DSETLATENCYMARKER); + nvApiD3DGetLatency = (PFN_NVAPI_D3DGETLATENCY )nvApiQueryInterface(NVAPI_D3DGETLATENCY); + initialized = true && NULL != nvApiInitialize && NULL != nvApiUnload @@ -179,6 +290,14 @@ namespace bgfx void NvApi::shutdown() { + m_reflexDevice = NULL; + + if (NULL != m_latencyResult) + { + bx::deleteObject(g_allocator, m_latencyResult); + m_latencyResult = NULL; + } + if (NULL != m_nvGpu) { nvApiUnload(); @@ -370,4 +489,125 @@ namespace bgfx } } + bool NvApi::initReflex(IUnknown* _device) + { + m_reflexDevice = NULL; + + // Reflex is available on Windows only. + if (!BX_ENABLED(BX_PLATFORM_WINDOWS) + || !isInitialized() + || NULL == nvApiD3DSetSleepMode + || NULL == nvApiD3DSleep + || NULL == nvApiD3DSetLatencyMarker + || NULL == nvApiD3DGetLatency) + { + return false; + } + + NvSetSleepModeParamsV1 params; + const NvApiStatus status = nvApiD3DSetSleepMode(_device, ¶ms); + + if (NVAPI_OK != status) + { + BX_TRACE("NVAPI: Reflex is not supported, NvAPI_D3D_SetSleepMode failed %d.", status); + return false; + } + + BX_TRACE("NVAPI: Reflex supported."); + + m_reflexDevice = _device; + + if (NULL == m_latencyResult) + { + // Latency result holds 64 frame reports, it's too big for the stack. + m_latencyResult = BX_NEW(g_allocator, NvLatencyResultParamsV1); + } + + return true; + } + + void NvApi::shutdownReflex() + { + if (NULL != m_reflexDevice) + { + setSleepMode(LatencyMode::Off); + m_reflexDevice = NULL; + } + + if (NULL != m_latencyResult) + { + bx::deleteObject(g_allocator, m_latencyResult); + m_latencyResult = NULL; + } + } + + void NvApi::setSleepMode(LatencyMode::Enum _mode) + { + if (NULL != m_reflexDevice) + { + NvSetSleepModeParamsV1 params; + params.lowLatencyMode = LatencyMode::Off != _mode; + params.lowLatencyBoost = LatencyMode::Boost == _mode; + + const NvApiStatus status = nvApiD3DSetSleepMode(m_reflexDevice, ¶ms); + BX_WARN(NVAPI_OK == status, "NVAPI: NvAPI_D3D_SetSleepMode failed %d.", status); BX_UNUSED(status); + } + } + + void NvApi::sleep() + { + if (NULL != m_reflexDevice) + { + nvApiD3DSleep(m_reflexDevice); + } + } + + void NvApi::setLatencyMarker(LatencyMarker::Enum _marker, uint64_t _frameId) + { + if (NULL != m_reflexDevice) + { + NvLatencyMarkerParamsV1 params; + params.frameId = _frameId; + params.markerType = s_nvLatencyMarker[_marker]; + + nvApiD3DSetLatencyMarker(m_reflexDevice, ¶ms); + } + } + + bool NvApi::getLatencyReport(LatencyReport& _report) + { + if (NULL == m_reflexDevice + || NULL == m_latencyResult) + { + return false; + } + + NvLatencyResultParamsV1& params = *m_latencyResult; + if (NVAPI_OK != nvApiD3DGetLatency(m_reflexDevice, ¶ms) ) + { + return false; + } + + for (uint32_t ii = BX_COUNTOF(params.frameReport); 0 < ii; --ii) + { + const NvLatencyFrameReport& frame = params.frameReport[ii-1]; + + if (0 != frame.simStartTime + && frame.gpuRenderEndTime > frame.simStartTime) + { + _report.simulationStart = frame.simStartTime; + _report.simulationEnd = frame.simEndTime; + _report.renderSubmitStart = frame.renderSubmitStartTime; + _report.renderSubmitEnd = frame.renderSubmitEndTime; + _report.presentStart = frame.presentStartTime; + _report.presentEnd = frame.presentEndTime; + _report.gpuRenderStart = frame.gpuRenderStartTime; + _report.gpuRenderEnd = frame.gpuRenderEndTime; + return true; + } + } + + return false; + } + } // namespace bgfx diff --git a/src/nvapi.h b/src/nvapi.h index 4fcefa112..16cfa7a2e 100644 --- a/src/nvapi.h +++ b/src/nvapi.h @@ -6,6 +6,7 @@ #ifndef BGFX_NVAPI_H_HEADER_GUARD #define BGFX_NVAPI_H_HEADER_GUARD +struct IUnknown; struct ID3D11Device; struct ID3D11DeviceContext; struct ID3D11Buffer; @@ -16,6 +17,7 @@ namespace bgfx { struct NvPhysicalGpuHandle; struct NvAftermathContextHandle; + struct NvLatencyResultParamsV1; struct NvAftermathDeviceStatus { @@ -68,6 +70,24 @@ namespace bgfx /// void setMarker(const bx::StringView& _marker); + /// + bool initReflex(IUnknown* _device); + + /// + void shutdownReflex(); + + /// + void setSleepMode(LatencyMode::Enum _mode); + + /// + void sleep(); + + /// + void setLatencyMarker(LatencyMarker::Enum _marker, uint64_t _frameId); + + /// + bool getLatencyReport(LatencyReport& _report); + /// void* m_nvApiDll; NvPhysicalGpuHandle* m_nvGpu; @@ -75,6 +95,9 @@ namespace bgfx void* m_nvAftermathDll; NvAftermathContextHandle* m_aftermathHandle; + IUnknown* m_reflexDevice; + NvLatencyResultParamsV1* m_latencyResult; + PFN_NVAPI_MULTIDRAWINDIRECT nvApiD3D11MultiDrawInstancedIndirect; PFN_NVAPI_MULTIDRAWINDIRECT nvApiD3D11MultiDrawIndexedInstancedIndirect; }; diff --git a/src/renderer.h b/src/renderer.h index 85cd51296..23a8b27ec 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -120,6 +120,24 @@ namespace bgfx uint32_t m_num; }; + inline int64_t getLatencyTime(uint64_t _beginUs, uint64_t _endUs) + { + return _endUs > _beginUs + ? int64_t(_endUs - _beginUs) * bx::getHPFrequency() / 1000000 + : 0 + ; + } + + inline void setLatencyStats(Stats& _stats, const LatencyReport& _report) + { + _stats.latencyTotal = getLatencyTime(_report.simulationStart, _report.gpuRenderEnd); + _stats.latencySimulation = getLatencyTime(_report.simulationStart, _report.simulationEnd); + _stats.latencyRenderSubmit = getLatencyTime(_report.renderSubmitStart, _report.renderSubmitEnd); + _stats.latencyPresent = getLatencyTime(_report.presentStart, _report.presentEnd); + _stats.latencyQueue = getLatencyTime(_report.presentEnd, _report.gpuRenderStart); + _stats.latencyGpu = getLatencyTime(_report.gpuRenderStart, _report.gpuRenderEnd); + } + template class TimerResultT { diff --git a/src/renderer_d3d11.cpp b/src/renderer_d3d11.cpp index c83ded6f5..3fe00475e 100644 --- a/src/renderer_d3d11.cpp +++ b/src/renderer_d3d11.cpp @@ -1656,6 +1656,16 @@ namespace bgfx { namespace d3d11 postReset(); } + // Reflex paces the main swap chain only. + if (0 != (_init.capabilities & BGFX_CAPS_LOW_LATENCY) + && mainFrameBuffer().isSwapChain() + && m_nvapi.initReflex(m_device) ) + { + g_caps.supported |= BGFX_CAPS_LOW_LATENCY; + + m_nvapi.setSleepMode(getLatencyMode(m_reset) ); + } + m_nvapi.initAftermath(m_device, m_deviceCtx); g_internalData.context = m_device; @@ -1719,6 +1729,8 @@ namespace bgfx { namespace d3d11 { preReset(); + m_nvapi.shutdownReflex(); + if (NULL != m_ags) { agsDeInit(m_ags); @@ -2224,6 +2236,16 @@ namespace bgfx { namespace d3d11 } } + void latencySleep() override + { + m_nvapi.sleep(); + } + + void setLatencyMarker(LatencyMarker::Enum _marker, uint64_t _frameId) override + { + m_nvapi.setLatencyMarker(_marker, _frameId); + } + virtual void setName(Handle _handle, const char* _name, uint16_t _len) override { switch (_handle.type) @@ -2535,10 +2557,21 @@ namespace bgfx { namespace d3d11 else m_reset &= ~BGFX_RESET_VSYNC; + if (0 != ( (m_reset ^ _reset) & BGFX_RESET_LOW_LATENCY_MASK) ) + { + m_reset = 0 + | (m_reset & ~BGFX_RESET_LOW_LATENCY_MASK) + | ( _reset & BGFX_RESET_LOW_LATENCY_MASK) + ; + + m_nvapi.setSleepMode(getLatencyMode(m_reset) ); + } + const uint32_t maskFlags = ~(0 | BGFX_RESET_MAXANISOTROPY | BGFX_RESET_SUSPEND | BGFX_RESET_VSYNC + | BGFX_RESET_LOW_LATENCY_MASK ); if (m_mainSwapChain.width != _swapChain.width @@ -7925,6 +7958,12 @@ namespace bgfx { namespace d3d11 bx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) ); m_nvapi.getMemoryInfo(perfStats.gpuMemoryUsed, perfStats.gpuMemoryMax); + LatencyReport latencyReport; + if (m_nvapi.getLatencyReport(latencyReport) ) + { + setLatencyStats(perfStats, latencyReport); + } + if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) ) { BGFX_D3D11_PROFILER_BEGIN_LITERAL("debugstats", kColorFrame); diff --git a/src/renderer_d3d12.cpp b/src/renderer_d3d12.cpp index 5c17cb4df..bd270a6fd 100644 --- a/src/renderer_d3d12.cpp +++ b/src/renderer_d3d12.cpp @@ -2065,6 +2065,16 @@ namespace bgfx { namespace d3d12 } } + // Reflex paces the main swap chain only. + if (0 != (_init.capabilities & BGFX_CAPS_LOW_LATENCY) + && mainFrameBuffer().isSwapChain() + && m_nvapi.initReflex(m_device) ) + { + g_caps.supported |= BGFX_CAPS_LOW_LATENCY; + + m_nvapi.setSleepMode(getLatencyMode(m_reset) ); + } + if (m_nvapi.isInitialized() ) { finish(); @@ -2120,6 +2130,8 @@ namespace bgfx { namespace d3d12 finishAll(true); m_batch.destroy(); + m_nvapi.shutdownReflex(); + DX_RELEASE(m_zeroInitBuffer, 0); preReset(); @@ -3034,6 +3046,16 @@ namespace bgfx { namespace d3d12 } } + void latencySleep() override + { + m_nvapi.sleep(); + } + + void setLatencyMarker(LatencyMarker::Enum _marker, uint64_t _frameId) override + { + m_nvapi.setLatencyMarker(_marker, _frameId); + } + virtual void setName(Handle _handle, const char* _name, uint16_t _len) override { switch (_handle.type) @@ -3258,10 +3280,21 @@ namespace bgfx { namespace d3d12 else m_reset &= ~BGFX_RESET_VSYNC; + if (0 != ( (m_reset ^ _reset) & BGFX_RESET_LOW_LATENCY_MASK) ) + { + m_reset = 0 + | (m_reset & ~BGFX_RESET_LOW_LATENCY_MASK) + | ( _reset & BGFX_RESET_LOW_LATENCY_MASK) + ; + + m_nvapi.setSleepMode(getLatencyMode(m_reset) ); + } + const uint32_t maskFlags = ~(0 | BGFX_RESET_MAXANISOTROPY | BGFX_RESET_SUSPEND | BGFX_RESET_VSYNC + | BGFX_RESET_LOW_LATENCY_MASK ); if (m_mainSwapChain.width != _swapChain.width @@ -10543,6 +10576,12 @@ namespace bgfx { namespace d3d12 perfStats.gpuMemoryUsed = int64_t(vmi[0].CurrentUsage); #endif // BX_PLATFORM_WINDOWS + LatencyReport latencyReport; + if (m_nvapi.getLatencyReport(latencyReport) ) + { + setLatencyStats(perfStats, latencyReport); + } + if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) ) { BGFX_D3D12_PROFILER_BEGIN_LITERAL("debugstats", kColorFrame); diff --git a/src/renderer_vk.cpp b/src/renderer_vk.cpp index 309c8932b..a4b283014 100644 --- a/src/renderer_vk.cpp +++ b/src/renderer_vk.cpp @@ -147,6 +147,19 @@ namespace bgfx { namespace vk { VK_PRESENT_MODE_IMMEDIATE_KHR, false, "VK_PRESENT_MODE_IMMEDIATE_KHR" }, }; + static const VkLatencyMarkerNV s_latencyMarker[] = + { + VK_LATENCY_MARKER_SIMULATION_START_NV, + VK_LATENCY_MARKER_SIMULATION_END_NV, + VK_LATENCY_MARKER_RENDERSUBMIT_START_NV, + VK_LATENCY_MARKER_RENDERSUBMIT_END_NV, + VK_LATENCY_MARKER_PRESENT_START_NV, + VK_LATENCY_MARKER_PRESENT_END_NV, + }; + static_assert(LatencyMarker::Count == BX_COUNTOF(s_latencyMarker) ); + + static constexpr uint64_t kLowLatencySleepTimeout = UINT64_C(500000000); + #define VK_IMPORT_FUNC(_optional, _func) PFN_##_func _func #define VK_IMPORT_INSTANCE_FUNC VK_IMPORT_FUNC #define VK_IMPORT_DEVICE_FUNC VK_IMPORT_FUNC @@ -383,12 +396,14 @@ VK_IMPORT_DEVICE KHR_fragment_shading_rate, KHR_get_physical_device_properties2, KHR_get_surface_capabilities2, + KHR_present_id, KHR_shader_float16_int8, KHR_video_queue, KHR_video_decode_queue, KHR_video_decode_h264, KHR_video_decode_h265, KHR_video_decode_av1, + NV_low_latency2, # if BX_PLATFORM_ANDROID KHR_android_surface, @@ -433,12 +448,14 @@ VK_IMPORT_DEVICE { "VK_KHR_fragment_shading_rate", 1, false, false, true, Layer::Count }, { "VK_KHR_get_physical_device_properties2", 1, false, false, true, Layer::Count }, { "VK_KHR_get_surface_capabilities2", 1, false, false, true, Layer::Count }, + { "VK_KHR_present_id", 1, false, false, false, Layer::Count }, { "VK_KHR_shader_float16_int8", 1, false, false, true, Layer::Count }, { "VK_KHR_video_queue", 1, false, false, true, Layer::Count }, { "VK_KHR_video_decode_queue", 1, false, false, true, Layer::Count }, { "VK_KHR_video_decode_h264", 1, false, false, true, Layer::Count }, { "VK_KHR_video_decode_h265", 1, false, false, true, Layer::Count }, { "VK_KHR_video_decode_av1", 1, false, false, true, Layer::Count }, + { "VK_NV_low_latency2", 2, false, false, false, Layer::Count }, # if BX_PLATFORM_ANDROID { VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, 1, false, false, true, Layer::Count }, # elif BX_PLATFORM_LINUX @@ -1307,6 +1324,7 @@ VK_IMPORT_DEVICE , m_captureMemory() , m_captureSize(0) , m_variableRateShadingSupported(false) + , m_lowLatencySupported(false) { } @@ -1343,6 +1361,8 @@ VK_IMPORT_DEVICE VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchainMaintenance1Features = {}; VkPhysicalDeviceShaderFloat16Int8Features shaderFloat16Int8Features = {}; VkPhysicalDevice16BitStorageFeatures storage16BitFeatures = {}; + VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeatures = {}; + VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures = {}; m_fbh = BGFX_INVALID_HANDLE; m_readOnlyDepthLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -1354,6 +1374,13 @@ VK_IMPORT_DEVICE m_videoDecodeQueueFamily = UINT32_MAX; m_videoDecodeQueue = VK_NULL_HANDLE; + m_lowLatencySupported = false; + m_lowLatencyMode = getLatencyMode(_init.reset); + m_lowLatencySwapChain = VK_NULL_HANDLE; + m_lowLatencySemaphore = VK_NULL_HANDLE; + m_lowLatencySemaphoreValue = 0; + m_lowLatencyPresentId = 0; + if (_init.debug || _init.profile) { @@ -1413,6 +1440,20 @@ VK_IMPORT s_extension[Extension::KHR_draw_indirect_count ].m_initialize = !!(_init.capabilities & BGFX_CAPS_DRAW_INDIRECT_COUNT ); s_extension[Extension::KHR_fragment_shading_rate ].m_initialize = !!(_init.capabilities & BGFX_CAPS_VARIABLE_RATE_SHADING); + // NVIDIA Reflex is Windows only. + const bool lowLatency = true + && BX_ENABLED(BX_PLATFORM_WINDOWS) + && 0 != (_init.capabilities & BGFX_CAPS_LOW_LATENCY) + && !headless + && NULL == g_platformData.context + ; + + s_extension[Extension::KHR_present_id ].m_initialize = lowLatency; + s_extension[Extension::NV_low_latency2].m_initialize = lowLatency; + + s_extension[Extension::KHR_present_id ].m_supported = false; + s_extension[Extension::NV_low_latency2].m_supported = false; + dumpExtensions(VK_NULL_HANDLE, s_extension); if (s_layer[Layer::VK_LAYER_KHRONOS_validation].m_device.m_supported @@ -1851,6 +1892,45 @@ VK_IMPORT_INSTANCE } } + { + bool lowLatencySupported = true + && s_extension[Extension::NV_low_latency2].m_supported + && s_extension[Extension::KHR_present_id ].m_supported + && VK_API_VERSION_1_2 <= m_instanceApiVersion + && VK_API_VERSION_1_2 <= m_deviceProperties.apiVersion + && NULL != vkGetPhysicalDeviceFeatures2KHR + ; + + if (lowLatencySupported) + { + VkPhysicalDeviceFeatures2KHR deviceFeatures2; + deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; + deviceFeatures2.pNext = &timelineSemaphoreFeatures; + + timelineSemaphoreFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES; + timelineSemaphoreFeatures.pNext = &presentIdFeatures; + + presentIdFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR; + presentIdFeatures.pNext = NULL; + + vkGetPhysicalDeviceFeatures2KHR(m_physicalDevice, &deviceFeatures2); + + lowLatencySupported = true + && timelineSemaphoreFeatures.timelineSemaphore + && presentIdFeatures.presentId + ; + } + + if (lowLatencySupported) + { + presentIdFeatures.pNext = (VkBaseOutStructure*)nextFeatures; + nextFeatures = &timelineSemaphoreFeatures; + } + + s_extension[Extension::KHR_present_id ].m_supported = lowLatencySupported; + s_extension[Extension::NV_low_latency2].m_supported = lowLatencySupported; + } + m_deviceFeatures = { .robustBufferAccess = true @@ -2277,6 +2357,34 @@ VK_IMPORT_DEVICE vkGetDeviceQueue(m_device, m_globalQueueFamily, 0, &m_globalQueue); vkGetDeviceQueue(m_device, m_videoDecodeQueueFamily, 0, &m_videoDecodeQueue); + m_lowLatencySupported = true + && s_extension[Extension::NV_low_latency2].m_supported + && NULL != vkSetLatencySleepModeNV + && NULL != vkLatencySleepNV + && NULL != vkSetLatencyMarkerNV + && NULL != vkGetLatencyTimingsNV + && NULL != vkWaitSemaphores + ; + + if (m_lowLatencySupported) + { + VkSemaphoreTypeCreateInfo stci; + stci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO; + stci.pNext = NULL; + stci.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + stci.initialValue = 0; + + VkSemaphoreCreateInfo sci; + sci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + sci.pNext = &stci; + sci.flags = 0; + + result = vkCreateSemaphore(m_device, &sci, m_allocatorCb, &m_lowLatencySemaphore); + BX_WARN(VK_SUCCESS == result, "vkCreateSemaphore failed %d: %s.", result, getName(result) ); + + m_lowLatencySupported = VK_SUCCESS == result; + } + if (_init.videoDecode) { initVideoDecoder(this @@ -2338,6 +2446,11 @@ VK_IMPORT_DEVICE goto error; } + if (m_lowLatencySupported) + { + g_caps.supported |= BGFX_CAPS_LOW_LATENCY; + } + m_windows[0] = BGFX_INVALID_HANDLE; m_numWindows++; @@ -2503,6 +2616,7 @@ VK_IMPORT_DEVICE [[fallthrough]]; case ErrorState::DeviceCreated: + vkDestroy(m_lowLatencySemaphore); vkDestroyDevice(m_device, m_allocatorCb); [[fallthrough]]; @@ -2574,6 +2688,7 @@ VK_IMPORT_DEVICE m_memoryLru.evictAll(); vkDestroy(m_pipelineCache); + vkDestroy(m_lowLatencySemaphore); for (uint32_t ii = 0; ii < m_maxFrameLatency; ++ii) { @@ -2992,6 +3107,159 @@ VK_IMPORT_DEVICE } } + void latencySleep() override + { + uint64_t value; + + { + bx::MutexScope scope(m_lowLatencyMutex); + + if (VK_NULL_HANDLE == m_lowLatencySwapChain) + { + return; + } + + value = ++m_lowLatencySemaphoreValue; + + VkLatencySleepInfoNV lsi; + lsi.sType = VK_STRUCTURE_TYPE_LATENCY_SLEEP_INFO_NV; + lsi.pNext = NULL; + lsi.signalSemaphore = m_lowLatencySemaphore; + lsi.value = value; + + const VkResult result = vkLatencySleepNV(m_device, m_lowLatencySwapChain, &lsi); + + if (VK_SUCCESS != result) + { + BX_TRACE("vkLatencySleepNV failed %d: %s.", result, getName(result) ); + return; + } + } + + VkSemaphoreWaitInfo swi; + swi.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO; + swi.pNext = NULL; + swi.flags = 0; + swi.semaphoreCount = 1; + swi.pSemaphores = &m_lowLatencySemaphore; + swi.pValues = &value; + + const VkResult result = vkWaitSemaphores(m_device, &swi, kLowLatencySleepTimeout); + BX_WARN(VK_SUCCESS == result, "vkWaitSemaphores failed %d: %s.", result, getName(result) ); BX_UNUSED(result); + } + + void setLatencyMarker(LatencyMarker::Enum _marker, uint64_t _frameId) override + { + bx::MutexScope scope(m_lowLatencyMutex); + + if (LatencyMarker::PresentStart == _marker) + { + m_lowLatencyPresentId = _frameId; + } + + if (VK_NULL_HANDLE != m_lowLatencySwapChain) + { + VkSetLatencyMarkerInfoNV slmi; + slmi.sType = VK_STRUCTURE_TYPE_SET_LATENCY_MARKER_INFO_NV; + slmi.pNext = NULL; + slmi.presentID = _frameId; + slmi.marker = s_latencyMarker[_marker]; + + vkSetLatencyMarkerNV(m_device, m_lowLatencySwapChain, &slmi); + } + } + + void setLowLatencySwapChain(VkSwapchainKHR _swapChain) + { + bx::MutexScope scope(m_lowLatencyMutex); + + m_lowLatencySwapChain = _swapChain; + + if (VK_NULL_HANDLE != m_lowLatencySwapChain) + { + setLowLatencySleepMode(); + } + } + + void setLowLatencyMode(LatencyMode::Enum _mode) + { + bx::MutexScope scope(m_lowLatencyMutex); + + m_lowLatencyMode = _mode; + + if (VK_NULL_HANDLE != m_lowLatencySwapChain) + { + setLowLatencySleepMode(); + } + } + + void setLowLatencySleepMode() + { + VkLatencySleepModeInfoNV lsmi; + lsmi.sType = VK_STRUCTURE_TYPE_LATENCY_SLEEP_MODE_INFO_NV; + lsmi.pNext = NULL; + lsmi.lowLatencyMode = LatencyMode::Off != m_lowLatencyMode; + lsmi.lowLatencyBoost = LatencyMode::Boost == m_lowLatencyMode; + lsmi.minimumIntervalUs = 0; + + VK_CHECK(vkSetLatencySleepModeNV(m_device, m_lowLatencySwapChain, &lsmi) ); + } + + bool getLatencyReport(LatencyReport& _report) + { + bx::MutexScope scope(m_lowLatencyMutex); + + if (VK_NULL_HANDLE == m_lowLatencySwapChain) + { + return false; + } + + VkLatencyTimingsFrameReportNV* timings = m_lowLatencyTimings; + + for (uint32_t ii = 0; ii < BX_COUNTOF(m_lowLatencyTimings); ++ii) + { + timings[ii].sType = VK_STRUCTURE_TYPE_LATENCY_TIMINGS_FRAME_REPORT_NV; + timings[ii].pNext = NULL; + } + + VkGetLatencyMarkerInfoNV glmi; + glmi.sType = VK_STRUCTURE_TYPE_GET_LATENCY_MARKER_INFO_NV; + glmi.pNext = NULL; + glmi.timingCount = BX_COUNTOF(m_lowLatencyTimings); + glmi.pTimings = timings; + + vkGetLatencyTimingsNV(m_device, m_lowLatencySwapChain, &glmi); + + const VkLatencyTimingsFrameReportNV* newest = NULL; + + for (uint32_t ii = 0, num = bx::min(glmi.timingCount, BX_COUNTOF(m_lowLatencyTimings) ); ii < num; ++ii) + { + const VkLatencyTimingsFrameReportNV& timing = timings[ii]; + + if (0 != timing.simStartTimeUs + && timing.gpuRenderEndTimeUs > timing.simStartTimeUs + && (NULL == newest || timing.presentID > newest->presentID) ) + { + newest = &timing; + } + } + + if (NULL == newest) + { + return false; + } + + _report.simulationStart = newest->simStartTimeUs; + _report.simulationEnd = newest->simEndTimeUs; + _report.renderSubmitStart = newest->renderSubmitStartTimeUs; + _report.renderSubmitEnd = newest->renderSubmitEndTimeUs; + _report.presentStart = newest->presentStartTimeUs; + _report.presentEnd = newest->presentEndTimeUs; + _report.gpuRenderStart = newest->gpuRenderStartTimeUs; + _report.gpuRenderEnd = newest->gpuRenderEndTimeUs; + return true; + } + virtual void setName(Handle _handle, const char* _name, uint16_t _len) override { switch (_handle.type) @@ -3251,8 +3519,17 @@ VK_IMPORT_DEVICE uint32_t maskFlags = ~(0 | BGFX_RESET_SUSPEND | BGFX_RESET_MAXANISOTROPY + | BGFX_RESET_LOW_LATENCY_MASK ); + const LatencyMode::Enum lowLatencyMode = getLatencyMode(_reset); + + if (m_lowLatencySupported + && m_lowLatencyMode != lowLatencyMode) + { + setLowLatencyMode(lowLatencyMode); + } + if (m_swapchainMaintenance1Supported && !!((_reset ^ m_reset) & BGFX_RESET_VSYNC) ) { @@ -5283,6 +5560,16 @@ VK_IMPORT_DEVICE bool m_variableRateShadingSupported; + bool m_lowLatencySupported; + LatencyMode::Enum m_lowLatencyMode; + VkSwapchainKHR m_lowLatencySwapChain; + VkSemaphore m_lowLatencySemaphore; + uint64_t m_lowLatencySemaphoreValue; + uint64_t m_lowLatencyPresentId; + bx::Mutex m_lowLatencyMutex; + + VkLatencyTimingsFrameReportNV m_lowLatencyTimings[64]; + TextVideoMem m_textVideoMem; uint8_t m_fsScratch[64<<10]; @@ -8119,6 +8406,37 @@ VK_DESTROY } } + bool SwapChainVK::isLowLatency() const + { + // Reflex paces the main swap chain only. + return true + && s_renderVK->m_lowLatencySupported + && this == &s_renderVK->m_backBuffer.m_swapChain + ; + } + + struct LowLatencyScope + { + LowLatencyScope(const SwapChainVK& _swapChain) + : m_mutex(_swapChain.isLowLatency() ? &s_renderVK->m_lowLatencyMutex : NULL) + { + if (NULL != m_mutex) + { + m_mutex->lock(); + } + } + + ~LowLatencyScope() + { + if (NULL != m_mutex) + { + m_mutex->unlock(); + } + } + + bx::Mutex* m_mutex; + }; + VkResult SwapChainVK::create(VkCommandBuffer _commandBuffer, void* _nwh, const SwapChain& _desc) { struct ErrorState @@ -8717,6 +9035,19 @@ VK_DESTROY m_sci.pNext = &modesInfo; } + const bool lowLatency = isLowLatency(); + + VkSwapchainLatencyCreateInfoNV latencyInfo; + + if (lowLatency) + { + latencyInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_LATENCY_CREATE_INFO_NV; + latencyInfo.pNext = m_sci.pNext; + latencyInfo.latencyModeEnable = VK_TRUE; + + m_sci.pNext = &latencyInfo; + } + result = vkCreateSwapchainKHR(device, &m_sci, allocatorCb, &m_swapChain); if (VK_SUCCESS != result) { @@ -8828,6 +9159,11 @@ VK_DESTROY m_needPresent = false; m_needToRecreateSwapchain = false; + if (lowLatency) + { + s_renderVK->setLowLatencySwapChain(m_swapChain); + } + return result; } @@ -8835,6 +9171,11 @@ VK_DESTROY { BGFX_PROFILER_SCOPE("SwapChainVK::releaseSwapChain", kColorFrame); + if (isLowLatency() ) + { + s_renderVK->setLowLatencySwapChain(VK_NULL_HANDLE); + } + for (uint32_t ii = 0; ii < BX_COUNTOF(m_backBufferColorImageView); ++ii) { release(m_backBufferColorImageView[ii]); @@ -9128,6 +9469,7 @@ VK_DESTROY VkResult result; { BGFX_PROFILER_SCOPE("vkAcquireNextImageKHR", kColorFrame); + LowLatencyScope lowLatencyScope(*this); result = vkAcquireNextImageKHR( device @@ -9238,9 +9580,25 @@ VK_DESTROY pi.pNext = &presentModeInfo; } + VkPresentIdKHR presentId; + const uint64_t lowLatencyPresentId = s_renderVK->m_lowLatencyPresentId; + + if (isLowLatency() + && 0 != lowLatencyPresentId) + { + presentId.sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR; + presentId.pNext = pi.pNext; + presentId.swapchainCount = 1; + presentId.pPresentIds = &lowLatencyPresentId; + + pi.pNext = &presentId; + s_renderVK->m_lowLatencyPresentId = 0; + } + VkResult result; { BGFX_PROFILER_SCOPE("vkQueuePresentHKR", kColorFrame); + LowLatencyScope lowLatencyScope(*this); result = vkQueuePresentKHR(m_queue, &pi); } @@ -11567,6 +11925,12 @@ VK_DESTROY perfStats.gpuMemoryMax = gpuMemoryAvailable; perfStats.gpuMemoryUsed = gpuMemoryUsed; + LatencyReport latencyReport; + if (getLatencyReport(latencyReport) ) + { + setLatencyStats(perfStats, latencyReport); + } + if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) ) { BGFX_VK_PROFILER_BEGIN_LITERAL("debugstats", kColorFrame); diff --git a/src/renderer_vk.h b/src/renderer_vk.h index 66256f278..a5d7a0e3f 100644 --- a/src/renderer_vk.h +++ b/src/renderer_vk.h @@ -232,6 +232,13 @@ VK_IMPORT_DEVICE_FUNC(true, vkCmdDrawIndexedIndirectCountKHR); \ /* VK_KHR_fragment_shading_rate */ \ VK_IMPORT_DEVICE_FUNC(true, vkCmdSetFragmentShadingRateKHR); \ + /* VK_NV_low_latency2 */ \ + VK_IMPORT_DEVICE_FUNC(true, vkSetLatencySleepModeNV); \ + VK_IMPORT_DEVICE_FUNC(true, vkLatencySleepNV); \ + VK_IMPORT_DEVICE_FUNC(true, vkSetLatencyMarkerNV); \ + VK_IMPORT_DEVICE_FUNC(true, vkGetLatencyTimingsNV); \ + /* 1.2 */ \ + VK_IMPORT_DEVICE_FUNC(true, vkWaitSemaphores); \ /* VK_KHR_video_queue / VK_KHR_video_decode_queue */ \ VK_IMPORT_DEVICE_FUNC(true, vkCmdBeginVideoCodingKHR); \ VK_IMPORT_DEVICE_FUNC(true, vkCmdEndVideoCodingKHR); \ @@ -812,6 +819,8 @@ VK_DESTROY_FUNC(DescriptorSet); bool hasDepthStencil() const { return VK_NULL_HANDLE != m_backBufferDepthStencilImageView; } + bool isLowLatency() const; + VkQueue m_queue; VkSwapchainCreateInfoKHR m_sci;