Mercurial > games > semicongine
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() | 
