aboutsummaryrefslogtreecommitdiffstatsci
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2019-04-27 03:59:01 +0200
committerj-p <jp_bruyere@hotmail.com>2019-05-02 03:07:11 +0200
commit997ebab9c71d81478581c672c8ba7dc0ed05e2fc (patch)
tree014fe2691a563f8233df13dcdace7ae3c1ed9207
parent568219e7719bfaf076ba23b539dd6ee7f647e273 (diff)
ResourceManager and MemoryPools
-rw-r--r--samples/data/models/Hubble.glb3
-rw-r--r--samples/deferred/main.cs1
-rw-r--r--samples/deferred/shaders/tone_mapping.frag24
-rw-r--r--vk.net.sln12
-rw-r--r--vke/src/Activable.cs4
-rw-r--r--vke/src/Buffer.cs36
-rw-r--r--vke/src/Device.cs10
-rw-r--r--vke/src/Image.cs24
-rw-r--r--vke/src/MemoryPool.cs98
-rw-r--r--vke/src/PipelineCache.cs36
-rw-r--r--vke/src/Resource.cs59
-rw-r--r--vke/src/ResourceManager.cs54
12 files changed, 281 insertions, 80 deletions
diff --git a/samples/data/models/Hubble.glb b/samples/data/models/Hubble.glb
new file mode 100644
index 0000000..9d19921
--- /dev/null
+++ b/samples/data/models/Hubble.glb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e6a40640d6846e8f5d9438821ebe2d7de730c8dadba1cc9dfdbcc148ff1052cd
+size 11463124
diff --git a/samples/deferred/main.cs b/samples/deferred/main.cs
index db60521..985a4d0 100644
--- a/samples/deferred/main.cs
+++ b/samples/deferred/main.cs
@@ -586,7 +586,6 @@ namespace deferred {
envCube.Dispose ();
descriptorPool.Dispose ();
- pipelineCache.Dispose ();
}
}
diff --git a/samples/deferred/shaders/tone_mapping.frag b/samples/deferred/shaders/tone_mapping.frag
index 0493ee4..9b8c94c 100644
--- a/samples/deferred/shaders/tone_mapping.frag
+++ b/samples/deferred/shaders/tone_mapping.frag
@@ -19,13 +19,13 @@ layout (location = 0) out vec4 outColor;
vec3 Uncharted2Tonemap(vec3 color)
{
- float A = 0.15;
- float B = 0.50;
- float C = 0.10;
- float D = 0.20;
- float E = 0.02;
- float F = 0.30;
- float W = 11.2;
+ const float A = 0.15;
+ const float B = 0.50;
+ const float C = 0.10;
+ const float D = 0.20;
+ const float E = 0.02;
+ const float F = 0.30;
+ const float W = 11.2;
return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F;
}
@@ -52,7 +52,11 @@ vec3 SRGBtoLINEAR(vec3 srgbIn)
void main()
{
- vec4 color = subpassLoad(samplerHDR, gl_SampleID);
- outColor = vec4(SRGBtoLINEAR(tonemap(color.rgb)), color.a);
-
+ vec4 hdrColor = subpassLoad(samplerHDR, gl_SampleID);
+
+ outColor = vec4(SRGBtoLINEAR(tonemap(hdrColor.rgb)), hdrColor.a);
+ /*
+ vec3 mapped = vec3(1.0) - exp(-hdrColor.rgb * ubo.exposure);
+ mapped = pow(mapped, vec3(1.0 / ubo.gamma));
+ outColor = vec4(mapped, hdrColor.a);*/
} \ No newline at end of file
diff --git a/vk.net.sln b/vk.net.sln
index 48d1a5a..560aa71 100644
--- a/vk.net.sln
+++ b/vk.net.sln
@@ -120,5 +120,17 @@ Global
$2.SpacingAfterMethodDeclarationName = True
$2.SpaceAfterMethodCallName = True
$2.scope = text/x-csharp
+ $0.DotNetNamingPolicy = $3
+ $3.DirectoryNamespaceAssociation = PrefixedHierarchical
+ $0.TextStylePolicy = $4
+ $4.FileWidth = 80
+ $4.TabsToSpaces = True
+ $4.scope = text/plain
+ $0.TextStylePolicy = $5
+ $5.inheritsSet = null
+ $5.scope = application/config+xml
+ $0.XmlFormattingPolicy = $6
+ $6.inheritsSet = null
+ $6.scope = application/config+xml
EndGlobalSection
EndGlobal
diff --git a/vke/src/Activable.cs b/vke/src/Activable.cs
index acced37..a98f780 100644
--- a/vke/src/Activable.cs
+++ b/vke/src/Activable.cs
@@ -34,6 +34,10 @@ namespace CVKL {
/// each time it is disposed, the count is decremented. When the count reach zero, the handle is destroyed and the finalizizer is unregistered. Once disposed, the
/// objecte may still be reactivated.
/// </summary>
+ /// <remarks>
+ /// Some of the activation trigger the first activation on creation, those activables have to be explicitly dispose at the end of the application.
+ /// The activables that trigger activation only on usage does not require an additional dispose at the end.
+ /// </remarks>
public abstract class Activable : IDisposable {
//count number of activation, only the first one will create a handle
protected uint references;
diff --git a/vke/src/Buffer.cs b/vke/src/Buffer.cs
index 45e03d8..565deeb 100644
--- a/vke/src/Buffer.cs
+++ b/vke/src/Buffer.cs
@@ -127,7 +127,7 @@ namespace CVKL {
protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
=> new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.BufferEXT, handle.Handle);
-#region CTORS
+ #region CTORS
public Buffer (Device device, VkBufferUsageFlags usage, VkMemoryPropertyFlags _memoryPropertyFlags, UInt64 size)
: base (device, _memoryPropertyFlags) {
@@ -135,23 +135,28 @@ namespace CVKL {
createInfo.usage = usage;
createInfo.sharingMode = VkSharingMode.Exclusive;
- Utils.CheckResult (vkCreateBuffer (Dev.VkDev, ref createInfo, IntPtr.Zero, out handle));
-
Activate ();//DONT OVERRIDE Activate in derived classes!!!!
}
-#endregion
+ #endregion
public override void Activate () {
if (state != ActivableState.Activated) {
- allocateMemory ();
- bindMemory (0);
+ Utils.CheckResult (vkCreateBuffer (Dev.VkDev, ref createInfo, IntPtr.Zero, out handle));
+ Dev.resourceManager.Add (this);
SetupDescriptor ();
}
base.Activate ();
}
+ internal override void updateMemoryRequirements () {
+ vkGetBufferMemoryRequirements (Dev.VkDev, handle, out memReqs);
+ }
+
+ internal override void bindMemory () {
+ Utils.CheckResult (vkBindBufferMemory (Dev.VkDev, handle, memoryPool.vkMemory, poolOffset));
+ }
- public void SetupDescriptor (ulong size = WholeSize, ulong offset = 0) {
+ public void SetupDescriptor (ulong size = WholeSize, ulong offset = 0) {
Descriptor.buffer = handle;
Descriptor.range = size;
Descriptor.offset = offset;
@@ -175,27 +180,16 @@ namespace CVKL {
}
public void CopyTo (CommandBuffer cmd, Buffer buff, ulong size = 0, ulong srcOffset = 0, ulong dstOffset = 0) {
VkBufferCopy bufferCopy = new VkBufferCopy {
- size = (size == 0) ? deviceMemSize : size,
+ size = (size == 0) ? AllocatedDeviceMemorySize : size,
srcOffset = srcOffset,
dstOffset = dstOffset
};
vkCmdCopyBuffer (cmd.Handle, handle, buff.handle, 1, ref bufferCopy);
}
public void Fill (CommandBuffer cmd, uint data, ulong size = 0, ulong offset = 0) {
- vkCmdFillBuffer (cmd.Handle, handle, offset, (size == 0) ? deviceMemSize : size, data);
+ vkCmdFillBuffer (cmd.Handle, handle, offset, (size == 0) ? AllocatedDeviceMemorySize : size, data);
}
-
- protected override VkMemoryRequirements getMemoryRequirements () {
- VkMemoryRequirements memReqs;
- vkGetBufferMemoryRequirements (Dev.VkDev, handle, out memReqs);
- return memReqs;
- }
-
- protected override void bindMemory (ulong offset) {
- Utils.CheckResult (vkBindBufferMemory (Dev.VkDev, handle, vkMemory, offset));
- }
-
public override string ToString () {
return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
}
@@ -209,5 +203,5 @@ namespace CVKL {
state = ActivableState.Disposed;
}
#endregion
- }
+ }
}
diff --git a/vke/src/Device.cs b/vke/src/Device.cs
index c86ff78..e66c28d 100644
--- a/vke/src/Device.cs
+++ b/vke/src/Device.cs
@@ -42,6 +42,8 @@ namespace CVKL {
internal List<Queue> queues = new List<Queue> ();
+ public ResourceManager resourceManager;
+
public Device (PhysicalDevice _phy, bool enableDebugMarkers = false) {
phy = _phy;
DebugMarkersEnabled = enableDebugMarkers;
@@ -105,9 +107,11 @@ namespace CVKL {
foreach (Queue q in queues)
q.updateHandle ();
+
+ resourceManager = new ResourceManager (this);
}
- unsafe public VkSemaphore CreateSemaphore () {
+ public VkSemaphore CreateSemaphore () {
VkSemaphore tmp;
VkSemaphoreCreateInfo info = VkSemaphoreCreateInfo.New();
Utils.CheckResult (vkCreateSemaphore (dev, ref info, IntPtr.Zero, out tmp));
@@ -116,7 +120,7 @@ namespace CVKL {
public void DestroySemaphore (VkSemaphore semaphore) {
vkDestroySemaphore (dev, semaphore, IntPtr.Zero);
}
- unsafe public VkFence CreateFence (bool signaled = false) {
+ public VkFence CreateFence (bool signaled = false) {
VkFence tmp;
VkFenceCreateInfo info = VkFenceCreateInfo.New();
info.flags = signaled ? VkFenceCreateFlags.Signaled : 0;
@@ -202,7 +206,7 @@ namespace CVKL {
// This is necessary as implementations can offer an arbitrary number of memory types with different
// memory properties.
// You can check http://vulkan.gpuinfo.org/ for details on different memory configurations
- internal unsafe uint GetMemoryTypeIndex (uint typeBits, VkMemoryPropertyFlags properties) {
+ internal uint GetMemoryTypeIndex (uint typeBits, VkMemoryPropertyFlags properties) {
// Iterate over all memory types available for the Device used in this example
for (uint i = 0; i < phy.memoryProperties.memoryTypeCount; i++) {
if ((typeBits & 1) == 1) {
diff --git a/vke/src/Image.cs b/vke/src/Image.cs
index a30cb4e..96015cb 100644
--- a/vke/src/Image.cs
+++ b/vke/src/Image.cs
@@ -30,6 +30,11 @@ using VK;
using static VK.Vk;
namespace CVKL {
+ /// <summary>
+ /// Combined Image/Descriptor class. Optional Sampler and View are disposed with the vkImage. If multiple view/sampler have to be
+ /// created for the same vkImage, you may call the constructor accepting a vkImage as parameter to import an existing one. vkImage handle of
+ /// such imported image will not be disposed with the sampler and the view.
+ /// </summary>
public class Image : Resource {
internal VkImage handle;
VkImageCreateInfo info = VkImageCreateInfo.New();
@@ -298,19 +303,16 @@ namespace CVKL {
}
#endregion
- protected override VkMemoryRequirements getMemoryRequirements () {
- VkMemoryRequirements memReqs;
- vkGetImageMemoryRequirements (Dev.VkDev, handle, out memReqs);
- return memReqs;
- }
- protected override void bindMemory (ulong offset = 0) {
- Utils.CheckResult (vkBindImageMemory (Dev.VkDev, handle, vkMemory, offset));
+ internal override void updateMemoryRequirements () {
+ vkGetImageMemoryRequirements (Dev.VkDev, handle, out memReqs);
}
+ internal override void bindMemory () {
+ Utils.CheckResult (vkBindImageMemory (Dev.VkDev, handle, memoryPool.vkMemory, poolOffset));
+ }
public override void Activate () {
if (state != ActivableState.Activated) {
Utils.CheckResult (vkCreateImage (Dev.VkDev, ref info, IntPtr.Zero, out handle));
- allocateMemory ();
- bindMemory ();
+ Dev.resourceManager.Add (this);
}
base.Activate ();
}
@@ -554,7 +556,7 @@ namespace CVKL {
public override string ToString () {
return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
}
-#region IDisposable Support
+ #region IDisposable Support
protected override void Dispose (bool disposing) {
if (state == ActivableState.Activated) {
if (Descriptor.sampler.Handle != 0)
@@ -568,6 +570,6 @@ namespace CVKL {
}
state = ActivableState.Disposed;
}
-#endregion
+ #endregion
}
}
diff --git a/vke/src/MemoryPool.cs b/vke/src/MemoryPool.cs
new file mode 100644
index 0000000..f544aaf
--- /dev/null
+++ b/vke/src/MemoryPool.cs
@@ -0,0 +1,98 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+ public enum MemoryPoolType {
+ Random,
+ Linear
+ }
+ public class MemoryPool : IDisposable {
+ Device dev;
+ internal VkDeviceMemory vkMemory;
+ VkMemoryAllocateInfo memInfo = VkMemoryAllocateInfo.New ();
+
+ Resource firstResource;
+ Resource lastResource;
+
+ //Resource mappedFrom;
+ //Resource mappedTo;
+
+ ulong freeMemPointer;
+ IntPtr mappedPointer;
+
+ public ulong Size => memInfo.allocationSize;
+ public bool IsMapped => mappedPointer != IntPtr.Zero;
+ public IntPtr MappedData => mappedPointer;
+
+ public MemoryPool (Device dev, uint memoryTypeIndex, UInt64 size) {
+ this.dev = dev;
+ memInfo.allocationSize = size;
+ memInfo.memoryTypeIndex = memoryTypeIndex;
+ Utils.CheckResult (vkAllocateMemory (dev.VkDev, ref memInfo, IntPtr.Zero, out vkMemory));
+ }
+
+ public void Add (Resource resource) {
+ resource.memoryPool = this;
+
+ if (lastResource == null) {
+ firstResource = lastResource = resource;
+ resource.poolOffset = 0;
+ } else {
+
+ resource.poolOffset = lastResource.poolOffset + lastResource.AllocatedDeviceMemorySize;
+ resource.poolOffset += resource.MemoryAlignment - (resource.poolOffset % resource.MemoryAlignment);
+
+ lastResource.next = resource;
+ resource.previous = lastResource;
+ lastResource = resource;
+ }
+ resource.bindMemory ();
+ }
+
+ public void Remove (Resource resource) {
+ if (resource == firstResource)
+ firstResource = resource.next;
+ if (resource == lastResource)
+ lastResource = resource.previous;
+ if (resource.previous != null)
+ resource.previous.next = resource.next;
+ if (resource.next != null)
+ resource.next.previous = resource.previous;
+
+ resource.next = resource.previous = null;
+ }
+
+ public void Map (ulong size = Vk.WholeSize, ulong offset = 0) {
+ Utils.CheckResult (vkMapMemory (dev.VkDev, vkMemory, offset, size, 0, ref mappedPointer));
+ }
+ public void Unmap () {
+ vkUnmapMemory (dev.VkDev, vkMemory);
+ mappedPointer = IntPtr.Zero;
+ }
+
+ #region IDisposable Support
+ private bool disposedValue;
+
+ protected virtual void Dispose (bool disposing) {
+ if (!disposedValue) {
+ if (!disposing)
+ System.Diagnostics.Debug.WriteLine ("MemoryPool disposed by Finalizer.");
+ vkFreeMemory (dev.VkDev, vkMemory, IntPtr.Zero);
+ disposedValue = true;
+ }
+ }
+
+ ~MemoryPool() {
+ Dispose(false);
+ }
+ public void Dispose () {
+ Dispose (true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/vke/src/PipelineCache.cs b/vke/src/PipelineCache.cs
index 879dda6..fb69e99 100644
--- a/vke/src/PipelineCache.cs
+++ b/vke/src/PipelineCache.cs
@@ -33,7 +33,25 @@ using VK;
using static VK.Vk;
namespace CVKL {
- public sealed class PipelineCache : Activable {
+ /// <summary>
+ /// Activable holding the pipeline cache handle. Activation is triggered by usage, so disposing pipelines that use this
+ /// cache is enough to have the cache disposed correctly.
+ /// </summary>
+ /// <remarks>
+ /// Restore and Saving of the cache may be controled through the two static members:
+ /// - `SaveOnDispose`
+ /// - `LoadOnActivation`
+ /// </remarks>
+ public sealed class PipelineCache : Activable {
+ /// <summary>
+ /// If true, cache will be saved on dispose
+ /// </summary>
+ public static bool SaveOnDispose;
+ /// <summary>
+ /// If true, cache will be restore on activation
+ /// </summary>
+ public static bool LoadOnActivation;
+
internal VkPipelineCache handle;
readonly string globalConfigPath;
readonly string cacheFile;
@@ -48,8 +66,6 @@ namespace CVKL {
Directory.CreateDirectory (globalConfigPath);
this.cacheFile = cacheFile;
-
- Activate ();
}
#endregion
@@ -59,7 +75,7 @@ namespace CVKL {
if (state != ActivableState.Activated) {
VkPipelineCacheCreateInfo info = VkPipelineCacheCreateInfo.New ();
- if (File.Exists (path)) {
+ if (File.Exists (path) && LoadOnActivation) {
using (FileStream fs = File.Open (path, FileMode.Open)) {
using (BinaryReader br = new BinaryReader (fs)) {
int length = (int)br.BaseStream.Length;
@@ -80,9 +96,16 @@ namespace CVKL {
protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo => throw new NotImplementedException ();
+ public void Delete () {
+ string path = Path.Combine (globalConfigPath, cacheFile);
+ if (File.Exists (path))
+ File.Delete (path);
+ }
+
public void Save () {
if (state != ActivableState.Activated)
return;
+
string path = Path.Combine (globalConfigPath, cacheFile);
if (File.Exists (path))
@@ -104,8 +127,11 @@ namespace CVKL {
protected override void Dispose (bool disposing) {
if (!disposing)
System.Diagnostics.Debug.WriteLine ($"CVKL PipelineCache '{name}' disposed by finalizer");
- if (state == ActivableState.Activated)
+ if (state == ActivableState.Activated) {
+ if (SaveOnDispose)
+ Save ();
vkDestroyPipelineCache (Dev.VkDev, handle, IntPtr.Zero);
+ }
base.Dispose (disposing);
}
#endregion
diff --git a/vke/src/Resource.cs b/vke/src/Resource.cs
index 183d4e8..1f25826 100644
--- a/vke/src/Resource.cs
+++ b/vke/src/Resource.cs
@@ -7,38 +7,44 @@ using static VK.Vk;
namespace CVKL {
public abstract class Resource : Activable {
- protected VkDeviceMemory vkMemory;
- protected UInt64 deviceMemSize;
- protected IntPtr mappedData;
+ protected VkMemoryRequirements memReqs;
+ internal MemoryPool memoryPool;
+ internal ulong poolOffset;
+ /// <summary> double linked list in memory pool </summary>
+ internal Resource previous;
+ internal Resource next;
- public readonly VkMemoryPropertyFlags MemoryFlags;
- public IntPtr MappedData => mappedData;
- public UInt64 AllocatedDeviceMemorySize => deviceMemSize;
+ public ulong AllocatedDeviceMemorySize => memReqs.size;
+ public uint TypeBits => memReqs.memoryTypeBits;
+ public ulong MemoryAlignment => memReqs.alignment;
+
+ protected IntPtr mappedData;
+ public IntPtr MappedData => mappedData;
+
+ public readonly VkMemoryPropertyFlags MemoryFlags;
protected Resource (Device device, VkMemoryPropertyFlags memoryFlags) : base (device) {
MemoryFlags = memoryFlags;
}
- protected abstract VkMemoryRequirements getMemoryRequirements ();
- protected abstract void bindMemory (ulong offset);
+ internal abstract void updateMemoryRequirements ();
- protected void allocateMemory () {
- VkMemoryRequirements memReqs = getMemoryRequirements ();
- VkMemoryAllocateInfo memInfo = VkMemoryAllocateInfo.New();
- memInfo.allocationSize = memReqs.size;
- memInfo.memoryTypeIndex = Dev.GetMemoryTypeIndex (memReqs.memoryTypeBits, MemoryFlags);
+ internal abstract void bindMemory ();
- Utils.CheckResult (vkAllocateMemory (Dev.VkDev, ref memInfo, IntPtr.Zero, out vkMemory));
-
- deviceMemSize = memInfo.allocationSize;
- }
+ internal VkMappedMemoryRange MapRange => new VkMappedMemoryRange {
+ sType = VkStructureType.MappedMemoryRange,
+ memory = memoryPool.vkMemory,
+ offset = poolOffset,
+ size = AllocatedDeviceMemorySize
+ };
- public void Map (ulong size = WholeSize, ulong offset = 0) {
- Utils.CheckResult (vkMapMemory (Dev.VkDev, vkMemory, offset, size, 0, ref mappedData));
+ public void Map (ulong offset = 0) {
+ if (!memoryPool.IsMapped)
+ memoryPool.Map ();
+ mappedData = new IntPtr(memoryPool.MappedData.ToInt64() + (long)(poolOffset + offset));
}
public void Unmap () {
- vkUnmapMemory (Dev.VkDev, vkMemory);
- mappedData = IntPtr.Zero;
+
}
public void Update (object data, ulong size, ulong offset = 0) {
GCHandle ptr = GCHandle.Alloc (data, GCHandleType.Pinned);
@@ -48,13 +54,8 @@ namespace CVKL {
}
ptr.Free ();
}
- public void Flush (ulong size = WholeSize, ulong offset = 0) {
- VkMappedMemoryRange range = new VkMappedMemoryRange {
- sType = VkStructureType.MappedMemoryRange,
- memory = vkMemory,
- offset = offset,
- size = size,
- };
+ public void Flush () {
+ VkMappedMemoryRange range = MapRange;
vkFlushMappedMemoryRanges (Dev.VkDev, 1, ref range);
}
@@ -65,7 +66,7 @@ namespace CVKL {
if (state == ActivableState.Activated) {
if (mappedData != IntPtr.Zero)
Unmap ();
- vkFreeMemory (Dev.VkDev, vkMemory, IntPtr.Zero);
+ memoryPool.Remove (this);
}
base.Dispose (disposing);
}
diff --git a/vke/src/ResourceManager.cs b/vke/src/ResourceManager.cs
new file mode 100644
index 0000000..02388b8
--- /dev/null
+++ b/vke/src/ResourceManager.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+ public class ResourceManager : IDisposable {
+ VkPhysicalDeviceMemoryProperties memoryProperties;
+ MemoryPool[] memoryPools;
+ ulong[] reservedHeapMemory;
+
+ VkMemoryHeap getHeapFromMemoryIndex (uint i) => memoryProperties.memoryHeaps[memoryProperties.memoryTypes[i].heapIndex];
+
+
+ public ResourceManager (Device dev, ulong defaultPoolsBlockDivisor = 8) {
+ memoryProperties = dev.phy.memoryProperties;
+ memoryPools = new MemoryPool[memoryProperties.memoryTypeCount];
+ reservedHeapMemory = new ulong[memoryProperties.memoryHeapCount];
+
+ for (uint i = 0; i < memoryPools.Length; i++) {
+ ulong size = getHeapFromMemoryIndex (i).size / defaultPoolsBlockDivisor;
+ memoryPools[i] = new MemoryPool (dev, i, size);
+ reservedHeapMemory[memoryProperties.memoryTypes[i].heapIndex] += size;
+ }
+ }
+
+ public void Add (params Resource[] resources) {
+ foreach (Resource res in resources) {
+ res.updateMemoryRequirements ();
+ GetMemoryPool (res.TypeBits, res.MemoryFlags).Add (res);
+ }
+ }
+
+ MemoryPool GetMemoryPool (uint typeBits, VkMemoryPropertyFlags properties) {
+ // Iterate over all memory types available for the Device used in this example
+ for (uint i = 0; i < memoryProperties.memoryTypeCount; i++) {
+ if ((typeBits & 1) == 1) {
+ if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
+ return memoryPools[i];
+ }
+ typeBits >>= 1;
+ }
+ throw new InvalidOperationException ("Could not find a suitable memory type!");
+ }
+
+ public void Dispose () {
+ for (uint i = 0; i < memoryPools.Length; i++)
+ memoryPools[i].Dispose ();
+ }
+ }
+} \ No newline at end of file