Mercurial > games > semicongine
diff src/engine.nim @ 5:4ed9cb098315
add: structure code for crossplatform, add some input handling + bugfixes
author | Sam <sam@basx.dev> |
---|---|
date | Thu, 22 Dec 2022 00:06:40 +0700 |
parents | af9183acb173 |
children | 1134f41a49e9 |
line wrap: on
line diff
--- a/src/engine.nim Tue Dec 20 00:28:05 2022 +0700 +++ b/src/engine.nim Thu Dec 22 00:06:40 2022 +0700 @@ -6,7 +6,8 @@ import ./vulkan import ./vulkan_helpers -import ./xlib_helpers +import ./window +import ./events import ./glslang/glslang @@ -40,10 +41,6 @@ outColor = vec4(fragColor, 1.0); }""" -import - x11/xlib, - x11/x - const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) type @@ -62,8 +59,6 @@ shaderStages*: seq[VkPipelineShaderStageCreateInfo] layout*: VkPipelineLayout pipeline*: VkPipeline - viewport*: VkViewport - scissor*: VkRect2D QueueFamily = object properties*: VkQueueFamilyProperties hasSurfaceSupport*: bool @@ -73,8 +68,7 @@ properties*: VkPhysicalDeviceProperties features*: VkPhysicalDeviceFeatures queueFamilies*: seq[QueueFamily] - surfaceCapabilities*: VkSurfaceCapabilitiesKHR - surfaceFormats: seq[VkSurfaceFormatKHR] + formats: seq[VkSurfaceFormatKHR] presentModes: seq[VkPresentModeKHR] Vulkan* = object debugMessenger: VkDebugUtilsMessengerEXT @@ -93,22 +87,17 @@ imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] - Window* = object - display*: PDisplay - window*: x.Window Engine* = object vulkan*: Vulkan - window: Window - + window: NativeWindow proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): var device = PhysicalDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice)) vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties)) vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features)) - checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanPhysicalDevice, surface, addr(device.surfaceCapabilities)) - device.surfaceFormats = getDeviceSurfaceFormats(vulkanPhysicalDevice, surface) - device.presentModes = getDeviceSurfacePresentModes(vulkanPhysicalDevice, surface) + device.formats = vulkanPhysicalDevice.getDeviceSurfaceFormats(surface) + device.presentModes = vulkanPhysicalDevice.getDeviceSurfacePresentModes(surface) debug(&"Physical device nr {int(vulkanPhysicalDevice)} {cleanString(device.properties.deviceName)}") for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): @@ -121,7 +110,7 @@ proc filterForDevice(devices: seq[PhysicalDevice]): seq[(PhysicalDevice, uint32, uint32)] = for device in devices: - if not (device.surfaceFormats.len > 0 and device.presentModes.len > 0 and "VK_KHR_swapchain" in device.extensions): + if not (device.formats.len > 0 and device.presentModes.len > 0 and "VK_KHR_swapchain" in device.extensions): continue var graphicsQueueFamily = high(uint32) var presentationQueueFamily = high(uint32) @@ -137,11 +126,12 @@ debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})") -proc getFrameDimension(window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = +proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice, surface: VkSurfaceKHR): VkExtent2D = + let capabilities = device.getSurfaceCapabilities(surface) if capabilities.currentExtent.width != high(uint32): return capabilities.currentExtent else: - let (width, height) = window.display.xlibFramebufferSize(window.window) + let (width, height) = window.size() return VkExtent2D( width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), @@ -165,14 +155,6 @@ ) checkVkResult instance.vkCreateDebugUtilsMessengerEXT(addr(createInfo), nil, addr(result)) -proc createVulkanSurface(instance: VkInstance, window: Window): VkSurfaceKHR = - var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( - sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, - dpy: window.display, - window: window.window, - ) - checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) - proc setupVulkanDeviceAndQueues(instance: VkInstance, surface: VkSurfaceKHR): Device = let usableDevices = instance.getAllPhysicalDevices(surface).filterForDevice() if len(usableDevices) == 0: @@ -191,12 +173,14 @@ ) proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice, surface: VkSurfaceKHR, dimension: VkExtent2D, surfaceFormat: VkSurfaceFormatKHR): Swapchain = + + let capabilities = physicalDevice.device.getSurfaceCapabilities(surface) var selectedPresentationMode = getPresentMode(physicalDevice.presentModes) # setup swapchain var swapchainCreateInfo = VkSwapchainCreateInfoKHR( sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, surface: surface, - minImageCount: max(physicalDevice.surfaceCapabilities.minImageCount + 1, physicalDevice.surfaceCapabilities.maxImageCount), + minImageCount: max(capabilities.minImageCount + 1, capabilities.maxImageCount), imageFormat: surfaceFormat.format, imageColorSpace: surfaceFormat.colorSpace, imageExtent: dimension, @@ -204,7 +188,7 @@ imageUsage: VkImageUsageFlags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), # VK_SHARING_MODE_CONCURRENT no supported (i.e cannot use different queue families for drawing to swap surface?) imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, - preTransform: physicalDevice.surfaceCapabilities.currentTransform, + preTransform: capabilities.currentTransform, compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, presentMode: selectedPresentationMode, clipped: VK_TRUE, @@ -308,24 +292,10 @@ ) # setup viewport - result.viewport = VkViewport( - x: 0.0, - y: 0.0, - width: (float) frameDimension.width, - height: (float) frameDimension.height, - minDepth: 0.0, - maxDepth: 1.0, - ) - result.scissor = VkRect2D( - offset: VkOffset2D(x: 0, y: 0), - extent: frameDimension - ) var viewportState = VkPipelineViewportStateCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, viewportCount: 1, - pViewports: addr(result.viewport), scissorCount: 1, - pScissors: addr(result.scissor), ) # rasterizerization config @@ -426,13 +396,18 @@ ) checkVkResult device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result[i])) +proc trash(device: VkDevice, swapchain: Swapchain, framebuffers: seq[VkFramebuffer]) = + for framebuffer in framebuffers: + device.vkDestroyFramebuffer(framebuffer, nil) + for imageview in swapchain.imageviews: + device.vkDestroyImageView(imageview, nil) + device.vkDestroySwapchainKHR(swapchain.swapchain, nil) + proc recreateSwapchain(vulkan: Vulkan): (Swapchain, seq[VkFramebuffer]) = + debug(&"Recreate swapchain with dimension {vulkan.frameDimension}") checkVkResult vulkan.device.device.vkDeviceWaitIdle() - for framebuffer in vulkan.framebuffers: - vulkan.device.device.vkDestroyFramebuffer(framebuffer, nil) - for imageview in vulkan.swapchain.imageviews: - vulkan.device.device.vkDestroyImageView(imageview, nil) - vulkan.device.device.vkDestroySwapchainKHR(vulkan.swapchain.swapchain, nil) + + vulkan.device.device.trash(vulkan.swapchain, vulkan.framebuffers) result[0] = vulkan.device.device.setupSwapChain( vulkan.device.physicalDevice, @@ -483,8 +458,7 @@ proc igniteEngine*(): Engine = - # init X11 window - (result.window.display, result.window.window) = xlibInit() + result.window = createWindow("Hello triangle") # setup vulkan functions vkLoad1_0() @@ -500,8 +474,8 @@ result.vulkan.device = result.vulkan.instance.setupVulkanDeviceAndQueues(result.vulkan.surface) # get basic frame information - result.vulkan.surfaceFormat = result.vulkan.device.physicalDevice.surfaceFormats.getSuitableSurfaceFormat() - result.vulkan.frameDimension = result.window.getFrameDimension(result.vulkan.device.physicalDevice.surfaceCapabilities) + result.vulkan.surfaceFormat = result.vulkan.device.physicalDevice.formats.getSuitableSurfaceFormat() + result.vulkan.frameDimension = result.window.getFrameDimension(result.vulkan.device.physicalDevice.device, result.vulkan.surface) # setup swapchain and render pipeline result.vulkan.swapchain = result.vulkan.device.device.setupSwapChain( @@ -530,14 +504,12 @@ ) = result.vulkan.device.device.setupSyncPrimitives() -proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer) = - var beginInfo = VkCommandBufferBeginInfo( - sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - pInheritanceInfo: nil, - ) - checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) - +proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: VkPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameDimension: VkExtent2D) = var + beginInfo = VkCommandBufferBeginInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + pInheritanceInfo: nil, + ) clearColor = VkClearValue(color: VkClearColorValue(float32: [0.2'f, 0.2'f, 0.2'f, 1.0'f])) renderPassInfo = VkRenderPassBeginInfo( sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, @@ -545,26 +517,34 @@ framebuffer: framebuffer, renderArea: VkRect2D( offset: VkOffset2D(x: 0, y: 0), - extent: VkExtent2D( - width: uint32(pipeline.viewport.width), - height: uint32(pipeline.viewport.height) - ), + extent: frameDimension, ), clearValueCount: 1, pClearValues: addr(clearColor), ) + viewport = VkViewport( + x: 0.0, + y: 0.0, + width: (float) frameDimension.width, + height: (float) frameDimension.height, + minDepth: 0.0, + maxDepth: 1.0, + ) + scissor = VkRect2D( + offset: VkOffset2D(x: 0, y: 0), + extent: frameDimension + ) + checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) - commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) - - commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(pipeline.viewport)) - commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(pipeline.scissor)) + commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline) + commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(viewport)) + commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(scissor)) commandBuffer.vkCmdDraw(vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) commandBuffer.vkCmdEndRenderPass() checkVkResult commandBuffer.vkEndCommandBuffer() -proc drawFrame(window: Window, vulkan: var Vulkan, currentFrame: int) = +proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool) = checkVkResult vulkan.device.device.vkWaitForFences(1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) - checkVkResult vulkan.device.device.vkResetFences(1, addr(vulkan.inFlightFences[currentFrame])) var bufferImageIndex: uint32 let nextImageResult = vulkan.device.device.vkAcquireNextImageKHR( vulkan.swapchain.swapchain, @@ -574,12 +554,15 @@ addr(bufferImageIndex) ) if nextImageResult == VK_ERROR_OUT_OF_DATE_KHR: - vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.surfaceCapabilities) + vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() return + elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): + raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) + checkVkResult vulkan.device.device.vkResetFences(1, addr(vulkan.inFlightFences[currentFrame])) checkVkResult vulkan.commandBuffers[currentFrame].vkResetCommandBuffer(VkCommandBufferResetFlags(0)) - vulkan.renderPass.recordCommandBuffer(vulkan.pipeline, vulkan.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex]) + vulkan.renderPass.recordCommandBuffer(vulkan.pipeline.pipeline, vulkan.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameDimension) var waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] @@ -607,50 +590,45 @@ ) let presentResult = vkQueuePresentKHR(vulkan.device.presentationQueue, addr(presentInfo)) - if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR: - vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.surfaceCapabilities) + if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR or resized: + vulkan.frameDimension = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() - return proc fullThrottle*(engine: var Engine) = var - event: XEvent killed = false currentFrame = 0 + resized = false while not killed: - while engine.window.display.XPending() > 0 and not killed: - discard engine.window.display.XNextEvent(addr(event)) - case event.theType - of ClientMessage: - if cast[Atom](event.xclient.data.l[0]) == deleteMessage: + for event in engine.window.pendingEvents(): + case event.eventType: + of Quit: killed = true - of KeyPress: - let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) - if key == XK_Escape: - killed = true - of ConfigureNotify: - engine.vulkan.frameDimension = engine.window.getFrameDimension(engine.vulkan.device.physicalDevice.surfaceCapabilities) - (engine.vulkan.swapchain, engine.vulkan.framebuffers) = engine.vulkan.recreateSwapchain() - else: - discard - engine.window.drawFrame(engine.vulkan, currentFrame) + of ResizedWindow: + resized = true + of KeyDown: + echo event + if event.key == Escape: + killed = true + else: + discard + engine.window.drawFrame(engine.vulkan, currentFrame, resized) + resized = false currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT; checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() +proc trash*(engine: Engine) = + engine.vulkan.device.device.trash(engine.vulkan.swapchain, engine.vulkan.framebuffers) + checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() -proc trash*(engine: Engine) = - checkVkResult engine.vulkan.device.device.vkDeviceWaitIdle() for i in 0 ..< MAX_FRAMES_IN_FLIGHT: engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.imageAvailableSemaphores[i], nil) engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.renderFinishedSemaphores[i], nil) engine.vulkan.device.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil) engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.commandPool, nil) - for framebuffer in engine.vulkan.framebuffers: - engine.vulkan.device.device.vkDestroyFramebuffer(framebuffer, nil) - engine.vulkan.device.device.vkDestroyPipeline(engine.vulkan.pipeline.pipeline, nil) engine.vulkan.device.device.vkDestroyPipelineLayout(engine.vulkan.pipeline.layout, nil) engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil) @@ -658,14 +636,10 @@ for shaderStage in engine.vulkan.pipeline.shaderStages: engine.vulkan.device.device.vkDestroyShaderModule(shaderStage.module, nil) - for imageview in engine.vulkan.swapchain.imageviews: - engine.vulkan.device.device.vkDestroyImageView(imageview, nil) - engine.vulkan.device.device.vkDestroySwapchainKHR(engine.vulkan.swapchain.swapchain, nil) engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) engine.vulkan.device.device.vkDestroyDevice(nil) when ENABLEVULKANVALIDATIONLAYERS: engine.vulkan.instance.vkDestroyDebugUtilsMessengerEXT(engine.vulkan.debugMessenger, nil) glslang_finalize_process() engine.vulkan.instance.vkDestroyInstance(nil) - checkXlibResult engine.window.display.XDestroyWindow(engine.window.window) - discard engine.window.display.XCloseDisplay() # always returns 0 + engine.window.trash()