Compare commits

..

10 Commits

Author SHA1 Message Date
gnisman
dde208b480 UI: Expose games build ID for cheat management (#4340)
* Ava UI: Expose games build ID for cheat management

* Fix bad merge

* Change integrity check level to error on invalid

* Add support for GDK

* Remove whitespace

* Add BID identifier

* PR Comments fix

* Restore title id in cheats GTK window

* use halign center instead of margin_left

* Merge

* fix after merge

* PR comments fix - design AVA

* PR fix - Move GetApplicationBuildId to ApplicationData class

* PR comment fix - Add empty line before method

* Align with PR #4755

* PR comments fix

* Change BuildId label to support translation

* Comments fix

* Remove unused BuildIdLabel property
2023-05-07 14:36:44 +00:00
Nico
4c3d2d5d75 UI: Add progress bar for re-packaging shaders (#4805)
* feat: introduce new shader loading state for progress tracking when writing shaders to disk

* fix: move translation to bottom of locale file

* fix: change back to foreach and add requested spacing between lines

* style: fix formatting

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-05-06 15:35:46 +02:00
Ac_K
fab11ba3f1 AM: Stub some service calls (#4825)
* AM: Stub some service call

Some IPC I have stubbed during private testing and I don't want to deal with them anymore. Nothing more.

* ICommonStateGetter disposable
2023-05-06 03:33:50 +02:00
riperiperi
332891b5ff Use correct offset for storage constant buffer elimination (#4821) 2023-05-05 23:59:36 +02:00
riperiperi
7df4fcada7 GPU: Remove CPU region handle containers (#4817)
* GPU: Remove CPU region handle containers.

Another one for the "I don't know why I didn't do this earlier" pile.

This removes the "Cpu" prefixed region handle classes, which each mirror a region handle type from Ryujinx.Memory.

Originally, not all projects had a reference to Ryujinx.Memory, so these classes were introduced to bridge the gap. Someone else crossed that bridge since, so these classes don't have much of a purpose anymore.

This PR replaces all uses of CpuRegionHandle etc to their direct Ryujinx.Memory versions.

RegionHandle methods (specifically QueryModified) are about the hottest path there is in the entire emulator, so there is a nice boost from doing this.

* Add docs
2023-05-05 23:40:46 +02:00
Ac_K
d6698680be UI: Fix sections extraction (#4820)
* UI: Fix sections extraction

There is currently an issue when the update NCA doesn't contains the section we want to extract, this is fixed by adding a check.
I have fixed the inverted handler of ExeFs/Logo introduced in #4755.

Fixes #4521

* Addresses feedback
2023-05-05 21:24:35 +00:00
ashuk
e5c9838b0b Correct tooltips for add,remove,removeall buttons (#4819) 2023-05-05 22:38:57 +02:00
Ikko Eltociear Ashimine
f8ec878796 Fix typo in TextureBindingsManager.cs (#4798)
accomodate -> accommodate
2023-05-05 22:17:36 +02:00
Skyth (Asilkan)
9ff21f9ab6 Use ToLowerInvariant when detecting GPU vendor. (#4815) 2023-05-05 16:35:59 +00:00
gdkchan
aa021085cf Allow any shader SSBO constant buffer slot and offset (#2237)
* Allow any shader SSBO constant buffer slot and offset

* Fix slot value passed to SetUsedStorageBuffer on fallback case

* Shader cache version

* Ensure that the storage buffer source constant buffer offset is word aligned

* Fix FirstBinding on GetUniformBufferDescriptors
2023-05-05 14:20:20 +00:00
45 changed files with 576 additions and 246 deletions

View File

@@ -590,6 +590,7 @@
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager",
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
"BuildId": "BuildId:",
"DlcWindowHeading": "{0} Downloadable Content(s)",
"UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel",
@@ -644,5 +645,6 @@
"UserEditorTitleCreate" : "Create User",
"SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN features",
"NetworkInterfaceDefault": "Default"
"NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders"
}

View File

@@ -233,9 +233,14 @@ namespace Ryujinx.Ava.Common
try
{
IFileSystem ncaFileSystem = patchNca != null
? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
bool sectionExistsInPatch = false;
if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
FileSystemClient fsClient = _horizonClient.Fs;

View File

@@ -65,7 +65,7 @@
</MenuItem>
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
<MenuItem
Click="ExtractApplicationLogo_Click"
Click="ExtractApplicationExeFs_Click"
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
<MenuItem
@@ -73,7 +73,7 @@
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem
Click="ExtractApplicationExeFs_Click"
Click="ExtractApplicationLogo_Click"
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>

View File

@@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Ui.App.Common;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.Common.Helper;
using System;
@@ -118,7 +119,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window);
await new CheatWindow(
viewModel.VirtualFileSystem,
viewModel.SelectedApplication.TitleId,
viewModel.SelectedApplication.TitleName,
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
}
}
@@ -289,13 +294,13 @@ namespace Ryujinx.Ava.UI.Controls
}
}
public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args)
public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
}
}
@@ -309,13 +314,13 @@ namespace Ryujinx.Ava.UI.Controls
}
}
public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args)
public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args)
{
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
if (viewModel?.SelectedApplication != null)
{
await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
}
}
}

View File

@@ -1099,6 +1099,10 @@ namespace Ryujinx.Ava.UI.ViewModels
LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Packaging:
LoadHeading = LocaleManager.Instance[LocaleKeys.PackagingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded:
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true;

View File

@@ -11,6 +11,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
@@ -176,7 +177,11 @@ namespace Ryujinx.Ava.UI.Views.Main
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window);
await new CheatWindow(
Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
Window.ViewModel.SelectedApplication.Path).ShowDialog(Window);
ViewModel.AppHost.Device.EnableCheats();
}

View File

@@ -21,23 +21,52 @@
</Window.Styles>
<Grid Name="CheatGrid" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxWidth="500"
Margin="20,15,20,20"
Margin="20,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="18"
Text="{Binding Heading}"
TextAlignment="Center"
TextWrapping="Wrap" />
<Border
<TextBlock
Grid.Row="2"
Grid.Column="0"
MaxWidth="500"
Margin="140,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="30"
Text="{locale:Locale BuildId}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Margin="0,5,110,5"
MinWidth="160"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding BuildId}"
IsReadOnly="True" />
<Border
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
@@ -81,7 +110,9 @@
</TreeView>
</Border>
<DockPanel
Grid.Row="3"
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0"
HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Right">

View File

@@ -1,8 +1,10 @@
using Avalonia.Collections;
using Avalonia;
using Avalonia.Collections;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -17,6 +19,7 @@ namespace Ryujinx.Ava.UI.Windows
private AvaloniaList<CheatsList> LoadedCheats { get; }
public string Heading { get; }
public string BuildId { get; }
public CheatWindow()
{
@@ -27,11 +30,12 @@ namespace Ryujinx.Ava.UI.Windows
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
}
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
{
LoadedCheats = new AvaloniaList<CheatsList>();
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
InitializeComponent();

View File

@@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
@@ -822,21 +821,21 @@ namespace Ryujinx.Cpu.AppleHv
}
/// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
public RegionHandle BeginTracking(ulong address, ulong size, int id)
{
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
return Tracking.BeginTracking(address, size, id);
}
/// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
}
/// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
/// <summary>

View File

@@ -1,5 +1,4 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory;
using Ryujinx.Memory;
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
@@ -30,7 +29,7 @@ namespace Ryujinx.Cpu
/// <param name="size">Size of the region</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns>
CpuRegionHandle BeginTracking(ulong address, ulong size, int id);
RegionHandle BeginTracking(ulong address, ulong size, int id);
/// <summary>
/// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@@ -41,7 +40,7 @@ namespace Ryujinx.Cpu
/// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns>
CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id);
MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id);
/// <summary>
/// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@@ -51,6 +50,6 @@ namespace Ryujinx.Cpu
/// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns>
CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id);
SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id);
}
}

View File

@@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
@@ -629,21 +628,21 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
public RegionHandle BeginTracking(ulong address, ulong size, int id)
{
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
return Tracking.BeginTracking(address, size, id);
}
/// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
}
/// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
/// <inheritdoc/>

View File

@@ -1,5 +1,4 @@
using ARMeilleure.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
@@ -706,21 +705,21 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
public RegionHandle BeginTracking(ulong address, ulong size, int id)
{
return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
return Tracking.BeginTracking(address, size, id);
}
/// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
}
/// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
/// <summary>

View File

@@ -1,28 +0,0 @@
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
namespace Ryujinx.Cpu.Tracking
{
public class CpuMultiRegionHandle : IMultiRegionHandle
{
private readonly MultiRegionHandle _impl;
public bool Dirty => _impl.Dirty;
internal CpuMultiRegionHandle(MultiRegionHandle impl)
{
_impl = impl;
}
public void Dispose() => _impl.Dispose();
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
public IEnumerable<IRegionHandle> GetHandles() => _impl.GetHandles();
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
public void RegisterAction(ulong address, ulong size, RegionSignal action) => _impl.RegisterAction(address, size, action);
public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action) => _impl.RegisterPreciseAction(address, size, action);
public void SignalWrite() => _impl.SignalWrite();
}
}

View File

@@ -1,37 +0,0 @@
using Ryujinx.Memory.Tracking;
using System;
namespace Ryujinx.Cpu.Tracking
{
public class CpuRegionHandle : IRegionHandle
{
private readonly RegionHandle _impl;
public bool Dirty => _impl.Dirty;
public bool Unmapped => _impl.Unmapped;
public ulong Address => _impl.Address;
public ulong Size => _impl.Size;
public ulong EndAddress => _impl.EndAddress;
internal CpuRegionHandle(RegionHandle impl)
{
_impl = impl;
}
public void Dispose() => _impl.Dispose();
public bool DirtyOrVolatile() => _impl.DirtyOrVolatile();
public void ForceDirty() => _impl.ForceDirty();
public IRegionHandle GetHandle() => _impl;
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
public void RegisterDirtyEvent(Action action) => _impl.RegisterDirtyEvent(action);
public void Reprotect(bool asDirty = false) => _impl.Reprotect(asDirty);
public bool OverlapsWith(ulong address, ulong size) => _impl.OverlapsWith(address, size);
public bool RangeEquals(CpuRegionHandle other)
{
return _impl.RealAddress == other._impl.RealAddress && _impl.RealSize == other._impl.RealSize;
}
}
}

View File

@@ -1,26 +0,0 @@
using Ryujinx.Memory.Tracking;
using System;
namespace Ryujinx.Cpu.Tracking
{
public class CpuSmartMultiRegionHandle : IMultiRegionHandle
{
private readonly SmartMultiRegionHandle _impl;
public bool Dirty => _impl.Dirty;
internal CpuSmartMultiRegionHandle(SmartMultiRegionHandle impl)
{
_impl = impl;
}
public void Dispose() => _impl.Dispose();
public void ForceDirty(ulong address, ulong size) => _impl.ForceDirty(address, size);
public void RegisterAction(RegionSignal action) => _impl.RegisterAction(action);
public void RegisterPreciseAction(PreciseRegionSignal action) => _impl.RegisterPreciseAction(action);
public void QueryModified(Action<ulong, ulong> modifiedAction) => _impl.QueryModified(modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction) => _impl.QueryModified(address, size, modifiedAction);
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber) => _impl.QueryModified(address, size, modifiedAction, sequenceNumber);
public void SignalWrite() => _impl.SignalWrite();
}
}

View File

@@ -157,11 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{
BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0);
int sbDescOffset = 0x310 + sb.Slot * 0x10;
sbDescAddress += (ulong)sbDescOffset;
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);

View File

@@ -351,11 +351,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
BufferDescriptor sb = info.SBuffers[index];
ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0);
int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
sbDescAddress += (ulong)sbDescOffset;
ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
sbDescAddress += (ulong)sb.SbCbOffset * 4;
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);

View File

@@ -1,5 +1,5 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Memory.Tracking;
using System;
using System.Runtime.InteropServices;
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public ulong Size { get; }
private readonly CpuMultiRegionHandle _memoryTracking;
private readonly MultiRegionHandle _memoryTracking;
private readonly Action<ulong, ulong> _modifiedDelegate;
private int _modifiedSequenceOffset;

View File

@@ -537,7 +537,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range.GetSubRange(0).Address, texture.Size, bindingInfo, bindingInfo.Format, false);
@@ -666,7 +666,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer)
{
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
Format format = bindingInfo.Format;

View File

@@ -1,10 +1,10 @@
using Ryujinx.Common.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
TextureGroupHandle group = _handles[baseHandle + i];
foreach (CpuRegionHandle handle in group.Handles)
foreach (RegionHandle handle in group.Handles)
{
if (handle.Dirty)
{
@@ -296,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Image
bool handleDirty = false;
bool handleUnmapped = false;
foreach (CpuRegionHandle handle in group.Handles)
foreach (RegionHandle handle in group.Handles)
{
if (handle.Dirty)
{
@@ -703,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="group">The group to register an action for</param>
public void RegisterAction(TextureGroupHandle group)
{
foreach (CpuRegionHandle handle in group.Handles)
foreach (RegionHandle handle in group.Handles)
{
handle.RegisterAction((address, size) => FlushAction(group, address, size));
}
@@ -985,7 +985,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="address">The start address of the tracked region</param>
/// <param name="size">The size of the tracked region</param>
/// <returns>A CpuRegionHandle covering the given range</returns>
private CpuRegionHandle GenerateHandle(ulong address, ulong size)
private RegionHandle GenerateHandle(ulong address, ulong size)
{
return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
}
@@ -1005,7 +1005,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel];
int size = endOffset - offset;
var result = new List<CpuRegionHandle>();
var result = new List<RegionHandle>();
for (int i = 0; i < TextureRange.Count; i++)
{
@@ -1050,7 +1050,7 @@ namespace Ryujinx.Graphics.Gpu.Image
views,
result.ToArray());
foreach (CpuRegionHandle handle in result)
foreach (RegionHandle handle in result)
{
handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
}
@@ -1248,7 +1248,7 @@ namespace Ryujinx.Graphics.Gpu.Image
continue;
}
foreach (CpuRegionHandle handle in groupHandle.Handles)
foreach (RegionHandle handle in groupHandle.Handles)
{
bool hasMatch = false;
@@ -1270,7 +1270,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
foreach (CpuRegionHandle handle in groupHandle.Handles)
foreach (RegionHandle handle in groupHandle.Handles)
{
handle.Reprotect();
}
@@ -1303,7 +1303,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!(_hasMipViews || _hasLayerViews))
{
// Single dirty region.
var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];
var cpuRegionHandles = new RegionHandle[TextureRange.Count];
int count = 0;
for (int i = 0; i < TextureRange.Count; i++)
@@ -1322,7 +1322,7 @@ namespace Ryujinx.Graphics.Gpu.Image
var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, 0, _allOffsets.Length, cpuRegionHandles);
foreach (CpuRegionHandle handle in cpuRegionHandles)
foreach (RegionHandle handle in cpuRegionHandles)
{
handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
}

View File

@@ -1,5 +1,5 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Tracking;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// The CPU memory tracking handles that cover this handle.
/// </summary>
public CpuRegionHandle[] Handles { get; }
public RegionHandle[] Handles { get; }
/// <summary>
/// True if a texture overlapping this handle has been modified. Is set false when the flush action is called.
@@ -127,7 +127,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int firstLevel,
int baseSlice,
int sliceCount,
CpuRegionHandle[] handles)
RegionHandle[] handles)
{
_group = group;
_firstLayer = firstLayer;
@@ -642,7 +642,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void Dispose()
{
foreach (CpuRegionHandle handle in Handles)
foreach (RegionHandle handle in Handles)
{
handle.Dispose();
}

View File

@@ -1,4 +1,3 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Synchronization;
using Ryujinx.Memory.Range;
@@ -54,8 +53,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </remarks>
private BufferModifiedRangeList _modifiedRanges = null;
private readonly CpuMultiRegionHandle _memoryTrackingGranular;
private readonly CpuRegionHandle _memoryTracking;
private readonly MultiRegionHandle _memoryTrackingGranular;
private readonly RegionHandle _memoryTracking;
private readonly RegionSignal _externalFlushDelegate;
private readonly Action<ulong, ulong> _loadDelegate;
@@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
return Enumerable.Repeat(buffer._memoryTracking.GetHandle(), 1);
return Enumerable.Repeat(buffer._memoryTracking, 1);
}
});
}

View File

@@ -1,5 +1,4 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Memory.Tracking;
using Ryujinx.Memory.Tracking;
using System;
namespace Ryujinx.Graphics.Gpu.Memory
@@ -9,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
class GpuRegionHandle : IRegionHandle
{
private readonly CpuRegionHandle[] _cpuRegionHandles;
private readonly RegionHandle[] _cpuRegionHandles;
public bool Dirty
{
@@ -35,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Create a new GpuRegionHandle, made up of mulitple CpuRegionHandles.
/// </summary>
/// <param name="cpuRegionHandles">The CpuRegionHandles that make up this handle</param>
public GpuRegionHandle(CpuRegionHandle[] cpuRegionHandles)
public GpuRegionHandle(RegionHandle[] cpuRegionHandles)
{
_cpuRegionHandles = cpuRegionHandles;
}

View File

@@ -1,5 +1,4 @@
using Ryujinx.Cpu;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Memory;
@@ -348,7 +347,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <returns>The memory tracking handle</returns>
public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind)
public RegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind)
{
return _cpuMemory.BeginTracking(address, size, (int)kind);
}
@@ -361,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <returns>The memory tracking handle</returns>
public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind)
{
var cpuRegionHandles = new CpuRegionHandle[range.Count];
var cpuRegionHandles = new RegionHandle[range.Count];
int count = 0;
for (int i = 0; i < range.Count; i++)
@@ -390,7 +389,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="handles">Handles to inherit state from or reuse</param>
/// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
{
return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind);
}
@@ -403,7 +402,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="kind">Kind of the resource being tracked</param>
/// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096)
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096)
{
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind);
}

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4735;
private const uint CodeGenVersion = 4821;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -299,10 +299,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (_programList.Count != 0)
{
_stateChangeCallback(ShaderCacheState.Packaging, 0, _programList.Count);
Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders...");
using var streams = _hostStorage.GetOutputStreams(_context);
int packagedShaders = 0;
foreach (var kv in _programList)
{
if (!Active)
@@ -311,7 +314,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
(CachedShaderProgram program, byte[] binaryCode) = kv.Value;
_hostStorage.AddShader(_context, program, binaryCode, streams);
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
}
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");

View File

@@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
Start,
/// <summary>Shader cache is loading</summary>
Loading,
/// <summary>Shader cache is written to disk</summary>
Packaging,
/// <summary>Shader cache finished loading</summary>
Loaded
}

View File

@@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.OpenGL
private static GpuVendor GetGpuVendor()
{
string vendor = GL.GetString(StringName.Vendor).ToLower();
string vendor = GL.GetString(StringName.Vendor).ToLowerInvariant();
if (vendor == "nvidia corporation")
{
@@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.OpenGL
}
else if (vendor == "intel")
{
string renderer = GL.GetString(StringName.Renderer).ToLower();
string renderer = GL.GetString(StringName.Renderer).ToLowerInvariant();
return renderer.Contains("mesa") ? GpuVendor.IntelUnix : GpuVendor.IntelWindows;
}

View File

@@ -5,13 +5,27 @@ namespace Ryujinx.Graphics.Shader
// New fields should be added to the end of the struct to keep disk shader cache compatibility.
public readonly int Binding;
public readonly int Slot;
public readonly byte Slot;
public readonly byte SbCbSlot;
public readonly ushort SbCbOffset;
public BufferUsageFlags Flags;
public BufferDescriptor(int binding, int slot)
{
Binding = binding;
Slot = slot;
Slot = (byte)slot;
SbCbSlot = 0;
SbCbOffset = 0;
Flags = BufferUsageFlags.None;
}
public BufferDescriptor(int binding, int slot, int sbCbSlot, int sbCbOffset)
{
Binding = binding;
Slot = (byte)slot;
SbCbSlot = (byte)sbCbSlot;
SbCbOffset = (ushort)sbCbOffset;
Flags = BufferUsageFlags.None;
}

View File

@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Shader
/// Flags that indicate how a buffer will be used in a shader.
/// </summary>
[Flags]
public enum BufferUsageFlags
public enum BufferUsageFlags : byte
{
None = 0,

View File

@@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int UbeDescsSize = StorageDescSize * UbeMaxCount;
public const int UbeFirstCbuf = 8;
public const int DriverReservedCb = 0;
public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind)
{
return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) ||

View File

@@ -8,6 +8,20 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class GlobalToStorage
{
private struct SearchResult
{
public static SearchResult NotFound => new SearchResult(-1, 0);
public bool Found => SbCbSlot != -1;
public int SbCbSlot { get; }
public int SbCbOffset { get; }
public SearchResult(int sbCbSlot, int sbCbOffset)
{
SbCbSlot = sbCbSlot;
SbCbOffset = sbCbOffset;
}
}
public static void RunPass(BasicBlock block, ShaderConfig config, ref int sbUseMask, ref int ubeUseMask)
{
int sbStart = GetStorageBaseCbOffset(config.Stage);
@@ -49,30 +63,33 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
Operand source = operation.GetSource(0);
int storageIndex = SearchForStorageBase(block, source, sbStart, sbEnd);
if (storageIndex >= 0)
var result = SearchForStorageBase(config, block, source);
if (!result.Found)
{
// Storage buffers are implemented using global memory access.
// If we know from where the base address of the access is loaded,
// we can guess which storage buffer it is accessing.
// We can then replace the global memory access with a storage
// buffer access.
node = ReplaceGlobalWithStorage(block, node, config, storageIndex);
continue;
}
else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal)
if (config.Stage == ShaderStage.Compute &&
operation.Inst == Instruction.LoadGlobal &&
result.SbCbSlot == DriverReservedCb &&
result.SbCbOffset >= UbeBaseOffset &&
result.SbCbOffset < UbeBaseOffset + UbeDescsSize)
{
// Here we effectively try to replace a LDG instruction with LDC.
// The hardware only supports a limited amount of constant buffers
// so NVN "emulates" more constant buffers using global memory access.
// Here we try to replace the global access back to a constant buffer
// load.
storageIndex = SearchForStorageBase(block, source, ubeStart, ubeStart + ubeEnd);
if (storageIndex >= 0)
{
node = ReplaceLdgWithLdc(node, config, storageIndex);
}
node = ReplaceLdgWithLdc(node, config, (result.SbCbOffset - UbeBaseOffset) / StorageDescSize);
}
else
{
// Storage buffers are implemented using global memory access.
// If we know from where the base address of the access is loaded,
// we can guess which storage buffer it is accessing.
// We can then replace the global memory access with a storage
// buffer access.
node = ReplaceGlobalWithStorage(block, node, config, config.GetSbSlot((byte)result.SbCbSlot, (ushort)result.SbCbOffset));
}
}
}
@@ -144,12 +161,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand addrLow,
bool isStg16Or8)
{
int baseAddressCbOffset = GetStorageCbOffset(config.Stage, storageIndex);
(int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex);
bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment);
(Operand byteOffset, int constantOffset) = storageAligned ?
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), sbCbSlot, sbCbOffset) :
(null, 0);
if (byteOffset != null)
@@ -159,7 +176,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (byteOffset == null)
{
Operand baseAddrLow = Cbuf(0, baseAddressCbOffset);
Operand baseAddrLow = Cbuf(sbCbSlot, sbCbOffset);
Operand baseAddrTrunc = Local();
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
@@ -198,9 +215,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return wordOffset;
}
private static bool IsCb0Offset(Operand operand, int offset)
private static bool IsCbOffset(Operand operand, int slot, int offset)
{
return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == 0 && operand.GetCbufOffset() == offset;
return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == slot && operand.GetCbufOffset() == offset;
}
private static void ReplaceAddressAlignment(LinkedList<INode> list, Operand address, Operand byteOffset, int constantOffset)
@@ -244,9 +261,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
}
}
private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int baseAddressCbOffset)
private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int cbSlot, int baseAddressCbOffset)
{
if (IsCb0Offset(address, baseAddressCbOffset))
if (IsCbOffset(address, cbSlot, baseAddressCbOffset))
{
// Direct offset: zero.
return (Const(0), 0);
@@ -256,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
address = Utils.FindLastOperation(address, block);
if (IsCb0Offset(address, baseAddressCbOffset))
if (IsCbOffset(address, cbSlot, baseAddressCbOffset))
{
// Only constant offset
return (Const(0), constantOffset);
@@ -270,11 +287,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand src1 = offsetAdd.GetSource(0);
Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block);
if (IsCb0Offset(src2, baseAddressCbOffset))
if (IsCbOffset(src2, cbSlot, baseAddressCbOffset))
{
return (src1, constantOffset);
}
else if (IsCb0Offset(src1, baseAddressCbOffset))
else if (IsCbOffset(src1, cbSlot, baseAddressCbOffset))
{
return (src2, constantOffset);
}
@@ -360,20 +377,20 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return node;
}
private static int SearchForStorageBase(BasicBlock block, Operand globalAddress, int sbStart, int sbEnd)
private static SearchResult SearchForStorageBase(ShaderConfig config, BasicBlock block, Operand globalAddress)
{
globalAddress = Utils.FindLastOperation(globalAddress, block);
if (globalAddress.Type == OperandType.ConstantBuffer)
{
return GetStorageIndex(globalAddress, sbStart, sbEnd);
return GetStorageIndex(config, globalAddress);
}
Operation operation = globalAddress.AsgOp as Operation;
if (operation == null || operation.Inst != Instruction.Add)
{
return -1;
return SearchResult.NotFound;
}
Operand src1 = operation.GetSource(0);
@@ -382,34 +399,65 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if ((src1.Type == OperandType.LocalVariable && src2.Type == OperandType.Constant) ||
(src2.Type == OperandType.LocalVariable && src1.Type == OperandType.Constant))
{
Operand baseAddr;
if (src1.Type == OperandType.LocalVariable)
{
operation = Utils.FindLastOperation(src1, block).AsgOp as Operation;
baseAddr = Utils.FindLastOperation(src1, block);
}
else
{
operation = Utils.FindLastOperation(src2, block).AsgOp as Operation;
baseAddr = Utils.FindLastOperation(src2, block);
}
var result = GetStorageIndex(config, baseAddr);
if (result.Found)
{
return result;
}
operation = baseAddr.AsgOp as Operation;
if (operation == null || operation.Inst != Instruction.Add)
{
return -1;
return SearchResult.NotFound;
}
}
var selectedResult = SearchResult.NotFound;
for (int index = 0; index < operation.SourcesCount; index++)
{
Operand source = operation.GetSource(index);
int storageIndex = GetStorageIndex(source, sbStart, sbEnd);
var result = GetStorageIndex(config, source);
if (storageIndex != -1)
// If we already have a result, we give preference to the ones from
// the driver reserved constant buffer, as those are the ones that
// contains the base address.
if (result.Found && (!selectedResult.Found || result.SbCbSlot == GlobalMemory.DriverReservedCb))
{
return storageIndex;
selectedResult = result;
}
}
return -1;
return selectedResult;
}
private static SearchResult GetStorageIndex(ShaderConfig config, Operand operand)
{
if (operand.Type == OperandType.ConstantBuffer)
{
int slot = operand.GetCbufSlot();
int offset = operand.GetCbufOffset();
if ((offset & 3) == 0)
{
return new SearchResult(slot, offset);
}
}
return SearchResult.NotFound;
}
private static int GetStorageIndex(Operand operand, int sbStart, int sbEnd)

View File

@@ -68,7 +68,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
}
ConstantFolding.RunPass(operation);
Simplification.RunPass(operation);
if (DestIsLocalVar(operation))

View File

@@ -110,9 +110,9 @@ namespace Ryujinx.Graphics.Shader.Translation
Operand BindingRangeCheck(int cbOffset, out Operand baseAddrLow)
{
baseAddrLow = Cbuf(0, cbOffset);
Operand baseAddrHigh = Cbuf(0, cbOffset + 1);
Operand size = Cbuf(0, cbOffset + 2);
baseAddrLow = Cbuf(DriverReservedCb, cbOffset);
Operand baseAddrHigh = Cbuf(DriverReservedCb, cbOffset + 1);
Operand size = Cbuf(DriverReservedCb, cbOffset + 2);
Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow);
Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow);
@@ -134,9 +134,10 @@ namespace Ryujinx.Graphics.Shader.Translation
sbUseMask &= ~(1 << slot);
config.SetUsedStorageBuffer(slot, isWrite);
int cbOffset = GetStorageCbOffset(config.Stage, slot);
slot = config.GetSbSlot(DriverReservedCb, (ushort)cbOffset);
config.SetUsedStorageBuffer(slot, isWrite);
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);

View File

@@ -125,6 +125,9 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
private readonly Dictionary<int, int> _sbSlots;
private readonly Dictionary<int, int> _sbSlotsReverse;
private BufferDescriptor[] _cachedConstantBufferDescriptors;
private BufferDescriptor[] _cachedStorageBufferDescriptors;
private TextureDescriptor[] _cachedTextureDescriptors;
@@ -152,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
_sbSlots = new Dictionary<int, int>();
_sbSlotsReverse = new Dictionary<int, int>();
}
public ShaderConfig(
@@ -770,9 +776,8 @@ namespace Ryujinx.Graphics.Shader.Translation
usedMask |= (int)GpuAccessor.QueryConstantBufferUse();
}
return _cachedConstantBufferDescriptors = GetBufferDescriptors(
return _cachedConstantBufferDescriptors = GetUniformBufferDescriptors(
usedMask,
0,
UsedFeatures.HasFlag(FeatureFlags.CbIndexing),
out _firstConstantBufferBinding,
GpuAccessor.QueryBindingConstantBuffer);
@@ -785,7 +790,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return _cachedStorageBufferDescriptors;
}
return _cachedStorageBufferDescriptors = GetBufferDescriptors(
return _cachedStorageBufferDescriptors = GetStorageBufferDescriptors(
_usedStorageBuffers,
_usedStorageBuffersWrite,
true,
@@ -793,7 +798,48 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor.QueryBindingStorageBuffer);
}
private static BufferDescriptor[] GetBufferDescriptors(
private static BufferDescriptor[] GetUniformBufferDescriptors(int usedMask, bool isArray, out int firstBinding, Func<int, int> getBindingCallback)
{
firstBinding = 0;
int lastSlot = -1;
bool hasFirstBinding = false;
var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
for (int i = 0; i < descriptors.Length; i++)
{
int slot = BitOperations.TrailingZeroCount(usedMask);
if (isArray)
{
// The next array entries also consumes bindings, even if they are unused.
for (int j = lastSlot + 1; j < slot; j++)
{
int binding = getBindingCallback(j);
if (!hasFirstBinding)
{
firstBinding = binding;
hasFirstBinding = true;
}
}
}
lastSlot = slot;
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot);
if (!hasFirstBinding)
{
firstBinding = descriptors[i].Binding;
hasFirstBinding = true;
}
usedMask &= ~(1 << slot);
}
return descriptors;
}
private BufferDescriptor[] GetStorageBufferDescriptors(
int usedMask,
int writtenMask,
bool isArray,
@@ -827,7 +873,9 @@ namespace Ryujinx.Graphics.Shader.Translation
lastSlot = slot;
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot);
(int sbCbSlot, int sbCbOffset) = GetSbCbInfo(slot);
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot, sbCbSlot, sbCbOffset);
if (!hasFirstBinding)
{
@@ -924,6 +972,40 @@ namespace Ryujinx.Graphics.Shader.Translation
return FindDescriptorIndex(GetImageDescriptors(), texOp);
}
public int GetSbSlot(byte sbCbSlot, ushort sbCbOffset)
{
int key = PackSbCbInfo(sbCbSlot, sbCbOffset);
if (!_sbSlots.TryGetValue(key, out int slot))
{
slot = _sbSlots.Count;
_sbSlots.Add(key, slot);
_sbSlotsReverse.Add(slot, key);
}
return slot;
}
public (int, int) GetSbCbInfo(int slot)
{
if (_sbSlotsReverse.TryGetValue(slot, out int key))
{
return UnpackSbCbInfo(key);
}
throw new ArgumentException($"Invalid slot {slot}.", nameof(slot));
}
private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset)
{
return sbCbOffset | ((int)sbCbSlot << 16);
}
private static (int, int) UnpackSbCbInfo(int key)
{
return ((byte)(key >> 16), (ushort)key);
}
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
{
return new ShaderProgramInfo(

View File

@@ -92,6 +92,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
return ResultCode.Success;
}
[CommandCmif(23)]
// GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions>
public ResultCode GetAppletCommonFunctions(ServiceCtx context)
{
MakeObject(context, new IAppletCommonFunctions());
return ResultCode.Success;
}
[CommandCmif(1000)]
// GetDebugFunctions() -> object<nn::am::service::IDebugFunctions>
public ResultCode GetDebugFunctions(ServiceCtx context)

View File

@@ -9,8 +9,10 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class ICommonStateGetter : IpcService
class ICommonStateGetter : DisposableIpcService
{
private readonly ServiceCtx _context;
private Apm.ManagerServer _apmManagerServer;
private Apm.SystemManagerServer _apmSystemManagerServer;
private Lbl.LblControllerServer _lblControllerServer;
@@ -23,11 +25,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private int _messageEventHandle;
private int _displayResolutionChangedEventHandle;
private KEvent _acquiredSleepLockEvent;
private int _acquiredSleepLockEventHandle;
public ICommonStateGetter(ServiceCtx context)
{
_context = context;
_apmManagerServer = new Apm.ManagerServer(context);
_apmSystemManagerServer = new Apm.SystemManagerServer(context);
_lblControllerServer = new Lbl.LblControllerServer(context);
_acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext);
}
[CommandCmif(0)]
@@ -117,6 +126,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandCmif(10)]
// RequestToAcquireSleepLock()
public ResultCode RequestToAcquireSleepLock(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(13)]
// GetAcquiredSleepLockEvent() -> handle<copy>
public ResultCode GetAcquiredSleepLockEvent(ServiceCtx context)
{
if (_acquiredSleepLockEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_acquiredSleepLockEvent.ReadableEvent, out _acquiredSleepLockEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(50)] // 3.0.0+
// IsVrModeEnabled() -> b8
public ResultCode IsVrModeEnabled(ServiceCtx context)
@@ -281,5 +318,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
if (_acquiredSleepLockEventHandle != 0)
{
_context.Process.HandleTable.CloseHandle(_acquiredSleepLockEventHandle);
_acquiredSleepLockEventHandle = 0;
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
namespace Ryujinx.Memory.Tracking
@@ -443,6 +444,16 @@ namespace Ryujinx.Memory.Tracking
return Address < address + size && address < EndAddress;
}
/// <summary>
/// Determines if this handle's memory range matches another exactly.
/// </summary>
/// <param name="other">The other handle</param>
/// <returns>True on a match, false otherwise</returns>
public bool RangeEquals(RegionHandle other)
{
return RealAddress == other.RealAddress && RealSize == other.RealSize;
}
/// <summary>
/// Dispose the handle. Within the tracking lock, this removes references from virtual regions.
/// </summary>

View File

@@ -1,5 +1,16 @@
using LibHac.Common;
using LibHac.Ns;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Loader;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using System;
using System.IO;
namespace Ryujinx.Ui.App.Common
{
@@ -19,5 +30,122 @@ namespace Ryujinx.Ui.App.Common
public double FileSizeBytes { get; set; }
public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
{
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
if (!System.IO.Path.Exists(titleFilePath))
{
Logger.Error?.Print(LogClass.Application, $"File does not exists. {titleFilePath}");
return string.Empty;
}
string extension = System.IO.Path.GetExtension(titleFilePath).ToLower();
if (extension is ".nsp" or ".xci")
{
PartitionFileSystem pfs;
if (extension == ".xci")
{
Xci xci = new(virtualFileSystem.KeySet, file.AsStorage());
pfs = xci.OpenPartition(XciPartitionType.Secure);
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType != NcaContentType.Program)
{
continue;
}
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
patchNca = nca;
}
else
{
mainNca = nca;
}
}
}
else if (extension == ".nca")
{
mainNca = new Nca(virtualFileSystem.KeySet, file.AsStorage());
}
if (mainNca == null)
{
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
return string.Empty;
}
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
if (updatePatchNca != null)
{
patchNca = updatePatchNca;
}
IFileSystem codeFs = null;
if (patchNca == null)
{
if (mainNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
}
}
else
{
if (patchNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
}
}
if (codeFs == null)
{
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
return string.Empty;
}
const string mainExeFs = "main";
if (!codeFs.FileExists($"/{mainExeFs}"))
{
Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS");
return string.Empty;
}
using var nsoFile = new UniqueRef<IFile>();
codeFs.OpenFile(ref nsoFile.Ref, $"/{mainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
NsoReader reader = new NsoReader();
reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();
return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16];
}
}
}

View File

@@ -1626,9 +1626,12 @@ namespace Ryujinx.Ui
private void ManageCheats_Pressed(object sender, EventArgs args)
{
var window = new CheatWindow(_virtualFileSystem,
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString());
var window = new CheatWindow(
_virtualFileSystem,
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties
.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
_currentEmulatedGamePath);
window.Destroyed += CheatWindow_Destroyed;
window.Show();

View File

@@ -270,8 +270,14 @@ namespace Ryujinx.Ui.Widgets
int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
IFileSystem ncaFileSystem = patchNca != null ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
bool sectionExistsInPatch = false;
if (patchNca != null)
{
sectionExistsInPatch = patchNca.CanOpenSection(index);
}
IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
FileSystemClient fsClient = _horizonClient.Fs;
@@ -455,7 +461,7 @@ namespace Ryujinx.Ui.Widgets
private void ManageCheats_Clicked(object sender, EventArgs args)
{
new CheatWindow(_virtualFileSystem, _titleId, _titleName).Show();
new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show();
}
private void OpenTitleModDir_Clicked(object sender, EventArgs args)

View File

@@ -1,6 +1,7 @@
using Gtk;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.IO;
@@ -17,16 +18,18 @@ namespace Ryujinx.Ui.Windows
#pragma warning disable CS0649, IDE0044
[GUI] Label _baseTitleInfoLabel;
[GUI] TextView _buildIdTextView;
[GUI] TreeView _cheatTreeView;
[GUI] Button _saveButton;
#pragma warning restore CS0649, IDE0044
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName) { }
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { }
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : base(builder.GetRawOwnedObject("_cheatWindow"))
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow"))
{
builder.Autoconnect(this);
_baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";
_buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}";
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));

View File

@@ -31,6 +31,21 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="_buildIdTextView">
<property name="visible">True</property>
<property name="margin_top">10</property>
<property name="halign">center</property>
<property name="margin_bottom">10</property>
<property name="editable">False</property>
<property name="cursor_visible">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
@@ -57,7 +72,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
</object>

View File

@@ -89,7 +89,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Adds an update to this list</property>
<property name="tooltip_text" translatable="yes">Adds a DLC to this list</property>
<property name="margin_left">10</property>
<signal name="clicked" handler="AddButton_Clicked" swapped="no"/>
</object>
@@ -105,7 +105,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Removes the selected update</property>
<property name="tooltip_text" translatable="yes">Removes the selected DLC</property>
<property name="margin_left">10</property>
<signal name="clicked" handler="RemoveButton_Clicked" swapped="no"/>
</object>
@@ -121,7 +121,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Removes the selected update</property>
<property name="tooltip_text" translatable="yes">Removes all DLCs</property>
<property name="margin_left">10</property>
<signal name="clicked" handler="RemoveAllButton_Clicked" swapped="no"/>
</object>

View File

@@ -133,7 +133,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Removes the selected update</property>
<property name="tooltip_text" translatable="yes">Removes all the updates</property>
<property name="margin_left">10</property>
<signal name="clicked" handler="RemoveAllButton_Clicked" swapped="no"/>
</object>