diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index d0b8277da..4e71e8582 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -40,6 +40,7 @@ namespace Ryujinx.Graphics.Gpu /// GPU synchronization manager. /// public SynchronizationManager Synchronization { get; } + public IOverlayManager OverlayManager { get; } /// /// Presentation window. @@ -121,14 +122,18 @@ namespace Ryujinx.Graphics.Gpu /// Creates a new instance of the GPU emulation context. /// /// Host renderer - public GpuContext(IRenderer renderer, DirtyHacks hacks) + /// Enabled dirty hacks + /// Overlay manager for rendering overlays + public GpuContext(IRenderer renderer, DirtyHacks hacks, IOverlayManager overlayManager) { Renderer = renderer; GPFifo = new GPFifoDevice(this); 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(); } } } diff --git a/src/Ryujinx.Graphics.Gpu/IOverlay.cs b/src/Ryujinx.Graphics.Gpu/IOverlay.cs new file mode 100644 index 000000000..700b6330a --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/IOverlay.cs @@ -0,0 +1,54 @@ +using SkiaSharp; +using System; + +namespace Ryujinx.Graphics.Gpu +{ + /// + /// Interface for overlay functionality + /// + public interface IOverlay : IDisposable + { + /// + /// Name of the overlay + /// + string Name { get; set; } + + /// + /// Whether the overlay is visible + /// + bool IsVisible { get; set; } + + /// + /// Opacity of the overlay (0.0 to 1.0) + /// + float Opacity { get; set; } + + /// + /// X position of the overlay + /// + float X { get; set; } + + /// + /// Y position of the overlay + /// + float Y { get; set; } + + /// + /// Z-index for overlay ordering + /// + int ZIndex { get; set; } + + /// + /// Update overlay (for animations) + /// + /// Time elapsed since last update + /// Current screen size + void Update(float deltaTime, SKSize screenSize = default); + + /// + /// Render this overlay + /// + /// The canvas to render to + void Render(SKCanvas canvas); + } +} diff --git a/src/Ryujinx.Graphics.Gpu/IOverlayManager.cs b/src/Ryujinx.Graphics.Gpu/IOverlayManager.cs new file mode 100644 index 000000000..d3aeba260 --- /dev/null +++ b/src/Ryujinx.Graphics.Gpu/IOverlayManager.cs @@ -0,0 +1,30 @@ +using SkiaSharp; +using System; + +namespace Ryujinx.Graphics.Gpu +{ + /// + /// Interface for overlay management functionality + /// + public interface IOverlayManager : IDisposable + { + /// + /// Add an overlay to the manager + /// + /// The overlay to add + void AddOverlay(IOverlay overlay); + + /// + /// Update all overlays (for animations) + /// + /// Time elapsed since last update + /// Current screen size + void Update(float deltaTime, SKSize screenSize = default); + + /// + /// Render all visible overlays + /// + /// The canvas to render to + void Render(SKCanvas canvas); + } +} diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 7c023d5a8..6bcedd2b7 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -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; /// @@ -105,7 +103,6 @@ namespace Ryujinx.Graphics.Gpu public Window(GpuContext context) { _context = context; - _overlayManager = new OverlayManager(); _frameQueue = new ConcurrentQueue(); } @@ -258,9 +255,9 @@ namespace Ryujinx.Graphics.Gpu /// /// Add overlay to the overlay manager /// - public void AddOverlay(Overlay.Overlay overlay) + public void AddOverlay(IOverlay overlay) { - _overlayManager.AddOverlay(overlay); + _context.OverlayManager.AddOverlay(overlay); } /// @@ -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; } - - /// - /// Dispose resources - /// - public void Dispose() - { - _overlayManager?.Dispose(); - } } } diff --git a/src/Ryujinx.HLE/HleConfiguration.cs b/src/Ryujinx.HLE/HleConfiguration.cs index 10c2a1f30..85d0e3502 100644 --- a/src/Ryujinx.HLE/HleConfiguration.cs +++ b/src/Ryujinx.HLE/HleConfiguration.cs @@ -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 /// This cannot be changed after instantiation. internal IHostUIHandler HostUIHandler { get; private set; } + /// + /// The overlay manager to use for all overlay operations. + /// + /// This cannot be changed after instantiation. + internal IOverlayManager OverlayManager { get; private set; } + /// /// Control the memory configuration used by the emulation context. /// @@ -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; } } diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index bdcbe82c7..071d4125d 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -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); diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index f15d24e8a..ce1597fb4 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -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() ) ); } diff --git a/src/Ryujinx/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs index 756771de5..3c9c6f6b3 100644 --- a/src/Ryujinx/Systems/AppHost.cs +++ b/src/Ryujinx/Systems/AppHost.cs @@ -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); } diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs b/src/Ryujinx/UI/Overlay/ControllerOverlay.cs similarity index 88% rename from src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs rename to src/Ryujinx/UI/Overlay/ControllerOverlay.cs index 9cfbb869b..e8ea8a547 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/ControllerOverlay.cs +++ b/src/Ryujinx/UI/Overlay/ControllerOverlay.cs @@ -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 { - /// - /// Localization strings for the controller overlay - /// - 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"; - } - /// /// Controller overlay that shows controller bindings matching the original AXAML design /// @@ -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("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] }; } diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs b/src/Ryujinx/UI/Overlay/ImageElement.cs similarity index 99% rename from src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs rename to src/Ryujinx/UI/Overlay/ImageElement.cs index 2f2fb0811..7745d5333 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/ImageElement.cs +++ b/src/Ryujinx/UI/Overlay/ImageElement.cs @@ -2,7 +2,7 @@ using Ryujinx.Common.Logging; using SkiaSharp; using System; -namespace Ryujinx.Graphics.Gpu.Overlay +namespace Ryujinx.UI.Overlay { /// /// Image overlay element diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/Overlay.cs b/src/Ryujinx/UI/Overlay/Overlay.cs similarity index 96% rename from src/Ryujinx.Graphics.Gpu/Overlay/Overlay.cs rename to src/Ryujinx/UI/Overlay/Overlay.cs index a414888e1..34616f1bc 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/Overlay.cs +++ b/src/Ryujinx/UI/Overlay/Overlay.cs @@ -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 { /// /// Base overlay class containing multiple elements /// - public abstract class Overlay : IDisposable + public abstract class Overlay : IOverlay { private readonly List _elements = new(); diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/OverlayElement.cs b/src/Ryujinx/UI/Overlay/OverlayElement.cs similarity index 97% rename from src/Ryujinx.Graphics.Gpu/Overlay/OverlayElement.cs rename to src/Ryujinx/UI/Overlay/OverlayElement.cs index c4c528d65..2c5bf66fa 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/OverlayElement.cs +++ b/src/Ryujinx/UI/Overlay/OverlayElement.cs @@ -1,7 +1,7 @@ using SkiaSharp; using System; -namespace Ryujinx.Graphics.Gpu.Overlay +namespace Ryujinx.UI.Overlay { /// /// Base class for all overlay elements diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/OverlayManager.cs b/src/Ryujinx/UI/Overlay/OverlayManager.cs similarity index 92% rename from src/Ryujinx.Graphics.Gpu/Overlay/OverlayManager.cs rename to src/Ryujinx/UI/Overlay/OverlayManager.cs index fb0a4906f..d54098ef4 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/OverlayManager.cs +++ b/src/Ryujinx/UI/Overlay/OverlayManager.cs @@ -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 { /// /// Manages multiple overlays and handles rendering /// - public class OverlayManager : IDisposable + public class OverlayManager : IOverlayManager { - private readonly List _overlays = new(); + private readonly List _overlays = new(); private readonly object _lock = new(); /// /// Add an overlay to the manager /// - public void AddOverlay(Overlay overlay) + public void AddOverlay(IOverlay overlay) { lock (_lock) { @@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay /// /// Find overlay by name /// - public Overlay FindOverlay(string name) + public IOverlay FindOverlay(string name) { lock (_lock) { @@ -67,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Overlay /// /// Get all overlays /// - public IReadOnlyList GetOverlays() + public IReadOnlyList GetOverlays() { lock (_lock) { diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/RectangleElement.cs b/src/Ryujinx/UI/Overlay/RectangleElement.cs similarity index 98% rename from src/Ryujinx.Graphics.Gpu/Overlay/RectangleElement.cs rename to src/Ryujinx/UI/Overlay/RectangleElement.cs index b310ef2fc..a0ce52845 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/RectangleElement.cs +++ b/src/Ryujinx/UI/Overlay/RectangleElement.cs @@ -1,6 +1,6 @@ using SkiaSharp; -namespace Ryujinx.Graphics.Gpu.Overlay +namespace Ryujinx.UI.Overlay { /// /// Rectangle overlay element diff --git a/src/Ryujinx.Graphics.Gpu/Overlay/TextElement.cs b/src/Ryujinx/UI/Overlay/TextElement.cs similarity index 99% rename from src/Ryujinx.Graphics.Gpu/Overlay/TextElement.cs rename to src/Ryujinx/UI/Overlay/TextElement.cs index a1788fde6..b4c412fda 100644 --- a/src/Ryujinx.Graphics.Gpu/Overlay/TextElement.cs +++ b/src/Ryujinx/UI/Overlay/TextElement.cs @@ -1,6 +1,6 @@ using SkiaSharp; -namespace Ryujinx.Graphics.Gpu.Overlay +namespace Ryujinx.UI.Overlay { /// /// Text overlay element