1188
|
1 import std/strformat
|
|
2
|
|
3 import semicongine/core/vulkanapi
|
|
4
|
|
5 type
|
|
6 VulkanGlobals* = object
|
|
7 instance*: VkInstance
|
|
8 device*: VkDevice
|
|
9 physicalDevice*: VkPhysicalDevice
|
|
10 queueFamilyIndex*: uint32
|
|
11 queue*: VkQueue
|
|
12 anisotropy*: float32 = 0 # needs to be enable during device creation
|
|
13
|
|
14 var vulkan*: VulkanGlobals
|
|
15
|
|
16 proc svkGetPhysicalDeviceProperties*(): VkPhysicalDeviceProperties =
|
|
17 vkGetPhysicalDeviceProperties(vulkan.physicalDevice, addr(result))
|
|
18
|
|
19 proc svkCreateBuffer*(size: uint64, usage: openArray[VkBufferUsageFlagBits]): VkBuffer =
|
|
20 var createInfo = VkBufferCreateInfo(
|
|
21 sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
22 flags: VkBufferCreateFlags(0),
|
|
23 size: size,
|
|
24 usage: usage.toBits,
|
|
25 sharingMode: VK_SHARING_MODE_EXCLUSIVE,
|
|
26 )
|
|
27 checkVkResult vkCreateBuffer(
|
|
28 device = vulkan.device,
|
|
29 pCreateInfo = addr(createInfo),
|
|
30 pAllocator = nil,
|
|
31 pBuffer = addr(result),
|
|
32 )
|
|
33
|
|
34 proc svkAllocateMemory*(size: uint64, typeIndex: uint32): VkDeviceMemory =
|
|
35 var memoryAllocationInfo = VkMemoryAllocateInfo(
|
|
36 sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
37 allocationSize: size,
|
|
38 memoryTypeIndex: typeIndex,
|
|
39 )
|
|
40 checkVkResult vkAllocateMemory(
|
|
41 vulkan.device,
|
|
42 addr(memoryAllocationInfo),
|
|
43 nil,
|
|
44 addr(result),
|
|
45 )
|
|
46
|
|
47 proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits]): VkImage =
|
|
48 var imageProps: VkImageFormatProperties
|
|
49 checkVkResult vkGetPhysicalDeviceImageFormatProperties(
|
|
50 vulkan.physicalDevice,
|
|
51 format,
|
|
52 VK_IMAGE_TYPE_2D,
|
|
53 VK_IMAGE_TILING_OPTIMAL,
|
|
54 usage.toBits,
|
|
55 VkImageCreateFlags(0),
|
|
56 addr(imageProps)
|
|
57 )
|
|
58
|
|
59 var imageInfo = VkImageCreateInfo(
|
|
60 sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
61 imageType: VK_IMAGE_TYPE_2D,
|
|
62 extent: VkExtent3D(width: width, height: height, depth: 1),
|
|
63 mipLevels: min(1'u32, imageProps.maxMipLevels),
|
|
64 arrayLayers: min(1'u32, imageProps.maxArrayLayers),
|
|
65 format: format,
|
|
66 tiling: VK_IMAGE_TILING_OPTIMAL,
|
|
67 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
|
|
68 usage: usage.toBits,
|
|
69 sharingMode: VK_SHARING_MODE_EXCLUSIVE,
|
|
70 samples: VK_SAMPLE_COUNT_1_BIT,
|
|
71 )
|
|
72 checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result))
|
|
73
|
|
74 proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue =
|
|
75 vkGetDeviceQueue(
|
|
76 device,
|
|
77 queueFamilyIndex,
|
|
78 0,
|
|
79 addr(result),
|
|
80 )
|
|
81
|
|
82 proc svkGetBufferMemoryRequirements*(buffer: VkBuffer): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] =
|
|
83 var reqs: VkMemoryRequirements
|
|
84 vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs))
|
|
85 result.size = reqs.size
|
|
86 result.alignment = reqs.alignment
|
|
87 for i in 0'u32 ..< VK_MAX_MEMORY_TYPES:
|
|
88 if ((1'u32 shl i) and reqs.memoryTypeBits) > 0:
|
|
89 result.memoryTypes.add i
|
|
90
|
|
91 proc svkGetImageMemoryRequirements*(image: VkImage): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] =
|
|
92 var reqs: VkMemoryRequirements
|
|
93 vkGetImageMemoryRequirements(vulkan.device, image, addr(reqs))
|
|
94 result.size = reqs.size
|
|
95 result.alignment = reqs.alignment
|
|
96 for i in 0'u32 ..< VK_MAX_MEMORY_TYPES:
|
|
97 if ((1'u32 shl i) and reqs.memoryTypeBits) > 0:
|
|
98 result.memoryTypes.add i
|
|
99
|
|
100 proc BestMemory*(mappable: bool, filter: seq[uint32] = @[]): uint32 =
|
|
101 var physicalProperties: VkPhysicalDeviceMemoryProperties
|
|
102 vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties))
|
|
103
|
|
104 var maxScore: float = -1
|
|
105 var maxIndex: uint32 = 0
|
|
106 for index in 0'u32 ..< physicalProperties.memoryTypeCount:
|
|
107 if filter.len == 0 or index in filter:
|
|
108 let flags = toEnums(physicalProperties.memoryTypes[index].propertyFlags)
|
|
109 if not mappable or VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags:
|
|
110 var score: float = 0
|
|
111 if VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT in flags: score += 1_000_000
|
|
112 if VK_MEMORY_PROPERTY_HOST_CACHED_BIT in flags: score += 1_000
|
|
113 score += float(physicalProperties.memoryHeaps[physicalProperties.memoryTypes[index].heapIndex].size) / 1_000_000_000
|
|
114 if score > maxScore:
|
|
115 maxScore = score
|
|
116 maxIndex = index
|
|
117 assert maxScore > 0, &"Unable to find memory type (mappable: {mappable}, filter: {filter})"
|
|
118 return maxIndex
|
|
119
|
|
120 template WithSingleUseCommandBuffer*(cmd, body: untyped): untyped =
|
|
121 block:
|
|
122 var
|
|
123 commandBufferPool: VkCommandPool
|
|
124 createInfo = VkCommandPoolCreateInfo(
|
|
125 sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
126 flags: toBits [VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT],
|
|
127 queueFamilyIndex: vulkan.queueFamilyIndex,
|
|
128 )
|
|
129 checkVkResult vkCreateCommandPool(vulkan.device, addr createInfo, nil, addr(commandBufferPool))
|
|
130 var
|
|
131 `cmd` {.inject.}: VkCommandBuffer
|
|
132 allocInfo = VkCommandBufferAllocateInfo(
|
|
133 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
134 commandPool: commandBufferPool,
|
|
135 level: VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
136 commandBufferCount: 1,
|
|
137 )
|
|
138 checkVkResult vulkan.device.vkAllocateCommandBuffers(addr allocInfo, addr(`cmd`))
|
|
139 var beginInfo = VkCommandBufferBeginInfo(
|
|
140 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
141 flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT),
|
|
142 )
|
|
143 checkVkResult `cmd`.vkBeginCommandBuffer(addr beginInfo)
|
|
144
|
|
145 body
|
|
146
|
|
147 checkVkResult `cmd`.vkEndCommandBuffer()
|
|
148 var submitInfo = VkSubmitInfo(
|
|
149 sType: VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
150 commandBufferCount: 1,
|
|
151 pCommandBuffers: addr(`cmd`),
|
|
152 )
|
|
153
|
|
154 var
|
|
155 fence: VkFence
|
|
156 fenceInfo = VkFenceCreateInfo(
|
|
157 sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
158 # flags: toBits [VK_FENCE_CREATE_SIGNALED_BIT]
|
|
159 )
|
|
160 checkVkResult vulkan.device.vkCreateFence(addr(fenceInfo), nil, addr(fence))
|
|
161 checkVkResult vkQueueSubmit(vulkan.queue, 1, addr(submitInfo), fence)
|
|
162 checkVkResult vkWaitForFences(vulkan.device, 1, addr fence, false, high(uint64))
|
|
163 vkDestroyCommandPool(vulkan.device, commandBufferPool, nil)
|
|
164
|
|
165 template WithStagingBuffer*[T: (VkBuffer, uint64)|(VkImage, uint32, uint32)](
|
|
166 target: T,
|
|
167 bufferSize: uint64,
|
|
168 dataPointer,
|
|
169 body: untyped
|
|
170 ): untyped =
|
|
171 var `dataPointer` {.inject.}: pointer
|
|
172 let stagingBuffer = svkCreateBuffer(bufferSize, [VK_BUFFER_USAGE_TRANSFER_SRC_BIT])
|
|
173 let memoryRequirements = svkGetBufferMemoryRequirements(stagingBuffer)
|
|
174 let memoryType = BestMemory(mappable = true, filter = memoryRequirements.memoryTypes)
|
|
175 let stagingMemory = svkAllocateMemory(memoryRequirements.size, memoryType)
|
|
176 checkVkResult vkMapMemory(
|
|
177 device = vulkan.device,
|
|
178 memory = stagingMemory,
|
|
179 offset = 0'u64,
|
|
180 size = VK_WHOLE_SIZE,
|
|
181 flags = VkMemoryMapFlags(0),
|
|
182 ppData = addr(`dataPointer`)
|
|
183 )
|
|
184 checkVkResult vkBindBufferMemory(vulkan.device, stagingBuffer, stagingMemory, 0)
|
|
185
|
|
186 block:
|
|
187 # usually: write data to dataPointer in body
|
|
188 body
|
|
189
|
|
190 var stagingRange = VkMappedMemoryRange(
|
|
191 sType: VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
|
|
192 memory: stagingMemory,
|
|
193 size: VK_WHOLE_SIZE,
|
|
194 )
|
|
195 checkVkResult vkFlushMappedMemoryRanges(vulkan.device, 1, addr(stagingRange))
|
|
196
|
|
197 WithSingleUseCommandBuffer(commandBuffer):
|
|
198 when T is (VkBuffer, uint64):
|
|
199 let copyRegion = VkBufferCopy(
|
|
200 size: bufferSize,
|
|
201 dstOffset: target[1],
|
|
202 srcOffset: 0
|
|
203 )
|
|
204 vkCmdCopyBuffer(
|
|
205 commandBuffer = commandBuffer,
|
|
206 srcBuffer = stagingBuffer,
|
|
207 dstBuffer = target[0],
|
|
208 regionCount = 1,
|
|
209 pRegions = addr(copyRegion)
|
|
210 )
|
|
211 elif T is (VkImage, uint32, uint32):
|
|
212 let region = VkBufferImageCopy(
|
|
213 bufferOffset: 0,
|
|
214 bufferRowLength: 0,
|
|
215 bufferImageHeight: 0,
|
|
216 imageSubresource: VkImageSubresourceLayers(
|
|
217 aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT],
|
|
218 mipLevel: 0,
|
|
219 baseArrayLayer: 0,
|
|
220 layerCount: 1,
|
|
221 ),
|
|
222 imageOffset: VkOffset3D(x: 0, y: 0, z: 0),
|
|
223 imageExtent: VkExtent3D(width: target[1], height: target[2], depth: 1)
|
|
224 )
|
|
225 vkCmdCopyBufferToImage(
|
|
226 commandBuffer = commandBuffer,
|
|
227 srcBuffer = stagingBuffer,
|
|
228 dstImage = target[0],
|
|
229 dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
230 regionCount = 1,
|
|
231 pRegions = addr(region)
|
|
232 )
|
|
233
|
|
234 vkDestroyBuffer(vulkan.device, stagingBuffer, nil)
|
|
235 vkFreeMemory(vulkan.device, stagingMemory, nil)
|
|
236
|