aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Memory/BufferManager.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/BufferManager.cs438
1 files changed, 49 insertions, 389 deletions
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 20fa1f3a..e43cb3b3 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -1,9 +1,7 @@
-using Ryujinx.Common;
+using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
-using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -14,26 +12,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Buffer manager.
/// </summary>
- class BufferManager
+ class BufferManager : IDisposable
{
private const int StackToHeapThreshold = 16;
- private const int OverlapsBufferInitialCapacity = 10;
- private const int OverlapsBufferMaxCapacity = 10000;
-
- private const ulong BufferAlignmentSize = 0x1000;
- private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
-
- private GpuContext _context;
-
- private RangeList<Buffer> _buffers;
-
- private Buffer[] _bufferOverlaps;
+ private readonly GpuContext _context;
private IndexBuffer _indexBuffer;
- private VertexBuffer[] _vertexBuffers;
- private BufferBounds[] _transformFeedbackBuffers;
- private List<BufferTextureBinding> _bufferTextures;
+ private readonly VertexBuffer[] _vertexBuffers;
+ private readonly BufferBounds[] _transformFeedbackBuffers;
+ private readonly List<BufferTextureBinding> _bufferTextures;
/// <summary>
/// Holds shader stage buffer state and binding information.
@@ -94,10 +82,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
- private BuffersPerStage _cpStorageBuffers;
- private BuffersPerStage _cpUniformBuffers;
- private BuffersPerStage[] _gpStorageBuffers;
- private BuffersPerStage[] _gpUniformBuffers;
+ private readonly BuffersPerStage _cpStorageBuffers;
+ private readonly BuffersPerStage _cpUniformBuffers;
+ private readonly BuffersPerStage[] _gpStorageBuffers;
+ private readonly BuffersPerStage[] _gpUniformBuffers;
private int _cpStorageBufferBindings;
private int _cpUniformBufferBindings;
@@ -114,20 +102,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
private bool _rebind;
- private Dictionary<ulong, BufferCacheEntry> _dirtyCache;
-
/// <summary>
/// Creates a new instance of the buffer manager.
/// </summary>
- /// <param name="context">The GPU context that the buffer manager belongs to</param>
+ /// <param name="context">GPU context that the buffer manager belongs to</param>
public BufferManager(GpuContext context)
{
_context = context;
- _buffers = new RangeList<Buffer>();
-
- _bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
-
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
_transformFeedbackBuffers = new BufferBounds[Constants.TotalTransformFeedbackBuffers];
@@ -146,9 +128,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
_bufferTextures = new List<BufferTextureBinding>();
- _dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
+ context.Methods.BufferCache.NotifyBuffersModified += Rebind;
}
+
/// <summary>
/// Sets the memory range with the index buffer data, to be used for subsequent draw calls.
/// </summary>
@@ -157,11 +140,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
_indexBuffer.Address = address;
- _indexBuffer.Size = size;
- _indexBuffer.Type = type;
+ _indexBuffer.Size = size;
+ _indexBuffer.Type = type;
_indexBufferDirty = true;
}
@@ -188,11 +171,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
{
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
_vertexBuffers[index].Address = address;
- _vertexBuffers[index].Size = size;
- _vertexBuffers[index].Stride = stride;
+ _vertexBuffers[index].Size = size;
+ _vertexBuffers[index].Stride = stride;
_vertexBuffers[index].Divisor = divisor;
_vertexBuffersDirty = true;
@@ -216,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
_transformFeedbackBuffers[index] = new BufferBounds(address, size);
_transformFeedbackBuffersDirty = true;
@@ -236,7 +219,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
_cpStorageBuffers.SetBounds(index, address, size, flags);
}
@@ -256,10 +239,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
- _gpStorageBuffers[stage].Buffers[index].Size != size)
+ _gpStorageBuffers[stage].Buffers[index].Size != size)
{
_gpStorageBuffersDirty = true;
}
@@ -276,7 +259,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
_cpUniformBuffers.SetBounds(index, address, size);
}
@@ -291,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
_gpUniformBuffers[stage].SetBounds(index, address, size);
_gpUniformBuffersDirty = true;
@@ -397,191 +380,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
return mask;
}
- /// <summary>
- /// Handles removal of buffers written to a memory region being unmapped.
- /// </summary>
- /// <param name="sender">Sender object</param>
- /// <param name="e">Event arguments</param>
- public void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
- {
- Buffer[] overlaps = new Buffer[10];
- int overlapCount;
-
- ulong address = _context.MemoryManager.Translate(e.Address);
- ulong size = e.Size;
-
- lock (_buffers)
- {
- overlapCount = _buffers.FindOverlaps(address, size, ref overlaps);
- }
-
- for (int i = 0; i < overlapCount; i++)
- {
- overlaps[i].Unmapped(address, size);
- }
- }
-
- /// <summary>
- /// Performs address translation of the GPU virtual address, and creates a
- /// new buffer, if needed, for the specified range.
- /// </summary>
- /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
- /// <param name="size">Size in bytes of the buffer</param>
- /// <returns>CPU virtual address of the buffer, after address translation</returns>
- private ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
- {
- if (gpuVa == 0)
- {
- return 0;
- }
-
- ulong address = _context.MemoryManager.Translate(gpuVa);
-
- if (address == MemoryManager.PteUnmapped)
- {
- return 0;
- }
-
- CreateBuffer(address, size);
-
- return address;
- }
-
- /// <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.
- /// </summary>
- /// <param name="address">Address of the buffer in memory</param>
- /// <param name="size">Size of the buffer in bytes</param>
- public void CreateBuffer(ulong address, ulong size)
- {
- ulong endAddress = address + size;
-
- ulong alignedAddress = address & ~BufferAlignmentMask;
-
- ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask;
-
- // The buffer must have the size of at least one page.
- if (alignedEndAddress == alignedAddress)
- {
- alignedEndAddress += BufferAlignmentSize;
- }
-
- CreateBufferAligned(alignedAddress, alignedEndAddress - alignedAddress);
- }
-
- /// <summary>
- /// Performs address translation of the GPU virtual address, and attempts to force
- /// the buffer in the region as dirty.
- /// The buffer lookup for this function is cached in a dictionary for quick access, which
- /// accelerates common UBO updates.
- /// </summary>
- /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
- /// <param name="size">Size in bytes of the buffer</param>
- public void ForceDirty(ulong gpuVa, ulong size)
- {
- BufferCacheEntry result;
-
- if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence)
- {
- ulong address = TranslateAndCreateBuffer(gpuVa, size);
- result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
-
- _dirtyCache[gpuVa] = result;
- }
-
- result.Buffer.ForceDirty(result.Address, size);
- }
-
- /// <summary>
- /// Creates a new buffer for the specified range, if needed.
- /// If a buffer where this range can be fully contained already exists,
- /// then the creation of a new buffer is not necessary.
- /// </summary>
- /// <param name="address">Address of the buffer in guest memory</param>
- /// <param name="size">Size in bytes of the buffer</param>
- private void CreateBufferAligned(ulong address, ulong size)
- {
- int overlapsCount;
-
- lock (_buffers)
- {
- overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
- }
-
- if (overlapsCount != 0)
- {
- // The buffer already exists. We can just return the existing buffer
- // if the buffer we need is fully contained inside the overlapping buffer.
- // Otherwise, we must delete the overlapping buffers and create a bigger buffer
- // that fits all the data we need. We also need to copy the contents from the
- // old buffer(s) to the new buffer.
- ulong endAddress = address + size;
-
- if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
- {
- for (int index = 0; index < overlapsCount; index++)
- {
- Buffer buffer = _bufferOverlaps[index];
-
- address = Math.Min(address, buffer.Address);
- endAddress = Math.Max(endAddress, buffer.EndAddress);
-
- lock (_buffers)
- {
- _buffers.Remove(buffer);
- }
- }
-
- Buffer newBuffer = new Buffer(_context, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
-
- lock (_buffers)
- {
- _buffers.Add(newBuffer);
- }
-
- for (int index = 0; index < overlapsCount; index++)
- {
- Buffer buffer = _bufferOverlaps[index];
-
- int dstOffset = (int)(buffer.Address - newBuffer.Address);
-
- buffer.CopyTo(newBuffer, dstOffset);
- newBuffer.InheritModifiedRanges(buffer);
-
- buffer.DisposeData();
- }
-
- newBuffer.SynchronizeMemory(address, endAddress - address);
-
- // Existing buffers were modified, we need to rebind everything.
- _rebind = true;
- }
- }
- else
- {
- // No overlap, just create a new buffer.
- Buffer buffer = new Buffer(_context, address, size);
-
- lock (_buffers)
- {
- _buffers.Add(buffer);
- }
- }
-
- ShrinkOverlapsBufferIfNeeded();
- }
-
- /// <summary>
- /// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
- /// </summary>
- private void ShrinkOverlapsBufferIfNeeded()
- {
- if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity)
- {
- Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity);
- }
- }
/// <summary>
/// Gets the address of the compute uniform buffer currently bound at the given index.
@@ -624,7 +422,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
// The storage buffer size is not reliable (it might be lower than the actual size),
// so we bind the entire buffer to allow otherwise out of range accesses to work.
- sRanges[bindingInfo.Binding] = GetBufferRangeTillEnd(
+ sRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRangeTillEnd(
bounds.Address,
bounds.Size,
bounds.Flags.HasFlag(BufferUsageFlags.Write));
@@ -645,7 +443,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (bounds.Address != 0)
{
- uRanges[bindingInfo.Binding] = GetBufferRange(bounds.Address, bounds.Size);
+ uRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
}
}
@@ -654,7 +452,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
CommitBufferTextureBindings();
// Force rebind after doing compute work.
- _rebind = true;
+ Rebind();
}
/// <summary>
@@ -666,7 +464,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
foreach (var binding in _bufferTextures)
{
- binding.Texture.SetStorage(GetBufferRange(binding.Address, binding.Size, binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore)));
+ var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
+ var range = _context.Methods.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
+ binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated.
@@ -696,14 +496,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_indexBuffer.Address != 0)
{
- BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
+ BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (_indexBuffer.Address != 0)
{
- SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
+ _context.Methods.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
}
uint vbEnableMask = _vertexBuffersEnableMask;
@@ -723,7 +523,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
- BufferRange buffer = GetBufferRange(vb.Address, vb.Size);
+ BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(vb.Address, vb.Size);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
}
@@ -741,7 +541,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
- SynchronizeBufferRange(vb.Address, vb.Size);
+ _context.Methods.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
}
}
@@ -761,7 +561,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
- tfbs[index] = GetBufferRange(tfb.Address, tfb.Size);
+ tfbs[index] = _context.Methods.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
}
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@@ -777,7 +577,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
- SynchronizeBufferRange(tfb.Address, tfb.Size);
+ _context.Methods.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
}
}
@@ -831,9 +631,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (bounds.Address != 0)
{
+ var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
ranges[bindingInfo.Binding] = isStorage
- ? GetBufferRangeTillEnd(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write))
- : GetBufferRange(bounds.Address, bounds.Size, bounds.Flags.HasFlag(BufferUsageFlags.Write));
+ ? _context.Methods.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
+ : _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
}
}
}
@@ -869,7 +670,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
- SynchronizeBufferRange(bounds.Address, bounds.Size);
+ _context.Methods.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
}
}
}
@@ -885,167 +686,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage)
{
- CreateBuffer(address, size);
+ _context.Methods.BufferCache.CreateBuffer(address, size);
_bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage));
}
/// <summary>
- /// Copy a buffer data from a given address to another.
- /// </summary>
- /// <remarks>
- /// This does a GPU side copy.
- /// </remarks>
- /// <param name="srcVa">GPU virtual address of the copy source</param>
- /// <param name="dstVa">GPU virtual address of the copy destination</param>
- /// <param name="size">Size in bytes of the copy</param>
- public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
- {
- ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
- ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
-
- Buffer srcBuffer = GetBuffer(srcAddress, size);
- Buffer dstBuffer = GetBuffer(dstAddress, size);
-
- int srcOffset = (int)(srcAddress - srcBuffer.Address);
- int dstOffset = (int)(dstAddress - dstBuffer.Address);
-
- _context.Renderer.Pipeline.CopyBuffer(
- srcBuffer.Handle,
- dstBuffer.Handle,
- srcOffset,
- dstOffset,
- (int)size);
-
- if (srcBuffer.IsModified(srcAddress, size))
- {
- dstBuffer.SignalModified(dstAddress, size);
- }
- else
- {
- // Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
-
- dstBuffer.ClearModified(dstAddress, size);
- _context.PhysicalMemory.WriteUntracked(dstAddress, _context.PhysicalMemory.GetSpan(srcAddress, (int)size));
- }
- }
-
- /// <summary>
- /// Clears a buffer at a given address with the specified value.
- /// </summary>
- /// <remarks>
- /// Both the address and size must be aligned to 4 bytes.
- /// </remarks>
- /// <param name="gpuVa">GPU virtual address of the region to clear</param>
- /// <param name="size">Number of bytes to clear</param>
- /// <param name="value">Value to be written into the buffer</param>
- public void ClearBuffer(GpuVa gpuVa, ulong size, uint value)
- {
- ulong address = TranslateAndCreateBuffer(gpuVa.Pack(), size);
-
- Buffer buffer = GetBuffer(address, size);
-
- int offset = (int)(address - buffer.Address);
-
- _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
-
- buffer.SignalModified(address, size);
- }
-
- /// <summary>
- /// Gets a buffer sub-range starting at a given memory address.
- /// </summary>
- /// <param name="address">Start address of the memory range</param>
- /// <param name="size">Size in bytes of the memory range</param>
- /// <param name="write">Whether the buffer will be written to by this use</param>
- /// <returns>The buffer sub-range starting at the given memory address</returns>
- private BufferRange GetBufferRangeTillEnd(ulong address, ulong size, bool write = false)
- {
- return GetBuffer(address, size, write).GetRange(address);
- }
-
- /// <summary>
- /// Gets a buffer sub-range for a given memory range.
- /// </summary>
- /// <param name="address">Start address of the memory range</param>
- /// <param name="size">Size in bytes of the memory range</param>
- /// <param name="write">Whether the buffer will be written to by this use</param>
- /// <returns>The buffer sub-range for the given range</returns>
- private BufferRange GetBufferRange(ulong address, ulong size, bool write = false)
- {
- return GetBuffer(address, size, write).GetRange(address, size);
- }
-
- /// <summary>
- /// Gets a buffer for a given memory range.
- /// A buffer overlapping with the specified range is assumed to already exist on the cache.
- /// </summary>
- /// <param name="address">Start address of the memory range</param>
- /// <param name="size">Size in bytes of the memory range</param>
- /// <param name="write">Whether the buffer will be written to by this use</param>
- /// <returns>The buffer where the range is fully contained</returns>
- private Buffer GetBuffer(ulong address, ulong size, bool write = false)
- {
- Buffer buffer;
-
- if (size != 0)
- {
- lock (_buffers)
- {
- buffer = _buffers.FindFirstOverlap(address, size);
- }
-
- buffer.SynchronizeMemory(address, size);
-
- if (write)
- {
- buffer.SignalModified(address, size);
- }
- }
- else
- {
- lock (_buffers)
- {
- buffer = _buffers.FindFirstOverlap(address, 1);
- }
- }
-
- return buffer;
- }
-
- /// <summary>
- /// Performs guest to host memory synchronization of a given memory range.
+ /// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </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)
+ public void Rebind()
{
- if (size != 0)
- {
- Buffer buffer;
-
- lock (_buffers)
- {
- buffer = _buffers.FindFirstOverlap(address, size);
- }
-
- buffer.SynchronizeMemory(address, size);
- }
+ _rebind = true;
}
/// <summary>
- /// Disposes all buffers in the cache.
- /// It's an error to use the buffer manager after disposal.
+ /// Disposes the buffer manager.
+ /// It is an error to use the buffer manager after disposal.
/// </summary>
public void Dispose()
{
- lock (_buffers)
- {
- foreach (Buffer buffer in _buffers)
- {
- buffer.Dispose();
- }
- }
+ _context.Methods.BufferCache.NotifyBuffersModified -= Rebind;
}
}
-} \ No newline at end of file
+}