Veldrid submodule update, EGL front buffer auto-refresh, Android thread priority#5
Conversation
…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>
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>
…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>
…csSurface 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>
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>
…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>
…0005) 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>
… 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; |
There was a problem hiding this comment.
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.
| 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")) | ||
| { |
There was a problem hiding this comment.
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).
| if (deviceHandle == IntPtr.Zero) return; | ||
|
|
||
| lowLatencyProvider.Initialize(deviceHandle); | ||
| setLowLatencyMode(latencyMode.Value); |
There was a problem hiding this comment.
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.
| setLowLatencyMode(latencyMode.Value); | |
| setLowLatencyMode(latencyMode?.Value ?? LatencyMode.Off); |
| /// </summary> | ||
| internal void TryInitializeLowLatencyProvider() | ||
| { | ||
| if (lowLatencyInitialized || lowLatencyProvider is NoOpLowLatencyProvider) return; |
There was a problem hiding this comment.
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.
| if (lowLatencyInitialized || lowLatencyProvider is NoOpLowLatencyProvider) return; | |
| if (lowLatencyInitialized || !lowLatencyProvider.IsAvailable) return; |
| // 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; |
There was a problem hiding this comment.
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.
| 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; | ||
| } |
There was a problem hiding this comment.
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.
| [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.")] |
There was a problem hiding this comment.
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.
| /// <param name="provider">The <see cref="ILowLatencyProvider"/> to use.</param> | ||
| public void SetLowLatencyProvider(ILowLatencyProvider provider) | ||
| { | ||
| lowLatencyProvider = provider ?? NoOpLowLatencyProvider.INSTANCE; |
There was a problem hiding this comment.
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.
| lowLatencyProvider = provider ?? NoOpLowLatencyProvider.INSTANCE; | |
| var newProvider = provider ?? NoOpLowLatencyProvider.INSTANCE; | |
| if (!ReferenceEquals(lowLatencyProvider, newProvider)) | |
| { | |
| lowLatencyProvider = newProvider; | |
| lowLatencyInitialized = false; | |
| } |
| latencyMode.BindValueChanged(mode => setLowLatencyMode(mode.NewValue), true); | ||
|
|
There was a problem hiding this comment.
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.
| latencyMode.BindValueChanged(mode => setLowLatencyMode(mode.NewValue), true); | |
| latencyMode.BindValueChanged(mode => | |
| { | |
| setLowLatencyMode(mode.NewValue); | |
| updateFrameSyncMode(); | |
| }, true); | |
| Config.GetBindable<int>(FrameworkSetting.CustomDrawLimit).ValueChanged += _ => updateFrameSyncMode(); |
| 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. | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
- 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>
[UnconditionalSuppressMessage]to 37 framework+test files for iOS trim warnings.sln/.slnf— fixes InspectCode "Veldrid not found" spamBindableNumberTest.cs— IL2060/IL2070/IL2075/IL2091 (reflection onGetMethod/GetProperty/MakeGenericMethod/Activator.CreateInstance)BindableTest.cs— IL2070/IL2072/IL2075 (Activator.CreateInstance/GetPropertyreflection)TestIsNullableTypeExtensions.cs— IL2070 (GetFieldreflection)