Overlays: Move the structure to Ryujinx/UI/Overlay (also no longer need to cross-pass locales)

This commit is contained in:
Barış Hamil 2025-06-22 01:09:14 +03:00 committed by GreemDev
parent 076dd9a56a
commit d6232008d5
15 changed files with 138 additions and 66 deletions

View file

@ -40,6 +40,7 @@ namespace Ryujinx.Graphics.Gpu
/// GPU synchronization manager. /// GPU synchronization manager.
/// </summary> /// </summary>
public SynchronizationManager Synchronization { get; } public SynchronizationManager Synchronization { get; }
public IOverlayManager OverlayManager { get; }
/// <summary> /// <summary>
/// Presentation window. /// Presentation window.
@ -121,14 +122,18 @@ namespace Ryujinx.Graphics.Gpu
/// Creates a new instance of the GPU emulation context. /// Creates a new instance of the GPU emulation context.
/// </summary> /// </summary>
/// <param name="renderer">Host renderer</param> /// <param name="renderer">Host renderer</param>
public GpuContext(IRenderer renderer, DirtyHacks hacks) /// <param name="hacks">Enabled dirty hacks</param>
/// <param name="overlayManager">Overlay manager for rendering overlays</param>
public GpuContext(IRenderer renderer, DirtyHacks hacks, IOverlayManager overlayManager)
{ {
Renderer = renderer; Renderer = renderer;
GPFifo = new GPFifoDevice(this); GPFifo = new GPFifoDevice(this);
Synchronization = new SynchronizationManager(); Synchronization = new SynchronizationManager();
OverlayManager = overlayManager;
Window = new Window(this); Window = new Window(this);
HostInitalized = new ManualResetEvent(false); HostInitalized = new ManualResetEvent(false);
@ -462,6 +467,8 @@ namespace Ryujinx.Graphics.Gpu
RunDeferredActions(); RunDeferredActions();
Renderer.Dispose(); Renderer.Dispose();
OverlayManager.Dispose();
} }
} }
} }

View file

@ -0,0 +1,54 @@
using SkiaSharp;
using System;
namespace Ryujinx.Graphics.Gpu
{
/// <summary>
/// Interface for overlay functionality
/// </summary>
public interface IOverlay : IDisposable
{
/// <summary>
/// Name of the overlay
/// </summary>
string Name { get; set; }
/// <summary>
/// Whether the overlay is visible
/// </summary>
bool IsVisible { get; set; }
/// <summary>
/// Opacity of the overlay (0.0 to 1.0)
/// </summary>
float Opacity { get; set; }
/// <summary>
/// X position of the overlay
/// </summary>
float X { get; set; }
/// <summary>
/// Y position of the overlay
/// </summary>
float Y { get; set; }
/// <summary>
/// Z-index for overlay ordering
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// Update overlay (for animations)
/// </summary>
/// <param name="deltaTime">Time elapsed since last update</param>
/// <param name="screenSize">Current screen size</param>
void Update(float deltaTime, SKSize screenSize = default);
/// <summary>
/// Render this overlay
/// </summary>
/// <param name="canvas">The canvas to render to</param>
void Render(SKCanvas canvas);
}
}

View file

@ -0,0 +1,30 @@
using SkiaSharp;
using System;
namespace Ryujinx.Graphics.Gpu
{
/// <summary>
/// Interface for overlay management functionality
/// </summary>
public interface IOverlayManager : IDisposable
{
/// <summary>
/// Add an overlay to the manager
/// </summary>
/// <param name="overlay">The overlay to add</param>
void AddOverlay(IOverlay overlay);
/// <summary>
/// Update all overlays (for animations)
/// </summary>
/// <param name="deltaTime">Time elapsed since last update</param>
/// <param name="screenSize">Current screen size</param>
void Update(float deltaTime, SKSize screenSize = default);
/// <summary>
/// Render all visible overlays
/// </summary>
/// <param name="canvas">The canvas to render to</param>
void Render(SKCanvas canvas);
}
}

View file

@ -3,7 +3,6 @@ using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Overlay;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using SkiaSharp; using SkiaSharp;
@ -20,7 +19,6 @@ namespace Ryujinx.Graphics.Gpu
public class Window public class Window
{ {
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly OverlayManager _overlayManager;
private DateTime? _lastUpdateTime = null; private DateTime? _lastUpdateTime = null;
/// <summary> /// <summary>
@ -105,7 +103,6 @@ namespace Ryujinx.Graphics.Gpu
public Window(GpuContext context) public Window(GpuContext context)
{ {
_context = context; _context = context;
_overlayManager = new OverlayManager();
_frameQueue = new ConcurrentQueue<PresentationTexture>(); _frameQueue = new ConcurrentQueue<PresentationTexture>();
} }
@ -258,9 +255,9 @@ namespace Ryujinx.Graphics.Gpu
/// <summary> /// <summary>
/// Add overlay to the overlay manager /// Add overlay to the overlay manager
/// </summary> /// </summary>
public void AddOverlay(Overlay.Overlay overlay) public void AddOverlay(IOverlay overlay)
{ {
_overlayManager.AddOverlay(overlay); _context.OverlayManager.AddOverlay(overlay);
} }
/// <summary> /// <summary>
@ -276,7 +273,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
// Calculate delta time for lifespan updates // Calculate delta time for lifespan updates
float deltaTime = (float)(currentTime - _lastUpdateTime.Value).TotalSeconds; float deltaTime = (float)(currentTime - _lastUpdateTime.Value).TotalSeconds;
_overlayManager.Update(deltaTime, new SKSize(texture.Info.Width, texture.Info.Height)); _context.OverlayManager.Update(deltaTime, new SKSize(texture.Info.Width, texture.Info.Height));
} }
// Update overlay animations // Update overlay animations
@ -326,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu
} }
// Render all overlays // Render all overlays
_overlayManager.Render(canvas); _context.OverlayManager.Render(canvas);
// Copy modified bitmap data back to texture data array // Copy modified bitmap data back to texture data array
var pixels = bitmap.Bytes; var pixels = bitmap.Bytes;
@ -370,13 +367,5 @@ namespace Ryujinx.Graphics.Gpu
return false; return false;
} }
/// <summary>
/// Dispose resources
/// </summary>
public void Dispose()
{
_overlayManager?.Dispose();
}
} }
} }

View file

@ -3,6 +3,7 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
@ -65,6 +66,12 @@ namespace Ryujinx.HLE
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal IHostUIHandler HostUIHandler { get; private set; } internal IHostUIHandler HostUIHandler { get; private set; }
/// <summary>
/// The overlay manager to use for all overlay operations.
/// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal IOverlayManager OverlayManager { get; private set; }
/// <summary> /// <summary>
/// Control the memory configuration used by the emulation context. /// Control the memory configuration used by the emulation context.
/// </summary> /// </summary>
@ -262,7 +269,8 @@ namespace Ryujinx.HLE
UserChannelPersistence userChannelPersistence, UserChannelPersistence userChannelPersistence,
IRenderer gpuRenderer, IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver, IHardwareDeviceDriver audioDeviceDriver,
IHostUIHandler hostUIHandler IHostUIHandler hostUIHandler,
IOverlayManager overlayManager
) )
{ {
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
@ -273,6 +281,7 @@ namespace Ryujinx.HLE
GpuRenderer = gpuRenderer; GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver; AudioDeviceDriver = audioDeviceDriver;
HostUIHandler = hostUIHandler; HostUIHandler = hostUIHandler;
OverlayManager = overlayManager;
return this; return this;
} }
} }

View file

@ -71,7 +71,7 @@ namespace Ryujinx.HLE
DirtyHacks = new DirtyHacks(Configuration.Hacks); DirtyHacks = new DirtyHacks(Configuration.Hacks);
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks, Configuration.OverlayManager);
System = new HOS.Horizon(this); System = new HOS.Horizon(this);
Statistics = new PerformanceStatistics(this); Statistics = new PerformanceStatistics(this);
Hid = new Hid(this, System.HidStorage); Hid = new Hid(this, System.HidStorage);

View file

@ -17,6 +17,7 @@ using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.UI.Overlay;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.IO; using System.IO;
@ -348,7 +349,8 @@ namespace Ryujinx.Headless
_userChannelPersistence, _userChannelPersistence,
renderer.TryMakeThreaded(options.BackendThreading), renderer.TryMakeThreaded(options.BackendThreading),
new SDL2HardwareDeviceDriver(), new SDL2HardwareDeviceDriver(),
window window,
new OverlayManager()
) )
); );
} }

View file

@ -33,7 +33,7 @@ using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Overlay; using Ryujinx.UI.Overlay;
using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@ -931,20 +931,12 @@ namespace Ryujinx.Ava.Systems
_userChannelPersistence, _userChannelPersistence,
renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading), renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading),
InitializeAudio(), InitializeAudio(),
_viewModel.UiHandler _viewModel.UiHandler,
new OverlayManager()
) )
); );
// Initialize controller overlay with localization _controllerOverlay = new ControllerOverlay();
var localization = new ControllerOverlayLocalization
{
TitleText = LocaleManager.Instance[LocaleKeys.ControllerOverlayTitle],
NoControllerText = LocaleManager.Instance[LocaleKeys.ControllerOverlayNoController],
KeyboardText = LocaleManager.Instance[LocaleKeys.ControllerOverlayKeyboard],
ControllerText = LocaleManager.Instance[LocaleKeys.ControllerOverlayController],
UnknownText = LocaleManager.Instance[LocaleKeys.ControllerOverlayUnknown]
};
_controllerOverlay = new ControllerOverlay(localization);
Device.Gpu.Window.AddOverlay(_controllerOverlay); Device.Gpu.Window.AddOverlay(_controllerOverlay);
} }

View file

@ -5,21 +5,10 @@ using System.Linq;
using OriginalInputConfig = Ryujinx.Common.Configuration.Hid.InputConfig; using OriginalInputConfig = Ryujinx.Common.Configuration.Hid.InputConfig;
using OriginalPlayerIndex = Ryujinx.Common.Configuration.Hid.PlayerIndex; using OriginalPlayerIndex = Ryujinx.Common.Configuration.Hid.PlayerIndex;
using OriginalInputBackendType = Ryujinx.Common.Configuration.Hid.InputBackendType; using OriginalInputBackendType = Ryujinx.Common.Configuration.Hid.InputBackendType;
using Ryujinx.Ava.Common.Locale;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary>
/// Localization strings for the controller overlay
/// </summary>
public class ControllerOverlayLocalization
{
public string TitleText { get; set; } = "Controller Bindings";
public string NoControllerText { get; set; } = "No controller assigned";
public string KeyboardText { get; set; } = "Keyboard";
public string ControllerText { get; set; } = "Controller";
public string UnknownText { get; set; } = "Unknown";
}
/// <summary> /// <summary>
/// Controller overlay that shows controller bindings matching the original AXAML design /// Controller overlay that shows controller bindings matching the original AXAML design
/// </summary> /// </summary>
@ -35,11 +24,9 @@ namespace Ryujinx.Graphics.Gpu.Overlay
private const float PlayerTextSize = 22; private const float PlayerTextSize = 22;
private float _lifespan = 0f; private float _lifespan = 0f;
private ControllerOverlayLocalization _localization;
public ControllerOverlay(ControllerOverlayLocalization localization) : base("ControllerOverlay") public ControllerOverlay() : base("ControllerOverlay")
{ {
_localization = localization;
CreateBaseElements(); CreateBaseElements();
} }
@ -57,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
AddElement(background); AddElement(background);
// Title text (will be updated with localized text) // Title text (will be updated with localized text)
var titleText = new TextElement(Padding + 30, Padding, _localization.TitleText, TitleTextSize, SKColors.White) var titleText = new TextElement(Padding + 30, Padding, LocaleManager.Instance[LocaleKeys.ControllerOverlayTitle], TitleTextSize, SKColors.White)
{ {
Name = "TitleText", Name = "TitleText",
FontStyle = SKFontStyle.Bold FontStyle = SKFontStyle.Bold
@ -74,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
var titleElement = FindElement<TextElement>("TitleText"); var titleElement = FindElement<TextElement>("TitleText");
if (titleElement != null) if (titleElement != null)
{ {
titleElement.Text = _localization.TitleText; titleElement.Text = LocaleManager.Instance[LocaleKeys.ControllerOverlayTitle];
} }
// Reset lifespan and opacity // Reset lifespan and opacity
@ -144,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
} }
else else
{ {
var noControllerTextElement = new TextElement(Padding + 56, rowY + 2, _localization.NoControllerText, PlayerTextSize, new SKColor(128, 128, 128)) // Gray var noControllerTextElement = new TextElement(Padding + 56, rowY + 2, LocaleManager.Instance[LocaleKeys.ControllerOverlayNoController], PlayerTextSize, new SKColor(128, 128, 128)) // Gray
{ {
Name = $"NoControllerText_{i}", Name = $"NoControllerText_{i}",
FontStyle = SKFontStyle.Italic FontStyle = SKFontStyle.Italic
@ -188,9 +175,9 @@ namespace Ryujinx.Graphics.Gpu.Overlay
{ {
return config.Backend switch return config.Backend switch
{ {
OriginalInputBackendType.WindowKeyboard => _localization.KeyboardText, OriginalInputBackendType.WindowKeyboard => LocaleManager.Instance[LocaleKeys.ControllerOverlayKeyboard],
OriginalInputBackendType.GamepadSDL2 => _localization.ControllerText, OriginalInputBackendType.GamepadSDL2 => LocaleManager.Instance[LocaleKeys.ControllerOverlayController],
_ => _localization.UnknownText _ => LocaleManager.Instance[LocaleKeys.ControllerOverlayUnknown]
}; };
} }

View file

@ -2,7 +2,7 @@ using Ryujinx.Common.Logging;
using SkiaSharp; using SkiaSharp;
using System; using System;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary> /// <summary>
/// Image overlay element /// Image overlay element

View file

@ -2,13 +2,14 @@ using SkiaSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Ryujinx.Graphics.Gpu;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary> /// <summary>
/// Base overlay class containing multiple elements /// Base overlay class containing multiple elements
/// </summary> /// </summary>
public abstract class Overlay : IDisposable public abstract class Overlay : IOverlay
{ {
private readonly List<OverlayElement> _elements = new(); private readonly List<OverlayElement> _elements = new();

View file

@ -1,7 +1,7 @@
using SkiaSharp; using SkiaSharp;
using System; using System;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary> /// <summary>
/// Base class for all overlay elements /// Base class for all overlay elements

View file

@ -3,21 +3,22 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary> /// <summary>
/// Manages multiple overlays and handles rendering /// Manages multiple overlays and handles rendering
/// </summary> /// </summary>
public class OverlayManager : IDisposable public class OverlayManager : IOverlayManager
{ {
private readonly List<Overlay> _overlays = new(); private readonly List<IOverlay> _overlays = new();
private readonly object _lock = new(); private readonly object _lock = new();
/// <summary> /// <summary>
/// Add an overlay to the manager /// Add an overlay to the manager
/// </summary> /// </summary>
public void AddOverlay(Overlay overlay) public void AddOverlay(IOverlay overlay)
{ {
lock (_lock) lock (_lock)
{ {
@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
/// <summary> /// <summary>
/// Find overlay by name /// Find overlay by name
/// </summary> /// </summary>
public Overlay FindOverlay(string name) public IOverlay FindOverlay(string name)
{ {
lock (_lock) lock (_lock)
{ {
@ -67,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
/// <summary> /// <summary>
/// Get all overlays /// Get all overlays
/// </summary> /// </summary>
public IReadOnlyList<Overlay> GetOverlays() public IReadOnlyList<IOverlay> GetOverlays()
{ {
lock (_lock) lock (_lock)
{ {

View file

@ -1,6 +1,6 @@
using SkiaSharp; using SkiaSharp;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary> /// <summary>
/// Rectangle overlay element /// Rectangle overlay element

View file

@ -1,6 +1,6 @@
using SkiaSharp; using SkiaSharp;
namespace Ryujinx.Graphics.Gpu.Overlay namespace Ryujinx.UI.Overlay
{ {
/// <summary> /// <summary>
/// Text overlay element /// Text overlay element