# HG changeset patch # User sam # Date 1720457253 -25200 # Node ID cfba2b7e00d0f66ab7b7e8210c0d4934b781748c # Parent 397c681f9c0c7abf327839b879f4712b4b1ff5eb did: most of swapchain, swap still needs to be done diff -r 397c681f9c0c -r cfba2b7e00d0 semicongine/rendering.nim --- a/semicongine/rendering.nim Mon Jul 08 20:13:11 2024 +0700 +++ b/semicongine/rendering.nim Mon Jul 08 23:47:33 2024 +0700 @@ -27,16 +27,31 @@ type VulkanGlobals* = object + # populated through InitVulkan proc instance*: VkInstance device*: VkDevice physicalDevice*: VkPhysicalDevice surface: VkSurfaceKHR - swapchain: VkSwapchainKHR - msaaImage: VkImage - msaaImageView: VkImageView window: NativeWindow graphicsQueueFamily*: uint32 graphicsQueue*: VkQueue + Swapchain = object + # parameters to InitSwapchain, required for swapchain recreation + renderPass: VkRenderPass + vSync: bool + samples: VkSampleCountFlagBits + # populated through InitSwapchain proc + vk: VkSwapchainKHR + msaaImage: VkImage + msaaMemory: VkDeviceMemory + msaaImageView: VkImageView + framebuffers: seq[VkFramebuffer] + framebufferViews: seq[VkImageView] + queueFinishedFence*: array[INFLIGHTFRAMES, VkFence] + imageAvailableSemaphore*: array[INFLIGHTFRAMES, VkSemaphore] + renderFinishedSemaphore*: array[INFLIGHTFRAMES, VkSemaphore] + currentFiF: int[0 .. INFLIGHTFRAMES - 1] + # unclear as of yet anisotropy*: float32 = 0 # needs to be enable during device creation var vulkan*: VulkanGlobals @@ -146,7 +161,7 @@ include ./rendering/renderer -proc initVulkan(appName: string = "semicongine app") = +proc InitVulkan(appName: string = "semicongine app"): VulkanGlobals = include ./platform/vulkan_extensions # for REQUIRED_PLATFORM_EXTENSIONS @@ -181,15 +196,15 @@ enabledExtensionCount: requiredExtensions.len.uint32, ppEnabledExtensionNames: instanceExtensionsC ) - checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance)) - loadVulkan(vulkan.instance) + checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result.instance)) + loadVulkan(result.instance) # load extensions # for extension in requiredExtensions: - loadExtension(vulkan.instance, $extension) - vulkan.window = CreateWindow(appName) - vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window) + loadExtension(result.instance, $extension) + result.window = CreateWindow(appName) + result.surface = CreateNativeSurface(result.instance, result.window) # logical device creation @@ -200,17 +215,17 @@ # var deviceExtensions = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"] var deviceExtensions = @["VK_KHR_swapchain"] for extension in deviceExtensions: - loadExtension(vulkan.instance, extension) + loadExtension(result.instance, extension) # get physical device and graphics queue family - vulkan.physicalDevice = GetBestPhysicalDevice(vulkan.instance) - vulkan.graphicsQueueFamily = GetQueueFamily(vulkan.physicalDevice, VK_QUEUE_GRAPHICS_BIT) + result.physicalDevice = GetBestPhysicalDevice(result.instance) + result.graphicsQueueFamily = GetQueueFamily(result.physicalDevice, VK_QUEUE_GRAPHICS_BIT) let priority = cfloat(1) queueInfo = VkDeviceQueueCreateInfo( sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - queueFamilyIndex: vulkan.graphicsQueueFamily, + queueFamilyIndex: result.graphicsQueueFamily, queueCount: 1, pQueuePriorities: addr(priority), ) @@ -227,11 +242,11 @@ pEnabledFeatures: nil, ) checkVkResult vkCreateDevice( - physicalDevice = vulkan.physicalDevice, + physicalDevice = result.physicalDevice, pCreateInfo = addr createDeviceInfo, pAllocator = nil, - pDevice = addr vulkan.device + pDevice = addr result.device ) - vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) + result.graphicsQueue = svkGetDeviceQueue(result.device, result.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) -initVulkan() +vulkan = InitVulkan() diff -r 397c681f9c0c -r cfba2b7e00d0 semicongine/rendering/renderer.nim --- a/semicongine/rendering/renderer.nim Mon Jul 08 20:13:11 2024 +0700 +++ b/semicongine/rendering/renderer.nim Mon Jul 08 23:47:33 2024 +0700 @@ -1,6 +1,3 @@ - -# some globals that will (likely?) never change during the life time of the engine - func depth(texture: Texture): int = default(elementType(texture.data)).len diff -r 397c681f9c0c -r cfba2b7e00d0 semicongine/rendering/swapchain.nim --- a/semicongine/rendering/swapchain.nim Mon Jul 08 20:13:11 2024 +0700 +++ b/semicongine/rendering/swapchain.nim Mon Jul 08 23:47:33 2024 +0700 @@ -1,27 +1,41 @@ -const N_FRAMEBUFFERS = 3'32 +const N_FRAMEBUFFERS = 3'u32 -proc svkCreateSwapchainKHR(vSync: bool, oldSwapchain = VkSwapchainKHR(0)): VkSwapchainKHR = +proc InitSwapchain*( + renderPass: VkRenderPass, + vSync: bool, + samples = VK_SAMPLE_COUNT_1_BIT, + nFramebuffers = N_FRAMEBUFFERS, + oldSwapchain = VkSwapchainKHR(0), +): Swapchain = + assert vulkan.instance.Valid + + result.renderPass = renderPass + result.vSync = vSync + result.samples = samples var capabilities: VkSurfaceCapabilitiesKHR - checkVkResult device.vk.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.surface, addr(capabilities)) + checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.physicalDevice, vulkan.surface, addr(capabilities)) + let + format = DefaultSurfaceFormat() + width = capabilities.currentExtent.width + height = capabilities.currentExtent.height - if capabilities.currentExtent.width == 0 or capabilities.currentExtent.height == 0: + if width == 0 or height == 0: return VkSwapchainKHR(0) - # following is according to vulkan specs + # following "count" is established according to vulkan specs var minFramebufferCount = N_FRAMEBUFFERS minFramebufferCount = max(minFramebufferCount, capabilities.minImageCount) if capabilities.maxImageCount != 0: minFramebufferCount = min(minFramebufferCount, capabilities.maxImageCount) - svkGetPhysicalDeviceSurfaceFormatsKHR() - + # create swapchain let hasTripleBuffering = VK_PRESENT_MODE_MAILBOX_KHR in svkGetPhysicalDeviceSurfacePresentModesKHR() var createInfo = VkSwapchainCreateInfoKHR( sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - surface: device.physicalDevice.surface, + surface: vulkan.surface, minImageCount: minFramebufferCount, - imageFormat: DefaultSurfaceFormat(), + imageFormat: format, imageColorSpace: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, # only one supported without special extensions imageExtent: capabilities.currentExtent, imageArrayLayers: 1, @@ -33,25 +47,101 @@ clipped: true, oldSwapchain: oldSwapchain, ) - if device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(result)) != VK_SUCCESS: + if vkCreateSwapchainKHR(vulkan.device, addr(createInfo), nil, addr(result.vk)) != VK_SUCCESS: return VkSwapchainKHR(0) + # create msaa image+view if desired if samples != VK_SAMPLE_COUNT_1_BIT: - vulkan.msaaImage = svkCreate2DImage( - width = capabilities.currentExtent.width, - height = capabilities.currentExtent.height, - format = DefaultSurfaceFormat(), + let imgSize = width * height * format.size + result.msaaImage = svkCreate2DImage( + width = width, + height = height, + format = format, usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT], ) - # TODO: memory - vk: svkAllocateMemory(size, mType), + result.msaaMemory = svkAllocateMemory(imgSize, BestMemory(mappable = false)) checkVkResult vkBindImageMemory( vulkan.device, - vulkan.msaaImage, - selectedBlock.vk, + result.msaaImage, + result.msaaMemory, 0, ) + result.msaaImageView = svkCreate2DImageView(result.msaaImage, format) + # create framebuffers + var actualNFramebuffers: uint32 + checkVkResult vkGetSwapchainImagesKHR(vulkan.device, result.vk, addr(actualNFramebuffers), nil) + var framebuffers = newSeq[VkImage](actualNFramebuffers) + checkVkResult vkGetSwapchainImagesKHR(vulkan.device, result.vk, addr(actualNFramebuffers), framebuffers.ToCPointer) + + for framebuffer in framebuffers: + result.framebufferViews.add svkCreate2DImageView(framebuffer, format) + if samples == VK_SAMPLE_COUNT_1_BIT: + svkCreateFramebuffer(renderPass, width, height, [result.framebufferViews[^1]]) + else: + svkCreateFramebuffer(renderPass, width, height, [result.msaaImageView, result.framebufferViews[^1]]) + + # create sync primitives + for i in 0 ..< INFLIGHTFRAMES: + result.queueFinishedFence[i] = svkCreateFence(signaled = true) + result.imageAvailableSemaphore[i] = svkCreateSemaphore() + result.renderFinishedSemaphore[i] = svkCreateSemaphore() + +proc TryAcquireNextImage*(swapchain: var Swapchain): bool = + swapchain.queueFinishedFence[swapchain.currentFiF].Await() + + let nextImageResult = vkAcquireNextImageKHR( + vulkan.device, + swapchain.vk, + high(uint64), + swapchain.imageAvailableSemaphore[swapchain.currentFiF], + VkFence(0), + addr(swapchain.currentFramebufferIndex), + ) + + swapchain.queueFinishedFence[swapchain.currentFiF].Reset() + + return nextImageResult == VK_SUCCESS +proc Swap*(swapchain: var Swapchain, queue: Queue, commandBuffer: VkCommandBuffer): bool = + var + waitStage = VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) + submitInfo = VkSubmitInfo( + sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, + waitSemaphoreCount: 1, + pWaitSemaphores: addr(swapchain.imageAvailableSemaphore[swapchain.currentFiF]), + pWaitDstStageMask: addr(waitStage), + commandBufferCount: 1, + pCommandBuffers: addr(commandBuffer), + signalSemaphoreCount: 1, + pSignalSemaphores: addr(swapchain.renderFinishedSemaphore[swapchain.currentFiF]), + ) + checkVkResult queue.vk.vkQueueSubmit( + submitCount = 1, + pSubmits = addr submitInfo, + fence = swapchain.queueFinishedFence[swapchain.currentInFlight].vk + ) - vulkan.msaaImageView = svkCreate2DImageView(vulkan.msaaImageView, DefaultSurfaceFormat()) + var presentInfo = VkPresentInfoKHR( + sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + waitSemaphoreCount: 1, + pWaitSemaphores: addr swapchain.renderFinishedSemaphore[swapchain.currentInFlight].vk, + swapchainCount: 1, + pSwapchains: addr swapchain.vk, + pImageIndices: addr swapchain.currentFramebufferIndex, + pResults: nil, + ) + let presentResult = vkQueuePresentKHR(swapchain.presentQueue.vk, addr presentInfo) + if presentResult != VK_SUCCESS: + return false + + return true + +proc Recreate*(swapchain: Swapchain): Swapchain = + initSwapchain( + renderPass = swapchain.renderPass, + vSync = swapchain.vSync, + samples = swapchain.samples, + nFramebuffers = swapchain.framebuffers.len.uint32, + oldSwapchain = swapchain.vk, + ) diff -r 397c681f9c0c -r cfba2b7e00d0 semicongine/rendering/vulkan_wrappers.nim --- a/semicongine/rendering/vulkan_wrappers.nim Mon Jul 08 20:13:11 2024 +0700 +++ b/semicongine/rendering/vulkan_wrappers.nim Mon Jul 08 23:47:33 2024 +0700 @@ -24,6 +24,12 @@ assert score > 0, "Unable to find integrated or discrete GPU" +proc svkGetPhysicalDeviceSurfaceSupportKHR*(queueFamily: uint32): bool = + assert surface.Valid + var presentation = VkBool32(false) + checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkan.device, queueFamily, vulkan.surface, addr(presentation)) + return bool(presentation) + proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = var nQueuefamilies: uint32 vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil) @@ -31,7 +37,9 @@ vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer) for i in 0'u32 ..< nQueuefamilies: if qType in toEnums(queuFamilies[i].queueFlags): - return i + # for graphics queues we always also want prsentation, they seem never to be separated in practice + if svkGetPhysicalDeviceSurfaceSupportKHR(i) or qType != VK_QUEUE_GRAPHICS_BIT: + return i assert false, &"Queue of type {qType} not found" proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue = @@ -46,17 +54,23 @@ # EVERY windows driver and almost every linux driver should support this VK_FORMAT_B8G8R8A8_SRGB +func size(format: VkFormat): uint64 = + const formatSize = [ + VK_FORMAT_B8G8R8A8_SRGB.int: 4'u64, + ] + return formatSize[format.int] + proc svkGetPhysicalDeviceSurfacePresentModesKHR*(): seq[VkPresentModeKHR] = var n_modes: uint32 - checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.device, vulkan.surface, addr(n_modes), nil) + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.physicalDevice, vulkan.surface, addr(n_modes), nil) result = newSeq[VkPresentModeKHR](n_modes) - checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.device, vulkan.surface, addr(n_modes), result.ToCPointer) + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(vulkan.physicalDevice, vulkan.surface, addr(n_modes), result.ToCPointer) -proc svkGetPhysicalDeviceSurfaceFormatsKHR() +proc svkGetPhysicalDeviceSurfaceFormatsKHR(): seq[VkSurfaceFormatKHR] = var n_formats: uint32 - checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.device, vulkan.surface, addr(n_formats), nil) + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.physicalDevice, vulkan.surface, addr(n_formats), nil) result = newSeq[VkSurfaceFormatKHR](n_formats) - checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.device, vulkan.surface, addr(n_formats), result.ToCPointer) + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(vulkan.physicalDevice, vulkan.surface, addr(n_formats), result.ToCPointer) proc hasValidationLayer*(): bool = var n_layers: uint32 @@ -149,6 +163,18 @@ ) checkVkResult vkCreateImageView(vulkan.device, addr(createInfo), nil, addr(result)) +proc svkCreateFramebuffer*(renderpass: VkRenderPass, width, height: uint32, attachments: openArray[VkImageView]): VkFramebuffer = + var framebufferInfo = VkFramebufferCreateInfo( + sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + renderPass: renderpass, + attachmentCount: attachments.len.uint32, + pAttachments: attachments.ToCPointer, + width: width, + height: height, + layers: 1, + ) + checkVkResult vkCreateFramebuffer(vulkan.device, addr(framebufferInfo), nil, addr(result)) + proc svkGetBufferMemoryRequirements*(buffer: VkBuffer): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = var reqs: VkMemoryRequirements vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs)) @@ -167,6 +193,23 @@ if ((1'u32 shl i) and reqs.memoryTypeBits) > 0: result.memoryTypes.add i +proc svkCreateFence*(signaled = false): VkFence = + var fenceInfo = VkFenceCreateInfo( + sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + flags: if signaled: toBits [VK_FENCE_CREATE_SIGNALED_BIT] else: VkFenceCreateFlags(0) + ) + checkVkResult vkCreateFence(vulkan.device, addr(fenceInfo), nil, addr(result)) + +proc svkCreateSemaphore*(): VkSemaphore = + var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) + checkVkResult vkCreateSemaphore(vulkan.deivce, addr(semaphoreInfo), nil, addr(result)) + +proc Await*(fence: VkFence, timeout = high(uint64)) = + checkVkResult vkWaitForFences(vulkan.device, 1, addr(fence), false, timeout) + +proc Reset*(fence: VkFence) = + checkVkResult vkResetFences(vulkan.device, 1, addr(fence)) + proc BestMemory*(mappable: bool, filter: seq[uint32] = @[]): uint32 = var physicalProperties: VkPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties)) @@ -221,15 +264,9 @@ pCommandBuffers: addr(`cmd`), ) - var - fence: VkFence - fenceInfo = VkFenceCreateInfo( - sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - # flags: toBits [VK_FENCE_CREATE_SIGNALED_BIT] - ) - checkVkResult vulkan.device.vkCreateFence(addr(fenceInfo), nil, addr(fence)) + var fence = svkCreateFence() checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), fence) - checkVkResult vkWaitForFences(vulkan.device, 1, addr fence, false, high(uint64)) + fence.Await() vkDestroyCommandPool(vulkan.device, commandBufferPool, nil) template WithStagingBuffer*[T: (VkBuffer, uint64)|(VkImage, uint32, uint32)](