mirror of
https://git.ryujinx.app/projects/Kenji-NX.git
synced 2026-09-25 16:57:55 +02:00
Memory changes 3
General memory improvements to decrease GC pressure and frequency. Pool big arrays and objects that are created and deleted often. Skip data copies when they aren't needed. Inline flag checks to skip unneeded allocations. From my testing the performance is about the same, but the GC frequency is much lower and collection is faster causing less and smaller spikes.
This commit is contained in:
@@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private Dictionary<ulong, StagingBufferReserved> _mirrors;
|
||||
private bool _useMirrors;
|
||||
|
||||
private Action _decrementReferenceCount;
|
||||
|
||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, MemoryAllocation allocation, int size, BufferAllocationType type, BufferAllocationType currentType)
|
||||
{
|
||||
_gd = gd;
|
||||
@@ -75,6 +77,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_flushLock = new ReaderWriterLockSlim();
|
||||
_useMirrors = gd.IsTBDR;
|
||||
|
||||
_decrementReferenceCount = _buffer.DecrementReferenceCount;
|
||||
}
|
||||
|
||||
public BufferHolder(VulkanRenderer gd, Device device, VkBuffer buffer, Auto<MemoryAllocation> allocation, int size, BufferAllocationType type, BufferAllocationType currentType, int offset)
|
||||
@@ -444,7 +448,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_flushLock.ExitReadLock();
|
||||
|
||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _buffer.DecrementReferenceCount);
|
||||
return PinnedSpan<byte>.UnsafeFromSpan(result, _decrementReferenceCount);
|
||||
}
|
||||
|
||||
BackgroundResource resource = _gd.BackgroundResources.Get();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
@@ -9,26 +11,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
class FramebufferParams
|
||||
{
|
||||
private readonly Device _device;
|
||||
private readonly Auto<DisposableImageView>[] _attachments;
|
||||
private readonly TextureView[] _colors;
|
||||
private readonly TextureView _depthStencil;
|
||||
private readonly TextureView[] _colorsCanonical;
|
||||
private readonly TextureView _baseAttachment;
|
||||
private readonly uint _validColorAttachments;
|
||||
private Auto<DisposableImageView>[] _attachments;
|
||||
private TextureView[] _colors;
|
||||
private TextureView _depthStencil;
|
||||
private TextureView[] _colorsCanonical;
|
||||
private TextureView _baseAttachment;
|
||||
private uint _validColorAttachments;
|
||||
private int _totalCount;
|
||||
|
||||
public uint Width { get; }
|
||||
public uint Height { get; }
|
||||
public uint Layers { get; }
|
||||
public uint Width { get; private set; }
|
||||
public uint Height { get; private set; }
|
||||
public uint Layers { get; private set; }
|
||||
|
||||
public uint[] AttachmentSamples { get; }
|
||||
public VkFormat[] AttachmentFormats { get; }
|
||||
public int[] AttachmentIndices { get; }
|
||||
public uint AttachmentIntegerFormatMask { get; }
|
||||
public bool LogicOpsAllowed { get; }
|
||||
public uint[] AttachmentSamples { get; private set; }
|
||||
public VkFormat[] AttachmentFormats { get; private set; }
|
||||
public int[] AttachmentIndices { get; private set; }
|
||||
public uint AttachmentIntegerFormatMask { get; private set; }
|
||||
public bool LogicOpsAllowed { get; private set; }
|
||||
|
||||
public int AttachmentsCount { get; }
|
||||
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1;
|
||||
public bool HasDepthStencil { get; }
|
||||
public int AttachmentsCount { get; private set; }
|
||||
public int MaxColorAttachmentIndex => ColorAttachmentsCount > 0 ? AttachmentIndices[ColorAttachmentsCount - 1] : -1;
|
||||
public bool HasDepthStencil { get; private set; }
|
||||
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
|
||||
|
||||
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
||||
@@ -49,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
else
|
||||
{
|
||||
_colors = [view];
|
||||
_colorsCanonical = _colors;
|
||||
_colorsCanonical = [view];
|
||||
}
|
||||
|
||||
Width = width;
|
||||
@@ -63,6 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
LogicOpsAllowed = !format.IsFloatOrSrgb();
|
||||
|
||||
AttachmentsCount = 1;
|
||||
_totalCount = 1;
|
||||
|
||||
HasDepthStencil = isDepthStencil;
|
||||
}
|
||||
@@ -132,7 +136,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||
|
||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
||||
if (depthStencil is TextureView { Valid: true } dsTexture)
|
||||
{
|
||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||
_depthStencil = dsTexture;
|
||||
@@ -158,11 +162,151 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Layers = layers;
|
||||
|
||||
AttachmentsCount = count;
|
||||
_totalCount = colors.Length;
|
||||
}
|
||||
|
||||
public FramebufferParams Update(ITexture[] colors, ITexture depthStencil)
|
||||
{
|
||||
int colorsCount = colors.Count(IsValidTextureView);
|
||||
|
||||
int count = colorsCount + (IsValidTextureView(depthStencil) ? 1 : 0);
|
||||
|
||||
Array.Clear(_attachments);
|
||||
Array.Clear(_colors);
|
||||
|
||||
if (_attachments.Length < count)
|
||||
{
|
||||
Array.Resize(ref _attachments, count);
|
||||
}
|
||||
if (_colors.Length < colorsCount)
|
||||
{
|
||||
Array.Resize(ref _colors, colorsCount);
|
||||
}
|
||||
if (_colorsCanonical.Length < colors.Length)
|
||||
{
|
||||
Array.Resize(ref _colorsCanonical, colors.Length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
ITexture color = colors[i];
|
||||
if (color is TextureView { Valid: true } view)
|
||||
{
|
||||
_colorsCanonical[i] = view;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colorsCanonical[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
Array.Clear(AttachmentSamples);
|
||||
Array.Clear(AttachmentFormats);
|
||||
Array.Clear(AttachmentIndices);
|
||||
|
||||
if (AttachmentSamples.Length < count)
|
||||
{
|
||||
uint[] attachmentSamples = AttachmentSamples;
|
||||
Array.Resize(ref attachmentSamples, count);
|
||||
AttachmentSamples = attachmentSamples;
|
||||
}
|
||||
if (AttachmentFormats.Length < count)
|
||||
{
|
||||
VkFormat[] attachmentFormats = AttachmentFormats;
|
||||
Array.Resize(ref attachmentFormats, count);
|
||||
AttachmentFormats = attachmentFormats;
|
||||
}
|
||||
if (AttachmentIndices.Length < colorsCount)
|
||||
{
|
||||
int[] attachmentIndices = AttachmentIndices;
|
||||
Array.Resize(ref attachmentIndices, colorsCount);
|
||||
AttachmentIndices = attachmentIndices;
|
||||
}
|
||||
|
||||
uint width = uint.MaxValue;
|
||||
uint height = uint.MaxValue;
|
||||
uint layers = uint.MaxValue;
|
||||
|
||||
int index = 0;
|
||||
uint attachmentIntegerFormatMask = 0;
|
||||
bool allFormatsFloatOrSrgb = colorsCount != 0;
|
||||
|
||||
_validColorAttachments = 0;
|
||||
_baseAttachment = null;
|
||||
|
||||
for (int bindIndex = 0; bindIndex < colors.Length; bindIndex++)
|
||||
{
|
||||
TextureView texture = _colorsCanonical[bindIndex];
|
||||
if (texture is not null)
|
||||
{
|
||||
_attachments[index] = texture.GetImageViewForAttachment();
|
||||
_colors[index] = texture;
|
||||
_validColorAttachments |= 1u << bindIndex;
|
||||
_baseAttachment = texture;
|
||||
|
||||
AttachmentSamples[index] = (uint)texture.Info.Samples;
|
||||
AttachmentFormats[index] = texture.VkFormat;
|
||||
AttachmentIndices[index] = bindIndex;
|
||||
|
||||
Format format = texture.Info.Format;
|
||||
|
||||
if (format.IsInteger())
|
||||
{
|
||||
attachmentIntegerFormatMask |= 1u << bindIndex;
|
||||
}
|
||||
|
||||
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
|
||||
|
||||
width = Math.Min(width, (uint)texture.Width);
|
||||
height = Math.Min(height, (uint)texture.Height);
|
||||
layers = Math.Min(layers, (uint)texture.Layers);
|
||||
|
||||
if (++index >= colorsCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||
_depthStencil = null;
|
||||
HasDepthStencil = false;
|
||||
|
||||
if (depthStencil is TextureView { Valid: true } dsTexture)
|
||||
{
|
||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||
_depthStencil = dsTexture;
|
||||
_baseAttachment ??= dsTexture;
|
||||
|
||||
AttachmentSamples[count - 1] = (uint)dsTexture.Info.Samples;
|
||||
AttachmentFormats[count - 1] = dsTexture.VkFormat;
|
||||
|
||||
width = Math.Min(width, (uint)dsTexture.Width);
|
||||
height = Math.Min(height, (uint)dsTexture.Height);
|
||||
layers = Math.Min(layers, (uint)dsTexture.Layers);
|
||||
|
||||
HasDepthStencil = true;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
width = height = layers = 1;
|
||||
}
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
Layers = layers;
|
||||
|
||||
AttachmentsCount = count;
|
||||
_totalCount = colors.Length;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Auto<DisposableImageView> GetAttachment(int index)
|
||||
{
|
||||
if ((uint)index >= _attachments.Length)
|
||||
if ((uint)index >= AttachmentsCount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -182,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public ComponentType GetAttachmentComponentType(int index)
|
||||
{
|
||||
if (_colors != null && (uint)index < _colors.Length)
|
||||
if (_colors != null && (uint)index < ColorAttachmentsCount)
|
||||
{
|
||||
var format = _colors[index].Info.Format;
|
||||
|
||||
@@ -217,7 +361,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private static bool IsValidTextureView(ITexture texture)
|
||||
{
|
||||
return texture is TextureView view && view.Valid;
|
||||
return texture is TextureView { Valid: true };
|
||||
}
|
||||
|
||||
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
|
||||
@@ -232,9 +376,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
|
||||
{
|
||||
ImageView* attachments = stackalloc ImageView[_attachments.Length];
|
||||
ImageView* attachments = stackalloc ImageView[AttachmentsCount];
|
||||
|
||||
for (int i = 0; i < _attachments.Length; i++)
|
||||
for (int i = 0; i < AttachmentsCount; i++)
|
||||
{
|
||||
attachments[i] = _attachments[i].Get(cbs).Value;
|
||||
}
|
||||
@@ -243,7 +387,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
SType = StructureType.FramebufferCreateInfo,
|
||||
RenderPass = renderPass.Get(cbs).Value,
|
||||
AttachmentCount = (uint)_attachments.Length,
|
||||
AttachmentCount = (uint)AttachmentsCount,
|
||||
PAttachments = attachments,
|
||||
Width = Width,
|
||||
Height = Height,
|
||||
@@ -251,14 +395,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
};
|
||||
|
||||
api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments[..AttachmentsCount]);
|
||||
}
|
||||
|
||||
public TextureView[] GetAttachmentViews()
|
||||
{
|
||||
var result = new TextureView[_attachments.Length];
|
||||
|
||||
_colors?.CopyTo(result, 0);
|
||||
var result = new TextureView[AttachmentsCount];
|
||||
_colors?.AsSpan(..ColorAttachmentsCount).CopyTo(result.AsSpan());
|
||||
|
||||
if (_depthStencil != null)
|
||||
{
|
||||
@@ -277,8 +420,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (_colors != null)
|
||||
{
|
||||
foreach (var color in _colors)
|
||||
int count = ColorAttachmentsCount;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TextureView color = _colors[i];
|
||||
// If Clear or DontCare were used, this would need to be write bit.
|
||||
color.Storage?.QueueLoadOpBarrier(cbs, false);
|
||||
}
|
||||
@@ -293,8 +439,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (_colors != null)
|
||||
{
|
||||
foreach (var color in _colors)
|
||||
int count = ColorAttachmentsCount;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TextureView color = _colors[i];
|
||||
color.Storage?.AddStoreOpUsage(false);
|
||||
}
|
||||
}
|
||||
@@ -306,7 +455,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_depthStencil?.Storage.ClearBindings();
|
||||
|
||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||
for (int i = 0; i < _totalCount; i++)
|
||||
{
|
||||
_colorsCanonical[i]?.Storage.ClearBindings();
|
||||
}
|
||||
@@ -316,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_depthStencil?.Storage.AddBinding(_depthStencil);
|
||||
|
||||
for (int i = 0; i < _colorsCanonical.Length; i++)
|
||||
for (int i = 0; i < _totalCount; i++)
|
||||
{
|
||||
TextureView color = _colorsCanonical[i];
|
||||
color?.Storage.AddBinding(color);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
private const int BufferUsageTrackingGranularity = 4096;
|
||||
|
||||
private readonly FenceHolder[] _fences;
|
||||
public FenceHolder[] Fences { get; }
|
||||
private readonly BufferUsageBitmap _bufferUsageBitmap;
|
||||
|
||||
/// <summary>
|
||||
@@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// </summary>
|
||||
public MultiFenceHolder()
|
||||
{
|
||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <param name="size">Size of the buffer</param>
|
||||
public MultiFenceHolder(int size)
|
||||
{
|
||||
_fences = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
Fences = ArrayPool<FenceHolder>.Shared.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||
_bufferUsageBitmap = new BufferUsageBitmap(size, BufferUsageTrackingGranularity);
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <returns>True if the command buffer's previous fence value was null</returns>
|
||||
public bool AddFence(int cbIndex, FenceHolder fence)
|
||||
{
|
||||
ref FenceHolder fenceRef = ref _fences[cbIndex];
|
||||
ref FenceHolder fenceRef = ref Fences[cbIndex];
|
||||
|
||||
if (fenceRef == null)
|
||||
{
|
||||
@@ -107,7 +108,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <param name="cbIndex">Command buffer index of the command buffer that owns the fence</param>
|
||||
public void RemoveFence(int cbIndex)
|
||||
{
|
||||
_fences[cbIndex] = null;
|
||||
Fences[cbIndex] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -117,7 +118,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <returns>True if referenced, false otherwise</returns>
|
||||
public bool HasFence(int cbIndex)
|
||||
{
|
||||
return _fences[cbIndex] != null;
|
||||
return Fences[cbIndex] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -227,9 +228,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _fences.Length; i++)
|
||||
for (int i = 0; i < Fences.Length; i++)
|
||||
{
|
||||
var fence = _fences[i];
|
||||
var fence = Fences[i];
|
||||
|
||||
if (fence != null)
|
||||
{
|
||||
@@ -251,9 +252,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < _fences.Length; i++)
|
||||
for (int i = 0; i < Fences.Length; i++)
|
||||
{
|
||||
var fence = _fences[i];
|
||||
var fence = Fences[i];
|
||||
|
||||
if (fence != null && _bufferUsageBitmap.OverlapsWith(i, offset, size))
|
||||
{
|
||||
|
||||
@@ -1448,7 +1448,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
FramebufferParams?.ClearBindings();
|
||||
}
|
||||
|
||||
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
|
||||
FramebufferParams = FramebufferParams?.Update(colors, depthStencil) ?? new FramebufferParams(Device, colors, depthStencil);
|
||||
|
||||
if (IsMainPipeline)
|
||||
{
|
||||
@@ -1466,18 +1466,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
protected void UpdatePipelineAttachmentFormats()
|
||||
{
|
||||
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
||||
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
||||
FramebufferParams.AttachmentFormats.AsSpan(..FramebufferParams.AttachmentsCount).CopyTo(dstAttachmentFormats);
|
||||
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
||||
_newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed;
|
||||
|
||||
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
||||
for (int i = FramebufferParams.AttachmentsCount; i < dstAttachmentFormats.Length; i++)
|
||||
{
|
||||
dstAttachmentFormats[i] = 0;
|
||||
}
|
||||
|
||||
_newState.ColorBlendAttachmentStateCount = (uint)(FramebufferParams.MaxColorAttachmentIndex + 1);
|
||||
_newState.HasDepthStencil = FramebufferParams.HasDepthStencil;
|
||||
_newState.SamplesCount = FramebufferParams.AttachmentSamples.Length != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
|
||||
_newState.SamplesCount = FramebufferParams.AttachmentsCount != 0 ? FramebufferParams.AttachmentSamples[0] : 1;
|
||||
}
|
||||
|
||||
protected unsafe void CreateRenderPass()
|
||||
|
||||
@@ -182,17 +182,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (_forcedFences.Count > 0)
|
||||
{
|
||||
_forcedFences.RemoveAll((entry) =>
|
||||
for (int i = 0; i < _forcedFences.Count; i++)
|
||||
{
|
||||
if (entry.Texture.Disposed)
|
||||
if (_forcedFences[i].Texture.Disposed)
|
||||
{
|
||||
return true;
|
||||
_forcedFences.RemoveAt(i--);
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
_forcedFences[i].Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, _forcedFences[i].StageFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Silk.NET.Vulkan;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -192,6 +193,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_firstHandle = first.ID + 1;
|
||||
_handles.RemoveAt(0);
|
||||
ArrayPool<FenceHolder>.Shared.Return(first.Waitable.Fences);
|
||||
first.Waitable = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user