Skip to content

Veldrid submodule update, EGL front buffer auto-refresh, Android thread priority#5

Merged
winnerspiros merged 12 commits into
masterfrom
copilot/optimize-veldrid-implementation
Apr 19, 2026
Merged

Veldrid submodule update, EGL front buffer auto-refresh, Android thread priority#5
winnerspiros merged 12 commits into
masterfrom
copilot/optimize-veldrid-implementation

Conversation

Copilot AI commented Apr 19, 2026

Copy link
Copy Markdown
  • Fix Code Quality errors (IDE0059, IDE0018, IDE1006, IDE0005)
  • Add [UnconditionalSuppressMessage] to 37 framework+test files for iOS trim warnings
  • Add Veldrid submodule projects to .sln/.slnf — fixes InspectCode "Veldrid not found" spam
  • Suppress ILLink IL2026/IL2045 in iOS props (System.Data internals + compiler-gen caches)
  • Suppress JAVAC Kotlin annotation warnings in Android props
  • Update JetBrains ReSharper 2025.2.3 → 2026.1.0.1
  • Pin CI InspectCode tool-version to 2026.1.0.1
  • NEW: Suppress remaining iOS trim warnings in test files
    • BindableNumberTest.cs — IL2060/IL2070/IL2075/IL2091 (reflection on GetMethod/GetProperty/MakeGenericMethod/Activator.CreateInstance)
    • BindableTest.cs — IL2070/IL2072/IL2075 (Activator.CreateInstance/GetProperty reflection)
    • TestIsNullableTypeExtensions.cs — IL2070 (GetField reflection)
  • Update README with full trim warning coverage list
  • Build: 0 warnings, 0 errors | Tests: 790 Bindable tests pass

Copilot AI and others added 2 commits April 19, 2026 13:49
…t Android thread priority

- Update Veldrid submodule to db70f1b (latest master) with OpenGL/ES pipeline
  state caching, Vulkan push descriptors/dynamic rendering/host image copy,
  D3D12 state caching, and cross-backend optimizations
- Enable EGL_ANDROID_front_buffer_auto_refresh on Android OpenGL ES surfaces
  for lower latency in unlocked frame rate scenarios
- Set THREAD_PRIORITY_DISPLAY on Android main thread for scheduler
  prioritization of rendering work

Agent-Logs-Url: https://github.com/winnerspiros/osu-framework/sessions/815620e0-8e94-42d7-843c-d0fede9aa35b

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Copilot AI and others added 2 commits April 19, 2026 14:19
…x EGL P/Invoke

- Fix CS0234 namespace conflict: use global::Android.OS.Process in
  AndroidGameHost to avoid shadowing by osu.Framework.Android namespace
- Update JetBrains/ReSharper-InspectCode from v0.11 to v0.12 to resolve
  Node.js 20 deprecation warning from CodeQL upload-sarif action
- Fix EGL P/Invoke: use int return type for eglSurfaceAttrib instead of
  MarshalAs(UnmanagedType.Bool) for correct EGLBoolean marshaling

Agent-Logs-Url: https://github.com/winnerspiros/osu-framework/sessions/e6f5840f-ed7b-4a44-81f7-cd6e01448fa7

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Copilot AI and others added 3 commits April 19, 2026 14:38
…VRR, raw keyboard, GridContainer optimization

- Add D3D12 as selectable renderer (RendererType.Direct3D12, GraphicsSurfaceType.Direct3D12)
- Add generalized ILowLatencyProvider (supports D3D11+D3D12 backends)
- Add latency markers in UpdateFrame/DrawFrame for NVIDIA Reflex integration
- Add Unbuffered VSync (UVSync) and Custom FPS limiter modes
- Enable SDL_HINT_WINDOWS_RAW_KEYBOARD for lower input latency
- Handle keyboard events in HandleEventFromFilter (async, bypasses SDL queue)
- GridContainer: use RequiredParentSizeToFit instead of BoundingBox (Issue#3215)

Agent-Logs-Url: https://github.com/winnerspiros/osu-framework/sessions/06cb33be-062c-41d2-8031-0c8ce5e1740a

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
…eatures

- FrameSync.Custom: add [Description("Custom")]
- RendererType.Deferred_Direct3D12: fix description to "(Experimental)"
- README: Document D3D12, low-latency provider, VRR, raw keyboard, GridContainer optimization

Agent-Logs-Url: https://github.com/winnerspiros/osu-framework/sessions/06cb33be-062c-41d2-8031-0c8ce5e1740a

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Copilot AI and others added 3 commits April 19, 2026 15:41
… attributes

Add trim analysis suppression attributes to 14 files across the framework
to resolve IL2026, IL2045, IL2067, IL2070, IL2072, IL2075, and IL2090
warnings that appear during iOS builds with trimming enabled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
…otation warnings

- Add [UnconditionalSuppressMessage] to 14 files for IL2026/IL2045/IL2067/IL2070/IL2072/IL2075/IL2090
- Add [DynamicallyAccessedMembers] to ReflectionUtils.GetMethodsWithAttribute
- Suppress IL2104 third-party assembly warnings in iOS props
- Suppress JAVAC Kotlin annotation warnings via -Xlint:-classfile in Android props

Agent-Logs-Url: https://github.com/winnerspiros/osu-framework/sessions/e224c5c0-75ea-4bf7-9d76-3f3872ce5cc5

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>

bool supportsEnhancedBarriers = info.SupportsEnhancedBarriers;
bool supportsMeshShaders = info.SupportsMeshShaders;
bool supportsVRS = info.SupportsVariableRateShading;
@winnerspiros winnerspiros marked this pull request as ready for review April 19, 2026 16:00
Copilot AI review requested due to automatic review settings April 19, 2026 16:00
@gitar-bot

gitar-bot Bot commented Apr 19, 2026

Copy link
Copy Markdown

Gitar is working

Gitar

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates rendering and latency-related infrastructure across the framework: adding Direct3D12 renderer/surface support, introducing a generic low-latency provider API with frame markers, and applying several platform-specific latency improvements (Android thread priority, SDL keyboard handling, Android EGL front-buffer refresh), alongside trimming-warning suppression and a few config/runtime tweaks.

Changes:

  • Add Direct3D12 support end-to-end (renderer selection, surface mapping, device creation, diagnostics, staging buffers).
  • Introduce low-latency provider plumbing (ILowLatencyProvider, markers, config setting, host integration + init path).
  • Add frame limiter enhancements (UVSync + Custom draw limit), Android/SDL input/latency tweaks, and trimming/build warning suppressions.

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
osu.Framework/Threading/DrawThread.cs Calls low-latency provider init after first renderer frame on draw thread.
osu.Framework/Testing/TestBrowser.cs Adds trimming warning suppressions for reflection-heavy test loading paths.
osu.Framework/Platform/Storage.cs Suppresses trimming warnings for Activator.CreateInstance() on storage subclasses.
osu.Framework/Platform/SDL3/SDL3Window.cs Enables raw keyboard hint on Windows; handles key events in event filter when IME inactive.
osu.Framework/Platform/SDL3/SDL3GraphicsSurface.cs Adds Android EGL front-buffer auto-refresh enablement during GL init; D3D12 surface type accepted.
osu.Framework/Platform/SDL2/SDL2GraphicsSurface.cs Suppresses trimming warnings for OpenGL binding reflection.
osu.Framework/Platform/GraphicsSurfaceType.cs Adds Direct3D12 surface type.
osu.Framework/Platform/GameHost.cs Integrates low-latency provider lifecycle + markers; adds D3D12 renderer preference; adds UVSync/Custom limiter logic; adds new config bindables.
osu.Framework/IO/Serialization/SortedListJsonConverter.cs Suppresses trimming warnings around activator usage during deserialization.
osu.Framework/Graphics/Visualisation/PropertyDisplay.cs Suppresses trimming warnings for reflection-based debug property display.
osu.Framework/Graphics/Veldrid/Vertices/VeldridVertexUtils.cs Suppresses trimming warnings for reflection-based vertex layout discovery.
osu.Framework/Graphics/Veldrid/VeldridRenderer.cs Treats D3D12 like D3D11/Vulkan for staging buffer selection.
osu.Framework/Graphics/Veldrid/VeldridExtensions.cs Adds LogD3D12() diagnostic logging.
osu.Framework/Graphics/Veldrid/VeldridDevice.cs Adds D3D12 device creation + logging path.
osu.Framework/Graphics/Transforms/TransformCustom.cs Suppresses trimming warnings for reflection-based accessor resolution.
osu.Framework/Graphics/Shaders/ShaderCompilationStore.cs Suppresses trimming warnings around cache (de)serialization types.
osu.Framework/Graphics/Rendering/Renderer.cs Suppresses trimming warnings for uniform layout validation.
osu.Framework/Graphics/Rendering/LowLatency/NoOpLowLatencyProvider.cs Adds default no-op implementation for ILowLatencyProvider.
osu.Framework/Graphics/Rendering/LowLatency/NoOpDirect3D11LowLatencyProvider.cs Adds D3D11-specific no-op provider type.
osu.Framework/Graphics/Rendering/LowLatency/LatencyMode.cs Adds latency mode enum.
osu.Framework/Graphics/Rendering/LowLatency/LatencyMarker.cs Adds standardized latency marker enum.
osu.Framework/Graphics/Rendering/LowLatency/ILowLatencyProvider.cs Introduces generic low-latency provider interface.
osu.Framework/Graphics/Rendering/LowLatency/IDirect3D11LowLatencyProvider.cs Adds D3D11-specific marker interface extending ILowLatencyProvider.
osu.Framework/Graphics/Containers/GridContainer.cs Uses RequiredParentSizeToFit for autosize measurements (avoid redundant transforms).
osu.Framework/Extensions/OSPlatformExtensions.cs Suppresses trimming warnings for platform-attribute reflection checks.
osu.Framework/Development/ReflectionUtils.cs Adds DynamicallyAccessedMembers annotation for reflection safety under trimming.
osu.Framework/Configuration/TypedRepopulatingConverter.cs Suppresses trimming warnings for config JSON serialization.
osu.Framework/Configuration/RendererType.cs Adds D3D12 renderer enum values (including deferred).
osu.Framework/Configuration/FrameworkConfigManager.cs Adds defaults/settings entries for LatencyMode and CustomDrawLimit.
osu.Framework/Configuration/FrameSync.cs Adds UVSync and Custom limiter modes.
osu.Framework/Allocation/ResolvedAttribute.cs Suppresses trimming warnings for DI/reflection activator generation.
osu.Framework.iOS.props Suppresses IL2104 trim warnings from unannotatable third-party assemblies.
osu.Framework.Android/AndroidGameHost.cs Sets Android main thread to display priority during setup.
osu.Framework.Android.props Adds -Xlint:-classfile to suppress benign javac warnings.
README.md Documents new D3D12, low-latency, limiter, input, and GridContainer changes.
.github/workflows/ci.yml Updates ReSharper InspectCode GitHub Action version.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +114 to +138
const int egl_extensions = 3;
const int egl_true = 1;
const int egl_front_buffer_auto_refresh_android = 0x314C;

try
{
IntPtr eglDisplay = SDL_EGL_GetCurrentDisplay();
IntPtr eglSurface = SDL_EGL_GetWindowSurface(window.SDLWindowHandle);

if (eglDisplay == IntPtr.Zero || eglSurface == IntPtr.Zero)
{
Logger.Log("EGL front buffer auto-refresh: could not obtain EGL display/surface.", LoggingTarget.Runtime, LogLevel.Debug);
return;
}

// Check extension availability before calling eglSurfaceAttrib.
IntPtr extensionsPtr = eglQueryString(eglDisplay, egl_extensions);

if (extensionsPtr == IntPtr.Zero)
return;

string? extensions = Marshal.PtrToStringAnsi(extensionsPtr);

if (extensions == null || !extensions.Contains("EGL_ANDROID_front_buffer_auto_refresh"))
{

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.

eglQueryString()'s name parameter is using egl_extensions = 3, but EGL_EXTENSIONS is 0x3055. With the current value the extension query will likely fail or return unrelated data, so the feature will never enable. Use the correct constant (and consider StringComparison.Ordinal for the Contains() check).

Copilot uses AI. Check for mistakes.
if (deviceHandle == IntPtr.Zero) return;

lowLatencyProvider.Initialize(deviceHandle);
setLowLatencyMode(latencyMode.Value);

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.

TryInitializeLowLatencyProvider() assumes latencyMode has already been set up (latencyMode.Value is accessed). Because SetLowLatencyProvider() is public, it can be called before SetupConfig(), which would make latencyMode null and cause a NullReferenceException during init. Guard for latencyMode == null (or default to LatencyMode.Off) before using it.

Suggested change
setLowLatencyMode(latencyMode.Value);
setLowLatencyMode(latencyMode?.Value ?? LatencyMode.Off);

Copilot uses AI. Check for mistakes.
/// </summary>
internal void TryInitializeLowLatencyProvider()
{
if (lowLatencyInitialized || lowLatencyProvider is NoOpLowLatencyProvider) return;

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.

TryInitializeLowLatencyProvider() only skips initialisation for NoOpLowLatencyProvider, but the interface already exposes IsAvailable. Providers that report IsAvailable == false (including the shipped no-op variants) will still be initialised and polled every frame. Prefer gating init and per-frame calls on IsAvailable/lowLatencyInitialized to avoid unnecessary work and to make the API safer for consumers.

Suggested change
if (lowLatencyInitialized || lowLatencyProvider is NoOpLowLatencyProvider) return;
if (lowLatencyInitialized || !lowLatencyProvider.IsAvailable) return;

Copilot uses AI. Check for mistakes.
Comment on lines +325 to +332
// When text input is disabled, handle keyboard events asynchronously in the event filter
// to bypass the SDL event queue for lower latency (matches raw keyboard input path).
case SDL_EventType.SDL_EVENT_KEY_DOWN:
case SDL_EventType.SDL_EVENT_KEY_UP:
if (!SDL_TextInputActive(SDLWindowHandle))
{
handleKeyboardEvent(e.key);
return false;

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.

The comment says keyboard events are handled "asynchronously" in the event filter, but the code invokes handleKeyboardEvent() synchronously before returning. Update the comment to match the actual behavior (handled immediately / bypassing the queue) to avoid confusion.

Copilot uses AI. Check for mistakes.
Comment on lines +382 to +389
if (dxgiFactory.EnumAdapters(0, out IDXGIAdapter? 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;
}

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.
Comment on lines +534 to +535
[UnconditionalSuppressMessage("Trimming", "IL2072:Target parameter of method", Justification = "Test source types are preserved by the test framework.")]
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Test source types are preserved by the test framework.")]

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.

The UnconditionalSuppressMessage checkId should be just the warning ID (e.g. IL2072). Including extra text ("IL2072:Target parameter of method") may prevent the suppression from matching, leaving the trimming warning unsuppressed.

Copilot uses AI. Check for mistakes.
/// <param name="provider">The <see cref="ILowLatencyProvider"/> to use.</param>
public void SetLowLatencyProvider(ILowLatencyProvider provider)
{
lowLatencyProvider = provider ?? NoOpLowLatencyProvider.INSTANCE;

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.

SetLowLatencyProvider() doesn't reset lowLatencyInitialized. If a provider is swapped at runtime, the new provider will never be initialised because TryInitializeLowLatencyProvider() will early-return, and the old lowLatencyInitialized value can also keep the low-latency draw limiter active after removing a provider. Reset lowLatencyInitialized (and any related state) when changing providers.

Suggested change
lowLatencyProvider = provider ?? NoOpLowLatencyProvider.INSTANCE;
var newProvider = provider ?? NoOpLowLatencyProvider.INSTANCE;
if (!ReferenceEquals(lowLatencyProvider, newProvider))
{
lowLatencyProvider = newProvider;
lowLatencyInitialized = false;
}

Copilot uses AI. Check for mistakes.
Comment on lines +1293 to +1294
latencyMode.BindValueChanged(mode => setLowLatencyMode(mode.NewValue), true);

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.

The new settings (LatencyMode and CustomDrawLimit) won't currently trigger updateFrameSyncMode() when they change. That means switching latency mode (On/Off) or adjusting the custom draw limit can leave MaximumDrawHz/MaximumUpdateHz stale until some unrelated event (display change / FrameSync change) occurs. Bind both FrameworkSetting.LatencyMode and FrameworkSetting.CustomDrawLimit to call updateFrameSyncMode() when their values change.

Suggested change
latencyMode.BindValueChanged(mode => setLowLatencyMode(mode.NewValue), true);
latencyMode.BindValueChanged(mode =>
{
setLowLatencyMode(mode.NewValue);
updateFrameSyncMode();
}, true);
Config.GetBindable<int>(FrameworkSetting.CustomDrawLimit).ValueChanged += _ => updateFrameSyncMode();

Copilot uses AI. Check for mistakes.
Comment on lines +1522 to +1533
internal void LatencyMark(LatencyMarker marker, ulong frameId)
{
try
{
lowLatencyProvider.SetMarker(marker, frameId);
}
catch (Exception)
{
// WARNING: Do not log anything here or otherwise catch the error.
// This method is called extremely frequently (multiple times per frame) and doing so could cause massive performance degradation.
}
}

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.

LatencyMark() is called multiple times per frame even when no provider is active, but it always enters a try/catch and calls into the provider. This adds avoidable overhead to the hottest path. Consider an early return when low-latency isn't initialised/available (e.g. if (!lowLatencyInitialized) return;) and only keep exception handling for the active-provider case.

Copilot uses AI. Check for mistakes.
- Add [UnconditionalSuppressMessage] to 23 more framework/test files for iOS trim warnings
- Fix IDE0005 errors from unnecessary using aliases in CompositeDrawable.cs and NativeStorage.cs
- Add Veldrid submodule projects to .sln and .slnf to fix InspectCode spam
- Suppress ILLink IL2026/IL2045 warnings in iOS props
- Suppress JAVAC Kotlin annotation warnings in Android props
- Update JetBrains ReSharper GlobalTools 2025.2.3 -> 2026.1.0.1
- Pin CI InspectCode tool-version to 2026.1.0.1

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Add [UnconditionalSuppressMessage] for IL2060/IL2070/IL2072/IL2075/IL2091
to BindableNumberTest, BindableTest, and TestIsNullableTypeExtensions.

These are all test-code reflection patterns (GetMethod, GetProperty,
GetField, MakeGenericMethod, Activator.CreateInstance) that operate on
types known at compile time and always preserved at runtime.

Agent-Logs-Url: https://github.com/winnerspiros/osu-framework/sessions/def3cfac-8372-413a-a8a7-0c10a1b771e9

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
@winnerspiros winnerspiros merged commit 394ecf5 into master Apr 19, 2026
22 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants