# HG changeset patch # User Sam # Date 1683218655 -25200 # Node ID c774b064def596a7068a5619f8091fc6cf136546 # Parent 4374c13b9b95c0a9b11e780c129ca414a495f886 add: image/texture creation, refactoring of some unclean parts diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/engine.nim --- a/src/semicongine/engine.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/engine.nim Thu May 04 23:44:15 2023 +0700 @@ -1,4 +1,6 @@ import std/sequtils +import std/os + import ./platform/window import ./vulkan/api @@ -75,6 +77,8 @@ if debug: instanceExtensions.add "VK_EXT_debug_utils" enabledLayers.add "VK_LAYER_KHRONOS_validation" + putEnv("VK_LAYER_ENABLES", "VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT") + if defined(linux): enabledLayers.add "VK_LAYER_MESA_overlay" result.instance = result.window.createInstance( diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/platform/linux/window.nim --- a/src/semicongine/platform/linux/window.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/platform/linux/window.nim Thu May 04 23:44:15 2023 +0700 @@ -5,8 +5,7 @@ import x11/xlib, x11/xutil, - x11/keysym, - x11/x11pragma + x11/keysym import x11/x import ../../events @@ -81,7 +80,6 @@ proc fullscreen*(window: var NativeWindow, enable: bool) = var wm_state = window.display.XInternAtom("_NET_WM_STATE", 0) - op = (if enable: "_NET_WM_STATE_ADD" else: "_NET_WM_STATE_REMOVE") wm_fullscreen = window.display.XInternAtom("_NET_WM_STATE_FULLSCREEN", 0) var xev: XEvent diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/buffer.nim --- a/src/semicongine/vulkan/buffer.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/buffer.nim Thu May 04 23:44:15 2023 +0700 @@ -20,10 +20,6 @@ of false: discard of true: memory*: DeviceMemory - MemoryRequirements = object - size: uint64 - alignment: uint64 - memoryTypes: seq[MemoryType] proc `==`*(a, b: Buffer): bool = @@ -102,42 +98,18 @@ assert VK_BUFFER_USAGE_TRANSFER_SRC_BIT in src.usage assert VK_BUFFER_USAGE_TRANSFER_DST_BIT in dst.usage - var queue: Queue - for q in src.device.queues.values: - if q.family.canDoTransfer: - queue = q - if not queue.vk.valid: - raise newException(Exception, "No queue that supports buffer transfer") - - var - commandBufferPool = src.device.createCommandBufferPool(family=queue.family, nBuffers=1) - commandBuffer = commandBufferPool.buffers[0] - - beginInfo = VkCommandBufferBeginInfo( - sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), - ) - copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: dstOffset) - checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) - commandBuffer.vkCmdCopyBuffer(src.vk, dst.vk, 1, addr(copyRegion)) - checkVkResult commandBuffer.vkEndCommandBuffer() - - var submitInfo = VkSubmitInfo( - sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, - commandBufferCount: 1, - pCommandBuffers: addr(commandBuffer), - ) - checkVkResult queue.vk.vkQueueSubmit(1, addr(submitInfo), VkFence(0)) - checkVkResult queue.vk.vkQueueWaitIdle() - commandBufferPool.destroy() + var copyRegion = VkBufferCopy(size: VkDeviceSize(src.size), dstOffset: dstOffset) + withSingleUseCommandBuffer(src.device, true, commandBuffer): + commandBuffer.vkCmdCopyBuffer(src.vk, dst.vk, 1, addr(copyRegion)) proc destroy*(buffer: var Buffer) = assert buffer.device.vk.valid assert buffer.vk.valid + buffer.device.vk.vkDestroyBuffer(buffer.vk, nil) if buffer.memoryAllocated: assert buffer.memory.vk.valid buffer.memory.free - buffer.device.vk.vkDestroyBuffer(buffer.vk, nil) + buffer.memoryAllocated = false buffer.vk.reset proc setData*(dst: Buffer, src: pointer, size: uint64, bufferOffset=0'u64) = diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/commandbuffer.nim --- a/src/semicongine/vulkan/commandbuffer.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/commandbuffer.nim Thu May 04 23:44:15 2023 +0700 @@ -31,6 +31,39 @@ checkVkResult device.vk.vkAllocateCommandBuffers(addr(allocInfo), result.buffers.toCPointer) +template withSingleUseCommandBuffer*(device: Device, needsTransfer: bool, commandBuffer, body: untyped): untyped = + assert device.vk.valid + + var queue: Queue + for q in device.queues.values: + if q.family.canDoTransfer or not needsTransfer: + queue = q + break + if not queue.vk.valid: + raise newException(Exception, "No queue that supports buffer transfer") + + var + commandBufferPool = createCommandBufferPool(device, queue.family, 1) + commandBuffer = commandBufferPool.buffers[0] + beginInfo = VkCommandBufferBeginInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), + ) + checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) + + block: + body + + checkVkResult commandBuffer.vkEndCommandBuffer() + var submitInfo = VkSubmitInfo( + sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, + commandBufferCount: 1, + pCommandBuffers: addr(commandBuffer), + ) + checkVkResult queue.vk.vkQueueSubmit(1, addr(submitInfo), VkFence(0)) + checkVkResult queue.vk.vkQueueWaitIdle() + commandBufferPool.destroy() + proc destroy*(commandpool: var CommandBufferPool) = assert commandpool.device.vk.valid assert commandpool.vk.valid diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/device.nim --- a/src/semicongine/vulkan/device.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/device.nim Thu May 04 23:44:15 2023 +0700 @@ -12,6 +12,7 @@ physicalDevice*: PhysicalDevice vk*: VkDevice queues*: Table[QueueFamily, Queue] + enabledFeatures*: VkPhysicalDeviceFeatures Queue* = object vk*: VkQueue family*: QueueFamily @@ -48,7 +49,6 @@ queueCount: 1, pQueuePriorities: addr(priority), ) - var queueList = deviceQueues.values.toSeq var createInfo = VkDeviceCreateInfo( sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, @@ -58,7 +58,7 @@ ppEnabledLayerNames: enabledLayersC, enabledExtensionCount: uint32(allExtensions.len), ppEnabledExtensionNames: enabledExtensionsC, - pEnabledFeatures: nil, + pEnabledFeatures: addr result.enabledFeatures, ) checkVkResult vkCreateDevice( diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/image.nim --- a/src/semicongine/vulkan/image.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/image.nim Thu May 04 23:44:15 2023 +0700 @@ -1,15 +1,201 @@ +import std/tables +import std/logging + import ./api import ./device +import ./physicaldevice +import ./buffer +import ./memory +import ./commandbuffer type + PixelDepth = 1'u32 .. 4'u32 Image* = object + device*: Device vk*: VkImage + width*: uint32 # pixel + height*: uint32 # pixel + depth*: PixelDepth format*: VkFormat - device*: Device + usage*: seq[VkImageUsageFlagBits] + case memoryAllocated*: bool + of false: discard + of true: + memory*: DeviceMemory ImageView* = object vk*: VkImageView image*: Image +const DEPTH_FORMAT_MAP = { + PixelDepth(1): VK_FORMAT_R8_SRGB, + PixelDepth(2): VK_FORMAT_R8G8_SRGB, + PixelDepth(3): VK_FORMAT_R8G8B8_SRGB, + PixelDepth(4): VK_FORMAT_R8G8B8A8_SRGB, +}.toTable + + +proc requirements(image: Image): 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 Image, 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 + ) + image.memoryAllocated = true + debug "Allocating memory for image: ", image.width, "x", image.height, "x", image.depth, " bytes of type ", memoryType + image.memory = image.device.allocate(requirements.size, memoryType) + checkVkResult image.device.vk.vkBindImageMemory(image.vk, image.memory.vk, VkDeviceSize(0)) + +proc transitionImageLayout*(image: Image, 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, + ), + ) + var + sourceStage, destinationStage: VkPipelineStageFlagBits + if oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.srcAccessMask = VkAccessFlags(0) + barrier.dstAccessMask = toBits [VK_ACCESS_TRANSFER_WRITE_BIT] + sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT + destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT + elif oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.srcAccessMask = toBits [VK_ACCESS_TRANSFER_WRITE_BIT] + barrier.dstAccessMask = toBits [VK_ACCESS_SHADER_READ_BIT] + sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT + destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT + else: + raise newException(Exception, "Unsupported layout transition!") + + withSingleUseCommandBuffer(image.device, false, commandBuffer): + vkCmdPipelineBarrier( + commandBuffer, + toBits [sourceStage], toBits [destinationStage], + VkDependencyFlags(0), + 0, nil, + 0, nil, + 1, addr barrier + ) + +proc copy*(src: Buffer, dst: Image) = + 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, true, commandBuffer): + commandBuffer.vkCmdCopyBufferToImage( + src.vk, + dst.vk, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + addr region + ) + +# currently only usable for texture access from shader +proc createImage(device: Device, width, height: uint32, depth: PixelDepth, data: pointer): Image = + assert device.vk.valid + + let size = width * height * depth + result.width = width + result.height = height + result.depth = depth + result.format = DEPTH_FORMAT_MAP[depth] + result.usage = @[VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_USAGE_SAMPLED_BIT] + + var imageInfo = VkImageCreateInfo( + sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + imageType: VK_IMAGE_TYPE_2D, + extent: VkExtent3D(width: width, height: 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: VK_SAMPLE_COUNT_1_BIT, + ) + checkVkResult device.vk.vkCreateImage(addr imageInfo, nil, addr result.vk) + result.transitionImageLayout(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(src=data, size=size) + stagingBuffer.copy(result) + result.transitionImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + stagingBuffer.destroy() + +proc destroy*(image: var Image) = + 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.memoryAllocated = false + image.vk.reset + +proc createSampler(device: Device): VkSampler = + var samplerInfo = VkSamplerCreateInfo( + sType: VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + magFilter: VK_FILTER_LINEAR, + minFilter: VK_FILTER_LINEAR, + addressModeU: VK_SAMPLER_ADDRESS_MODE_REPEAT, + addressModeV: VK_SAMPLER_ADDRESS_MODE_REPEAT, + 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, + ) + checkVkResult device.vk.vkCreateSampler(addr samplerInfo, nil, addr result) + + proc createImageView*( image: Image, imageviewtype=VK_IMAGE_VIEW_TYPE_2D, diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/memory.nim --- a/src/semicongine/vulkan/memory.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/memory.nim Thu May 04 23:44:15 2023 +0700 @@ -24,6 +24,10 @@ of false: discard of true: data*: pointer needsFlushing*: bool + MemoryRequirements* = object + size*: uint64 + alignment*: uint64 + memoryTypes*: seq[MemoryType] func `$`*(memoryType: MemoryType): string = &"Memorytype {memoryType.flags} (heap size: {memoryType.heap.size}, heap flags: {memoryType.heap.flags})" diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/physicaldevice.nim --- a/src/semicongine/vulkan/physicaldevice.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/physicaldevice.nim Thu May 04 23:44:15 2023 +0700 @@ -12,16 +12,14 @@ name*: string devicetype*: VkPhysicalDeviceType surface*: VkSurfaceKHR + properties*: VkPhysicalDeviceProperties + features*: VkPhysicalDeviceFeatures QueueFamily* = object device: PhysicalDevice properties*: VkQueueFamilyProperties index*: uint32 flags*: seq[VkQueueFlagBits] -proc getProperties*(device: PhysicalDevice): VkPhysicalDeviceProperties = - assert device.vk.valid - device.vk.vkGetPhysicalDeviceProperties(addr result) - proc getPhysicalDevices*(instance: Instance): seq[PhysicalDevice] = assert instance.vk.valid assert instance.surface.valid @@ -31,9 +29,10 @@ checkVkResult vkEnumeratePhysicalDevices(instance.vk, addr(nDevices), devices.toCPointer) for i in 0 ..< nDevices: var device = PhysicalDevice(vk: devices[i], surface: instance.surface) - let props = device.getProperties() - device.name = props.deviceName.cleanString() - device.devicetype = props.deviceType + device.vk.vkGetPhysicalDeviceProperties(addr device.properties) + device.vk.vkGetPhysicalDeviceFeatures(addr device.features) + device.name = device.properties.deviceName.cleanString() + device.devicetype = device.properties.deviceType result.add device proc getExtensions*(device: PhysicalDevice): seq[string] = @@ -46,10 +45,6 @@ for extension in extensions: result.add(cleanString(extension.extensionName)) -proc getFeatures*(device: PhysicalDevice): VkPhysicalDeviceFeatures = - assert device.vk.valid - device.vk.vkGetPhysicalDeviceFeatures(addr result) - proc getSurfaceCapabilities*(device: PhysicalDevice): VkSurfaceCapabilitiesKHR = assert device.vk.valid assert device.surface.valid diff -r 4374c13b9b95 -r c774b064def5 src/semicongine/vulkan/swapchain.nim --- a/src/semicongine/vulkan/swapchain.nim Wed May 03 23:57:25 2023 +0700 +++ b/src/semicongine/vulkan/swapchain.nim Thu May 04 23:44:15 2023 +0700 @@ -43,7 +43,7 @@ surfaceFormat: VkSurfaceFormatKHR, queueFamily: QueueFamily, desiredNumberOfImages=3'u32, - presentMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR, + preferedPresentMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR, inFlightFrames=2, oldSwapchain=VkSwapchainKHR(0) ): Option[Swapchain] = @@ -57,6 +57,22 @@ return none(Swapchain) var imageCount = desiredNumberOfImages + + const PRESENTMODES_BY_PREFERENCE = [ + VK_PRESENT_MODE_MAILBOX_KHR, + VK_PRESENT_MODE_FIFO_RELAXED_KHR, + VK_PRESENT_MODE_FIFO_KHR, + VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, + VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, + ] + var supportedModes = device.physicalDevice.getSurfacePresentModes() + var presentMode: VkPresentModeKHR + for mode in PRESENTMODES_BY_PREFERENCE: + if mode in supportedModes: + presentMode = mode + break + # following is according to vulkan specs if presentMode in [VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR]: imageCount = 1 @@ -214,7 +230,6 @@ surfaceFormat=swapchain.surfaceFormat, queueFamily=swapchain.queueFamily, desiredNumberOfImages=swapchain.imageCount, - presentMode=swapchain.presentMode, inFlightFrames=swapchain.inFlightFrames, oldSwapchain=swapchain.vk, ) diff -r 4374c13b9b95 -r c774b064def5 tests/test_vulkan_wrapper.nim --- a/tests/test_vulkan_wrapper.nim Wed May 03 23:57:25 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Thu May 04 23:44:15 2023 +0700 @@ -20,9 +20,9 @@ for extension in device.getExtensions(): echo " " & $extension echo " Properties" - echo " " & $device.getProperties() + echo " " & $device.properties echo " Features" - echo " " & $device.getFeatures() + echo " " & $device.features echo " Queue families" for queueFamily in device.getQueueFamilies(): echo " " & $queueFamily @@ -130,7 +130,7 @@ inputs=vertexOutput, uniforms=uniforms, outputs=fragOutput, - main="color = vec4(outcolor, 1);" + main="color = vec4(outcolor, 0.8);" ) var renderPass = engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode) engine.setRenderer(renderPass)