comparison semiconginev2/old/vulkan/image.nim @ 1218:56781cc0fc7c compiletime-tests

did: renamge main package
author sam <sam@basx.dev>
date Wed, 17 Jul 2024 21:01:37 +0700
parents semicongine/old/vulkan/image.nim@a3eb305bcac2
children
comparison
equal deleted inserted replaced
1217:f819a874058f 1218:56781cc0fc7c
1 import std/strformat
2 import std/tables
3 import std/logging
4
5 import ../core
6 import ./device
7 import ./physicaldevice
8 import ./buffer
9 import ./memory
10 import ./commandbuffer
11
12 type
13 PixelDepth = 1 .. 4
14 VulkanImage* = object
15 device*: Device
16 vk*: VkImage
17 width*: uint32 # pixel
18 height*: uint32 # pixel
19 depth*: PixelDepth
20 format*: VkFormat
21 usage*: seq[VkImageUsageFlagBits]
22 case memoryAllocated*: bool
23 of false: discard
24 of true:
25 memory*: DeviceMemory
26 VulkanSampler* = object
27 device*: Device
28 vk*: VkSampler
29 ImageView* = object
30 vk*: VkImageView
31 image*: VulkanImage
32 VulkanTexture* = object
33 image*: VulkanImage
34 imageView*: ImageView
35 sampler*: VulkanSampler
36
37 const DEPTH_FORMAT_MAP = {
38 PixelDepth(1): [VK_FORMAT_R8_SRGB, VK_FORMAT_R8_UNORM],
39 PixelDepth(2): [VK_FORMAT_R8G8_SRGB, VK_FORMAT_R8G8_UNORM],
40 PixelDepth(3): [VK_FORMAT_R8G8B8_SRGB, VK_FORMAT_R8G8B8_UNORM],
41 PixelDepth(4): [VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_R8G8B8A8_UNORM],
42 }.toTable
43
44 const LINEAR_FORMATS = [
45 VK_FORMAT_R8_UNORM,
46 VK_FORMAT_R8G8_UNORM,
47 VK_FORMAT_R8G8B8_UNORM,
48 VK_FORMAT_R8G8B8A8_UNORM,
49 ]
50
51
52 proc requirements(image: VulkanImage): MemoryRequirements =
53 assert image.vk.Valid
54 assert image.device.vk.Valid
55 var req: VkMemoryRequirements
56 image.device.vk.vkGetImageMemoryRequirements(image.vk, addr req)
57 result.size = req.size
58 result.alignment = req.alignment
59 let memorytypes = image.device.physicaldevice.vk.GetMemoryProperties().types
60 for i in 0 ..< sizeof(req.memoryTypeBits) * 8:
61 if ((req.memoryTypeBits shr i) and 1) == 1:
62 result.memoryTypes.add memorytypes[i]
63
64 proc allocateMemory(image: var VulkanImage, requireMappable: bool, preferVRAM: bool, preferAutoFlush: bool) =
65 assert image.device.vk.Valid
66 assert image.memoryAllocated == false
67
68 let requirements = image.requirements()
69 let memoryType = requirements.memoryTypes.SelectBestMemoryType(
70 requireMappable = requireMappable,
71 preferVRAM = preferVRAM,
72 preferAutoFlush = preferAutoFlush
73 )
74
75 debug "Allocating memory for image: ", image.width, "x", image.height, "x", image.depth, ", ", requirements.size, " bytes of type ", memoryType
76 image = VulkanImage(
77 device: image.device,
78 vk: image.vk,
79 width: image.width,
80 height: image.height,
81 depth: image.depth,
82 format: image.format,
83 usage: image.usage,
84 memoryAllocated: true,
85 memory: image.device.Allocate(requirements.size, memoryType),
86 )
87 checkVkResult image.device.vk.vkBindImageMemory(image.vk, image.memory.vk, VkDeviceSize(0))
88
89 proc transitionImageLayout(image: VulkanImage, queue: Queue, oldLayout, newLayout: VkImageLayout) =
90 var
91 barrier = VkImageMemoryBarrier(
92 sType: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
93 oldLayout: oldLayout,
94 newLayout: newLayout,
95 srcQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED,
96 dstQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED,
97 image: image.vk,
98 subresourceRange: VkImageSubresourceRange(
99 aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT],
100 baseMipLevel: 0,
101 levelCount: 1,
102 baseArrayLayer: 0,
103 layerCount: 1,
104 ),
105 )
106 srcStage: VkPipelineStageFlagBits
107 dstStage: VkPipelineStageFlagBits
108 if oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
109 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
110 barrier.srcAccessMask = VkAccessFlags(0)
111 dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT
112 barrier.dstAccessMask = [VK_ACCESS_TRANSFER_WRITE_BIT].toBits
113 elif oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
114 srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT
115 barrier.srcAccessMask = [VK_ACCESS_TRANSFER_WRITE_BIT].toBits
116 dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
117 barrier.dstAccessMask = [VK_ACCESS_SHADER_READ_BIT].toBits
118 else:
119 raise newException(Exception, "Unsupported layout transition!")
120
121 WithSingleUseCommandBuffer(image.device, queue, commandBuffer):
122 commandBuffer.PipelineBarrier([srcStage], [dstStage], imageBarriers = [barrier])
123
124 proc Copy*(src: Buffer, dst: VulkanImage, queue: Queue) =
125 assert src.device.vk.Valid
126 assert dst.device.vk.Valid
127 assert src.device == dst.device
128 assert VK_BUFFER_USAGE_TRANSFER_SRC_BIT in src.usage
129 assert VK_IMAGE_USAGE_TRANSFER_DST_BIT in dst.usage
130
131 var region = VkBufferImageCopy(
132 bufferOffset: 0,
133 bufferRowLength: 0,
134 bufferImageHeight: 0,
135 imageSubresource: VkImageSubresourceLayers(
136 aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT],
137 mipLevel: 0,
138 baseArrayLayer: 0,
139 layerCount: 1,
140 ),
141 imageOffset: VkOffset3D(x: 0, y: 0, z: 0),
142 imageExtent: VkExtent3D(width: dst.width, height: dst.height, depth: 1)
143 )
144 WithSingleUseCommandBuffer(src.device, queue, commandBuffer):
145 commandBuffer.vkCmdCopyBufferToImage(
146 src.vk,
147 dst.vk,
148 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
149 1,
150 addr region
151 )
152
153 proc CreateImage*(device: Device, width, height, depth: uint32, format: VkFormat, samples: VkSampleCountFlagBits, usage: openArray[VkImageUsageFlagBits]): VulkanImage =
154 assert device.vk.Valid
155 assert width > 0
156 assert height > 0
157
158 result.device = device
159 result.usage = @usage
160
161
162 result.width = width
163 result.height = height
164 result.depth = depth
165 result.format = format
166
167 var imageInfo = VkImageCreateInfo(
168 sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
169 imageType: VK_IMAGE_TYPE_2D,
170 extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1),
171 mipLevels: 1,
172 arrayLayers: 1,
173 format: result.format,
174 tiling: VK_IMAGE_TILING_OPTIMAL,
175 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
176 usage: toBits result.usage,
177 sharingMode: VK_SHARING_MODE_EXCLUSIVE,
178 samples: samples,
179 )
180 checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk)
181 result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false)
182
183 # currently only usable for texture access from shader
184 proc createTextureImage[T](device: Device, queue: Queue, width, height: uint32, depth: PixelDepth, image: Image[T]): VulkanImage =
185 assert device.vk.Valid
186 assert width > 0
187 assert height > 0
188 assert depth != 2
189
190 result.device = device
191 result.usage = @[VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_USAGE_SAMPLED_BIT]
192
193 let size: uint64 = width * height * uint32(depth)
194 let usageBits = toBits result.usage
195 var formatList = DEPTH_FORMAT_MAP[depth]
196 var selectedFormat: VkFormat
197 var formatProperties = VkImageFormatProperties2(sType: VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2)
198
199 for format in formatList:
200 var formatInfo = VkPhysicalDeviceImageFormatInfo2(
201 sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
202 format: format,
203 thetype: VK_IMAGE_TYPE_2D,
204 tiling: VK_IMAGE_TILING_OPTIMAL,
205 usage: usageBits,
206 )
207 let formatCheck = device.physicalDevice.vk.vkGetPhysicalDeviceImageFormatProperties2(
208 addr formatInfo,
209 addr formatProperties,
210 )
211 if formatCheck == VK_SUCCESS: # found suitable format
212 selectedFormat = format
213 break
214 elif formatCheck == VK_ERROR_FORMAT_NOT_SUPPORTED: # nope, try to find other format
215 continue
216 else: # raise error
217 checkVkResult formatCheck
218
219 # assumption: images comes in sRGB color space
220 # convert to linear space if there is not support for sRGB
221 var data = addr image.imagedata[0]
222 if selectedFormat in LINEAR_FORMATS:
223 let linearImage = image.AsLinear()
224 data = addr linearImage.imagedata[0]
225
226 assert size <= uint64(formatProperties.imageFormatProperties.maxResourceSize)
227 assert width <= uint64(formatProperties.imageFormatProperties.maxExtent.width)
228 assert height <= uint64(formatProperties.imageFormatProperties.maxExtent.height)
229
230 result.width = width
231 result.height = height
232 result.depth = depth
233 result.format = selectedFormat
234
235 var imageInfo = VkImageCreateInfo(
236 sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
237 imageType: VK_IMAGE_TYPE_2D,
238 extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1),
239 mipLevels: min(1'u32, formatProperties.imageFormatProperties.maxMipLevels),
240 arrayLayers: min(1'u32, formatProperties.imageFormatProperties.maxArrayLayers),
241 format: result.format,
242 tiling: VK_IMAGE_TILING_OPTIMAL,
243 initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
244 usage: usageBits,
245 sharingMode: VK_SHARING_MODE_EXCLUSIVE,
246 samples: VK_SAMPLE_COUNT_1_BIT,
247 )
248 checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk)
249 result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false)
250 result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
251
252 var stagingBuffer = device.CreateBuffer(size = size, usage = [VK_BUFFER_USAGE_TRANSFER_SRC_BIT], requireMappable = true, preferVRAM = false, preferAutoFlush = true)
253 stagingBuffer.SetData(queue, src = data, size = size)
254 stagingBuffer.Copy(result, queue)
255 result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
256 stagingBuffer.Destroy()
257
258 proc Destroy*(image: var VulkanImage) =
259 assert image.device.vk.Valid
260 assert image.vk.Valid
261 image.device.vk.vkDestroyImage(image.vk, nil)
262 if image.memoryAllocated:
263 assert image.memory.vk.Valid
264 image.memory.Free()
265 image = VulkanImage(
266 device: image.device,
267 vk: image.vk,
268 width: image.width,
269 height: image.height,
270 depth: image.depth,
271 format: image.format,
272 usage: image.usage,
273 memoryAllocated: false,
274 )
275 image.vk.Reset
276
277 proc CreateSampler*(device: Device, sampler: Sampler): VulkanSampler =
278 assert device.vk.Valid
279 var samplerInfo = VkSamplerCreateInfo(
280 sType: VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
281 magFilter: sampler.magnification,
282 minFilter: sampler.minification,
283 addressModeU: sampler.wrapModeS,
284 addressModeV: sampler.wrapModeT,
285 addressModeW: VK_SAMPLER_ADDRESS_MODE_REPEAT,
286 anisotropyEnable: device.enabledFeatures.samplerAnisotropy,
287 maxAnisotropy: device.physicalDevice.properties.limits.maxSamplerAnisotropy,
288 borderColor: VK_BORDER_COLOR_INT_OPAQUE_BLACK,
289 unnormalizedCoordinates: VK_FALSE,
290 compareEnable: VK_FALSE,
291 compareOp: VK_COMPARE_OP_ALWAYS,
292 mipmapMode: VK_SAMPLER_MIPMAP_MODE_LINEAR,
293 mipLodBias: 0,
294 minLod: 0,
295 maxLod: 0,
296 )
297 result.device = device
298 checkVkResult device.vk.vkCreateSampler(addr samplerInfo, nil, addr result.vk)
299
300 proc Destroy*(sampler: var VulkanSampler) =
301 assert sampler.device.vk.Valid
302 assert sampler.vk.Valid
303 sampler.device.vk.vkDestroySampler(sampler.vk, nil)
304 sampler.vk.Reset
305
306 proc CreateImageView*(
307 image: VulkanImage,
308 imageviewtype = VK_IMAGE_VIEW_TYPE_2D,
309 baseMipLevel = 0'u32,
310 levelCount = 1'u32,
311 baseArrayLayer = 0'u32,
312 layerCount = 1'u32
313 ): ImageView =
314 assert image.device.vk.Valid
315 assert image.vk.Valid
316
317 var createInfo = VkImageViewCreateInfo(
318 sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
319 image: image.vk,
320 viewType: imageviewtype,
321 format: image.format,
322 components: VkComponentMapping(
323 r: VK_COMPONENT_SWIZZLE_IDENTITY,
324 g: VK_COMPONENT_SWIZZLE_IDENTITY,
325 b: VK_COMPONENT_SWIZZLE_IDENTITY,
326 a: VK_COMPONENT_SWIZZLE_IDENTITY,
327 ),
328 subresourceRange: VkImageSubresourceRange(
329 aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT),
330 baseMipLevel: baseMipLevel,
331 levelCount: levelCount,
332 baseArrayLayer: baseArrayLayer,
333 layerCount: layerCount,
334 ),
335 )
336 result.image = image
337 checkVkResult image.device.vk.vkCreateImageView(addr(createInfo), nil, addr(result.vk))
338
339 proc Destroy*(imageview: var ImageView) =
340 assert imageview.image.device.vk.Valid
341 assert imageview.vk.Valid
342 imageview.image.device.vk.vkDestroyImageView(imageview.vk, nil)
343 imageview.vk.Reset()
344
345 func `$`*(texture: VulkanTexture): string =
346 &"VulkanTexture({texture.image.width}x{texture.image.height})"
347
348
349 proc UploadTexture*(device: Device, queue: Queue, texture: Texture): VulkanTexture =
350 assert device.vk.Valid
351 if texture.isGrayscale:
352 result.image = createTextureImage(device = device, queue = queue, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, image = texture.grayImage)
353 else:
354 result.image = createTextureImage(device = device, queue = queue, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, image = texture.colorImage)
355 result.imageView = result.image.CreateImageView()
356 result.sampler = result.image.device.CreateSampler(texture.sampler)
357
358 proc Destroy*(texture: var VulkanTexture) =
359 texture.image.Destroy()
360 texture.imageView.Destroy()
361 texture.sampler.Destroy()