Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
167f50bbcd | ||
|
ba91f5d401 | ||
|
79f6c18a9b | ||
|
4f63782bac | ||
|
6f5fcb7970 | ||
|
6ef8946169 | ||
|
42340fc743 | ||
|
103e7cb021 |
@@ -155,7 +155,7 @@
|
||||
"SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)",
|
||||
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
|
||||
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
|
||||
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p)",
|
||||
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)",
|
||||
"SettingsTabGraphicsAspectRatio": "Aspect Ratio:",
|
||||
"SettingsTabGraphicsAspectRatio4x3": "4:3",
|
||||
"SettingsTabGraphicsAspectRatio16x9": "16:9",
|
||||
@@ -452,13 +452,13 @@
|
||||
"CustomThemePathTooltip": "Path to custom GUI theme",
|
||||
"CustomThemeBrowseTooltip": "Browse for a custom GUI theme",
|
||||
"DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.",
|
||||
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.",
|
||||
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.",
|
||||
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.",
|
||||
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.",
|
||||
"RegionTooltip": "Change System Region",
|
||||
"LanguageTooltip": "Change System Language",
|
||||
"TimezoneTooltip": "Change System TimeZone",
|
||||
"TimeTooltip": "Change System Time",
|
||||
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference. We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
|
||||
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
|
||||
"PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.",
|
||||
"FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.",
|
||||
"AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.",
|
||||
@@ -472,10 +472,10 @@
|
||||
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||
"ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.",
|
||||
"ResolutionScaleTooltip": "Resolution Scale applied to applicable render targets",
|
||||
"ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
|
||||
"ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.",
|
||||
"AnisotropyTooltip": "Level of Anisotropic Filtering (set to Auto to use the value requested by the game)",
|
||||
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.",
|
||||
"AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.",
|
||||
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
|
||||
"ShaderDumpPathTooltip": "Graphics Shaders Dump Path",
|
||||
"FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.",
|
||||
"StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.",
|
||||
@@ -616,9 +616,9 @@
|
||||
"UserProfilesName": "Name:",
|
||||
"UserProfilesUserId": "User ID:",
|
||||
"SettingsTabGraphicsBackend": "Graphics Backend",
|
||||
"SettingsTabGraphicsBackendTooltip": "Graphics Backend to use",
|
||||
"SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
|
||||
"SettingsEnableTextureRecompression": "Enable Texture Recompression",
|
||||
"SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GiB VRAM.\n\nLeave OFF if unsure.",
|
||||
"SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
|
||||
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
|
||||
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
|
||||
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
|
||||
@@ -644,12 +644,12 @@
|
||||
"Recover": "Recover",
|
||||
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
|
||||
"GraphicsAALabel": "Anti-Aliasing:",
|
||||
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
||||
"GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling",
|
||||
"GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
||||
"GraphicsScalingFilterLevelLabel": "Level",
|
||||
"GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level",
|
||||
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
||||
"SmaaLow": "SMAA Low",
|
||||
"SmaaMedium": "SMAA Medium",
|
||||
"SmaaHigh": "SMAA High",
|
||||
@@ -657,12 +657,12 @@
|
||||
"UserEditorTitle": "Edit User",
|
||||
"UserEditorTitleCreate": "Create User",
|
||||
"SettingsTabNetworkInterface": "Network Interface:",
|
||||
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features",
|
||||
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.",
|
||||
"NetworkInterfaceDefault": "Default",
|
||||
"PackagingShaders": "Packaging Shaders",
|
||||
"AboutChangelogButton": "View Changelog on GitHub",
|
||||
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
|
||||
"SettingsTabNetworkMultiplayer": "Multiplayer",
|
||||
"MultiplayerMode": "Mode:",
|
||||
"MultiplayerModeTooltip": "Change multiplayer mode"
|
||||
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure."
|
||||
}
|
||||
|
@@ -59,6 +59,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
EnableMultiTouch = true,
|
||||
EnableIme = true,
|
||||
EnableInputFocusProxy = true,
|
||||
RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
|
||||
})
|
||||
.With(new Win32PlatformOptions
|
||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
{
|
||||
static class Decoder
|
||||
{
|
||||
private const int MaxInstructionsPerBlock = 1000;
|
||||
private const int MaxInstructionsPerFunction = 10000;
|
||||
|
||||
private const uint NzcvFlags = 0xfu << 28;
|
||||
private const uint CFlag = 0x1u << 29;
|
||||
@@ -22,10 +22,11 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
|
||||
bool hasHostCall = false;
|
||||
bool hasMemoryInstruction = false;
|
||||
int totalInsts = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Block block = Decode(cpuPreset, memoryManager, address, ref useMask, ref hasHostCall, ref hasMemoryInstruction);
|
||||
Block block = Decode(cpuPreset, memoryManager, address, ref totalInsts, ref useMask, ref hasHostCall, ref hasMemoryInstruction);
|
||||
|
||||
if (!block.IsTruncated && TryGetBranchTarget(block, out ulong targetAddress))
|
||||
{
|
||||
@@ -230,6 +231,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
CpuPreset cpuPreset,
|
||||
IMemoryManager memoryManager,
|
||||
ulong address,
|
||||
ref int totalInsts,
|
||||
ref RegisterMask useMask,
|
||||
ref bool hasHostCall,
|
||||
ref bool hasMemoryInstruction)
|
||||
@@ -272,7 +274,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
||||
|
||||
uint tempGprUseMask = gprUseMask | instGprReadMask | instGprWriteMask;
|
||||
|
||||
if (CalculateAvailableTemps(tempGprUseMask) < CalculateRequiredGprTemps(tempGprUseMask) || insts.Count >= MaxInstructionsPerBlock)
|
||||
if (CalculateAvailableTemps(tempGprUseMask) < CalculateRequiredGprTemps(tempGprUseMask) || totalInsts++ >= MaxInstructionsPerFunction)
|
||||
{
|
||||
isTruncated = true;
|
||||
address -= 4UL;
|
||||
|
@@ -5,6 +5,8 @@ using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
@@ -65,6 +67,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
private readonly Action<ulong, ulong> _loadDelegate;
|
||||
private readonly Action<ulong, ulong> _modifiedDelegate;
|
||||
|
||||
private HashSet<MultiRangeBuffer> _virtualDependencies;
|
||||
private readonly ReaderWriterLockSlim _virtualDependenciesLock;
|
||||
|
||||
private int _sequenceNumber;
|
||||
|
||||
private readonly bool _useGranular;
|
||||
@@ -152,6 +157,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
_externalFlushDelegate = new RegionSignal(ExternalFlush);
|
||||
_loadDelegate = new Action<ulong, ulong>(LoadRegion);
|
||||
_modifiedDelegate = new Action<ulong, ulong>(RegionModified);
|
||||
|
||||
_virtualDependenciesLock = new ReaderWriterLockSlim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -220,6 +227,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// </remarks>
|
||||
/// <param name="address">Start address of the range to synchronize</param>
|
||||
/// <param name="size">Size in bytes of the range to synchronize</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SynchronizeMemory(ulong address, ulong size)
|
||||
{
|
||||
if (_useGranular)
|
||||
@@ -239,6 +247,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
else
|
||||
{
|
||||
_context.Renderer.SetBufferData(Handle, 0, _physicalMemory.GetSpan(Address, (int)Size));
|
||||
CopyToDependantVirtualBuffers();
|
||||
}
|
||||
|
||||
_sequenceNumber = _context.SequenceNumber;
|
||||
@@ -460,6 +469,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
int offset = (int)(mAddress - Address);
|
||||
|
||||
_context.Renderer.SetBufferData(Handle, offset, _physicalMemory.GetSpan(mAddress, (int)mSize));
|
||||
|
||||
CopyToDependantVirtualBuffers(mAddress, mSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -520,6 +531,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="dstOffset">The offset of the destination buffer to copy into</param>
|
||||
public void CopyTo(Buffer destination, int dstOffset)
|
||||
{
|
||||
CopyFromDependantVirtualBuffers();
|
||||
_context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
|
||||
}
|
||||
|
||||
@@ -536,7 +548,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
||||
|
||||
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
||||
_physicalMemory.WriteUntracked(address, data.Get());
|
||||
_physicalMemory.WriteUntracked(address, CopyFromDependantVirtualBuffers(data.Get(), address, size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -617,6 +629,207 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
UnmappedSequence++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a virtual buffer dependency, indicating that a virtual buffer depends on data from this buffer.
|
||||
/// </summary>
|
||||
/// <param name="virtualBuffer">Dependant virtual buffer</param>
|
||||
public void AddVirtualDependency(MultiRangeBuffer virtualBuffer)
|
||||
{
|
||||
_virtualDependenciesLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
(_virtualDependencies ??= new()).Add(virtualBuffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_virtualDependenciesLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a virtual buffer dependency, indicating that a virtual buffer no longer depends on data from this buffer.
|
||||
/// </summary>
|
||||
/// <param name="virtualBuffer">Dependant virtual buffer</param>
|
||||
public void RemoveVirtualDependency(MultiRangeBuffer virtualBuffer)
|
||||
{
|
||||
_virtualDependenciesLock.EnterWriteLock();
|
||||
|
||||
try
|
||||
{
|
||||
if (_virtualDependencies != null)
|
||||
{
|
||||
_virtualDependencies.Remove(virtualBuffer);
|
||||
|
||||
if (_virtualDependencies.Count == 0)
|
||||
{
|
||||
_virtualDependencies = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_virtualDependenciesLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the buffer data to all virtual buffers that depends on it.
|
||||
/// </summary>
|
||||
public void CopyToDependantVirtualBuffers()
|
||||
{
|
||||
CopyToDependantVirtualBuffers(Address, Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the buffer data inside the specifide range to all virtual buffers that depends on it.
|
||||
/// </summary>
|
||||
/// <param name="address">Address of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
public void CopyToDependantVirtualBuffers(ulong address, ulong size)
|
||||
{
|
||||
if (_virtualDependencies != null)
|
||||
{
|
||||
foreach (var virtualBuffer in _virtualDependencies)
|
||||
{
|
||||
CopyToDependantVirtualBuffer(virtualBuffer, address, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all modified ranges from all virtual buffers back into this buffer.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CopyFromDependantVirtualBuffers()
|
||||
{
|
||||
if (_virtualDependencies != null)
|
||||
{
|
||||
CopyFromDependantVirtualBuffersImpl();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all modified ranges from all virtual buffers back into this buffer.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void CopyFromDependantVirtualBuffersImpl()
|
||||
{
|
||||
foreach (var virtualBuffer in _virtualDependencies.OrderBy(x => x.ModificationSequenceNumber))
|
||||
{
|
||||
virtualBuffer.ConsumeModifiedRegion(this, (mAddress, mSize) =>
|
||||
{
|
||||
// Get offset inside both this and the virtual buffer.
|
||||
// Note that sometimes there is no right answer for the virtual offset,
|
||||
// as the same physical range might be mapped multiple times inside a virtual buffer.
|
||||
// We just assume it does not happen in practice as it can only be implemented correctly
|
||||
// when the host has support for proper sparse mapping.
|
||||
|
||||
ulong mEndAddress = mAddress + mSize;
|
||||
mAddress = Math.Max(mAddress, Address);
|
||||
mSize = Math.Min(mEndAddress, EndAddress) - mAddress;
|
||||
|
||||
int physicalOffset = (int)(mAddress - Address);
|
||||
int virtualOffset = virtualBuffer.Range.FindOffset(new(mAddress, mSize));
|
||||
|
||||
_context.Renderer.Pipeline.CopyBuffer(virtualBuffer.Handle, Handle, virtualOffset, physicalOffset, (int)mSize);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all overlapping modified ranges from all virtual buffers back into this buffer, and returns an updated span with the data.
|
||||
/// </summary>
|
||||
/// <param name="dataSpan">Span where the unmodified data will be taken from for the output</param>
|
||||
/// <param name="address">Address of the region to copy</param>
|
||||
/// <param name="size">Size of the region to copy in bytes</param>
|
||||
/// <returns>A span with <paramref name="dataSpan"/>, and the data for all modified ranges if any</returns>
|
||||
private ReadOnlySpan<byte> CopyFromDependantVirtualBuffers(ReadOnlySpan<byte> dataSpan, ulong address, ulong size)
|
||||
{
|
||||
_virtualDependenciesLock.EnterReadLock();
|
||||
|
||||
try
|
||||
{
|
||||
if (_virtualDependencies != null)
|
||||
{
|
||||
byte[] storage = dataSpan.ToArray();
|
||||
|
||||
foreach (var virtualBuffer in _virtualDependencies.OrderBy(x => x.ModificationSequenceNumber))
|
||||
{
|
||||
virtualBuffer.ConsumeModifiedRegion(address, size, (mAddress, mSize) =>
|
||||
{
|
||||
// Get offset inside both this and the virtual buffer.
|
||||
// Note that sometimes there is no right answer for the virtual offset,
|
||||
// as the same physical range might be mapped multiple times inside a virtual buffer.
|
||||
// We just assume it does not happen in practice as it can only be implemented correctly
|
||||
// when the host has support for proper sparse mapping.
|
||||
|
||||
ulong mEndAddress = mAddress + mSize;
|
||||
mAddress = Math.Max(mAddress, address);
|
||||
mSize = Math.Min(mEndAddress, address + size) - mAddress;
|
||||
|
||||
int physicalOffset = (int)(mAddress - Address);
|
||||
int virtualOffset = virtualBuffer.Range.FindOffset(new(mAddress, mSize));
|
||||
|
||||
_context.Renderer.Pipeline.CopyBuffer(virtualBuffer.Handle, Handle, virtualOffset, physicalOffset, (int)size);
|
||||
virtualBuffer.GetData(storage.AsSpan().Slice((int)(mAddress - address), (int)mSize), virtualOffset, (int)mSize);
|
||||
});
|
||||
}
|
||||
|
||||
dataSpan = storage;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_virtualDependenciesLock.ExitReadLock();
|
||||
}
|
||||
|
||||
return dataSpan;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the buffer data to the specified virtual buffer.
|
||||
/// </summary>
|
||||
/// <param name="virtualBuffer">Virtual buffer to copy the data into</param>
|
||||
public void CopyToDependantVirtualBuffer(MultiRangeBuffer virtualBuffer)
|
||||
{
|
||||
CopyToDependantVirtualBuffer(virtualBuffer, Address, Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the buffer data inside the given range to the specified virtual buffer.
|
||||
/// </summary>
|
||||
/// <param name="virtualBuffer">Virtual buffer to copy the data into</param>
|
||||
/// <param name="address">Address of the range</param>
|
||||
/// <param name="size">Size of the range in bytes</param>
|
||||
public void CopyToDependantVirtualBuffer(MultiRangeBuffer virtualBuffer, ulong address, ulong size)
|
||||
{
|
||||
// Broadcast data to all ranges of the virtual buffer that are contained inside this buffer.
|
||||
|
||||
ulong lastOffset = 0;
|
||||
|
||||
while (virtualBuffer.TryGetPhysicalOffset(this, lastOffset, out ulong srcOffset, out ulong dstOffset, out ulong copySize))
|
||||
{
|
||||
ulong innerOffset = address - Address;
|
||||
ulong innerEndOffset = (address + size) - Address;
|
||||
|
||||
lastOffset = dstOffset + copySize;
|
||||
|
||||
// Clamp range to the specified range.
|
||||
ulong copySrcOffset = Math.Max(srcOffset, innerOffset);
|
||||
ulong copySrcEndOffset = Math.Min(innerEndOffset, srcOffset + copySize);
|
||||
|
||||
if (copySrcEndOffset > copySrcOffset)
|
||||
{
|
||||
copySize = copySrcEndOffset - copySrcOffset;
|
||||
dstOffset += copySrcOffset - srcOffset;
|
||||
srcOffset = copySrcOffset;
|
||||
|
||||
_context.Renderer.Pipeline.CopyBuffer(Handle, virtualBuffer.Handle, (int)srcOffset, (int)dstOffset, (int)copySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the buffer reference count.
|
||||
/// </summary>
|
||||
|
@@ -3,6 +3,7 @@ using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
@@ -46,6 +47,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
|
||||
private bool _pruneCaches;
|
||||
private int _virtualModifiedSequenceNumber;
|
||||
|
||||
public event Action NotifyBuffersModified;
|
||||
|
||||
@@ -125,7 +127,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
/// <summary>
|
||||
/// Performs address translation of the GPU virtual address, and creates
|
||||
/// new buffers, if needed, for the specified range.
|
||||
/// new physical and virtual buffers, if needed, for the specified range.
|
||||
/// </summary>
|
||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
@@ -138,12 +140,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
return new MultiRange(MemoryManager.PteUnmapped, size);
|
||||
}
|
||||
|
||||
bool supportsSparse = _context.Capabilities.SupportsSparseBuffer;
|
||||
|
||||
// Fast path not taken for non-contiguous ranges,
|
||||
// since multi-range buffers are not coalesced, so a buffer that covers
|
||||
// the entire cached range might not actually exist.
|
||||
if (memoryManager.VirtualBufferCache.TryGetOrAddRange(gpuVa, size, supportsSparse, out MultiRange range) &&
|
||||
if (memoryManager.VirtualRangeCache.TryGetOrAddRange(gpuVa, size, out MultiRange range) &&
|
||||
range.Count == 1)
|
||||
{
|
||||
return range;
|
||||
@@ -154,6 +154,50 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
return range;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs address translation of the GPU virtual address, and creates
|
||||
/// new physical buffers, if needed, for the specified range.
|
||||
/// </summary>
|
||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
/// <returns>Physical ranges of the buffer, after address translation</returns>
|
||||
public MultiRange TranslateAndCreateMultiBuffersPhysicalOnly(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
return new MultiRange(MemoryManager.PteUnmapped, size);
|
||||
}
|
||||
|
||||
// Fast path not taken for non-contiguous ranges,
|
||||
// since multi-range buffers are not coalesced, so a buffer that covers
|
||||
// the entire cached range might not actually exist.
|
||||
if (memoryManager.VirtualRangeCache.TryGetOrAddRange(gpuVa, size, out MultiRange range) &&
|
||||
range.Count == 1)
|
||||
{
|
||||
return range;
|
||||
}
|
||||
|
||||
for (int i = 0; i < range.Count; i++)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(i);
|
||||
|
||||
if (subRange.Address != MemoryManager.PteUnmapped)
|
||||
{
|
||||
if (range.Count > 1)
|
||||
{
|
||||
CreateBuffer(subRange.Address, subRange.Size, SparseBufferAlignmentSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateBuffer(subRange.Address, subRange.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new buffer for the specified range, if it does not yet exist.
|
||||
/// This can be used to ensure the existance of a buffer.
|
||||
@@ -263,41 +307,108 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
}
|
||||
|
||||
BufferRange[] storages = new BufferRange[range.Count];
|
||||
MultiRangeBuffer multiRangeBuffer;
|
||||
|
||||
MemoryRange[] alignedSubRanges = new MemoryRange[range.Count];
|
||||
|
||||
ulong alignmentMask = SparseBufferAlignmentSize - 1;
|
||||
|
||||
for (int i = 0; i < range.Count; i++)
|
||||
if (_context.Capabilities.SupportsSparseBuffer)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(i);
|
||||
BufferRange[] storages = new BufferRange[range.Count];
|
||||
|
||||
for (int i = 0; i < range.Count; i++)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(i);
|
||||
|
||||
if (subRange.Address != MemoryManager.PteUnmapped)
|
||||
{
|
||||
ulong endAddress = subRange.Address + subRange.Size;
|
||||
|
||||
ulong alignedAddress = subRange.Address & ~alignmentMask;
|
||||
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
|
||||
ulong alignedSize = alignedEndAddress - alignedAddress;
|
||||
|
||||
Buffer buffer = _buffers.FindFirstOverlap(alignedAddress, alignedSize);
|
||||
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
|
||||
|
||||
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
|
||||
storages[i] = bufferRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong alignedSize = (subRange.Size + alignmentMask) & ~alignmentMask;
|
||||
|
||||
alignedSubRanges[i] = new MemoryRange(MemoryManager.PteUnmapped, alignedSize);
|
||||
storages[i] = new BufferRange(BufferHandle.Null, 0, (int)alignedSize);
|
||||
}
|
||||
}
|
||||
|
||||
multiRangeBuffer = new(_context, new MultiRange(alignedSubRanges), storages);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < range.Count; i++)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(i);
|
||||
|
||||
if (subRange.Address != MemoryManager.PteUnmapped)
|
||||
{
|
||||
ulong endAddress = subRange.Address + subRange.Size;
|
||||
|
||||
ulong alignedAddress = subRange.Address & ~alignmentMask;
|
||||
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
|
||||
ulong alignedSize = alignedEndAddress - alignedAddress;
|
||||
|
||||
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong alignedSize = (subRange.Size + alignmentMask) & ~alignmentMask;
|
||||
|
||||
alignedSubRanges[i] = new MemoryRange(MemoryManager.PteUnmapped, alignedSize);
|
||||
}
|
||||
}
|
||||
|
||||
multiRangeBuffer = new(_context, new MultiRange(alignedSubRanges));
|
||||
|
||||
UpdateVirtualBufferDependencies(multiRangeBuffer);
|
||||
}
|
||||
|
||||
_multiRangeBuffers.Add(multiRangeBuffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two-way dependencies to all physical buffers contained within a given virtual buffer.
|
||||
/// </summary>
|
||||
/// <param name="virtualBuffer">Virtual buffer to have dependencies added</param>
|
||||
private void UpdateVirtualBufferDependencies(MultiRangeBuffer virtualBuffer)
|
||||
{
|
||||
virtualBuffer.ClearPhysicalDependencies();
|
||||
|
||||
ulong dstOffset = 0;
|
||||
|
||||
HashSet<Buffer> physicalBuffers = new();
|
||||
|
||||
for (int i = 0; i < virtualBuffer.Range.Count; i++)
|
||||
{
|
||||
MemoryRange subRange = virtualBuffer.Range.GetSubRange(i);
|
||||
|
||||
if (subRange.Address != MemoryManager.PteUnmapped)
|
||||
{
|
||||
ulong endAddress = subRange.Address + subRange.Size;
|
||||
Buffer buffer = _buffers.FindFirstOverlap(subRange.Address, subRange.Size);
|
||||
|
||||
ulong alignedAddress = subRange.Address & ~alignmentMask;
|
||||
ulong alignedEndAddress = (endAddress + alignmentMask) & ~alignmentMask;
|
||||
ulong alignedSize = alignedEndAddress - alignedAddress;
|
||||
|
||||
Buffer buffer = _buffers.FindFirstOverlap(alignedAddress, alignedSize);
|
||||
BufferRange bufferRange = buffer.GetRange(alignedAddress, alignedSize, false);
|
||||
|
||||
storages[i] = bufferRange;
|
||||
alignedSubRanges[i] = new MemoryRange(alignedAddress, alignedSize);
|
||||
virtualBuffer.AddPhysicalDependency(buffer, subRange.Address, dstOffset, subRange.Size);
|
||||
physicalBuffers.Add(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong alignedSize = (subRange.Size + alignmentMask) & ~alignmentMask;
|
||||
|
||||
storages[i] = new BufferRange(BufferHandle.Null, 0, (int)alignedSize);
|
||||
alignedSubRanges[i] = new MemoryRange(MemoryManager.PteUnmapped, alignedSize);
|
||||
}
|
||||
dstOffset += subRange.Size;
|
||||
}
|
||||
|
||||
MultiRangeBuffer multiRangeBuffer = new(_context, new MultiRange(alignedSubRanges), storages);
|
||||
|
||||
_multiRangeBuffers.Add(multiRangeBuffer);
|
||||
foreach (var buffer in physicalBuffers)
|
||||
{
|
||||
buffer.CopyToDependantVirtualBuffer(virtualBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -620,8 +731,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="size">Size in bytes of the copy</param>
|
||||
public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
|
||||
{
|
||||
MultiRange srcRange = TranslateAndCreateMultiBuffers(memoryManager, srcVa, size);
|
||||
MultiRange dstRange = TranslateAndCreateMultiBuffers(memoryManager, dstVa, size);
|
||||
MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size);
|
||||
MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size);
|
||||
|
||||
if (srcRange.Count == 1 && dstRange.Count == 1)
|
||||
{
|
||||
@@ -701,6 +812,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
dstBuffer.ClearModified(dstAddress, size);
|
||||
memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
|
||||
}
|
||||
|
||||
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -715,7 +828,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="value">Value to be written into the buffer</param>
|
||||
public void ClearBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size, uint value)
|
||||
{
|
||||
MultiRange range = TranslateAndCreateMultiBuffers(memoryManager, gpuVa, size);
|
||||
MultiRange range = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, gpuVa, size);
|
||||
|
||||
for (int index = 0; index < range.Count; index++)
|
||||
{
|
||||
@@ -727,6 +840,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
|
||||
|
||||
memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
|
||||
|
||||
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,6 +921,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
}
|
||||
|
||||
if (write && buffer != null && !_context.Capabilities.SupportsSparseBuffer)
|
||||
{
|
||||
buffer.AddModifiedRegion(range, ++_virtualModifiedSequenceNumber);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -825,6 +945,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
buffer = _buffers.FindFirstOverlap(address, size);
|
||||
|
||||
buffer.CopyFromDependantVirtualBuffers();
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
|
||||
if (write)
|
||||
@@ -849,14 +970,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
if (range.Count == 1)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(0);
|
||||
SynchronizeBufferRange(subRange.Address, subRange.Size);
|
||||
SynchronizeBufferRange(subRange.Address, subRange.Size, copyBackVirtual: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < range.Count; index++)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(index);
|
||||
SynchronizeBufferRange(subRange.Address, subRange.Size);
|
||||
SynchronizeBufferRange(subRange.Address, subRange.Size, copyBackVirtual: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -866,12 +987,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the memory range</param>
|
||||
/// <param name="size">Size in bytes of the memory range</param>
|
||||
private void SynchronizeBufferRange(ulong address, ulong size)
|
||||
/// <param name="copyBackVirtual">Whether virtual buffers that uses this buffer as backing memory should have its data copied back if modified</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SynchronizeBufferRange(ulong address, ulong size, bool copyBackVirtual)
|
||||
{
|
||||
if (size != 0)
|
||||
{
|
||||
Buffer buffer = _buffers.FindFirstOverlap(address, size);
|
||||
|
||||
if (copyBackVirtual)
|
||||
{
|
||||
buffer.CopyFromDependantVirtualBuffers();
|
||||
}
|
||||
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
}
|
||||
}
|
||||
|
@@ -40,9 +40,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
internal PhysicalMemory Physical { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Virtual buffer cache.
|
||||
/// Virtual range cache.
|
||||
/// </summary>
|
||||
internal VirtualBufferCache VirtualBufferCache { get; }
|
||||
internal VirtualRangeCache VirtualRangeCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache of GPU counters.
|
||||
@@ -56,12 +56,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
internal MemoryManager(PhysicalMemory physicalMemory)
|
||||
{
|
||||
Physical = physicalMemory;
|
||||
VirtualBufferCache = new VirtualBufferCache(this);
|
||||
VirtualRangeCache = new VirtualRangeCache(this);
|
||||
CounterCache = new CounterCache();
|
||||
_pageTable = new ulong[PtLvl0Size][];
|
||||
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
|
||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
||||
MemoryUnmapped += VirtualBufferCache.MemoryUnmappedHandler;
|
||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
@@ -21,12 +22,73 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// </summary>
|
||||
public MultiRange Range { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Ever increasing counter value indicating when the buffer was modified relative to other buffers.
|
||||
/// </summary>
|
||||
public int ModificationSequenceNumber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Physical buffer dependency entry.
|
||||
/// </summary>
|
||||
private readonly struct PhysicalDependency
|
||||
{
|
||||
/// <summary>
|
||||
/// Physical buffer.
|
||||
/// </summary>
|
||||
public readonly Buffer PhysicalBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of the range on the physical buffer.
|
||||
/// </summary>
|
||||
public readonly ulong PhysicalOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of the range on the virtual buffer.
|
||||
/// </summary>
|
||||
public readonly ulong VirtualOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Size of the range.
|
||||
/// </summary>
|
||||
public readonly ulong Size;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new physical dependency.
|
||||
/// </summary>
|
||||
/// <param name="physicalBuffer">Physical buffer</param>
|
||||
/// <param name="physicalOffset">Offset of the range on the physical buffer</param>
|
||||
/// <param name="virtualOffset">Offset of the range on the virtual buffer</param>
|
||||
/// <param name="size">Size of the range</param>
|
||||
public PhysicalDependency(Buffer physicalBuffer, ulong physicalOffset, ulong virtualOffset, ulong size)
|
||||
{
|
||||
PhysicalBuffer = physicalBuffer;
|
||||
PhysicalOffset = physicalOffset;
|
||||
VirtualOffset = virtualOffset;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private List<PhysicalDependency> _dependencies;
|
||||
private BufferModifiedRangeList _modifiedRanges = null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the buffer belongs to</param>
|
||||
/// <param name="range">Range of memory where the data is mapped</param>
|
||||
/// <param name="storages">Backing memory for the buffers</param>
|
||||
public MultiRangeBuffer(GpuContext context, MultiRange range)
|
||||
{
|
||||
_context = context;
|
||||
Range = range;
|
||||
Handle = context.Renderer.CreateBuffer((int)range.GetSize());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the buffer belongs to</param>
|
||||
/// <param name="range">Range of memory where the data is mapped</param>
|
||||
/// <param name="storages">Backing memory for the buffer</param>
|
||||
public MultiRangeBuffer(GpuContext context, MultiRange range, ReadOnlySpan<BufferRange> storages)
|
||||
{
|
||||
_context = context;
|
||||
@@ -49,11 +111,134 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
return new BufferRange(Handle, offset, (int)range.GetSize());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all physical buffer dependencies.
|
||||
/// </summary>
|
||||
public void ClearPhysicalDependencies()
|
||||
{
|
||||
_dependencies?.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a physical buffer dependency.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Physical buffer to be added</param>
|
||||
/// <param name="rangeAddress">Address inside the physical buffer where the virtual buffer range is located</param>
|
||||
/// <param name="dstOffset">Offset inside the virtual buffer where the physical range is located</param>
|
||||
/// <param name="rangeSize">Size of the range in bytes</param>
|
||||
public void AddPhysicalDependency(Buffer buffer, ulong rangeAddress, ulong dstOffset, ulong rangeSize)
|
||||
{
|
||||
(_dependencies ??= new()).Add(new(buffer, rangeAddress - buffer.Address, dstOffset, rangeSize));
|
||||
buffer.AddVirtualDependency(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the physical range corresponding to the given physical buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Physical buffer</param>
|
||||
/// <param name="minimumVirtOffset">Minimum virtual offset that a range match can have</param>
|
||||
/// <param name="physicalOffset">Physical offset of the match</param>
|
||||
/// <param name="virtualOffset">Virtual offset of the match, always greater than or equal <paramref name="minimumVirtOffset"/></param>
|
||||
/// <param name="size">Size of the range match</param>
|
||||
/// <returns>True if a match was found for the given parameters, false otherwise</returns>
|
||||
public bool TryGetPhysicalOffset(Buffer buffer, ulong minimumVirtOffset, out ulong physicalOffset, out ulong virtualOffset, out ulong size)
|
||||
{
|
||||
physicalOffset = 0;
|
||||
virtualOffset = 0;
|
||||
size = 0;
|
||||
|
||||
if (_dependencies != null)
|
||||
{
|
||||
foreach (var dependency in _dependencies)
|
||||
{
|
||||
if (dependency.PhysicalBuffer == buffer && dependency.VirtualOffset >= minimumVirtOffset)
|
||||
{
|
||||
physicalOffset = dependency.PhysicalOffset;
|
||||
virtualOffset = dependency.VirtualOffset;
|
||||
size = dependency.Size;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a modified virtual memory range.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only required when the host does not support sparse buffers, otherwise only physical buffers need to track modification.
|
||||
/// </remarks>
|
||||
/// <param name="range">Modified range</param>
|
||||
/// <param name="modifiedSequenceNumber">ModificationSequenceNumber</param>
|
||||
public void AddModifiedRegion(MultiRange range, int modifiedSequenceNumber)
|
||||
{
|
||||
_modifiedRanges ??= new(_context, null, null);
|
||||
|
||||
for (int i = 0; i < range.Count; i++)
|
||||
{
|
||||
MemoryRange subRange = range.GetSubRange(i);
|
||||
|
||||
_modifiedRanges.SignalModified(subRange.Address, subRange.Size);
|
||||
}
|
||||
|
||||
ModificationSequenceNumber = modifiedSequenceNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the specified <paramref name="rangeAction"/> for all modified ranges that overlaps with <paramref name="buffer"/>.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer to have its range checked</param>
|
||||
/// <param name="rangeAction">Action to perform for modified ranges</param>
|
||||
public void ConsumeModifiedRegion(Buffer buffer, Action<ulong, ulong> rangeAction)
|
||||
{
|
||||
ConsumeModifiedRegion(buffer.Address, buffer.Size, rangeAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the specified <paramref name="rangeAction"/> for all modified ranges that overlaps with <paramref name="address"/> and <paramref name="size"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Address of the region to consume</param>
|
||||
/// <param name="size">Size of the region to consume</param>
|
||||
/// <param name="rangeAction">Action to perform for modified ranges</param>
|
||||
public void ConsumeModifiedRegion(ulong address, ulong size, Action<ulong, ulong> rangeAction)
|
||||
{
|
||||
if (_modifiedRanges != null)
|
||||
{
|
||||
_modifiedRanges.GetRanges(address, size, rangeAction);
|
||||
_modifiedRanges.Clear(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets data from the specified region of the buffer, and places it on <paramref name="output"/>.
|
||||
/// </summary>
|
||||
/// <param name="output">Span to put the data into</param>
|
||||
/// <param name="offset">Offset of the buffer to get the data from</param>
|
||||
/// <param name="size">Size of the data in bytes</param>
|
||||
public void GetData(Span<byte> output, int offset, int size)
|
||||
{
|
||||
using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, size);
|
||||
data.Get().CopyTo(output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the host buffer.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_dependencies != null)
|
||||
{
|
||||
foreach (var dependency in _dependencies)
|
||||
{
|
||||
dependency.PhysicalBuffer.RemoveVirtualDependency(this);
|
||||
}
|
||||
|
||||
_dependencies = null;
|
||||
}
|
||||
|
||||
_context.Renderer.DeleteBuffer(Handle);
|
||||
}
|
||||
}
|
||||
|
@@ -6,9 +6,9 @@ using System.Threading;
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Virtual buffer cache.
|
||||
/// Virtual range cache.
|
||||
/// </summary>
|
||||
class VirtualBufferCache
|
||||
class VirtualRangeCache
|
||||
{
|
||||
private readonly MemoryManager _memoryManager;
|
||||
|
||||
@@ -68,10 +68,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
private int _hasDeferredUnmaps;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the virtual buffer cache.
|
||||
/// Creates a new instance of the virtual range cache.
|
||||
/// </summary>
|
||||
/// <param name="memoryManager">Memory manager that the virtual buffer cache belongs to</param>
|
||||
public VirtualBufferCache(MemoryManager memoryManager)
|
||||
/// <param name="memoryManager">Memory manager that the virtual range cache belongs to</param>
|
||||
public VirtualRangeCache(MemoryManager memoryManager)
|
||||
{
|
||||
_memoryManager = memoryManager;
|
||||
_virtualRanges = new RangeList<VirtualRange>();
|
||||
@@ -102,10 +102,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">GPU virtual address to get the physical range from</param>
|
||||
/// <param name="size">Size in bytes of the region</param>
|
||||
/// <param name="supportsSparse">Indicates host support for sparse buffer mapping of non-contiguous ranges</param>
|
||||
/// <param name="range">Physical range for the specified GPU virtual region</param>
|
||||
/// <returns>True if the range already existed, false if a new one was created and added</returns>
|
||||
public bool TryGetOrAddRange(ulong gpuVa, ulong size, bool supportsSparse, out MultiRange range)
|
||||
public bool TryGetOrAddRange(ulong gpuVa, ulong size, out MultiRange range)
|
||||
{
|
||||
VirtualRange[] overlaps = _virtualRangeOverlaps;
|
||||
int overlapsCount;
|
||||
@@ -158,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
else
|
||||
{
|
||||
found = true;
|
||||
found = overlap0.Range.Count == 1 || IsSparseAligned(overlap0.Range);
|
||||
range = overlap0.Range.Slice(gpuVa - overlap0.Address, size);
|
||||
}
|
||||
}
|
||||
@@ -175,11 +174,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
ShrinkOverlapsBufferIfNeeded();
|
||||
|
||||
// If the the range is not properly aligned for sparse mapping,
|
||||
// or if the host does not support sparse mapping, let's just
|
||||
// force it to a single range.
|
||||
// let's just force it to a single range.
|
||||
// This might cause issues in some applications that uses sparse
|
||||
// mappings.
|
||||
if (!IsSparseAligned(range) || !supportsSparse)
|
||||
if (!IsSparseAligned(range))
|
||||
{
|
||||
range = new MultiRange(range.GetSubRange(0).Address, size);
|
||||
}
|
@@ -95,6 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
List<BarrierWithStageFlags<T>> list) where T : unmanaged
|
||||
{
|
||||
int firstMatch = -1;
|
||||
int end = list.Count;
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
@@ -111,6 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (count >= target.Length)
|
||||
{
|
||||
end = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -128,6 +130,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (count >= target.Length)
|
||||
{
|
||||
end = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -141,18 +144,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
i -= deleteCount;
|
||||
|
||||
firstMatch = -1;
|
||||
end = list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstMatch == 0)
|
||||
if (firstMatch == 0 && end == list.Count)
|
||||
{
|
||||
list.Clear();
|
||||
}
|
||||
else if (firstMatch != -1)
|
||||
{
|
||||
int deleteCount = list.Count - firstMatch;
|
||||
int deleteCount = end - firstMatch;
|
||||
|
||||
list.RemoveRange(firstMatch, deleteCount);
|
||||
}
|
||||
|
@@ -299,13 +299,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
// When the pipeline layout changes, push descriptor bindings are invalidated.
|
||||
|
||||
AdvancePdSequence();
|
||||
|
||||
if (_gd.IsNvidiaPreTuring && !program.UsePushDescriptors && _program?.UsePushDescriptors == true && isBound)
|
||||
{
|
||||
// On older nvidia GPUs, we need to clear out the active push descriptor bindings when switching
|
||||
// to normal descriptors. Keeping them bound can prevent buffers from binding properly in future.
|
||||
ClearAndBindUniformBufferPd(cbs);
|
||||
}
|
||||
}
|
||||
|
||||
_program = program;
|
||||
@@ -806,35 +799,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAndBindUniformBufferPd(CommandBufferScoped cbs)
|
||||
{
|
||||
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||
|
||||
long updatedBindings = 0;
|
||||
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int index = binding + i;
|
||||
updatedBindings |= 1L << index;
|
||||
|
||||
var bufferInfo = new DescriptorBufferInfo();
|
||||
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref bufferInfo, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedBindings > 0)
|
||||
{
|
||||
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
|
||||
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc)
|
||||
{
|
||||
// We don't support clearing texture descriptors currently.
|
||||
|
@@ -111,6 +111,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bool usePushDescriptors = !isMinimal &&
|
||||
VulkanConfiguration.UsePushDescriptors &&
|
||||
_gd.Capabilities.SupportsPushDescriptors &&
|
||||
!_gd.IsNvidiaPreTuring &&
|
||||
!IsCompute &&
|
||||
CanUsePushDescriptors(gd, resourceLayout, IsCompute);
|
||||
|
||||
|
@@ -1821,5 +1821,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(1004)] // 17.0.0+
|
||||
// SetTouchScreenResolution(int width, int height, nn::applet::AppletResourceUserId)
|
||||
public ResultCode SetTouchScreenResolution(ServiceCtx context)
|
||||
{
|
||||
int width = context.RequestData.ReadInt32();
|
||||
int height = context.RequestData.ReadInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { width, height, appletResourceUserId });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user