Compare commits

..

4 Commits

Author SHA1 Message Date
gdkchan
efb135b74c Clear CPU side data on GPU buffer clears (#4125)
* Clear CPU side data on GPU buffer clears

* Implement tracked fill operation that can signal other resource types except buffer

* Fix tests, add missing XML doc

* PR feedback
2023-02-16 18:28:49 -03:00
gdkchan
a707842e14 Validate dimensions before creating texture (#4430) 2023-02-16 11:16:31 -03:00
TSRBerry
a5a9b9bc8b GUI: Small Updater refactor & Set correct permissions on Linux when extracting files (#4315)
* ava: Refactor Updater.cs

Fix typos
Remove unused usings
Rename variables to follow naming scheme

* ava: Set file permissions when extracting update files

* gtk: Apply the same refactor to Updater.cs

* updater: Replace assert with if statement

* updater: Remove await usings again
2023-02-15 22:36:35 +00:00
Mary
17078ad929 vulkan: Respect VK_KHR_portability_subset vertex stride alignment (#4419)
* vulkan: Respect VK_KHR_portability_subset vertex stride alignment

We were hardcoding alignment to 4, but by specs it can be any values that
is a power of 2.

This also enable VK_KHR_portability_subset if present as per specs
requirements.

* address gdkchan's comment

* Make NeedsVertexBufferAlignment internal
2023-02-15 08:41:48 +00:00
33 changed files with 432 additions and 364 deletions

View File

@@ -71,6 +71,7 @@ namespace ARMeilleure.Memory
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="write">True if the region was written, false if read</param> /// <param name="write">True if the region was written, false if read</param>
/// <param name="precise">True if the access is precise, false otherwise</param> /// <param name="precise">True if the access is precise, false otherwise</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
} }
} }

View File

@@ -222,7 +222,7 @@ namespace ARMeilleure.Signal
// Tracking action should be non-null to call it, otherwise assume false return. // Tracking action should be non-null to call it, otherwise assume false return.
context.BranchIfFalse(skipActionLabel, trackingActionPtr); context.BranchIfFalse(skipActionLabel, trackingActionPtr);
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite, Const(0)); Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite);
context.Copy(inRegionLocal, result); context.Copy(inRegionLocal, result);
context.MarkLabel(skipActionLabel); context.MarkLabel(skipActionLabel);

View File

@@ -132,8 +132,8 @@ namespace Ryujinx.Modules
} }
} }
// If build not done, assume no new update are availaible. // If build not done, assume no new update are available.
if (_buildUrl == null) if (_buildUrl is null)
{ {
if (showVersionUpToDate) if (showVersionUpToDate)
{ {
@@ -240,13 +240,13 @@ namespace Ryujinx.Modules
{ {
HttpClient result = new(); HttpClient result = new();
// Required by GitHub to interract with APIs. // Required by GitHub to interact with APIs.
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
return result; return result;
} }
public static async void UpdateRyujinx(Window parent, string downloadUrl) private static async void UpdateRyujinx(Window parent, string downloadUrl)
{ {
_updateSuccessful = false; _updateSuccessful = false;
@@ -300,8 +300,6 @@ namespace Ryujinx.Modules
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
} }
SetFileExecutable(ryuExe);
Process.Start(ryuExe, CommandLineState.Arguments); Process.Start(ryuExe, CommandLineState.Arguments);
Environment.Exit(0); Environment.Exit(0);
@@ -408,9 +406,9 @@ namespace Ryujinx.Modules
Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
for (int j = 0; j < webClients.Count; j++) foreach (WebClient webClient in webClients)
{ {
webClients[j].CancelAsync(); webClient.CancelAsync();
} }
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
@@ -472,22 +470,6 @@ namespace Ryujinx.Modules
worker.Start(); worker.Start();
} }
private static void SetFileExecutable(string path)
{
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
UnixFileMode.UserWrite |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows() && File.Exists(path))
{
File.SetUnixFileMode(path, ExecutableFileMode);
}
}
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile) private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
{ {
// Extract Update // Extract Update
@@ -503,7 +485,10 @@ namespace Ryujinx.Modules
await Task.Run(() => await Task.Run(() =>
{ {
TarEntry tarEntry; TarEntry tarEntry;
while ((tarEntry = tarStream.GetNextEntry()) != null)
if (!OperatingSystem.IsWindows())
{
while ((tarEntry = tarStream.GetNextEntry()) is not null)
{ {
if (tarEntry.IsDirectory) continue; if (tarEntry.IsDirectory) continue;
@@ -516,15 +501,15 @@ namespace Ryujinx.Modules
tarStream.CopyEntryContents(outStream); tarStream.CopyEntryContents(outStream);
} }
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
TarEntry entry = tarEntry;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
taskDialog.SetProgressBarState(GetPercentage(entry.Size, inStream.Length), TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal);
}); });
} }
}
}); });
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
@@ -603,8 +588,6 @@ namespace Ryujinx.Modules
Directory.Delete(UpdateDir, true); Directory.Delete(UpdateDir, true);
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
_updateSuccessful = true; _updateSuccessful = true;
taskDialog.Hide(); taskDialog.Hide();

View File

@@ -634,13 +634,13 @@ namespace Ryujinx.Cpu.AppleHv
/// <remarks> /// <remarks>
/// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
/// </remarks> /// </remarks>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
AssertValidAddressAndSize(va, size); AssertValidAddressAndSize(va, size);
if (precise) if (precise)
{ {
Tracking.VirtualMemoryEvent(va, size, write, precise: true); Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
return; return;
} }
@@ -663,7 +663,7 @@ namespace Ryujinx.Cpu.AppleHv
if (state >= tag) if (state >= tag)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
return; return;
} }
else if (state == 0) else if (state == 0)
@@ -706,7 +706,7 @@ namespace Ryujinx.Cpu.AppleHv
// Only trigger tracking from reads if both bits are set on any page. // Only trigger tracking from reads if both bits are set on any page.
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
break; break;
} }
} }
@@ -822,21 +822,21 @@ namespace Ryujinx.Cpu.AppleHv
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size)); return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
} }
/// <summary> /// <summary>

View File

@@ -28,8 +28,9 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuRegionHandle BeginTracking(ulong address, ulong size); CpuRegionHandle BeginTracking(ulong address, ulong size, int id);
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@@ -38,8 +39,9 @@ namespace Ryujinx.Cpu
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity); CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id);
/// <summary> /// <summary>
/// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with.
@@ -47,7 +49,8 @@ namespace Ryujinx.Cpu
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity); CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id);
} }
} }

View File

@@ -629,31 +629,31 @@ namespace Ryujinx.Cpu.Jit
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size)); return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
AssertValidAddressAndSize(va, size); AssertValidAddressAndSize(va, size);
if (precise) if (precise)
{ {
Tracking.VirtualMemoryEvent(va, size, write, precise: true); Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
return; return;
} }
@@ -676,7 +676,7 @@ namespace Ryujinx.Cpu.Jit
if ((pte & tag) != 0) if ((pte & tag) != 0)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
break; break;
} }

View File

@@ -518,13 +518,13 @@ namespace Ryujinx.Cpu.Jit
/// <remarks> /// <remarks>
/// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// This function also validates that the given range is both valid and mapped, and will throw if it is not.
/// </remarks> /// </remarks>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
AssertValidAddressAndSize(va, size); AssertValidAddressAndSize(va, size);
if (precise) if (precise)
{ {
Tracking.VirtualMemoryEvent(va, size, write, precise: true); Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId);
return; return;
} }
@@ -547,7 +547,7 @@ namespace Ryujinx.Cpu.Jit
if (state >= tag) if (state >= tag)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
return; return;
} }
else if (state == 0) else if (state == 0)
@@ -590,7 +590,7 @@ namespace Ryujinx.Cpu.Jit
// Only trigger tracking from reads if both bits are set on any page. // Only trigger tracking from reads if both bits are set on any page.
if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) if (write || (pte & (pte >> 1) & BlockMappedMask) != 0)
{ {
Tracking.VirtualMemoryEvent(va, size, write); Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
break; break;
} }
} }
@@ -706,21 +706,21 @@ namespace Ryujinx.Cpu.Jit
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
return new CpuRegionHandle(Tracking.BeginTracking(address, size)); return new CpuRegionHandle(Tracking.BeginTracking(address, size, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id));
} }
/// <inheritdoc/> /// <inheritdoc/>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id));
} }
/// <summary> /// <summary>

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Cpu
{ {
public class MemoryEhMeilleure : IDisposable public class MemoryEhMeilleure : IDisposable
{ {
private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write, bool precise = false); private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write);
private readonly MemoryTracking _tracking; private readonly MemoryTracking _tracking;
private readonly TrackingEventDelegate _trackingEvent; private readonly TrackingEventDelegate _trackingEvent;

View File

@@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Image
Address = address; Address = address;
Size = size; Size = size;
_memoryTracking = physicalMemory.BeginGranularTracking(address, size); _memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool);
_memoryTracking.RegisterPreciseAction(address, size, PreciseAction); _memoryTracking.RegisterPreciseAction(address, size, PreciseAction);
_modifiedDelegate = RegionModified; _modifiedDelegate = RegionModified;
} }

View File

@@ -477,7 +477,23 @@ namespace Ryujinx.Graphics.Gpu.Image
// If the start address is unmapped, let's try to find a page of memory that is mapped. // If the start address is unmapped, let's try to find a page of memory that is mapped.
if (address == MemoryManager.PteUnmapped) if (address == MemoryManager.PteUnmapped)
{ {
address = memoryManager.TranslateFirstMapped(info.GpuAddress, (ulong)info.CalculateSizeInfo(layerSize).TotalSize); // Make sure that the dimensions are valid before calculating the texture size.
if (info.Width < 1 || info.Height < 1 || info.Levels < 1)
{
return null;
}
if ((info.Target == Target.Texture3D ||
info.Target == Target.Texture2DArray ||
info.Target == Target.Texture2DMultisampleArray ||
info.Target == Target.CubemapArray) && info.DepthOrLayers < 1)
{
return null;
}
ulong dataSize = (ulong)info.CalculateSizeInfo(layerSize).TotalSize;
address = memoryManager.TranslateFirstMapped(info.GpuAddress, dataSize);
} }
// If address is still invalid, the texture is fully unmapped, so it has no data, just return null. // If address is still invalid, the texture is fully unmapped, so it has no data, just return null.

View File

@@ -854,7 +854,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A CpuRegionHandle covering the given range</returns> /// <returns>A CpuRegionHandle covering the given range</returns>
private CpuRegionHandle GenerateHandle(ulong address, ulong size) private CpuRegionHandle GenerateHandle(ulong address, ulong size)
{ {
return _physicalMemory.BeginTracking(address, size); return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture);
} }
/// <summary> /// <summary>

View File

@@ -105,13 +105,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_useGranular) if (_useGranular)
{ {
_memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles); _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Buffer, baseHandles);
_memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction); _memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction);
} }
else else
{ {
_memoryTracking = physicalMemory.BeginTracking(address, size); _memoryTracking = physicalMemory.BeginTracking(address, size, ResourceKind.Buffer);
if (baseHandles != null) if (baseHandles != null)
{ {

View File

@@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value); _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
buffer.SignalModified(address, size); memoryManager.Physical.FillTrackedResource(address, size, value, ResourceKind.Buffer);
} }
/// <summary> /// <summary>

View File

@@ -7,6 +7,7 @@ using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
@@ -295,23 +296,41 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Fills the specified memory region with a 32-bit integer value.
/// </summary>
/// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param>
/// <param name="value">Value to fill the region with</param>
/// <param name="kind">Kind of the resource being filled, which will not be signalled as CPU modified</param>
public void FillTrackedResource(ulong address, ulong size, uint value, ResourceKind kind)
{
_cpuMemory.SignalMemoryTracking(address, size, write: true, precise: true, (int)kind);
using WritableRegion region = _cpuMemory.GetWritableRegion(address, (int)size);
MemoryMarshal.Cast<byte, uint>(region.Memory.Span).Fill(value);
}
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuRegionHandle BeginTracking(ulong address, ulong size) public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind)
{ {
return _cpuMemory.BeginTracking(address, size); return _cpuMemory.BeginTracking(address, size, (int)kind);
} }
/// <summary> /// <summary>
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
/// </summary> /// </summary>
/// <param name="range">Ranges of physical memory where the data is located</param> /// <param name="range">Ranges of physical memory where the data is located</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public GpuRegionHandle BeginTracking(MultiRange range) public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind)
{ {
var cpuRegionHandles = new CpuRegionHandle[range.Count]; var cpuRegionHandles = new CpuRegionHandle[range.Count];
int count = 0; int count = 0;
@@ -321,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
var currentRange = range.GetSubRange(i); var currentRange = range.GetSubRange(i);
if (currentRange.Address != MemoryManager.PteUnmapped) if (currentRange.Address != MemoryManager.PteUnmapped)
{ {
cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size); cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size, (int)kind);
} }
} }
@@ -338,12 +357,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <param name="handles">Handles to inherit state from or reuse</param> /// <param name="handles">Handles to inherit state from or reuse</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096) public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable<IRegionHandle> handles = null, ulong granularity = 4096)
{ {
return _cpuMemory.BeginGranularTracking(address, size, handles, granularity); return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind);
} }
/// <summary> /// <summary>
@@ -351,11 +371,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="kind">Kind of the resource being tracked</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity = 4096) public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096)
{ {
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity); return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind);
} }
/// <summary> /// <summary>

View File

@@ -0,0 +1,13 @@
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Kind of a GPU resource.
/// </summary>
enum ResourceKind
{
None,
Buffer,
Texture,
Pool
}
}

View File

@@ -8,11 +8,10 @@ namespace Ryujinx.Graphics.Vulkan
{ {
None = 0, None = 0,
VertexBufferAlignment4B = 1, NoTriangleFans = 1,
NoTriangleFans = 1 << 1, NoPointMode = 1 << 1,
NoPointMode = 1 << 2, No3DImageView = 1 << 2,
No3DImageView = 1 << 3, NoLodBias = 1 << 3
NoLodBias = 1 << 4
} }
readonly struct HardwareCapabilities readonly struct HardwareCapabilities
@@ -40,6 +39,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly ShaderStageFlags RequiredSubgroupSizeStages;
public readonly SampleCountFlags SupportedSampleCounts; public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset; public readonly PortabilitySubsetFlags PortabilitySubset;
public readonly uint VertexBufferAlignment;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
@@ -64,7 +64,8 @@ namespace Ryujinx.Graphics.Vulkan
uint maxSubgroupSize, uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages, ShaderStageFlags requiredSubgroupSizeStages,
SampleCountFlags supportedSampleCounts, SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset) PortabilitySubsetFlags portabilitySubset,
uint vertexBufferAlignment)
{ {
SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor; SupportsCustomBorderColor = supportsCustomBorderColor;
@@ -89,6 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
RequiredSubgroupSizeStages = requiredSubgroupSizeStages; RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
SupportedSampleCounts = supportedSampleCounts; SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset; PortabilitySubset = portabilitySubset;
VertexBufferAlignment = vertexBufferAlignment;
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
@@ -1136,7 +1137,7 @@ namespace Ryujinx.Graphics.Vulkan
buffer.Dispose(); buffer.Dispose();
if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) && if (Gd.Capabilities.VertexBufferAlignment < 2 &&
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{ {
buffer = new VertexBufferState( buffer = new VertexBufferState(

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
@@ -253,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment)) if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
{ {
alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment; alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment);
} }
// TODO: Support divisor > 1 // TODO: Support divisor > 1

View File

@@ -36,7 +36,8 @@ namespace Ryujinx.Graphics.Vulkan
"VK_KHR_shader_float16_int8", "VK_KHR_shader_float16_int8",
"VK_EXT_shader_subgroup_ballot", "VK_EXT_shader_subgroup_ballot",
"VK_EXT_subgroup_size_control", "VK_EXT_subgroup_size_control",
"VK_NV_geometry_shader_passthrough" "VK_NV_geometry_shader_passthrough",
"VK_KHR_portability_subset", // By spec, we should enable this if present.
}; };
public static string[] RequiredExtensions { get; } = new string[] public static string[] RequiredExtensions { get; } = new string[]

View File

@@ -234,10 +234,12 @@ namespace Ryujinx.Graphics.Vulkan
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
var portabilityFlags = PortabilitySubsetFlags.None; var portabilityFlags = PortabilitySubsetFlags.None;
uint vertexBufferAlignment = 1;
if (usePortability) if (usePortability)
{ {
portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0; vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment;
portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans; portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode; portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView; portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
@@ -278,7 +280,8 @@ namespace Ryujinx.Graphics.Vulkan
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
supportedSampleCounts, supportedSampleCounts,
portabilityFlags); portabilityFlags,
vertexBufferAlignment);
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
@@ -636,11 +639,11 @@ namespace Ryujinx.Graphics.Vulkan
PrintGpuInformation(); PrintGpuInformation();
} }
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{ {
if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B)) if (Capabilities.VertexBufferAlignment > 1)
{ {
alignment = 4; alignment = (int)Capabilities.VertexBufferAlignment;
return true; return true;
} }

View File

@@ -96,7 +96,7 @@ namespace Ryujinx.Memory.Tests
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -34,8 +34,8 @@ namespace Ryujinx.Memory.Tests
private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity) private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity)
{ {
return smart ? return smart ?
_tracking.BeginSmartGranularTracking(address, size, granularity) : _tracking.BeginSmartGranularTracking(address, size, granularity, 0) :
(IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity); (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity, 0);
} }
private void RandomOrder(Random random, List<int> indices, Action<int> action) private void RandomOrder(Random random, List<int> indices, Action<int> action)
@@ -294,7 +294,7 @@ namespace Ryujinx.Memory.Tests
bool[] actionsTriggered = new bool[3]; bool[] actionsTriggered = new bool[3];
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0);
PreparePages(granular, 3, PageSize * 3); PreparePages(granular, 3, PageSize * 3);
// Write to the second handle in the multiregion. // Write to the second handle in the multiregion.
@@ -307,7 +307,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize); singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize, 0);
singlePages[i].Reprotect(); singlePages[i].Reprotect();
} }
@@ -321,7 +321,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2); doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2, 0);
doublePages[i].Reprotect(); doublePages[i].Reprotect();
} }
@@ -340,7 +340,7 @@ namespace Ryujinx.Memory.Tests
doublePages doublePages
}; };
MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize); MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize, 0);
bool[] expectedDirty = new bool[] bool[] expectedDirty = new bool[]
{ {
@@ -405,7 +405,7 @@ namespace Ryujinx.Memory.Tests
{ {
bool actionTriggered = false; bool actionTriggered = false;
MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0);
PreparePages(granular, 3, PageSize * 3); PreparePages(granular, 3, PageSize * 3);
// Add a precise action to the second and third handle in the multiregion. // Add a precise action to the second and third handle in the multiregion.

View File

@@ -44,7 +44,7 @@ namespace Ryujinx.Memory.Tests
[Test] [Test]
public void SingleRegion() public void SingleRegion()
{ {
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
(ulong address, ulong size)? readTrackingTriggered = null; (ulong address, ulong size)? readTrackingTriggered = null;
handle.RegisterAction((address, size) => handle.RegisterAction((address, size) =>
{ {
@@ -97,7 +97,7 @@ namespace Ryujinx.Memory.Tests
[Test] [Test]
public void OverlappingRegions() public void OverlappingRegions()
{ {
RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16); RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16, 0);
allHandle.Reprotect(); allHandle.Reprotect();
(ulong address, ulong size)? readTrackingTriggeredAll = null; (ulong address, ulong size)? readTrackingTriggeredAll = null;
@@ -116,7 +116,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0);
containedHandles[i].Reprotect(); containedHandles[i].Reprotect();
} }
@@ -163,7 +163,7 @@ namespace Ryujinx.Memory.Tests
ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize; ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize;
ulong alignedSize = alignedEnd - alignedStart; ulong alignedSize = alignedEnd - alignedStart;
RegionHandle handle = _tracking.BeginTracking(address, size); RegionHandle handle = _tracking.BeginTracking(address, size, 0);
// Anywhere inside the pages the region is contained on should trigger. // Anywhere inside the pages the region is contained on should trigger.
@@ -207,7 +207,7 @@ namespace Ryujinx.Memory.Tests
for (int i = 0; i < handles.Length; i++) for (int i = 0; i < handles.Length; i++)
{ {
handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0);
handles[i].Reprotect(); handles[i].Reprotect();
} }
@@ -263,7 +263,7 @@ namespace Ryujinx.Memory.Tests
Random random = new Random(randSeed + 512); Random random = new Random(randSeed + 512);
while (Stopwatch.GetTimestamp() < finishedTime) while (Stopwatch.GetTimestamp() < finishedTime)
{ {
RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536)); RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536), 0);
handle.Dispose(); handle.Dispose();
@@ -295,7 +295,7 @@ namespace Ryujinx.Memory.Tests
// Read actions should only be triggered once for each registration. // Read actions should only be triggered once for each registration.
// The implementation should use an interlocked exchange to make sure other threads can't get the action. // The implementation should use an interlocked exchange to make sure other threads can't get the action.
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
int triggeredCount = 0; int triggeredCount = 0;
int registeredCount = 0; int registeredCount = 0;
@@ -359,7 +359,7 @@ namespace Ryujinx.Memory.Tests
{ {
// Ensure that disposed handles correctly remove their virtual and physical regions. // Ensure that disposed handles correctly remove their virtual and physical regions.
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
handle.Reprotect(); handle.Reprotect();
Assert.AreEqual(1, _tracking.GetRegionCount()); Assert.AreEqual(1, _tracking.GetRegionCount());
@@ -372,8 +372,8 @@ namespace Ryujinx.Memory.Tests
// We expect there to be three regions after creating both, one for the small region and two covering the big one around it. // We expect there to be three regions after creating both, one for the small region and two covering the big one around it.
// Regions are always split to avoid overlapping, which is why there are three instead of two. // Regions are always split to avoid overlapping, which is why there are three instead of two.
RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize); RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize, 0);
RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4); RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4, 0);
Assert.AreEqual(3, _tracking.GetRegionCount()); Assert.AreEqual(3, _tracking.GetRegionCount());
@@ -398,7 +398,7 @@ namespace Ryujinx.Memory.Tests
protection = newProtection; protection = newProtection;
}; };
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
// After creating the handle, there is no protection yet. // After creating the handle, there is no protection yet.
Assert.AreEqual(MemoryPermission.ReadAndWrite, protection); Assert.AreEqual(MemoryPermission.ReadAndWrite, protection);
@@ -453,7 +453,7 @@ namespace Ryujinx.Memory.Tests
[Test] [Test]
public void PreciseAction() public void PreciseAction()
{ {
RegionHandle handle = _tracking.BeginTracking(0, PageSize); RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0);
(ulong address, ulong size, bool write)? preciseTriggered = null; (ulong address, ulong size, bool write)? preciseTriggered = null;
handle.RegisterPreciseAction((address, size, write) => handle.RegisterPreciseAction((address, size, write) =>

View File

@@ -462,7 +462,7 @@ namespace Ryujinx.Memory
} }
/// <inheritdoc/> /// <inheritdoc/>
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
// Only the ARM Memory Manager has tracking for now. // Only the ARM Memory Manager has tracking for now.
} }

View File

@@ -175,7 +175,8 @@ namespace Ryujinx.Memory
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="write">True if the region was written, false if read</param> /// <param name="write">True if the region was written, false if read</param>
/// <param name="precise">True if the access is precise, false otherwise</param> /// <param name="precise">True if the access is precise, false otherwise</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
/// <summary> /// <summary>
/// Reprotect a region of virtual memory for tracking. /// Reprotect a region of virtual memory for tracking.

View File

@@ -50,7 +50,8 @@ namespace Ryujinx.Memory.Tracking
/// <param name="address">Address accessed</param> /// <param name="address">Address accessed</param>
/// <param name="size">Size of the region affected in bytes</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
public abstract void Signal(ulong address, ulong size, bool write); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
public abstract void Signal(ulong address, ulong size, bool write, int? exemptId);
/// <summary> /// <summary>
/// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained. /// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained.
@@ -58,7 +59,8 @@ namespace Ryujinx.Memory.Tracking
/// <param name="address">Address accessed</param> /// <param name="address">Address accessed</param>
/// <param name="size">Size of the region affected in bytes</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
public abstract void SignalPrecise(ulong address, ulong size, bool write); /// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
public abstract void SignalPrecise(ulong address, ulong size, bool write, int? exemptId);
/// <summary> /// <summary>
/// Split this region into two, around the specified address. /// Split this region into two, around the specified address.

View File

@@ -136,10 +136,11 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param> /// <param name="handles">Handles to inherit state from or reuse. When none are present, provide null</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
{ {
return new MultiRegionHandle(this, address, size, handles, granularity); return new MultiRegionHandle(this, address, size, handles, granularity, id);
} }
/// <summary> /// <summary>
@@ -148,12 +149,13 @@ namespace Ryujinx.Memory.Tracking
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="granularity">Desired granularity of write tracking</param> /// <param name="granularity">Desired granularity of write tracking</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
{ {
(address, size) = PageAlign(address, size); (address, size) = PageAlign(address, size);
return new SmartMultiRegionHandle(this, address, size, granularity); return new SmartMultiRegionHandle(this, address, size, granularity, id);
} }
/// <summary> /// <summary>
@@ -161,14 +163,16 @@ namespace Ryujinx.Memory.Tracking
/// </summary> /// </summary>
/// <param name="address">CPU virtual address of the region</param> /// <param name="address">CPU virtual address of the region</param>
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
public RegionHandle BeginTracking(ulong address, ulong size) public RegionHandle BeginTracking(ulong address, ulong size, int id)
{ {
var (paAddress, paSize) = PageAlign(address, size); var (paAddress, paSize) = PageAlign(address, size);
lock (TrackingLock) lock (TrackingLock)
{ {
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, _memoryManager.IsRangeMapped(address, size)); bool mapped = _memoryManager.IsRangeMapped(address, size);
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped);
return handle; return handle;
} }
@@ -181,28 +185,31 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region</param> /// <param name="size">Size of the region</param>
/// <param name="bitmap">The bitmap owning the dirty flag for this handle</param> /// <param name="bitmap">The bitmap owning the dirty flag for this handle</param>
/// <param name="bit">The bit of this handle within the dirty flag</param> /// <param name="bit">The bit of this handle within the dirty flag</param>
/// <param name="id">Handle ID</param>
/// <returns>The memory tracking handle</returns> /// <returns>The memory tracking handle</returns>
internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit) internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit, int id)
{ {
var (paAddress, paSize) = PageAlign(address, size); var (paAddress, paSize) = PageAlign(address, size);
lock (TrackingLock) lock (TrackingLock)
{ {
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); bool mapped = _memoryManager.IsRangeMapped(address, size);
RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped);
return handle; return handle;
} }
} }
/// <summary> /// <summary>
/// Signal that a virtual memory event happened at the given location (one byte). /// Signal that a virtual memory event happened at the given location.
/// </summary> /// </summary>
/// <param name="address">Virtual address accessed</param> /// <param name="address">Virtual address accessed</param>
/// <param name="write">Whether the address was written to or read</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param>
/// <returns>True if the event triggered any tracking regions, false otherwise</returns> /// <returns>True if the event triggered any tracking regions, false otherwise</returns>
public bool VirtualMemoryEventTracking(ulong address, bool write) public bool VirtualMemoryEvent(ulong address, ulong size, bool write)
{ {
return VirtualMemoryEvent(address, 1, write); return VirtualMemoryEvent(address, size, write, precise: false, null);
} }
/// <summary> /// <summary>
@@ -214,8 +221,9 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region affected in bytes</param> /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param> /// <param name="write">Whether the region was written to or read</param>
/// <param name="precise">True if the access is precise, false otherwise</param> /// <param name="precise">True if the access is precise, false otherwise</param>
/// <param name="exemptId">Optional ID that of the handles that should not be signalled</param>
/// <returns>True if the event triggered any tracking regions, false otherwise</returns> /// <returns>True if the event triggered any tracking regions, false otherwise</returns>
public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise = false) public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise, int? exemptId = null)
{ {
// Look up the virtual region using the region list. // Look up the virtual region using the region list.
// Signal up the chain to relevant handles. // Signal up the chain to relevant handles.
@@ -250,11 +258,11 @@ namespace Ryujinx.Memory.Tracking
if (precise) if (precise)
{ {
region.SignalPrecise(address, size, write); region.SignalPrecise(address, size, write, exemptId);
} }
else else
{ {
region.Signal(address, size, write); region.Signal(address, size, write, exemptId);
} }
} }
} }

View File

@@ -30,7 +30,13 @@ namespace Ryujinx.Memory.Tracking
public bool Dirty { get; private set; } = true; public bool Dirty { get; private set; } = true;
internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity) internal MultiRegionHandle(
MemoryTracking tracking,
ulong address,
ulong size,
IEnumerable<IRegionHandle> handles,
ulong granularity,
int id)
{ {
_handles = new RegionHandle[(size + granularity - 1) / granularity]; _handles = new RegionHandle[(size + granularity - 1) / granularity];
Granularity = granularity; Granularity = granularity;
@@ -55,7 +61,7 @@ namespace Ryujinx.Memory.Tracking
// Fill any gap left before this handle. // Fill any gap left before this handle.
while (i < startIndex) while (i < startIndex)
{ {
RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
fillHandle.Parent = this; fillHandle.Parent = this;
_handles[i++] = fillHandle; _handles[i++] = fillHandle;
} }
@@ -76,7 +82,7 @@ namespace Ryujinx.Memory.Tracking
while (i < endIndex) while (i < endIndex)
{ {
RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
splitHandle.Parent = this; splitHandle.Parent = this;
splitHandle.Reprotect(handle.Dirty); splitHandle.Reprotect(handle.Dirty);
@@ -99,7 +105,7 @@ namespace Ryujinx.Memory.Tracking
// Fill any remaining space with new handles. // Fill any remaining space with new handles.
while (i < _handles.Length) while (i < _handles.Length)
{ {
RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id);
handle.Parent = this; handle.Parent = this;
_handles[i++] = handle; _handles[i++] = handle;
} }

View File

@@ -15,12 +15,12 @@ namespace Ryujinx.Memory.Tracking
/// If more than this number of checks have been performed on a dirty flag since its last reprotect, /// If more than this number of checks have been performed on a dirty flag since its last reprotect,
/// then it is dirtied infrequently. /// then it is dirtied infrequently.
/// </summary> /// </summary>
private static int CheckCountForInfrequent = 3; private const int CheckCountForInfrequent = 3;
/// <summary> /// <summary>
/// Number of frequent dirty/consume in a row to make this handle volatile. /// Number of frequent dirty/consume in a row to make this handle volatile.
/// </summary> /// </summary>
private static int VolatileThreshold = 5; private const int VolatileThreshold = 5;
public bool Dirty public bool Dirty
{ {
@@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
} }
internal int SequenceNumber { get; set; } internal int SequenceNumber { get; set; }
internal int Id { get; }
public bool Unmapped { get; private set; } public bool Unmapped { get; private set; }
@@ -97,14 +98,26 @@ namespace Ryujinx.Memory.Tracking
/// <param name="realSize">The real, unaligned size of the handle</param> /// <param name="realSize">The real, unaligned size of the handle</param>
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param> /// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
/// <param name="bit">The bit index representing the dirty flag for this handle</param> /// <param name="bit">The bit index representing the dirty flag for this handle</param>
/// <param name="id">Handle ID</param>
/// <param name="mapped">True if the region handle starts mapped</param> /// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, ConcurrentBitmap bitmap, int bit, bool mapped = true) internal RegionHandle(
MemoryTracking tracking,
ulong address,
ulong size,
ulong realAddress,
ulong realSize,
ConcurrentBitmap bitmap,
int bit,
int id,
bool mapped = true)
{ {
Bitmap = bitmap; Bitmap = bitmap;
DirtyBit = bit; DirtyBit = bit;
Dirty = mapped; Dirty = mapped;
Id = id;
Unmapped = !mapped; Unmapped = !mapped;
Address = address; Address = address;
Size = size; Size = size;
@@ -131,11 +144,14 @@ namespace Ryujinx.Memory.Tracking
/// <param name="size">Size of the region to track</param> /// <param name="size">Size of the region to track</param>
/// <param name="realAddress">The real, unaligned address of the handle</param> /// <param name="realAddress">The real, unaligned address of the handle</param>
/// <param name="realSize">The real, unaligned size of the handle</param> /// <param name="realSize">The real, unaligned size of the handle</param>
/// <param name="id">Handle ID</param>
/// <param name="mapped">True if the region handle starts mapped</param> /// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, bool mapped = true) internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, int id, bool mapped = true)
{ {
Bitmap = new ConcurrentBitmap(1, mapped); Bitmap = new ConcurrentBitmap(1, mapped);
Id = id;
Unmapped = !mapped; Unmapped = !mapped;
Address = address; Address = address;

View File

@@ -18,10 +18,11 @@ namespace Ryujinx.Memory.Tracking
private readonly ulong _granularity; private readonly ulong _granularity;
private readonly ulong _size; private readonly ulong _size;
private MemoryTracking _tracking; private MemoryTracking _tracking;
private readonly int _id;
public bool Dirty { get; private set; } = true; public bool Dirty { get; private set; } = true;
internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity) internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id)
{ {
// For this multi-region handle, the handle list starts empty. // For this multi-region handle, the handle list starts empty.
// As regions are queried, they are added to the _handles array at their start index. // As regions are queried, they are added to the _handles array at their start index.
@@ -34,6 +35,7 @@ namespace Ryujinx.Memory.Tracking
_address = address; _address = address;
_size = size; _size = size;
_id = id;
} }
public void SignalWrite() public void SignalWrite()
@@ -102,7 +104,7 @@ namespace Ryujinx.Memory.Tracking
RegionSignal signal = handle.PreAction; RegionSignal signal = handle.PreAction;
handle.Dispose(); handle.Dispose();
RegionHandle splitLow = _tracking.BeginTracking(address, size); RegionHandle splitLow = _tracking.BeginTracking(address, size, _id);
splitLow.Parent = this; splitLow.Parent = this;
if (signal != null) if (signal != null)
{ {
@@ -110,7 +112,7 @@ namespace Ryujinx.Memory.Tracking
} }
_handles[handleIndex] = splitLow; _handles[handleIndex] = splitLow;
RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size); RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id);
splitHigh.Parent = this; splitHigh.Parent = this;
if (signal != null) if (signal != null)
{ {
@@ -145,7 +147,7 @@ namespace Ryujinx.Memory.Tracking
if (handle != null) if (handle != null)
{ {
// Fill up to the found handle. // Fill up to the found handle.
handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle)); handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id);
handle.Parent = this; handle.Parent = this;
_handles[startHandle] = handle; _handles[startHandle] = handle;
return; return;
@@ -153,7 +155,7 @@ namespace Ryujinx.Memory.Tracking
} }
// Can fill the whole range. // Can fill the whole range.
_handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle)); _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id);
_handles[startHandle].Parent = this; _handles[startHandle].Parent = this;
} }

View File

@@ -19,28 +19,36 @@ namespace Ryujinx.Memory.Tracking
_tracking = tracking; _tracking = tracking;
} }
public override void Signal(ulong address, ulong size, bool write) /// <inheritdoc/>
public override void Signal(ulong address, ulong size, bool write, int? exemptId)
{ {
IList<RegionHandle> handles = Handles; IList<RegionHandle> handles = Handles;
for (int i = 0; i < handles.Count; i++) for (int i = 0; i < handles.Count; i++)
{
if (exemptId == null || handles[i].Id != exemptId.Value)
{ {
handles[i].Signal(address, size, write, ref handles); handles[i].Signal(address, size, write, ref handles);
} }
}
UpdateProtection(); UpdateProtection();
} }
public override void SignalPrecise(ulong address, ulong size, bool write) /// <inheritdoc/>
public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId)
{ {
IList<RegionHandle> handles = Handles; IList<RegionHandle> handles = Handles;
bool allPrecise = true; bool allPrecise = true;
for (int i = 0; i < handles.Count; i++) for (int i = 0; i < handles.Count; i++)
{
if (exemptId == null || handles[i].Id != exemptId.Value)
{ {
allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
} }
}
// Only update protection if a regular signal handler was called. // Only update protection if a regular signal handler was called.
// This allows precise actions to skip reprotection costs if they want (they can still do it manually). // This allows precise actions to skip reprotection costs if they want (they can still do it manually).

View File

@@ -40,7 +40,7 @@ namespace Ryujinx.Tests.Memory
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -9,6 +9,7 @@ using Ryujinx.Ui;
using Ryujinx.Ui.Widgets; using Ryujinx.Ui.Widgets;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -23,20 +24,20 @@ namespace Ryujinx.Modules
{ {
public static class Updater public static class Updater
{ {
private const string GitHubApiURL = "https://api.github.com";
private const int ConnectionCount = 4;
internal static bool Running; internal static bool Running;
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish");
private static readonly int ConnectionCount = 4;
private static string _buildVer; private static string _buildVer;
private static string _platformExt; private static string _platformExt;
private static string _buildUrl; private static string _buildUrl;
private static long _buildSize; private static long _buildSize;
private const string GitHubApiURL = "https://api.github.com";
// On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates.
private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" }; private static readonly string[] WindowsDependencyDirs = new string[] { "bin", "etc", "lib", "share" };
@@ -44,7 +45,7 @@ namespace Ryujinx.Modules
{ {
HttpClient result = new HttpClient(); HttpClient result = new HttpClient();
// Required by GitHub to interract with APIs. // Required by GitHub to interact with APIs.
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
return result; return result;
@@ -101,8 +102,7 @@ namespace Ryujinx.Modules
// Get latest version number from GitHub API // Get latest version number from GitHub API
try try
{ {
using (HttpClient jsonClient = ConstructHttpClient()) using HttpClient jsonClient = ConstructHttpClient();
{
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
// Fetch latest build information // Fetch latest build information
@@ -146,7 +146,6 @@ namespace Ryujinx.Modules
return; return;
} }
} }
}
catch (Exception exception) catch (Exception exception)
{ {
Logger.Error?.Print(LogClass.Application, exception.Message); Logger.Error?.Print(LogClass.Application, exception.Message);
@@ -247,16 +246,15 @@ namespace Ryujinx.Modules
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < ConnectionCount; i++)
{ {
list.Add(new byte[0]); list.Add(Array.Empty<byte>());
} }
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < ConnectionCount; i++)
{ {
#pragma warning disable SYSLIB0014 #pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
using (WebClient client = new WebClient()) using WebClient client = new WebClient();
#pragma warning restore SYSLIB0014 #pragma warning restore SYSLIB0014
{
webClients.Add(client); webClients.Add(client);
if (i == ConnectionCount - 1) if (i == ConnectionCount - 1)
@@ -329,9 +327,9 @@ namespace Ryujinx.Modules
Logger.Warning?.Print(LogClass.Application, ex.Message); Logger.Warning?.Print(LogClass.Application, ex.Message);
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
for (int j = 0; j < webClients.Count; j++) foreach (WebClient webClient in webClients)
{ {
webClients[j].CancelAsync(); webClient.CancelAsync();
} }
DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile);
@@ -340,12 +338,10 @@ namespace Ryujinx.Modules
} }
} }
} }
}
private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{ {
using (HttpClient client = new HttpClient()) using HttpClient client = new HttpClient();
{
// We do not want to timeout while downloading // We do not want to timeout while downloading
client.Timeout = TimeSpan.FromDays(1); client.Timeout = TimeSpan.FromDays(1);
@@ -378,31 +374,16 @@ namespace Ryujinx.Modules
InstallUpdate(updateDialog, updateFile); InstallUpdate(updateDialog, updateFile);
} }
}
private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile)
{ {
Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)); Thread worker = new Thread(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile))
worker.Name = "Updater.SingleThreadWorker"; {
Name = "Updater.SingleThreadWorker"
};
worker.Start(); worker.Start();
} }
private static void SetFileExecutable(string path)
{
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
UnixFileMode.UserWrite |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows() && File.Exists(path))
{
File.SetUnixFileMode(path, ExecutableFileMode);
}
}
private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile)
{ {
// Extract Update // Extract Update
@@ -411,15 +392,17 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
{ {
using (Stream inStream = File.OpenRead(updateFile)) using Stream inStream = File.OpenRead(updateFile);
using (Stream gzipStream = new GZipInputStream(inStream)) using Stream gzipStream = new GZipInputStream(inStream);
using (TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII)) using TarInputStream tarStream = new TarInputStream(gzipStream, Encoding.ASCII);
{
updateDialog.ProgressBar.MaxValue = inStream.Length; updateDialog.ProgressBar.MaxValue = inStream.Length;
await Task.Run(() => await Task.Run(() =>
{ {
TarEntry tarEntry; TarEntry tarEntry;
if (!OperatingSystem.IsWindows())
{
while ((tarEntry = tarStream.GetNextEntry()) != null) while ((tarEntry = tarStream.GetNextEntry()) != null)
{ {
if (tarEntry.IsDirectory) continue; if (tarEntry.IsDirectory) continue;
@@ -433,6 +416,7 @@ namespace Ryujinx.Modules
tarStream.CopyEntryContents(outStream); tarStream.CopyEntryContents(outStream);
} }
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
TarEntry entry = tarEntry; TarEntry entry = tarEntry;
@@ -442,16 +426,15 @@ namespace Ryujinx.Modules
updateDialog.ProgressBar.Value += entry.Size; updateDialog.ProgressBar.Value += entry.Size;
}); });
} }
}
}); });
updateDialog.ProgressBar.Value = inStream.Length; updateDialog.ProgressBar.Value = inStream.Length;
} }
}
else else
{ {
using (Stream inStream = File.OpenRead(updateFile)) using Stream inStream = File.OpenRead(updateFile);
using (ZipFile zipFile = new ZipFile(inStream)) using ZipFile zipFile = new ZipFile(inStream);
{
updateDialog.ProgressBar.MaxValue = zipFile.Count; updateDialog.ProgressBar.MaxValue = zipFile.Count;
await Task.Run(() => await Task.Run(() =>
@@ -479,7 +462,6 @@ namespace Ryujinx.Modules
} }
}); });
} }
}
// Delete downloaded zip // Delete downloaded zip
File.Delete(updateFile); File.Delete(updateFile);
@@ -522,8 +504,6 @@ namespace Ryujinx.Modules
Directory.Delete(UpdateDir, true); Directory.Delete(UpdateDir, true);
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
updateDialog.MainText.Text = "Update Complete!"; updateDialog.MainText.Text = "Update Complete!";
updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?";
updateDialog.Modal = true; updateDialog.Modal = true;