diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2019-04-27 03:59:01 +0200 |
|---|---|---|
| committer | j-p <jp_bruyere@hotmail.com> | 2019-05-02 03:07:11 +0200 |
| commit | 997ebab9c71d81478581c672c8ba7dc0ed05e2fc (patch) | |
| tree | 014fe2691a563f8233df13dcdace7ae3c1ed9207 | |
| parent | 568219e7719bfaf076ba23b539dd6ee7f647e273 (diff) | |
ResourceManager and MemoryPools
| -rw-r--r-- | samples/data/models/Hubble.glb | 3 | ||||
| -rw-r--r-- | samples/deferred/main.cs | 1 | ||||
| -rw-r--r-- | samples/deferred/shaders/tone_mapping.frag | 24 | ||||
| -rw-r--r-- | vk.net.sln | 12 | ||||
| -rw-r--r-- | vke/src/Activable.cs | 4 | ||||
| -rw-r--r-- | vke/src/Buffer.cs | 36 | ||||
| -rw-r--r-- | vke/src/Device.cs | 10 | ||||
| -rw-r--r-- | vke/src/Image.cs | 24 | ||||
| -rw-r--r-- | vke/src/MemoryPool.cs | 98 | ||||
| -rw-r--r-- | vke/src/PipelineCache.cs | 36 | ||||
| -rw-r--r-- | vke/src/Resource.cs | 59 | ||||
| -rw-r--r-- | vke/src/ResourceManager.cs | 54 |
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 @@ -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 |