diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/semiconginev2/old/vulkan/image.nim	Wed Jul 17 21:01:37 2024 +0700
@@ -0,0 +1,361 @@
+import std/strformat
+import std/tables
+import std/logging
+
+import ../core
+import ./device
+import ./physicaldevice
+import ./buffer
+import ./memory
+import ./commandbuffer
+
+type
+  PixelDepth = 1 .. 4
+  VulkanImage* = object
+    device*: Device
+    vk*: VkImage
+    width*: uint32  # pixel
+    height*: uint32 # pixel
+    depth*: PixelDepth
+    format*: VkFormat
+    usage*: seq[VkImageUsageFlagBits]
+    case memoryAllocated*: bool
+      of false: discard
+      of true:
+        memory*: DeviceMemory
+  VulkanSampler* = object
+    device*: Device
+    vk*: VkSampler
+  ImageView* = object
+    vk*: VkImageView
+    image*: VulkanImage
+  VulkanTexture* = object
+    image*: VulkanImage
+    imageView*: ImageView
+    sampler*: VulkanSampler
+
+const DEPTH_FORMAT_MAP = {
+  PixelDepth(1): [VK_FORMAT_R8_SRGB, VK_FORMAT_R8_UNORM],
+  PixelDepth(2): [VK_FORMAT_R8G8_SRGB, VK_FORMAT_R8G8_UNORM],
+  PixelDepth(3): [VK_FORMAT_R8G8B8_SRGB, VK_FORMAT_R8G8B8_UNORM],
+  PixelDepth(4): [VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_R8G8B8A8_UNORM],
+}.toTable
+
+const LINEAR_FORMATS = [
+  VK_FORMAT_R8_UNORM,
+  VK_FORMAT_R8G8_UNORM,
+  VK_FORMAT_R8G8B8_UNORM,
+  VK_FORMAT_R8G8B8A8_UNORM,
+]
+
+
+proc requirements(image: VulkanImage): MemoryRequirements =
+  assert image.vk.Valid
+  assert image.device.vk.Valid
+  var req: VkMemoryRequirements
+  image.device.vk.vkGetImageMemoryRequirements(image.vk, addr req)
+  result.size = req.size
+  result.alignment = req.alignment
+  let memorytypes = image.device.physicaldevice.vk.GetMemoryProperties().types
+  for i in 0 ..< sizeof(req.memoryTypeBits) * 8:
+    if ((req.memoryTypeBits shr i) and 1) == 1:
+      result.memoryTypes.add memorytypes[i]
+
+proc allocateMemory(image: var VulkanImage, requireMappable: bool, preferVRAM: bool, preferAutoFlush: bool) =
+  assert image.device.vk.Valid
+  assert image.memoryAllocated == false
+
+  let requirements = image.requirements()
+  let memoryType = requirements.memoryTypes.SelectBestMemoryType(
+    requireMappable = requireMappable,
+    preferVRAM = preferVRAM,
+    preferAutoFlush = preferAutoFlush
+  )
+
+  debug "Allocating memory for image: ", image.width, "x", image.height, "x", image.depth, ", ", requirements.size, " bytes of type ", memoryType
+  image = VulkanImage(
+    device: image.device,
+    vk: image.vk,
+    width: image.width,
+    height: image.height,
+    depth: image.depth,
+    format: image.format,
+    usage: image.usage,
+    memoryAllocated: true,
+    memory: image.device.Allocate(requirements.size, memoryType),
+  )
+  checkVkResult image.device.vk.vkBindImageMemory(image.vk, image.memory.vk, VkDeviceSize(0))
+
+proc transitionImageLayout(image: VulkanImage, queue: Queue, oldLayout, newLayout: VkImageLayout) =
+  var
+    barrier = VkImageMemoryBarrier(
+      sType: VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+      oldLayout: oldLayout,
+      newLayout: newLayout,
+      srcQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED,
+      dstQueueFamilyIndex: VK_QUEUE_FAMILY_IGNORED,
+      image: image.vk,
+      subresourceRange: VkImageSubresourceRange(
+        aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT],
+        baseMipLevel: 0,
+        levelCount: 1,
+        baseArrayLayer: 0,
+        layerCount: 1,
+      ),
+    )
+    srcStage: VkPipelineStageFlagBits
+    dstStage: VkPipelineStageFlagBits
+  if oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+    srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
+    barrier.srcAccessMask = VkAccessFlags(0)
+    dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT
+    barrier.dstAccessMask = [VK_ACCESS_TRANSFER_WRITE_BIT].toBits
+  elif oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+    srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT
+    barrier.srcAccessMask = [VK_ACCESS_TRANSFER_WRITE_BIT].toBits
+    dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
+    barrier.dstAccessMask = [VK_ACCESS_SHADER_READ_BIT].toBits
+  else:
+    raise newException(Exception, "Unsupported layout transition!")
+
+  WithSingleUseCommandBuffer(image.device, queue, commandBuffer):
+    commandBuffer.PipelineBarrier([srcStage], [dstStage], imageBarriers = [barrier])
+
+proc Copy*(src: Buffer, dst: VulkanImage, queue: Queue) =
+  assert src.device.vk.Valid
+  assert dst.device.vk.Valid
+  assert src.device == dst.device
+  assert VK_BUFFER_USAGE_TRANSFER_SRC_BIT in src.usage
+  assert VK_IMAGE_USAGE_TRANSFER_DST_BIT in dst.usage
+
+  var region = VkBufferImageCopy(
+    bufferOffset: 0,
+    bufferRowLength: 0,
+    bufferImageHeight: 0,
+    imageSubresource: VkImageSubresourceLayers(
+      aspectMask: toBits [VK_IMAGE_ASPECT_COLOR_BIT],
+      mipLevel: 0,
+      baseArrayLayer: 0,
+      layerCount: 1,
+    ),
+    imageOffset: VkOffset3D(x: 0, y: 0, z: 0),
+    imageExtent: VkExtent3D(width: dst.width, height: dst.height, depth: 1)
+  )
+  WithSingleUseCommandBuffer(src.device, queue, commandBuffer):
+    commandBuffer.vkCmdCopyBufferToImage(
+      src.vk,
+      dst.vk,
+      VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+      1,
+      addr region
+    )
+
+proc CreateImage*(device: Device, width, height, depth: uint32, format: VkFormat, samples: VkSampleCountFlagBits, usage: openArray[VkImageUsageFlagBits]): VulkanImage =
+  assert device.vk.Valid
+  assert width > 0
+  assert height > 0
+
+  result.device = device
+  result.usage = @usage
+
+
+  result.width = width
+  result.height = height
+  result.depth = depth
+  result.format = format
+
+  var imageInfo = VkImageCreateInfo(
+    sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+    imageType: VK_IMAGE_TYPE_2D,
+    extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1),
+    mipLevels: 1,
+    arrayLayers: 1,
+    format: result.format,
+    tiling: VK_IMAGE_TILING_OPTIMAL,
+    initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
+    usage: toBits result.usage,
+    sharingMode: VK_SHARING_MODE_EXCLUSIVE,
+    samples: samples,
+  )
+  checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk)
+  result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false)
+
+# currently only usable for texture access from shader
+proc createTextureImage[T](device: Device, queue: Queue, width, height: uint32, depth: PixelDepth, image: Image[T]): VulkanImage =
+  assert device.vk.Valid
+  assert width > 0
+  assert height > 0
+  assert depth != 2
+
+  result.device = device
+  result.usage = @[VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_USAGE_SAMPLED_BIT]
+
+  let size: uint64 = width * height * uint32(depth)
+  let usageBits = toBits result.usage
+  var formatList = DEPTH_FORMAT_MAP[depth]
+  var selectedFormat: VkFormat
+  var formatProperties = VkImageFormatProperties2(sType: VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2)
+
+  for format in formatList:
+    var formatInfo = VkPhysicalDeviceImageFormatInfo2(
+      sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+      format: format,
+      thetype: VK_IMAGE_TYPE_2D,
+      tiling: VK_IMAGE_TILING_OPTIMAL,
+      usage: usageBits,
+    )
+    let formatCheck = device.physicalDevice.vk.vkGetPhysicalDeviceImageFormatProperties2(
+      addr formatInfo,
+      addr formatProperties,
+    )
+    if formatCheck == VK_SUCCESS: # found suitable format
+      selectedFormat = format
+      break
+    elif formatCheck == VK_ERROR_FORMAT_NOT_SUPPORTED: # nope, try to find other format
+      continue
+    else: # raise error
+      checkVkResult formatCheck
+
+  # assumption: images comes in sRGB color space
+  # convert to linear space if there is not support for sRGB
+  var data = addr image.imagedata[0]
+  if selectedFormat in LINEAR_FORMATS:
+    let linearImage = image.AsLinear()
+    data = addr linearImage.imagedata[0]
+
+  assert size <= uint64(formatProperties.imageFormatProperties.maxResourceSize)
+  assert width <= uint64(formatProperties.imageFormatProperties.maxExtent.width)
+  assert height <= uint64(formatProperties.imageFormatProperties.maxExtent.height)
+
+  result.width = width
+  result.height = height
+  result.depth = depth
+  result.format = selectedFormat
+
+  var imageInfo = VkImageCreateInfo(
+    sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+    imageType: VK_IMAGE_TYPE_2D,
+    extent: VkExtent3D(width: uint32(width), height: uint32(height), depth: 1),
+    mipLevels: min(1'u32, formatProperties.imageFormatProperties.maxMipLevels),
+    arrayLayers: min(1'u32, formatProperties.imageFormatProperties.maxArrayLayers),
+    format: result.format,
+    tiling: VK_IMAGE_TILING_OPTIMAL,
+    initialLayout: VK_IMAGE_LAYOUT_UNDEFINED,
+    usage: usageBits,
+    sharingMode: VK_SHARING_MODE_EXCLUSIVE,
+    samples: VK_SAMPLE_COUNT_1_BIT,
+  )
+  checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk)
+  result.allocateMemory(requireMappable = false, preferVRAM = true, preferAutoFlush = false)
+  result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
+
+  var stagingBuffer = device.CreateBuffer(size = size, usage = [VK_BUFFER_USAGE_TRANSFER_SRC_BIT], requireMappable = true, preferVRAM = false, preferAutoFlush = true)
+  stagingBuffer.SetData(queue, src = data, size = size)
+  stagingBuffer.Copy(result, queue)
+  result.transitionImageLayout(queue, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+  stagingBuffer.Destroy()
+
+proc Destroy*(image: var VulkanImage) =
+  assert image.device.vk.Valid
+  assert image.vk.Valid
+  image.device.vk.vkDestroyImage(image.vk, nil)
+  if image.memoryAllocated:
+    assert image.memory.vk.Valid
+    image.memory.Free()
+    image = VulkanImage(
+      device: image.device,
+      vk: image.vk,
+      width: image.width,
+      height: image.height,
+      depth: image.depth,
+      format: image.format,
+      usage: image.usage,
+      memoryAllocated: false,
+    )
+  image.vk.Reset
+
+proc CreateSampler*(device: Device, sampler: Sampler): VulkanSampler =
+  assert device.vk.Valid
+  var samplerInfo = VkSamplerCreateInfo(
+    sType: VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+    magFilter: sampler.magnification,
+    minFilter: sampler.minification,
+    addressModeU: sampler.wrapModeS,
+    addressModeV: sampler.wrapModeT,
+    addressModeW: VK_SAMPLER_ADDRESS_MODE_REPEAT,
+    anisotropyEnable: device.enabledFeatures.samplerAnisotropy,
+    maxAnisotropy: device.physicalDevice.properties.limits.maxSamplerAnisotropy,
+    borderColor: VK_BORDER_COLOR_INT_OPAQUE_BLACK,
+    unnormalizedCoordinates: VK_FALSE,
+    compareEnable: VK_FALSE,
+    compareOp: VK_COMPARE_OP_ALWAYS,
+    mipmapMode: VK_SAMPLER_MIPMAP_MODE_LINEAR,
+    mipLodBias: 0,
+    minLod: 0,
+    maxLod: 0,
+  )
+  result.device = device
+  checkVkResult device.vk.vkCreateSampler(addr samplerInfo, nil, addr result.vk)
+
+proc Destroy*(sampler: var VulkanSampler) =
+  assert sampler.device.vk.Valid
+  assert sampler.vk.Valid
+  sampler.device.vk.vkDestroySampler(sampler.vk, nil)
+  sampler.vk.Reset
+
+proc CreateImageView*(
+  image: VulkanImage,
+  imageviewtype = VK_IMAGE_VIEW_TYPE_2D,
+  baseMipLevel = 0'u32,
+  levelCount = 1'u32,
+  baseArrayLayer = 0'u32,
+  layerCount = 1'u32
+): ImageView =
+  assert image.device.vk.Valid
+  assert image.vk.Valid
+
+  var createInfo = VkImageViewCreateInfo(
+    sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+    image: image.vk,
+    viewType: imageviewtype,
+    format: image.format,
+    components: VkComponentMapping(
+      r: VK_COMPONENT_SWIZZLE_IDENTITY,
+      g: VK_COMPONENT_SWIZZLE_IDENTITY,
+      b: VK_COMPONENT_SWIZZLE_IDENTITY,
+      a: VK_COMPONENT_SWIZZLE_IDENTITY,
+    ),
+    subresourceRange: VkImageSubresourceRange(
+      aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT),
+      baseMipLevel: baseMipLevel,
+      levelCount: levelCount,
+      baseArrayLayer: baseArrayLayer,
+      layerCount: layerCount,
+    ),
+  )
+  result.image = image
+  checkVkResult image.device.vk.vkCreateImageView(addr(createInfo), nil, addr(result.vk))
+
+proc Destroy*(imageview: var ImageView) =
+  assert imageview.image.device.vk.Valid
+  assert imageview.vk.Valid
+  imageview.image.device.vk.vkDestroyImageView(imageview.vk, nil)
+  imageview.vk.Reset()
+
+func `$`*(texture: VulkanTexture): string =
+  &"VulkanTexture({texture.image.width}x{texture.image.height})"
+
+
+proc UploadTexture*(device: Device, queue: Queue, texture: Texture): VulkanTexture =
+  assert device.vk.Valid
+  if texture.isGrayscale:
+    result.image = createTextureImage(device = device, queue = queue, width = texture.grayImage.width, height = texture.grayImage.height, depth = 1, image = texture.grayImage)
+  else:
+    result.image = createTextureImage(device = device, queue = queue, width = texture.colorImage.width, height = texture.colorImage.height, depth = 4, image = texture.colorImage)
+  result.imageView = result.image.CreateImageView()
+  result.sampler = result.image.device.CreateSampler(texture.sampler)
+
+proc Destroy*(texture: var VulkanTexture) =
+  texture.image.Destroy()
+  texture.imageView.Destroy()
+  texture.sampler.Destroy()