Compare commits

..

6 Commits

Author SHA1 Message Date
1b28ecd63e Vulkan: Simplify MultiFenceHolder and managing them (#4845)
* Vulkan: Simplify waitable add/remove

Removal of unnecessary hashset and dictionary

* Thread safety for GetBufferData in PersistentFlushBuffer

* Fix WaitForFencesImpl thread safety

* Proper methods for risky reference increments

* Wrong type of CB.

* Address feedback
2023-05-08 12:45:12 +02:00
895d9b53bc Vulkan: Batch vertex buffer updates (#4843)
* Vulkan: Batch vertex buffer updates

Some games can bind a large number of vertex buffers for draws. This PR allows for vertex buffers to be updated with one call rather than one per buffer.

This mostly affects the AMD Mesa driver, the testing platform was Steam Deck with Super Mario Odyssey. It was taking about 12% before, should be greatly reduced now.

A small optimization has been added to avoid looking up the same buffer multiple times, as a common pattern is for the same buffer to be bound many times in a row with different ranges.

* Only rebind vertex buffers if they have changed

* Address feedback
2023-05-08 10:59:26 +02:00
0e06aace45 misc: Avoid copy of ApplicationControlProperty (#4849)
Avoid more giant copy when passing it around.
2023-05-08 01:50:07 +02:00
adf4ebcd60 Ava: Fix SystemTimeOffset calculation (#4848)
* Ava: Fix SystemTimeOffset calculation

During testing of #4822, Mary pointed out the way we calculate time offset is wrong in our Avalonia UI. This PR fixed that.
The axaml file is autoformatted too.

* DateTime.Now in local var
2023-05-08 00:31:08 +02:00
470a8031a4 time: Update for 15.0.0 changes and fixes long standing issues (#4822)
* time: Update for 15.0.0 changes

Last time we did an upgrade on the time service was during 9.x era, it was about time to take back that reverse again!

15.0.0 added a new structure on the shared memory to get steady clock raw timepoints with a granularity in nanoseconds.

This commit implements this new part.

I plan to write a follow up with a bit of refactoring of this ancient part of the emulator.

As always, reverse and work done by your truly.

PS: As a reminder, if this change is reused anywhere else, work should be credited as Ryujinx and not my person.

* time: Do not set setup value to posix time

This should fix local and network clock returning 0 under usage with
shared memory.

This probably fix #2430.

* Address gdkchan's comment

* Fix internal offset not working since changes and ensure that user clock have a valid clock id

* time: Report auto correcting clock and hardcode steady clock unique id

Fix Pokemon Sword Pokejobs for real.

* Address gdkchan's comment
2023-05-08 00:15:58 +02:00
5440d4ad5c misc: Switch ProcessResult to a class (#4846)
This avoid giant copies being performed when being returned or passed.
2023-05-07 20:50:45 +00:00
18 changed files with 395 additions and 190 deletions

View File

@ -238,8 +238,9 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public DateTimeOffset DateOffset { get; set; }
public TimeSpan TimeOffset { get; set; }
public DateTimeOffset CurrentDate { get; set; }
public TimeSpan CurrentTime { get; set; }
internal AvaloniaList<TimeZone> TimeZones { get; set; }
public AvaloniaList<string> GameDirectories { get; set; }
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
@ -397,10 +398,11 @@ namespace Ryujinx.Ava.UI.ViewModels
Language = (int)config.System.Language.Value;
TimeZone = config.System.TimeZone;
DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset);
DateTime currentDateTime = DateTime.Now;
CurrentDate = currentDateTime.Date;
CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset));
DateOffset = dateTimeOffset.Date;
TimeOffset = dateTimeOffset.TimeOfDay;
EnableVsync = config.Graphics.EnableVsync;
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
ExpandDramSize = config.System.ExpandRam;
@ -487,9 +489,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.TimeZone.Value = TimeZone;
}
TimeSpan systemTimeOffset = DateOffset - DateTime.Now;
config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
config.Graphics.EnableVsync.Value = EnableVsync;
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.ExpandRam.Value = ExpandDramSize;

View File

@ -3,12 +3,12 @@
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
x:DataType="viewModels:SettingsViewModel"
mc:Ignorable="d">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
@ -27,13 +27,15 @@
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemRegion}"
Width="250" />
<ComboBox SelectedIndex="{Binding Region}"
ToolTip.Tip="{locale:Locale RegionTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<TextBlock
Width="250"
VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemRegion}" />
<ComboBox
Width="350"
HorizontalContentAlignment="Left"
SelectedIndex="{Binding Region}"
ToolTip.Tip="{locale:Locale RegionTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
</ComboBoxItem>
@ -58,20 +60,21 @@
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding Language}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<TextBlock
Width="250"
VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
ToolTip.Tip="{locale:Locale LanguageTooltip}" />
<ComboBox
Width="350"
HorizontalContentAlignment="Left"
SelectedIndex="{Binding Language}"
ToolTip.Tip="{locale:Locale LanguageTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
@ -104,71 +107,67 @@
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}"
Width="250" />
<TextBlock
Width="250"
VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
<AutoCompleteBox
Name="TimeZoneBox"
Width="350"
MaxDropDownHeight="500"
FilterMode="Contains"
Items="{Binding TimeZones}"
MaxDropDownHeight="500"
SelectionChanged="TimeZoneBox_OnSelectionChanged"
Text="{Binding Path=TimeZone, Mode=OneWay}"
TextChanged="TimeZoneBox_OnTextChanged"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTime}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="250"/>
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="350" />
<TextBlock
Width="250"
VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTime}"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
<DatePicker
Width="350"
VerticalAlignment="Center"
SelectedDate="{Binding CurrentDate}"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel>
<StackPanel Margin="250,0,0,10" Orientation="Horizontal">
<TimePicker
Width="350"
VerticalAlignment="Center"
ClockIdentifier="24HourClock"
SelectedTime="{Binding TimeOffset}"
Width="350"
SelectedTime="{Binding CurrentTime}"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableVsync}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}" ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}" ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
@ -180,12 +179,10 @@
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding ExpandDramSize}"
ToolTip.Tip="{locale:Locale DRamTooltip}">
<CheckBox IsChecked="{Binding ExpandDramSize}" ToolTip.Tip="{locale:Locale DRamTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
</CheckBox>
<CheckBox IsChecked="{Binding IgnoreMissingServices}"
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
<CheckBox IsChecked="{Binding IgnoreMissingServices}" ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
</CheckBox>
</StackPanel>

View File

@ -105,6 +105,23 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public bool TryIncrementReferenceCount()
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
return true;
}
public void IncrementReferenceCount()
{
if (Interlocked.Increment(ref _referenceCount) == 1)

View File

@ -599,9 +599,10 @@ namespace Ryujinx.Graphics.Vulkan
Auto<DisposableBuffer> dst,
int srcOffset,
int dstOffset,
int size)
int size,
bool registerSrcUsage = true)
{
var srcBuffer = src.Get(cbs, srcOffset, size).Value;
var srcBuffer = registerSrcUsage ? src.Get(cbs, srcOffset, size).Value : src.GetUnsafe().Value;
var dstBuffer = dst.Get(cbs, dstOffset, size).Value;
InsertBufferBarrier(

View File

@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan
public SemaphoreHolder Semaphore;
public List<IAuto> Dependants;
public HashSet<MultiFenceHolder> Waitables;
public List<MultiFenceHolder> Waitables;
public HashSet<SemaphoreHolder> Dependencies;
public void Initialize(Vk api, Device device, CommandPool pool)
@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Vulkan
api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
Dependants = new List<IAuto>();
Waitables = new HashSet<MultiFenceHolder>();
Waitables = new List<MultiFenceHolder>();
Dependencies = new HashSet<SemaphoreHolder>();
}
}
@ -143,8 +143,10 @@ namespace Ryujinx.Graphics.Vulkan
public void AddWaitable(int cbIndex, MultiFenceHolder waitable)
{
ref var entry = ref _commandBuffers[cbIndex];
waitable.AddFence(cbIndex, entry.Fence);
entry.Waitables.Add(waitable);
if (waitable.AddFence(cbIndex, entry.Fence))
{
entry.Waitables.Add(waitable);
}
}
public bool HasWaitableOnRentedCommandBuffer(MultiFenceHolder waitable, int offset, int size)
@ -156,7 +158,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var entry = ref _commandBuffers[i];
if (entry.InUse &&
entry.Waitables.Contains(waitable) &&
waitable.HasFence(i) &&
waitable.IsBufferRangeInUse(i, offset, size))
{
return true;
@ -331,7 +333,7 @@ namespace Ryujinx.Graphics.Vulkan
foreach (var waitable in entry.Waitables)
{
waitable.RemoveFence(cbIndex, entry.Fence);
waitable.RemoveFence(cbIndex);
waitable.RemoveBufferUses(cbIndex);
}

View File

@ -32,6 +32,25 @@ namespace Ryujinx.Graphics.Vulkan
return _fence;
}
public bool TryGet(out Fence fence)
{
int lastValue;
do
{
lastValue = _referenceCount;
if (lastValue == 0)
{
fence = default;
return false;
}
}
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
fence = _fence;
return true;
}
public Fence Get()
{
Interlocked.Increment(ref _referenceCount);

View File

@ -1,6 +1,5 @@
using Silk.NET.Vulkan;
using System.Collections.Generic;
using System.Linq;
using System;
namespace Ryujinx.Graphics.Vulkan
{
@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
{
private static int BufferUsageTrackingGranularity = 4096;
private readonly Dictionary<FenceHolder, int> _fences;
private readonly FenceHolder[] _fences;
private BufferUsageBitmap _bufferUsageBitmap;
/// <summary>
@ -19,7 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary>
public MultiFenceHolder()
{
_fences = new Dictionary<FenceHolder, int>();
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
}
/// <summary>
@ -28,7 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
/// <param name="size">Size of the buffer</param>
public MultiFenceHolder(int size)
{
_fences = new Dictionary<FenceHolder, int>();
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
}
@ -80,25 +79,37 @@ namespace Ryujinx.Graphics.Vulkan
/// </summary>
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
/// <param name="fence">Fence to be added</param>
public void AddFence(int cbIndex, FenceHolder fence)
/// <returns>True if the command buffer's previous fence value was null</returns>
public bool AddFence(int cbIndex, FenceHolder fence)
{
lock (_fences)
ref FenceHolder fenceRef = ref _fences[cbIndex];
if (fenceRef == null)
{
_fences.TryAdd(fence, cbIndex);
fenceRef = fence;
return true;
}
return false;
}
/// <summary>
/// Removes a fence from the holder.
/// </summary>
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
/// <param name="fence">Fence to be removed</param>
public void RemoveFence(int cbIndex, FenceHolder fence)
public void RemoveFence(int cbIndex)
{
lock (_fences)
{
_fences.Remove(fence);
}
_fences[cbIndex] = null;
}
/// <summary>
/// Determines if a fence referenced on the given command buffer.
/// </summary>
/// <param name="cbIndex">Index of the command buffer to check if it's used</param>
/// <returns>True if referenced, false otherwise</returns>
public bool HasFence(int cbIndex)
{
return _fences[cbIndex] != null;
}
/// <summary>
@ -147,21 +158,29 @@ namespace Ryujinx.Graphics.Vulkan
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
{
FenceHolder[] fenceHolders;
Fence[] fences;
Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
lock (_fences)
int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
Span<Fence> fences = stackalloc Fence[count];
int fenceCount = 0;
for (int i = 0; i < count; i++)
{
fenceHolders = size != 0 ? GetOverlappingFences(offset, size) : _fences.Keys.ToArray();
fences = new Fence[fenceHolders.Length];
for (int i = 0; i < fenceHolders.Length; i++)
if (fenceHolders[i].TryGet(out Fence fence))
{
fences[i] = fenceHolders[i].Get();
fences[fenceCount] = fence;
if (fenceCount < i)
{
fenceHolders[fenceCount] = fenceHolders[i];
}
fenceCount++;
}
}
if (fences.Length == 0)
if (fenceCount == 0)
{
return true;
}
@ -170,14 +189,14 @@ namespace Ryujinx.Graphics.Vulkan
if (hasTimeout)
{
signaled = FenceHelper.AllSignaled(api, device, fences, timeout);
signaled = FenceHelper.AllSignaled(api, device, fences.Slice(0, fenceCount), timeout);
}
else
{
FenceHelper.WaitAllIndefinitely(api, device, fences);
FenceHelper.WaitAllIndefinitely(api, device, fences.Slice(0, fenceCount));
}
for (int i = 0; i < fenceHolders.Length; i++)
for (int i = 0; i < fenceCount; i++)
{
fenceHolders[i].Put();
}
@ -186,27 +205,49 @@ namespace Ryujinx.Graphics.Vulkan
}
/// <summary>
/// Gets fences to wait for use of a given buffer region.
/// Gets fences to wait for.
/// </summary>
/// <param name="offset">Offset of the range</param>
/// <param name="size">Size of the range in bytes</param>
/// <returns>Fences for the specified region</returns>
private FenceHolder[] GetOverlappingFences(int offset, int size)
/// <param name="storage">Span to store fences in</param>
/// <returns>Number of fences placed in storage</returns>
private int GetFences(Span<FenceHolder> storage)
{
List<FenceHolder> overlapping = new List<FenceHolder>();
int count = 0;
foreach (var kv in _fences)
for (int i = 0; i < _fences.Length; i++)
{
var fence = kv.Key;
var ownerCbIndex = kv.Value;
var fence = _fences[i];
if (_bufferUsageBitmap.OverlapsWith(ownerCbIndex, offset, size))
if (fence != null)
{
overlapping.Add(fence);
storage[count++] = fence;
}
}
return overlapping.ToArray();
return count;
}
/// <summary>
/// Gets fences to wait for use of a given buffer region.
/// </summary>
/// <param name="storage">Span to store overlapping fences in</param>
/// <param name="offset">Offset of the range</param>
/// <param name="size">Size of the range in bytes</param>
/// <returns>Number of fences for the specified region placed in storage</returns>
private int GetOverlappingFences(Span<FenceHolder> storage, int offset, int size)
{
int count = 0;
for (int i = 0; i < _fences.Length; i++)
{
var fence = _fences[i];
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
{
storage[count++] = fence;
}
}
return count;
}
}
}

View File

@ -34,16 +34,26 @@ namespace Ryujinx.Graphics.Vulkan
public Span<byte> GetBufferData(CommandBufferPool cbp, BufferHolder buffer, int offset, int size)
{
var flushStorage = ResizeIfNeeded(size);
Auto<DisposableBuffer> srcBuffer;
using (var cbs = cbp.Rent())
{
var srcBuffer = buffer.GetBuffer(cbs.CommandBuffer);
srcBuffer = buffer.GetBuffer(cbs.CommandBuffer);
var dstBuffer = flushStorage.GetBuffer(cbs.CommandBuffer);
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size);
if (srcBuffer.TryIncrementReferenceCount())
{
BufferHolder.Copy(_gd, cbs, srcBuffer, dstBuffer, offset, 0, size, registerSrcUsage: false);
}
else
{
// Source buffer is no longer alive, don't copy anything to flush storage.
srcBuffer = null;
}
}
flushStorage.WaitForFences();
srcBuffer?.DecrementReferenceCount();
return flushStorage.GetDataStorage(0, size);
}

View File

@ -66,6 +66,8 @@ namespace Ryujinx.Graphics.Vulkan
private ulong _vertexBuffersDirty;
protected Rectangle<int> ClearScissor;
private readonly VertexBufferUpdater _vertexBufferUpdater;
public SupportBufferUpdater SupportBufferUpdater;
public IndexBufferPattern QuadsToTrisPattern;
public IndexBufferPattern TriFanToTrisPattern;
@ -96,6 +98,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
_descriptorSetUpdater = new DescriptorSetUpdater(gd, this);
_vertexBufferUpdater = new VertexBufferUpdater(gd);
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
_vertexBuffers = new VertexBufferState[Constants.MaxVertexBuffers + 1];
@ -1185,6 +1188,9 @@ namespace Ryujinx.Graphics.Vulkan
int validCount = 1;
BufferHandle lastHandle = default;
Auto<DisposableBuffer> lastBuffer = default;
for (int i = 0; i < count; i++)
{
var vertexBuffer = vertexBuffers[i];
@ -1194,7 +1200,12 @@ namespace Ryujinx.Graphics.Vulkan
if (vertexBuffer.Buffer.Handle != BufferHandle.Null)
{
var vb = Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false);
Auto<DisposableBuffer> vb = (vertexBuffer.Buffer.Handle == lastHandle) ? lastBuffer :
Gd.BufferManager.GetBuffer(CommandBuffer, vertexBuffer.Buffer.Handle, false);
lastHandle = vertexBuffer.Buffer.Handle;
lastBuffer = vb;
if (vb != null)
{
int binding = i + 1;
@ -1222,24 +1233,29 @@ namespace Ryujinx.Graphics.Vulkan
ref var buffer = ref _vertexBuffers[binding];
int oldScalarAlign = buffer.AttributeScalarAlignment;
buffer.Dispose();
if (Gd.Capabilities.VertexBufferAlignment < 2 &&
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{
buffer = new VertexBufferState(
vb,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
if (!buffer.Matches(vb, descriptorIndex, vertexBuffer.Buffer.Offset, vbSize, vertexBuffer.Stride))
{
buffer.Dispose();
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState);
buffer = new VertexBufferState(
vb,
descriptorIndex,
vertexBuffer.Buffer.Offset,
vbSize,
vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater);
}
}
else
{
// May need to be rewritten. Bind this buffer before draw.
buffer.Dispose();
buffer = new VertexBufferState(
vertexBuffer.Buffer.Handle,
descriptorIndex,
@ -1255,6 +1271,8 @@ namespace Ryujinx.Graphics.Vulkan
}
}
_vertexBufferUpdater.Commit(Cbs);
_newState.VertexBindingDescriptionsCount = (uint)validCount;
SignalStateChange();
}
@ -1596,10 +1614,12 @@ namespace Ryujinx.Graphics.Vulkan
{
int i = BitOperations.TrailingZeroCount(_vertexBuffersDirty);
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState);
_vertexBuffers[i].BindVertexBuffer(Gd, Cbs, (uint)i, ref _newState, _vertexBufferUpdater);
_vertexBuffersDirty &= ~(1UL << i);
}
_vertexBufferUpdater.Commit(Cbs);
}
if (_stateDirty || Pbp != pbp)
@ -1712,6 +1732,7 @@ namespace Ryujinx.Graphics.Vulkan
_framebuffer?.Dispose();
_newState.Dispose();
_descriptorSetUpdater.Dispose();
_vertexBufferUpdater.Dispose();
for (int i = 0; i < _vertexBuffers.Length; i++)
{

View File

@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Vulkan
AttributeScalarAlignment = 1;
}
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state)
public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state, VertexBufferUpdater updater)
{
var autoBuffer = _buffer;
@ -65,21 +65,7 @@ namespace Ryujinx.Graphics.Vulkan
var buffer = autoBuffer.Get(cbs, 0, newSize).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
0,
(ulong)newSize,
(ulong)stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
}
updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride);
_buffer = autoBuffer;
@ -106,21 +92,7 @@ namespace Ryujinx.Graphics.Vulkan
{
var buffer = autoBuffer.Get(cbs, _offset, _size).Value;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
binding,
1,
buffer,
(ulong)_offset,
(ulong)_size,
(ulong)_stride);
}
else
{
gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
}
updater.BindVertexBuffer(cbs, binding, buffer, (ulong)_offset, (ulong)_size, (ulong)_stride);
}
}
@ -129,6 +101,11 @@ namespace Ryujinx.Graphics.Vulkan
return _buffer == buffer;
}
public bool Matches(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0)
{
return _buffer == buffer && DescriptorIndex == descriptorIndex && _offset == offset && _size == size && _stride == stride;
}
public void Swap(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
{
if (_buffer == from)

View File

@ -0,0 +1,84 @@
using Silk.NET.Vulkan;
using System;
using VkBuffer = Silk.NET.Vulkan.Buffer;
namespace Ryujinx.Graphics.Vulkan
{
internal class VertexBufferUpdater : IDisposable
{
private VulkanRenderer _gd;
private uint _baseBinding;
private uint _count;
private NativeArray<VkBuffer> _buffers;
private NativeArray<ulong> _offsets;
private NativeArray<ulong> _sizes;
private NativeArray<ulong> _strides;
public VertexBufferUpdater(VulkanRenderer gd)
{
_gd = gd;
_buffers = new NativeArray<VkBuffer>(Constants.MaxVertexBuffers);
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
}
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride)
{
if (_count == 0)
{
_baseBinding = binding;
}
else if (_baseBinding + _count != binding)
{
Commit(cbs);
_baseBinding = binding;
}
int index = (int)_count;
_buffers[index] = buffer;
_offsets[index] = offset;
_sizes[index] = size;
_strides[index] = stride;
_count++;
}
public unsafe void Commit(CommandBufferScoped cbs)
{
if (_count != 0)
{
if (_gd.Capabilities.SupportsExtendedDynamicState)
{
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,
_baseBinding,
_count,
_buffers.Pointer,
_offsets.Pointer,
_sizes.Pointer,
_strides.Pointer);
}
else
{
_gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, _baseBinding, _count, _buffers.Pointer, _offsets.Pointer);
}
_count = 0;
}
}
public void Dispose()
{
_buffers.Dispose();
_offsets.Dispose();
_sizes.Dispose();
_strides.Dispose();
}
}
}

View File

@ -200,9 +200,10 @@ namespace Ryujinx.HLE.HOS
LibHacHorizonManager = device.Configuration.LibHacHorizonManager;
// We hardcode a clock source id to avoid it changing between each start.
// TODO: use set:sys (and get external clock source id from settings)
// TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate.
UInt128 clockSourceId = UInt128Utils.CreateRandom();
UInt128 clockSourceId = new UInt128(0x36a0328702ce8bc1, 0x1608eaba02333284);
IRtcManager.GetExternalRtcValue(out ulong rtcValue);
// We assume the rtc is system time.
@ -222,22 +223,22 @@ namespace Ryujinx.HLE.HOS
internalOffset = internalOffset.AddSeconds(-3600L);
}
internalOffset = new TimeSpanType(-internalOffset.NanoSeconds);
systemTime = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds);
// First init the standard steady clock
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false);
TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, TimeSpanType.Zero, TimeSpanType.Zero, TimeSpanType.Zero, false);
TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds());
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
{
TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
// The network system clock needs a valid system clock, as such we setup this system clock using the local system clock.
TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext);
TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy);
}
TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom());
TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, true, localSytemClockContext.SteadyTimePoint);
// FIXME: TimeZone should be init here but it's actually done in ContentManager

View File

@ -0,0 +1,13 @@
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.Clock.Types
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ContinuousAdjustmentTimePoint
{
public ulong ClockOffset;
public long Multiplier;
public long DivisorLog2;
public SystemClockContext Context;
}
}

View File

@ -1,8 +1,8 @@
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.Clock.Types;
using Ryujinx.HLE.HOS.Services.Time.Types;
using Ryujinx.HLE.Utilities;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
@ -16,10 +16,11 @@ namespace Ryujinx.HLE.HOS.Services.Time
private SharedMemoryStorage _timeSharedMemoryStorage;
private int _timeSharedMemorySize;
private const uint SteadyClockContextOffset = 0x00;
private const uint LocalSystemClockContextOffset = 0x38;
private const uint NetworkSystemClockContextOffset = 0x80;
private const uint SteadyClockContextOffset = 0x00;
private const uint LocalSystemClockContextOffset = 0x38;
private const uint NetworkSystemClockContextOffset = 0x80;
private const uint AutomaticCorrectionEnabledOffset = 0xC8;
private const uint ContinuousAdjustmentTimePointOffset = 0xD0;
public void Initialize(Switch device, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize)
{
@ -39,15 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint)
{
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
SteadyClockContext context = new SteadyClockContext
{
InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds),
ClockSourceId = clockSourceId
};
WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context);
UpdateSteadyClock(tickSource, clockSourceId, currentTimePoint);
}
public void SetAutomaticCorrectionEnabled(bool isAutomaticCorrectionEnabled)
@ -58,10 +51,38 @@ namespace Ryujinx.HLE.HOS.Services.Time
public void SetSteadyClockRawTimePoint(ITickSource tickSource, TimeSpanType currentTimePoint)
{
SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4);
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
SteadyClockContext context = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4);
context.InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds);
UpdateSteadyClock(tickSource, context.ClockSourceId, currentTimePoint);
}
private void UpdateSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint)
{
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency);
ContinuousAdjustmentTimePoint adjustmentTimePoint = new ContinuousAdjustmentTimePoint
{
ClockOffset = (ulong)ticksTimeSpan.NanoSeconds,
Multiplier = 1,
DivisorLog2 = 0,
Context = new SystemClockContext
{
Offset = 0,
SteadyTimePoint = new SteadyClockTimePoint
{
ClockSourceId = clockSourceId,
TimePoint = 0
}
}
};
WriteObjectToSharedMemory(ContinuousAdjustmentTimePointOffset, 4, adjustmentTimePoint);
SteadyClockContext context = new SteadyClockContext
{
InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds),
ClockSourceId = clockSourceId
};
WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context);
}

View File

@ -116,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
device,
device.System.KernelContext,
metaLoader,
nacpData.Value,
nacpData,
enablePtc,
allowCodeMemoryForJit,
programName,

View File

@ -209,7 +209,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
_device.System.KernelContext,
dummyExeFs.GetNpdm(),
nacpData.Value,
nacpData,
diskCacheEnabled: false,
allowCodeMemoryForJit: true,
programName,

View File

@ -219,7 +219,7 @@ namespace Ryujinx.HLE.Loaders.Processes
Switch device,
KernelContext context,
MetaLoader metaLoader,
ApplicationControlProperty applicationControlProperties,
BlitStruct<ApplicationControlProperty> applicationControlProperties,
bool diskCacheEnabled,
bool allowCodeMemoryForJit,
string name,
@ -355,7 +355,7 @@ namespace Ryujinx.HLE.Loaders.Processes
context.Device.System.TickSource,
context.Device.Gpu,
$"{programId:x16}",
applicationControlProperties.DisplayVersionString.ToString(),
applicationControlProperties.Value.DisplayVersionString.ToString(),
diskCacheEnabled,
codeStart,
codeSize);

View File

@ -1,4 +1,5 @@
using LibHac.Loader;
using LibHac.Common;
using LibHac.Loader;
using LibHac.Ns;
using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
@ -9,9 +10,9 @@ using System.Linq;
namespace Ryujinx.HLE.Loaders.Processes
{
public struct ProcessResult
public class ProcessResult
{
public static ProcessResult Failed => new(null, new ApplicationControlProperty(), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish);
public static ProcessResult Failed => new(null, new BlitStruct<ApplicationControlProperty>(1), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish);
private readonly byte _mainThreadPriority;
private readonly uint _mainThreadStackSize;
@ -31,15 +32,15 @@ namespace Ryujinx.HLE.Loaders.Processes
public readonly bool AllowCodeMemoryForJit;
public ProcessResult(
MetaLoader metaLoader,
ApplicationControlProperty applicationControlProperties,
bool diskCacheEnabled,
bool allowCodeMemoryForJit,
IDiskCacheLoadState diskCacheLoadState,
ulong pid,
byte mainThreadPriority,
uint mainThreadStackSize,
TitleLanguage titleLanguage)
MetaLoader metaLoader,
BlitStruct<ApplicationControlProperty> applicationControlProperties,
bool diskCacheEnabled,
bool allowCodeMemoryForJit,
IDiskCacheLoadState diskCacheLoadState,
ulong pid,
byte mainThreadPriority,
uint mainThreadStackSize,
TitleLanguage titleLanguage)
{
_mainThreadPriority = mainThreadPriority;
_mainThreadStackSize = mainThreadStackSize;
@ -48,7 +49,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ProcessId = pid;
MetaLoader = metaLoader;
ApplicationControlProperties = applicationControlProperties;
ApplicationControlProperties = applicationControlProperties.Value;
if (metaLoader is not null)
{