Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
exit $exit_code

- name: InspectCode
uses: JetBrains/ReSharper-InspectCode@v0.11
uses: JetBrains/ReSharper-InspectCode@v0.12
with:
# this is WTF tier but if you don't specify *both* of these the defaults assume `build: true`
build: false
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,35 @@ This fork ([winnerspiros/osu-framework](https://github.com/winnerspiros/osu-fram
- Hot-path LINQ allocations eliminated across the framework (replaced with `for` loops, span-based code, and cached collections).
- `object`-based locks migrated to `System.Threading.Lock` for lower overhead on modern runtimes.
- BASS audio, GL state-change, shader warm-up, texture upload, and mobile vertex-batching improvements.
- **GridContainer cell sizing** uses `RequiredParentSizeToFit` instead of `BoundingBox`, avoiding redundant matrix-to-parent-space transforms on every layout pass ([upstream Issue #3215](https://github.com/ppy/osu-framework/issues/3215)).

### Direct3D 12 renderer support

- Added `RendererType.Direct3D12` / `RendererType.Deferred_Direct3D12` and `GraphicsSurfaceType.Direct3D12`.
- Full pipeline: `VeldridDevice.CreateD3D12` swapchain creation, `LogD3D12` diagnostics (adapter info, Enhanced Barriers, Mesh Shaders, VRS, Raytracing support), and `PersistentStagingBuffer` staging.
- D3D12 is included in the Windows renderer fallback order (after D3D11, before OpenGL).
- Leverages the [winnerspiros/veldrid](https://github.com/winnerspiros/veldrid) submodule which contains a full D3D12 backend.

### Low-latency rendering infrastructure

- Generic `ILowLatencyProvider` interface for GPU-side latency reduction (NVIDIA Reflex, LatencyFlex, or any future API).
- `IDirect3D11LowLatencyProvider` (D3D11-specific) extends `ILowLatencyProvider`.
- `NoOpLowLatencyProvider` (default no-op) and `NoOpDirect3D11LowLatencyProvider` included.
- Latency markers (`SimulationStart/End`, `RenderSubmitStart/End`, `PresentStart/End`, `InputSample`, `TriggerFlash`) inserted into `GameHost.UpdateFrame()` and `GameHost.DrawFrame()`.
- `FrameSleep()` called at the start of each update frame for provider-controlled sleep (Reflex Boost mode).
- Provider auto-initialises on the draw thread using the native D3D11 or D3D12 device handle from Veldrid's `BackendInfoD3D11` / `BackendInfoD3D12`.
- `LatencyMode` setting (`Off` / `On` / `Boost`) added to `FrameworkConfigManager`.
- Inspired by [upstream PR #6666](https://github.com/ppy/osu-framework/pull/6666).

### Frame rate limiter enhancements

- **Unbuffered VSync (`UVSync`)**: Limits both draw *and* update threads to the exact display refresh rate. Useful for VRR/G-Sync/FreeSync displays where regular VSync introduces unwanted buffering ([upstream PR #6696](https://github.com/ppy/osu-framework/pull/6696)).
- **Custom FPS limiter**: `FrameSync.Custom` mode with a `CustomDrawLimit` setting (0–1000 Hz). When set to 0, the draw thread is unlimited. Update thread runs at max Hz. Useful for benchmarking or VRR-specific tuning ([upstream PR #6725](https://github.com/ppy/osu-framework/pull/6725)).

### Input latency improvements

- **Raw keyboard input on Windows**: `SDL_HINT_WINDOWS_RAW_KEYBOARD` enabled by default, bypassing the Windows message translation layer for lower-latency key events ([upstream PR #6507](https://github.com/ppy/osu-framework/pull/6507)).
- **Async keyboard event handling**: When text input (IME) is not active, keyboard events (`KEY_DOWN`/`KEY_UP`) are handled directly in SDL's event filter (`HandleEventFromFilter`), bypassing the SDL event queue for reduced input-to-render latency ([upstream PR #6506](https://github.com/ppy/osu-framework/pull/6506)).

### Code quality

Expand Down
18 changes: 18 additions & 0 deletions osu.Framework.Android/AndroidGameHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,24 @@ protected override void SetupConfig(IDictionary<FrameworkSetting, object> defaul
base.SetupConfig(defaultOverrides);
}

protected override void SetupForRun()
{
base.SetupForRun();

// Set the main thread to THREAD_PRIORITY_DISPLAY (-4) for scheduler prioritisation.
// In SingleThread mode (the default on Android), the main thread handles all game threads
// including rendering. This gives the rendering work higher scheduler priority.
try
{
global::Android.OS.Process.SetThreadPriority(global::Android.OS.ThreadPriority.Display);
Logger.Log("Android thread priority set to THREAD_PRIORITY_DISPLAY.", LoggingTarget.Runtime, LogLevel.Important);
}
catch (Exception ex)
{
Logger.Log($"Failed to set Android thread priority: {ex.Message}", LoggingTarget.Runtime, LogLevel.Debug);
}
}

protected override IWindow CreateWindow(GraphicsSurfaceType preferredSurface) => new AndroidGameWindow(preferredSurface, Options.FriendlyGameName);

protected override void DrawFrame()
Expand Down
6 changes: 6 additions & 0 deletions osu.Framework/Configuration/FrameSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public enum FrameSync
{
VSync,

[Description("VSync Unbuffered")]
UVSync,

[Description("2x refresh rate")]
Limit2x,

Expand All @@ -23,5 +26,8 @@ public enum FrameSync

[Description("Basically unlimited")]
Unlimited,

[Description("Custom")]
Custom,
}
}
5 changes: 5 additions & 0 deletions osu.Framework/Configuration/FrameworkConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Drawing;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Rendering.LowLatency;
using osu.Framework.Graphics.Video;
using osu.Framework.Input;
using osu.Framework.Platform;
Expand Down Expand Up @@ -40,8 +41,10 @@ protected override void InitialiseDefaults()
SetDefault(FrameworkSetting.SizeFullscreen, new Size(9999, 9999), new Size(320, 240));
SetDefault(FrameworkSetting.MinimiseOnFocusLossInFullscreen, RuntimeInfo.IsDesktop);
SetDefault(FrameworkSetting.FrameSync, FrameSync.Limit2x);
SetDefault(FrameworkSetting.CustomDrawLimit, 0, 0, 1000);
SetDefault(FrameworkSetting.WindowMode, WindowMode.Windowed);
SetDefault(FrameworkSetting.Renderer, RendererType.Automatic);
SetDefault(FrameworkSetting.LatencyMode, LatencyMode.Off);
SetDefault(FrameworkSetting.ShowUnicode, false);
SetDefault(FrameworkSetting.Locale, string.Empty);

Expand Down Expand Up @@ -101,8 +104,10 @@ public enum FrameworkSetting

Renderer,
WindowMode,
LatencyMode,
ConfineMouseMode,
FrameSync,
CustomDrawLimit,
ExecutionMode,

ShowUnicode,
Expand Down
6 changes: 6 additions & 0 deletions osu.Framework/Configuration/RendererType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public enum RendererType
[Description("Direct3D 11")]
Direct3D11,

[Description("Direct3D 12")]
Direct3D12,

/// <summary>
/// Uses <see cref="GLRenderer"/>.
/// </summary>
Expand All @@ -44,6 +47,9 @@ public enum RendererType
[Description("Direct3D 11 (Experimental)")]
Deferred_Direct3D11,

[Description("Direct3D 12 (Experimental)")]
Deferred_Direct3D12,

[Description("OpenGL (Experimental)")]
Deferred_OpenGL
}
Expand Down
4 changes: 2 additions & 2 deletions osu.Framework/Graphics/Containers/GridContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ private float[] getCellSizesAlongAxis(Axes axis, float spanLength)
}

private static bool shouldConsiderCell(Drawable cell) => cell != null && cell.IsAlive && cell.IsPresent;
private static float getCellWidth(Drawable cell) => shouldConsiderCell(cell) ? cell.BoundingBox.Width : 0;
private static float getCellHeight(Drawable cell) => shouldConsiderCell(cell) ? cell.BoundingBox.Height : 0;
private static float getCellWidth(Drawable cell) => shouldConsiderCell(cell) ? cell.RequiredParentSizeToFit.X : 0;
private static float getCellHeight(Drawable cell) => shouldConsiderCell(cell) ? cell.RequiredParentSizeToFit.Y : 0;

/// <summary>
/// Distributes any available length along all distributed dimensions, if required.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;

Check failure on line 4 in osu.Framework/Graphics/Rendering/LowLatency/IDirect3D11LowLatencyProvider.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in osu.Framework/Graphics/Rendering/LowLatency/IDirect3D11LowLatencyProvider.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

namespace osu.Framework.Graphics.Rendering.LowLatency
{
/// <summary>
/// Low-latency provider specifically for Direct3D 11 backends (e.g. NVIDIA Reflex via D3D11).
/// </summary>
public interface IDirect3D11LowLatencyProvider : ILowLatencyProvider
{
}
}
24 changes: 24 additions & 0 deletions osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;

namespace osu.Framework.Graphics.Rendering.LowLatency
{
/// <summary>
/// Generic low-latency provider interface supporting any graphics backend.
/// Implementations may use NVIDIA Reflex, LatencyFlex, or other latency reduction APIs.
/// </summary>
public interface ILowLatencyProvider
{
bool IsAvailable { get; }

void Initialize(IntPtr nativeDeviceHandle);

void SetMode(LatencyMode mode);

void SetMarker(LatencyMarker marker, ulong frameId);

void FrameSleep();
}
}
17 changes: 17 additions & 0 deletions osu.Framework/Graphics/Rendering/LowLatency/LatencyMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

namespace osu.Framework.Graphics.Rendering.LowLatency
{
public enum LatencyMarker
{
SimulationStart,
SimulationEnd,
RenderSubmitStart,
RenderSubmitEnd,
PresentStart,
PresentEnd,
InputSample,
TriggerFlash
}
}
12 changes: 12 additions & 0 deletions osu.Framework/Graphics/Rendering/LowLatency/LatencyMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

namespace osu.Framework.Graphics.Rendering.LowLatency
{
public enum LatencyMode
{
Off = 0,
On = 1,
Boost = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;

namespace osu.Framework.Graphics.Rendering.LowLatency
{
internal sealed class NoOpDirect3D11LowLatencyProvider : IDirect3D11LowLatencyProvider
{
public static readonly NoOpDirect3D11LowLatencyProvider INSTANCE = new NoOpDirect3D11LowLatencyProvider();

public bool IsAvailable => false;

public void Initialize(IntPtr deviceHandle) { }

public void SetMode(LatencyMode mode) { }

public void SetMarker(LatencyMarker marker, ulong frameId) { }

public void FrameSleep() { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;

namespace osu.Framework.Graphics.Rendering.LowLatency
{
internal sealed class NoOpLowLatencyProvider : ILowLatencyProvider
{
public static readonly NoOpLowLatencyProvider INSTANCE = new NoOpLowLatencyProvider();

public bool IsAvailable => false;

public void Initialize(IntPtr deviceHandle) { }

public void SetMode(LatencyMode mode) { }

public void SetMarker(LatencyMarker marker, ulong frameId) { }

public void FrameSleep() { }
}
}
7 changes: 7 additions & 0 deletions osu.Framework/Graphics/Veldrid/VeldridDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@ public VeldridDevice(IGraphicsSurface graphicsSurface)
#pragma warning restore CA1416
break;

case GraphicsSurfaceType.Direct3D12:
#pragma warning disable CA1416 // D3D12 is only reachable on Windows via the GraphicsSurfaceType switch
Device = GraphicsDevice.CreateD3D12(options, swapchain);
Device.LogD3D12(out maxTextureSize);
#pragma warning restore CA1416
break;

case GraphicsSurfaceType.Metal:
Device = GraphicsDevice.CreateMetal(options, swapchain);
Device.LogMetal(out maxTextureSize);
Expand Down
44 changes: 44 additions & 0 deletions osu.Framework/Graphics/Veldrid/VeldridExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,50 @@
Direct3D 11 Shared System Memory: {dxgiAdapter.Description.SharedSystemMemory / 1024 / 1024} MB");
}

[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public static void LogD3D12(this GraphicsDevice device, out int maxTextureSize)
{
Debug.Assert(device.BackendType == GraphicsBackend.Direct3D12);

var info = device.GetD3D12Info();

// D3D12 uses the same DXGI factory; query the adapter via the factory pointer.
var dxgiFactory = MarshallingHelpers.FromPointer<IDXGIFactory4>(info.DxgiFactory).AsNonNull();

IDXGIAdapter? adapter = null;

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Variable declaration can be inlined (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0018)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Unnecessary assignment of a value to 'adapter' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0059)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Variable declaration can be inlined (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0018)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Unnecessary assignment of a value to 'adapter' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0059)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Variable declaration can be inlined (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0018)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Unnecessary assignment of a value to 'adapter' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0059)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Variable declaration can be inlined (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0018)

Check failure on line 377 in osu.Framework/Graphics/Veldrid/VeldridExtensions.cs

View workflow job for this annotation

GitHub Actions / Code Quality

Unnecessary assignment of a value to 'adapter' (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0059)
string adapterDescription = "Unknown";
long dedicatedVideoMemory = 0;
long dedicatedSystemMemory = 0;
long sharedSystemMemory = 0;

if (dxgiFactory.EnumAdapters(0, out adapter).Success && adapter != null)
{
var desc = adapter.Description;
adapterDescription = desc.Description;
dedicatedVideoMemory = desc.DedicatedVideoMemory / 1024 / 1024;
dedicatedSystemMemory = desc.DedicatedSystemMemory / 1024 / 1024;
sharedSystemMemory = desc.SharedSystemMemory / 1024 / 1024;
}
Comment on lines +382 to +389

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnumAdapters() returns a COM object that should be released/disposed after use. As written, adapter will remain undisposed, which can leak native resources. Wrap the returned adapter in a using/Dispose() (or otherwise release it) after reading the description.

Copilot uses AI. Check for mistakes.

// D3D12 max texture size is 16384 (D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION)
maxTextureSize = 16384;

bool supportsEnhancedBarriers = info.SupportsEnhancedBarriers;
bool supportsMeshShaders = info.SupportsMeshShaders;
bool supportsVRS = info.SupportsVariableRateShading;
bool supportsRaytracing = info.SupportsRaytracing;

Logger.Log($@"Direct3D 12 Initialized
Direct3D 12 Adapter: {adapterDescription}
Direct3D 12 Dedicated Video Memory: {dedicatedVideoMemory} MB
Direct3D 12 Dedicated System Memory: {dedicatedSystemMemory} MB
Direct3D 12 Shared System Memory: {sharedSystemMemory} MB
Direct3D 12 Enhanced Barriers: {supportsEnhancedBarriers}
Direct3D 12 Mesh Shaders: {supportsMeshShaders}
Direct3D 12 Variable Rate Shading: {supportsVRS}
Direct3D 12 Raytracing: {supportsRaytracing}");
}

public static unsafe void LogOpenGL(this GraphicsDevice device, out int maxTextureSize)
{
var info = device.GetOpenGLInfo();
Expand Down
1 change: 1 addition & 0 deletions osu.Framework/Graphics/Veldrid/VeldridRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ internal IStagingBuffer<T> CreateStagingBuffer<T>(uint count)
switch (Device.BackendType)
{
case GraphicsBackend.Direct3D11:
case GraphicsBackend.Direct3D12:
case GraphicsBackend.Vulkan:
return new PersistentStagingBuffer<T>(this, count);

Expand Down
Loading
Loading