From 2073ba29197be4fcdcd750db7e7b8a36110efd71 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 4 Jun 2022 20:36:36 +0100 Subject: [PATCH 1/5] Fix a potential GPFIFO submission race (#3378) The syncpoint maximum value represents the maximum possible syncpt value at a given time, however due to PBs being submitted before max was incremented, for a brief moment of time this is not the case which could lead to invalid behaviour if a game waits on the fence at that specific time. --- .../Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index f73e38917..9f16a280f 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -429,8 +429,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence)); } - Channel.PushEntries(entries); - header.Fence.Id = _channelSyncpoint.Id; if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) || header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue)) @@ -449,6 +447,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel header.Fence.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(header.Fence.Id); } + Channel.PushEntries(entries); + if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement)) { Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags)); From a3e7bb8eb40b66e61a5a3bfef0b780d0c76a31c1 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 5 Jun 2022 14:06:47 -0300 Subject: [PATCH 2/5] Copy dependency for multisample and non-multisample textures (#3382) * Use copy dependency for textures that differs in multisample but are otherwise compatible * Remove allowMs flag as it's no longer required for correctness, it's just an optimization now * Dispose intermmediate pool --- Ryujinx.Graphics.GAL/Target.cs | 8 ++ Ryujinx.Graphics.Gpu/Image/Texture.cs | 20 +--- Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 2 - .../Image/TextureCompatibility.cs | 6 +- .../Image/IntermmediatePool.cs | 98 +++++++++++++++++++ Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 51 ++++++++-- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 69 ++++++++++++- 7 files changed, 225 insertions(+), 29 deletions(-) create mode 100644 Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs diff --git a/Ryujinx.Graphics.GAL/Target.cs b/Ryujinx.Graphics.GAL/Target.cs index a62d628a8..e20bd3c84 100644 --- a/Ryujinx.Graphics.GAL/Target.cs +++ b/Ryujinx.Graphics.GAL/Target.cs @@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL CubemapArray, TextureBuffer } + + public static class TargetExtensions + { + public static bool IsMultisample(this Target target) + { + return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray; + } + } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index cfb7a3b76..aadb4260b 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1136,32 +1136,22 @@ namespace Ryujinx.Graphics.Gpu.Image /// Texture view physical memory ranges /// Layer size on the given texture /// Host GPU capabilities - /// Indicates that multisample textures are allowed to match non-multisample requested textures /// Texture view initial layer on this texture /// Texture view first mipmap level on this texture /// The level of compatiblilty a view with the given parameters created from this texture has - public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, bool allowMs, out int firstLayer, out int firstLevel) + public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel) { TextureViewCompatibility result = TextureViewCompatibility.Full; result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); if (result != TextureViewCompatibility.Incompatible) { - bool msTargetCompatible = false; + result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); - if (allowMs) + bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample(); + if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)) { - msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D; - } - - if (!msTargetCompatible) - { - result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info)); - - if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY) - { - result = TextureViewCompatibility.Incompatible; - } + result = TextureViewCompatibility.Incompatible; } if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat) diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 4fa80c95d..045410571 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -547,7 +547,6 @@ namespace Ryujinx.Graphics.Gpu.Image range.Value, sizeInfo.LayerSize, _context.Capabilities, - flags.HasFlag(TextureSearchFlags.ForCopy), out int firstLayer, out int firstLevel); @@ -662,7 +661,6 @@ namespace Ryujinx.Graphics.Gpu.Image overlap.Range, overlap.LayerSize, _context.Capabilities, - false, out int firstLayer, out int firstLevel); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index b798441f1..61b48dc4d 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -2,7 +2,6 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Texture; using System; -using System.Numerics; namespace Ryujinx.Graphics.Gpu.Image { @@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image case Target.Texture2DMultisample: case Target.Texture2DMultisampleArray: + if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray) + { + return TextureViewCompatibility.CopyOnly; + } + result = rhs.Target == Target.Texture2DMultisample || rhs.Target == Target.Texture2DMultisampleArray; break; diff --git a/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs new file mode 100644 index 000000000..bd6efc763 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs @@ -0,0 +1,98 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.OpenGL.Image +{ + class IntermmediatePool : IDisposable + { + private readonly Renderer _renderer; + private readonly List _entries; + + public IntermmediatePool(Renderer renderer) + { + _renderer = renderer; + _entries = new List(); + } + + public TextureView GetOrCreateWithAtLeast( + Target target, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + int width, + int height, + int depth, + int levels) + { + TextureView entry; + + for (int i = 0; i < _entries.Count; i++) + { + entry = _entries[i]; + + if (entry.Target == target && entry.Format == format) + { + if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels) + { + width = Math.Max(width, entry.Width); + height = Math.Max(height, entry.Height); + depth = Math.Max(depth, entry.Info.Depth); + levels = Math.Max(levels, entry.Info.Levels); + + entry.Dispose(); + entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels); + _entries[i] = entry; + } + + return entry; + } + } + + entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels); + _entries.Add(entry); + + return entry; + } + + private TextureView CreateNew( + Target target, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + int width, + int height, + int depth, + int levels) + { + return (TextureView)_renderer.CreateTexture(new TextureCreateInfo( + width, + height, + depth, + levels, + 1, + blockWidth, + blockHeight, + bytesPerPixel, + format, + DepthStencilMode.Depth, + target, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha), 1f); + } + + public void Dispose() + { + foreach (TextureView entry in _entries) + { + entry.Dispose(); + } + + _entries.Clear(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 9be865613..44804d436 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image { private readonly Renderer _renderer; + public IntermmediatePool IntermmediatePool { get; } + private int _srcFramebuffer; private int _dstFramebuffer; @@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureCopy(Renderer renderer) { _renderer = renderer; + IntermmediatePool = new IntermmediatePool(renderer); } public void Copy( @@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureView dst, Extents2D srcRegion, Extents2D dstRegion, - bool linearFilter) + bool linearFilter, + int srcLayer = 0, + int dstLayer = 0, + int srcLevel = 0, + int dstLevel = 0) + { + int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel); + int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer); + + Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels); + } + + public void Copy( + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter, + int srcLayer, + int dstLayer, + int srcLevel, + int dstLevel, + int layers, + int levels) { TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src; @@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); - int levels = Math.Min(src.Info.Levels, dst.Info.Levels); - int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers()); + if (srcLevel != 0) + { + srcRegion = srcRegion.Reduce(srcLevel); + } + + if (dstLevel != 0) + { + dstRegion = dstRegion.Reduce(dstLevel); + } for (int level = 0; level < levels; level++) { for (int layer = 0; layer < layers; layer++) { - if (layers > 1) + if ((srcLayer | dstLayer) != 0 || layers > 1) { - Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer); - Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer); + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer); } else { - Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level); - Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level); + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level); } ClearBufferMask mask = GetMask(src.Format); @@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image _copyPboHandle = 0; } + + IntermmediatePool.Dispose(); } } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 909a06200..afb9a278c 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); + if (destinationView.Target.IsMultisample() || Target.IsMultisample()) + { + Extents2D srcRegion = new Extents2D(0, 0, Width, Height); + Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + + TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast( + GetIntermmediateTarget(Target), + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + Width, + Height, + Info.Depth, + Info.Levels); + + GL.Disable(EnableCap.FramebufferSrgb); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true); + _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel); + + GL.Enable(EnableCap.FramebufferSrgb); + } + else + { + _renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); + } } public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { - TextureView destinationView = (TextureView)destination; + TextureView destinationView = (TextureView)destination; - _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + if (destinationView.Target.IsMultisample() || Target.IsMultisample()) + { + Extents2D srcRegion = new Extents2D(0, 0, Width, Height); + Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height); + + TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast( + GetIntermmediateTarget(Target), + Info.BlockWidth, + Info.BlockHeight, + Info.BytesPerPixel, + Format, + Math.Max(1, Width >> srcLevel), + Math.Max(1, Height >> srcLevel), + 1, + 1); + + GL.Disable(EnableCap.FramebufferSrgb); + + _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1); + _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1); + + GL.Enable(EnableCap.FramebufferSrgb); + } + else + { + _renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); + } + } + + private static Target GetIntermmediateTarget(Target srcTarget) + { + return srcTarget switch + { + Target.Texture2D => Target.Texture2DMultisample, + Target.Texture2DArray => Target.Texture2DMultisampleArray, + Target.Texture2DMultisampleArray => Target.Texture2DArray, + _ => Target.Texture2D + }; } public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) From 633c5ec3306e474dab15617c5f93b94546bd9e09 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 5 Jun 2022 14:15:27 -0300 Subject: [PATCH 3/5] Extend uses count from ushort to uint on Operand Data structure (#3374) --- .../IntermediateRepresentation/Operand.cs | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/ARMeilleure/IntermediateRepresentation/Operand.cs index 74b1c71ef..b3aafa7ae 100644 --- a/ARMeilleure/IntermediateRepresentation/Operand.cs +++ b/ARMeilleure/IntermediateRepresentation/Operand.cs @@ -14,10 +14,11 @@ namespace ARMeilleure.IntermediateRepresentation public byte Kind; public byte Type; public byte SymbolType; + public byte Padding; // Unused space. public ushort AssignmentsCount; public ushort AssignmentsCapacity; - public ushort UsesCount; - public ushort UsesCapacity; + public uint UsesCount; + public uint UsesCapacity; public Operation* Assignments; public Operation* Uses; public ulong Value; @@ -84,11 +85,11 @@ namespace ARMeilleure.IntermediateRepresentation { Debug.Assert(Kind != OperandKind.Memory); - return new ReadOnlySpan(_data->Uses, _data->UsesCount); + return new ReadOnlySpan(_data->Uses, (int)_data->UsesCount); } } - public int UsesCount => _data->UsesCount; + public int UsesCount => (int)_data->UsesCount; public int AssignmentsCount => _data->AssignmentsCount; public bool Relocatable => Symbol.Type != SymbolType.None; @@ -178,7 +179,7 @@ namespace ARMeilleure.IntermediateRepresentation { Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity); } - + if (index != default) { Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity); @@ -265,6 +266,13 @@ namespace ARMeilleure.IntermediateRepresentation data = Allocators.References.Allocate(initialCapacity); } + private static void New(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged + { + count = 0; + capacity = initialCapacity; + data = Allocators.References.Allocate(initialCapacity); + } + private static void Add(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged { if (count < capacity) @@ -294,6 +302,40 @@ namespace ARMeilleure.IntermediateRepresentation } } + private static void Add(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged + { + if (count < capacity) + { + data[count++] = item; + + return; + } + + // Could not add item in the fast path, fallback onto the slow path. + ExpandAdd(item, ref data, ref count, ref capacity); + + static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity) + { + uint newCount = checked(count + 1); + uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue); + + if (newCapacity <= capacity) + { + throw new OverflowException(); + } + + var oldSpan = new Span(data, (int)count); + + capacity = newCapacity; + data = Allocators.References.Allocate(capacity); + + oldSpan.CopyTo(new Span(data, (int)count)); + + data[count] = item; + count = newCount; + } + } + private static void Remove(in T item, ref T* data, ref ushort count) where T : unmanaged { var span = new Span(data, count); @@ -314,6 +356,26 @@ namespace ARMeilleure.IntermediateRepresentation } } + private static void Remove(in T item, ref T* data, ref uint count) where T : unmanaged + { + var span = new Span(data, (int)count); + + for (int i = 0; i < span.Length; i++) + { + if (EqualityComparer.Default.Equals(span[i], item)) + { + if (i + 1 < count) + { + span.Slice(i + 1).CopyTo(span.Slice(i)); + } + + count--; + + return; + } + } + } + public override int GetHashCode() { if (Kind == OperandKind.LocalVariable) From dd8f97ab9e77dde25c323feaff97cfc8f19207fa Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 5 Jun 2022 15:12:42 -0300 Subject: [PATCH 4/5] Remove freed memory range from tree on memory block disposal (#3347) * Remove freed memory range from tree on memory block disposal * PR feedback --- Ryujinx.Memory/MemoryBlock.cs | 6 +- Ryujinx.Memory/MemoryManagement.cs | 12 ++-- Ryujinx.Memory/MemoryManagementWindows.cs | 10 +-- .../WindowsShared/PlaceholderManager.cs | 69 ++++++++++++++++--- 4 files changed, 76 insertions(+), 21 deletions(-) diff --git a/Ryujinx.Memory/MemoryBlock.cs b/Ryujinx.Memory/MemoryBlock.cs index c6b85b582..b68a10005 100644 --- a/Ryujinx.Memory/MemoryBlock.cs +++ b/Ryujinx.Memory/MemoryBlock.cs @@ -19,6 +19,8 @@ namespace Ryujinx.Memory private ConcurrentDictionary _viewStorages; private int _viewCount; + internal bool ForceWindows4KBView => _forceWindows4KBView; + /// /// Pointer to the memory block data. /// @@ -145,7 +147,7 @@ namespace Ryujinx.Memory srcBlock.IncrementViewCount(); } - MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView); + MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, this); } /// @@ -156,7 +158,7 @@ namespace Ryujinx.Memory /// Size of the range to be unmapped public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size) { - MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView); + MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, this); } /// diff --git a/Ryujinx.Memory/MemoryManagement.cs b/Ryujinx.Memory/MemoryManagement.cs index 3b8a96649..77a8a1efb 100644 --- a/Ryujinx.Memory/MemoryManagement.cs +++ b/Ryujinx.Memory/MemoryManagement.cs @@ -68,17 +68,17 @@ namespace Ryujinx.Memory } } - public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, bool force4KBMap) + public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, MemoryBlock owner) { if (OperatingSystem.IsWindows()) { - if (force4KBMap) + if (owner.ForceWindows4KBView) { MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size); } else { - MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size); + MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner); } } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) @@ -91,17 +91,17 @@ namespace Ryujinx.Memory } } - public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, bool force4KBMap) + public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, MemoryBlock owner) { if (OperatingSystem.IsWindows()) { - if (force4KBMap) + if (owner.ForceWindows4KBView) { MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size); } else { - MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size); + MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner); } } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) diff --git a/Ryujinx.Memory/MemoryManagementWindows.cs b/Ryujinx.Memory/MemoryManagementWindows.cs index 3d51b64f7..6b4e7fbee 100644 --- a/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/Ryujinx.Memory/MemoryManagementWindows.cs @@ -68,9 +68,9 @@ namespace Ryujinx.Memory return WindowsApi.VirtualFree(location, size, AllocationType.Decommit); } - public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size) + public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner) { - _placeholders.MapView(sharedMemory, srcOffset, location, size); + _placeholders.MapView(sharedMemory, srcOffset, location, size, owner); } public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size) @@ -106,9 +106,9 @@ namespace Ryujinx.Memory } } - public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size) + public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) { - _placeholders.UnmapView(sharedMemory, location, size); + _placeholders.UnmapView(sharedMemory, location, size, owner); } public static void UnmapView4KB(IntPtr location, IntPtr size) @@ -154,7 +154,7 @@ namespace Ryujinx.Memory } else { - _placeholders.UnmapView(IntPtr.Zero, address, size); + _placeholders.UnreserveRange((ulong)address, (ulong)size); } return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release); diff --git a/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs b/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs index d465f3416..1b425d66f 100644 --- a/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs +++ b/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs @@ -44,6 +44,50 @@ namespace Ryujinx.Memory.WindowsShared } } + /// + /// Unreserves a range of memory that has been previously reserved with . + /// + /// Start address of the region to unreserve + /// Size in bytes of the region to unreserve + /// Thrown when the Windows API returns an error unreserving the memory + public void UnreserveRange(ulong address, ulong size) + { + ulong endAddress = address + size; + + var overlaps = Array.Empty>(); + int count; + + lock (_mappings) + { + count = _mappings.Get(address, endAddress, ref overlaps); + + for (int index = 0; index < count; index++) + { + var overlap = overlaps[index]; + + if (IsMapped(overlap.Value)) + { + if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2)) + { + throw new WindowsApiException("UnmapViewOfFile2"); + } + } + + _mappings.Remove(overlap); + } + } + + if (count > 1) + { + CheckFreeResult(WindowsApi.VirtualFree( + (IntPtr)address, + (IntPtr)size, + AllocationType.Release | AllocationType.CoalescePlaceholders)); + } + + RemoveProtection(address, size); + } + /// /// Maps a shared memory view on a previously reserved memory region. /// @@ -51,13 +95,14 @@ namespace Ryujinx.Memory.WindowsShared /// Offset in the shared memory to map /// Address to map the view into /// Size of the view in bytes - public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size) + /// Memory block that owns the mapping + public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner) { _partialUnmapLock.AcquireReaderLock(Timeout.Infinite); try { - UnmapViewInternal(sharedMemory, location, size); + UnmapViewInternal(sharedMemory, location, size, owner); MapViewInternal(sharedMemory, srcOffset, location, size); } finally @@ -173,13 +218,14 @@ namespace Ryujinx.Memory.WindowsShared /// Shared memory that the view being unmapped belongs to /// Address to unmap /// Size of the region to unmap in bytes - public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size) + /// Memory block that owns the mapping + public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) { _partialUnmapLock.AcquireReaderLock(Timeout.Infinite); try { - UnmapViewInternal(sharedMemory, location, size); + UnmapViewInternal(sharedMemory, location, size, owner); } finally { @@ -197,8 +243,9 @@ namespace Ryujinx.Memory.WindowsShared /// Shared memory that the view being unmapped belongs to /// Address to unmap /// Size of the region to unmap in bytes + /// Memory block that owns the mapping /// Thrown when the Windows API returns an error unmapping or remapping the memory - private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size) + private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; @@ -272,7 +319,7 @@ namespace Ryujinx.Memory.WindowsShared } } - CoalesceForUnmap(startAddress, unmapSize); + CoalesceForUnmap(startAddress, unmapSize, owner); RemoveProtection(startAddress, unmapSize); } @@ -281,15 +328,21 @@ namespace Ryujinx.Memory.WindowsShared /// /// Address of the region that was unmapped /// Size of the region that was unmapped in bytes - private void CoalesceForUnmap(ulong address, ulong size) + /// Memory block that owns the mapping + private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner) { ulong endAddress = address + size; + ulong blockAddress = (ulong)owner.Pointer; + ulong blockEnd = blockAddress + owner.Size; var overlaps = Array.Empty>(); int unmappedCount = 0; lock (_mappings) { - int count = _mappings.Get(address - MinimumPageSize, endAddress + MinimumPageSize, ref overlaps); + int count = _mappings.Get( + Math.Max(address - MinimumPageSize, blockAddress), + Math.Min(endAddress + MinimumPageSize, blockEnd), ref overlaps); + if (count < 2) { // Nothing to coalesce if we only have 1 or no overlaps. From 46cc7b55f02fc9e9cbe0637d710b5e5a6c76eaef Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 5 Jun 2022 21:24:28 -0300 Subject: [PATCH 5/5] Fix instanced indexed inline draws (#3383) --- .../Engine/Threed/DrawManager.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index 44d2d393d..7e35fd2de 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed private bool _instancedDrawPending; private bool _instancedIndexed; + private bool _instancedIndexedInline; private int _instancedFirstIndex; private int _instancedFirstVertex; @@ -135,6 +136,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _instancedDrawPending = true; _instancedIndexed = _drawState.DrawIndexed; + _instancedIndexedInline = _drawState.IbStreamer.HasInlineIndexData; _instancedFirstIndex = firstIndex; _instancedFirstVertex = (int)_state.State.FirstVertex; @@ -451,7 +453,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { _instancedDrawPending = false; - if (_instancedIndexed) + if (_instancedIndexedInline) + { + int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(); + BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); + + _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); + + _context.Renderer.Pipeline.DrawIndexed( + inlineIndexCount, + _instanceIndex + 1, + _instancedFirstIndex, + _instancedFirstVertex, + _instancedFirstInstance); + } + else if (_instancedIndexed) { _context.Renderer.Pipeline.DrawIndexed( _instancedIndexCount,