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.
/// </summary>
public SynchronizationManager Synchronization { get; }
public IOverlayManager OverlayManager { get; }
/// <summary>
/// Presentation window.
@ -121,7 +122,9 @@ namespace Ryujinx.Graphics.Gpu
/// Creates a new instance of the GPU emulation context.
/// </summary>
/// <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;
@ -129,6 +132,8 @@ namespace Ryujinx.Graphics.Gpu
Synchronization = new SynchronizationManager();
OverlayManager = overlayManager;
Window = new Window(this);
HostInitalized = new ManualResetEvent(false);
@ -462,6 +467,8 @@ namespace Ryujinx.Graphics.Gpu
RunDeferredActions();
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.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Overlay;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range;
using SkiaSharp;
@ -20,7 +19,6 @@ namespace Ryujinx.Graphics.Gpu
public class Window
{
private readonly GpuContext _context;
private readonly OverlayManager _overlayManager;
private DateTime? _lastUpdateTime = null;
/// <summary>
@ -105,7 +103,6 @@ namespace Ryujinx.Graphics.Gpu
public Window(GpuContext context)
{
_context = context;
_overlayManager = new OverlayManager();
_frameQueue = new ConcurrentQueue<PresentationTexture>();
}
@ -258,9 +255,9 @@ namespace Ryujinx.Graphics.Gpu
/// <summary>
/// Add overlay to the overlay manager
/// </summary>
public void AddOverlay(Overlay.Overlay overlay)
public void AddOverlay(IOverlay overlay)
{
_overlayManager.AddOverlay(overlay);
_context.OverlayManager.AddOverlay(overlay);
}
/// <summary>
@ -276,7 +273,7 @@ namespace Ryujinx.Graphics.Gpu
{
// Calculate delta time for lifespan updates
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
@ -326,7 +323,7 @@ namespace Ryujinx.Graphics.Gpu
}
// Render all overlays
_overlayManager.Render(canvas);
_context.OverlayManager.Render(canvas);
// Copy modified bitmap data back to texture data array
var pixels = bitmap.Bytes;
@ -370,13 +367,5 @@ namespace Ryujinx.Graphics.Gpu
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.Multiplayer;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
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>
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>
/// Control the memory configuration used by the emulation context.
/// </summary>
@ -262,7 +269,8 @@ namespace Ryujinx.HLE
UserChannelPersistence userChannelPersistence,
IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver,
IHostUIHandler hostUIHandler
IHostUIHandler hostUIHandler,
IOverlayManager overlayManager
)
{
VirtualFileSystem = virtualFileSystem;
@ -273,6 +281,7 @@ namespace Ryujinx.HLE
GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver;
HostUIHandler = hostUIHandler;
OverlayManager = overlayManager;
return this;
}
}

View file

@ -71,7 +71,7 @@ namespace Ryujinx.HLE
DirtyHacks = new DirtyHacks(Configuration.Hacks);
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
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);
Statistics = new PerformanceStatistics(this);
Hid = new Hid(this, System.HidStorage);

View file

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

View file

@ -33,7 +33,7 @@ using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Overlay;
using Ryujinx.UI.Overlay;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE.FileSystem;
@ -931,20 +931,12 @@ namespace Ryujinx.Ava.Systems
_userChannelPersistence,
renderer.TryMakeThreaded(ConfigurationState.Instance.Graphics.BackendThreading),
InitializeAudio(),
_viewModel.UiHandler
_viewModel.UiHandler,
new OverlayManager()
)
);
// Initialize controller overlay with localization
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);
_controllerOverlay = new ControllerOverlay();
Device.Gpu.Window.AddOverlay(_controllerOverlay);
}

View file

@ -5,21 +5,10 @@ using System.Linq;
using OriginalInputConfig = Ryujinx.Common.Configuration.Hid.InputConfig;
using OriginalPlayerIndex = Ryujinx.Common.Configuration.Hid.PlayerIndex;
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>
/// Controller overlay that shows controller bindings matching the original AXAML design
/// </summary>
@ -35,11 +24,9 @@ namespace Ryujinx.Graphics.Gpu.Overlay
private const float PlayerTextSize = 22;
private float _lifespan = 0f;
private ControllerOverlayLocalization _localization;
public ControllerOverlay(ControllerOverlayLocalization localization) : base("ControllerOverlay")
public ControllerOverlay() : base("ControllerOverlay")
{
_localization = localization;
CreateBaseElements();
}
@ -57,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
AddElement(background);
// 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",
FontStyle = SKFontStyle.Bold
@ -74,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
var titleElement = FindElement<TextElement>("TitleText");
if (titleElement != null)
{
titleElement.Text = _localization.TitleText;
titleElement.Text = LocaleManager.Instance[LocaleKeys.ControllerOverlayTitle];
}
// Reset lifespan and opacity
@ -144,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
}
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}",
FontStyle = SKFontStyle.Italic
@ -188,9 +175,9 @@ namespace Ryujinx.Graphics.Gpu.Overlay
{
return config.Backend switch
{
OriginalInputBackendType.WindowKeyboard => _localization.KeyboardText,
OriginalInputBackendType.GamepadSDL2 => _localization.ControllerText,
_ => _localization.UnknownText
OriginalInputBackendType.WindowKeyboard => LocaleManager.Instance[LocaleKeys.ControllerOverlayKeyboard],
OriginalInputBackendType.GamepadSDL2 => LocaleManager.Instance[LocaleKeys.ControllerOverlayController],
_ => LocaleManager.Instance[LocaleKeys.ControllerOverlayUnknown]
};
}

View file

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

View file

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

View file

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

View file

@ -3,21 +3,22 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu;
namespace Ryujinx.Graphics.Gpu.Overlay
namespace Ryujinx.UI.Overlay
{
/// <summary>
/// Manages multiple overlays and handles rendering
/// </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();
/// <summary>
/// Add an overlay to the manager
/// </summary>
public void AddOverlay(Overlay overlay)
public void AddOverlay(IOverlay overlay)
{
lock (_lock)
{
@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
/// <summary>
/// Find overlay by name
/// </summary>
public Overlay FindOverlay(string name)
public IOverlay FindOverlay(string name)
{
lock (_lock)
{
@ -67,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay
/// <summary>
/// Get all overlays
/// </summary>
public IReadOnlyList<Overlay> GetOverlays()
public IReadOnlyList<IOverlay> GetOverlays()
{
lock (_lock)
{

View file

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

View file

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