# HG changeset patch # User Sam # Date 1671421280 -25200 # Node ID 91544fc1afe5a5e30b26b619b2bf30c4510bfd78 # Parent 0bc8643cfe2519581837b45b3641a3e6f03a8c1e did: hello world triangle, a bit of code organization diff -r 0bc8643cfe25 -r 91544fc1afe5 Makefile --- a/Makefile Fri Dec 16 00:05:41 2022 +0700 +++ b/Makefile Mon Dec 19 10:41:20 2022 +0700 @@ -1,11 +1,25 @@ SOURCES := $(shell find src -name '*.nim') +COMPILE_OPTIONS := --path:src --mm:orc --experimental:strictEffects --threads:on +DEBUG_OPTIONS := --debugger:native --checks:on --assertions:on +RELEASE_OPTIONS := -d:release --checks:off --assertions:off build/debug/linux: mkdir -p $@ build/debug/linux/test: build/debug/linux ${SOURCES} - nim c -o:$@ --gc:orc --debugger:native --checks:on --assertions:on src/test.nim + nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -o:$@ examples/test.nim build/release/linux: mkdir -p $@ build/release/linux/test: build/release/linux ${SOURCES} - nim c -d:release --gc:orc -o:$@ --checks:off --assertions:off src/test.nim + nim c ${COMPILE_OPTIONS} ${RELEASE_OPTIONS} -o:$@ examples/test.nim + +# not working yet, need to implement windows window-API +# build/debug/windows: + # mkdir -p $@ +# build/debug/windows/test: build/debug/windows ${SOURCES} + # nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -d:mingw -o:$@ examples/test.nim +# build/release/windows: + # mkdir -p $@ +# build/release/windows/test: build/release/windows ${SOURCES} + # nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -d:mingw -o:$@ examples/test.nim + diff -r 0bc8643cfe25 -r 91544fc1afe5 examples/test.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/test.nim Mon Dec 19 10:41:20 2022 +0700 @@ -0,0 +1,7 @@ +import engine + + +when isMainModule: + var myengine = igniteEngine() + myengine.fullThrottle() + myengine.trash() diff -r 0bc8643cfe25 -r 91544fc1afe5 src/engine.nim --- a/src/engine.nim Fri Dec 16 00:05:41 2022 +0700 +++ b/src/engine.nim Mon Dec 19 10:41:20 2022 +0700 @@ -1,4 +1,7 @@ +import std/strformat import std/enumerate +import std/logging + import ./vulkan import ./vulkan_helpers @@ -6,6 +9,12 @@ import ./glslang/glslang +const MAX_FRAMES_IN_FLIGHT = 2 + +var logger = newConsoleLogger() +addHandler(logger) + + var vertexShaderCode: string = """#version 450 layout(location = 0) out vec3 fragColor; vec3 colors[3] = vec3[]( @@ -39,10 +48,13 @@ type GraphicsPipeline = object shaderStages*: seq[VkPipelineShaderStageCreateInfo] + layout*: VkPipelineLayout + renderPass*: VkRenderPass + pipeline*: VkPipeline QueueFamily = object properties*: VkQueueFamilyProperties hasSurfaceSupport*: bool - PhyscialDevice = object + PhysicalDevice = object device*: VkPhysicalDevice extensions*: seq[string] properties*: VkPhysicalDeviceProperties @@ -52,61 +64,74 @@ surfaceFormats: seq[VkSurfaceFormatKHR] presentModes: seq[VkPresentModeKHR] Vulkan* = object + debugMessenger: VkDebugUtilsMessengerEXT instance*: VkInstance - deviceList*: seq[PhyscialDevice] - activePhysicalDevice*: PhyscialDevice - activeQueueFamily*: uint32 + deviceList*: seq[PhysicalDevice] + activePhysicalDevice*: PhysicalDevice + graphicsQueueFamily*: uint32 + graphicsQueue*: VkQueue + presentationQueueFamily*: uint32 + presentationQueue*: VkQueue device*: VkDevice - presentationQueue*: VkQueue surface*: VkSurfaceKHR selectedSurfaceFormat: VkSurfaceFormatKHR selectedPresentationMode: VkPresentModeKHR - selectedExtent: VkExtent2D + frameDimension: VkExtent2D swapChain: VkSwapchainKHR swapImages: seq[VkImage] + swapFramebuffers: seq[VkFramebuffer] swapImageViews: seq[VkImageView] + pipeline*: GraphicsPipeline + commandPool*: VkCommandPool + commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] + viewport*: VkViewport + scissor*: VkRect2D + imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] + renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] + inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] Engine* = object display*: PDisplay window*: x.Window vulkan*: Vulkan - pipeline*: GraphicsPipeline -proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhyscialDevice] = +proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): - var device = PhyscialDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice)) + 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) + debug(&"Physical device nr {int(vulkanPhysicalDevice)} {cleanString(device.properties.deviceName)}") for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): var hasSurfaceSupport: VkBool32 = VK_FALSE checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport)) device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport))) + debug(&" Queue family {i} {queueFamilyProperty}") result.add(device) -proc filterForDevice(devices: seq[PhyscialDevice]): seq[(PhyscialDevice, uint32)] = +proc filterForDevice(devices: seq[PhysicalDevice]): seq[(PhysicalDevice, uint32, uint32)] = for device in devices: - if "VK_KHR_swapchain" in device.extensions: - for i, queueFamily in enumerate(device.queueFamilies): - let hasGraphics = bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)) - if ( - queueFamily.hasSurfaceSupport and - hasGraphics and - device.surfaceFormats.len > 0 and - device.presentModes.len > 0 - ): - result.add((device, uint32(i))) + if not (device.surfaceFormats.len > 0 and device.presentModes.len > 0 and "VK_KHR_swapchain" in device.extensions): + continue + var graphicsQueueFamily = high(uint32) + var presentationQueueFamily = high(uint32) + for i, queueFamily in enumerate(device.queueFamilies): + if queueFamily.hasSurfaceSupport: + presentationQueueFamily = uint32(i) + if bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)): + graphicsQueueFamily = uint32(i) + if graphicsQueueFamily != high(uint32) and presentationQueueFamily != high(uint32): + result.add((device, graphicsQueueFamily, presentationQueueFamily)) -proc filterForSurfaceFormat(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = - for format in formats: - if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: - result.add(format) + for (device, graphicsQueueFamily, presentationQueueFamily) in result: + debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})") -proc getSwapExtent(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = + +proc getFrameDimension(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = if capabilities.currentExtent.width != high(uint32): return capabilities.currentExtent else: @@ -116,37 +141,69 @@ height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), ) +proc createVulkanSurface(instance: VkInstance, display: PDisplay, window: Window): VkSurfaceKHR = + var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( + sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + dpy: display, + window: window, + ) + checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) + +proc setupVulkanDeviceAndQueues(instance: VkInstance, surface: VkSurfaceKHR): (PhysicalDevice, uint32, uint32, VkDevice, VkQueue, VkQueue) = + let usableDevices = instance.getAllPhysicalDevices(surface).filterForDevice() + if len(usableDevices) == 0: + raise newException(Exception, "No suitable graphics device found") + result[0] = usableDevices[0][0] + result[1] = usableDevices[0][1] + result[2] = usableDevices[0][2] + + debug(&"Chose device {cleanString(result[0].properties.deviceName)}") + + (result[3], result[4], result[5]) = getVulcanDevice( + result[0].device, + result[0].features, + result[1], + result[2], + ) + proc igniteEngine*(): Engine = - vkLoad1_0() - vkLoad1_1() - vkLoad1_2() # init X11 window (result.display, result.window) = xlibInit() # create vulkan instance + vkLoad1_0() + vkLoad1_1() + vkLoad1_2() result.vulkan.instance = createVulkanInstance(VULKAN_VERSION) - - # create vulkan-X11 surface - var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( - sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, - dpy: result.display, - window: result.window, - ) - checkVkResult vkCreateXlibSurfaceKHR(result.vulkan.instance, addr(surfaceCreateInfo), nil, addr(result.vulkan.surface)) + when ENABLEVULKANVALIDATIONLAYERS: + var createInfo = VkDebugUtilsMessengerCreateInfoEXT( + sType: VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + messageSeverity: VkDebugUtilsMessageSeverityFlagsEXT( + ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) or + ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) or + ord(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + ), + messageType: VkDebugUtilsMessageTypeFlagsEXT( + ord(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) or + ord(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) or + ord(VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) + ), + pfnUserCallback: debugCallback, + pUserData: nil, + ) + checkVkResult vkCreateDebugUtilsMessengerEXT(result.vulkan.instance, addr(createInfo), nil, addr(result.vulkan.debugMessenger)) - # determine device and queue to use and instantiate - result.vulkan.deviceList = result.vulkan.instance.getAllPhysicalDevices(result.vulkan.surface) - let usableDevices = result.vulkan.deviceList.filterForDevice() - if len(usableDevices) == 0: - raise newException(Exception, "No suitable graphics device found") - (result.vulkan.activePhysicalDevice, result.vulkan.activeQueueFamily) = usableDevices[0] - - (result.vulkan.device, result.vulkan.presentationQueue) = getVulcanDevice( - result.vulkan.activePhysicalDevice.device, - result.vulkan.activePhysicalDevice.features, - result.vulkan.activeQueueFamily - ) + result.vulkan.surface = result.vulkan.instance.createVulkanSurface(result.display, result.window) + + ( + result.vulkan.activePhysicalDevice, + result.vulkan.graphicsQueueFamily, + result.vulkan.presentationQueueFamily, + result.vulkan.device, + result.vulkan.graphicsQueue, + result.vulkan.presentationQueue + ) = result.vulkan.instance.setupVulkanDeviceAndQueues(result.vulkan.surface) # determine surface format for swapchain let usableSurfaceFormats = filterForSurfaceFormat(result.vulkan.activePhysicalDevice.surfaceFormats) @@ -154,7 +211,7 @@ raise newException(Exception, "No suitable surface formats found") result.vulkan.selectedSurfaceFormat = usableSurfaceFormats[0] result.vulkan.selectedPresentationMode = getPresentMode(result.vulkan.activePhysicalDevice.presentModes) - result.vulkan.selectedExtent = getSwapExtent(result.display, result.window, result.vulkan.activePhysicalDevice.surfaceCapabilities) + result.vulkan.frameDimension = result.display.getFrameDimension(result.window, result.vulkan.activePhysicalDevice.surfaceCapabilities) # setup swapchain var swapchainCreateInfo = VkSwapchainCreateInfoKHR( @@ -163,7 +220,7 @@ minImageCount: max(result.vulkan.activePhysicalDevice.surfaceCapabilities.minImageCount + 1, result.vulkan.activePhysicalDevice.surfaceCapabilities.maxImageCount), imageFormat: result.vulkan.selectedSurfaceFormat.format, imageColorSpace: result.vulkan.selectedSurfaceFormat.colorSpace, - imageExtent: result.vulkan.selectedExtent, + imageExtent: result.vulkan.frameDimension, imageArrayLayers: 1, 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?) @@ -174,8 +231,8 @@ clipped: VK_TRUE, oldSwapchain: VkSwapchainKHR(0), ) - checkVkResult vkCreateSwapchainKHR(result.vulkan.device, addr(swapchainCreateInfo), nil, addr(result.vulkan.swapChain)) - result.vulkan.swapImages = getSwapChainImages(result.vulkan.device, result.vulkan.swapChain) + checkVkResult result.vulkan.device.vkCreateSwapchainKHR(addr(swapchainCreateInfo), nil, addr(result.vulkan.swapChain)) + result.vulkan.swapImages = result.vulkan.device.getSwapChainImages(result.vulkan.swapChain) # setup swapchian image views result.vulkan.swapImageViews = newSeq[VkImageView](result.vulkan.swapImages.len) @@ -199,77 +256,344 @@ layerCount: 1, ), ) - checkVkResult vkCreateImageView(result.vulkan.device, addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i])) + checkVkResult result.vulkan.device.vkCreateImageView(addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i])) # init shader system checkGlslangResult glslang_initialize_process() # load shaders - result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) - result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) + result.vulkan.pipeline.shaderStages.add(result.vulkan.device.createShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) + result.vulkan.pipeline.shaderStages.add(result.vulkan.device.createShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) + + # setup render passes + var + colorAttachment = VkAttachmentDescription( + format: result.vulkan.selectedSurfaceFormat.format, + samples: VK_SAMPLE_COUNT_1_BIT, + loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, + storeOp: VK_ATTACHMENT_STORE_OP_STORE, + stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + finalLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + ) + colorAttachmentRef = VkAttachmentReference( + attachment: 0, + layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + ) + subpass = VkSubpassDescription( + pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, + colorAttachmentCount: 1, + pColorAttachments: addr(colorAttachmentRef) + ) + dependency = VkSubpassDependency( + srcSubpass: VK_SUBPASS_EXTERNAL, + dstSubpass: 0, + srcStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), + srcAccessMask: VkAccessFlags(0), + dstStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), + dstAccessMask: VkAccessFlags(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), + ) + renderPassCreateInfo = VkRenderPassCreateInfo( + sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + attachmentCount: 1, + pAttachments: addr(colorAttachment), + subpassCount: 1, + pSubpasses: addr(subpass), + dependencyCount: 1, + pDependencies: addr(dependency), + ) + checkVkResult result.vulkan.device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result.vulkan.pipeline.renderPass)) # create graphis pipeline - var dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] - var dynamicState = VkPipelineDynamicStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - dynamicStateCount: uint32(dynamicStates.len), - pDynamicStates: addr(dynamicStates[0]), - ) - var vertexInputInfo = VkPipelineVertexInputStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - vertexBindingDescriptionCount: 0, - pVertexBindingDescriptions: nil, - vertexAttributeDescriptionCount: 0, - pVertexAttributeDescriptions: nil, - ) - var inputAssembly = VkPipelineInputAssemblyStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - primitiveRestartEnable: VK_FALSE, - ) + + var + # define which parts can be dynamic (pipeline is fixed after setup) + dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] + dynamicState = VkPipelineDynamicStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + dynamicStateCount: uint32(dynamicStates.len), + pDynamicStates: addr(dynamicStates[0]), + ) + + # define input data format + vertexInputInfo = VkPipelineVertexInputStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + vertexBindingDescriptionCount: 0, + pVertexBindingDescriptions: nil, + vertexAttributeDescriptionCount: 0, + pVertexAttributeDescriptions: nil, + ) + inputAssembly = VkPipelineInputAssemblyStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + primitiveRestartEnable: VK_FALSE, + ) # setup viewport - var viewport = VkViewport( + result.vulkan.viewport = VkViewport( x: 0.0, y: 0.0, - width: (float) result.vulkan.selectedExtent.width, - height: (float) result.vulkan.selectedExtent.height, + width: (float) result.vulkan.frameDimension.width, + height: (float) result.vulkan.frameDimension.height, minDepth: 0.0, maxDepth: 1.0, ) - var scissor = VkRect2D( + result.vulkan.scissor = VkRect2D( offset: VkOffset2D(x: 0, y: 0), - extent: result.vulkan.selectedExtent + extent: result.vulkan.frameDimension + ) + var viewportState = VkPipelineViewportStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + viewportCount: 1, + pViewports: addr(result.vulkan.viewport), + scissorCount: 1, + pScissors: addr(result.vulkan.scissor), ) + # rasterizerization config + var + rasterizer = VkPipelineRasterizationStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + depthClampEnable: VK_FALSE, + rasterizerDiscardEnable: VK_FALSE, + polygonMode: VK_POLYGON_MODE_FILL, + lineWidth: 1.0, + cullMode: VkCullModeFlags(VK_CULL_MODE_BACK_BIT), + frontFace: VK_FRONT_FACE_CLOCKWISE, + depthBiasEnable: VK_FALSE, + depthBiasConstantFactor: 0.0, + depthBiasClamp: 0.0, + depthBiasSlopeFactor: 0.0, + ) + multisampling = VkPipelineMultisampleStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + sampleShadingEnable: VK_FALSE, + rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, + minSampleShading: 1.0, + pSampleMask: nil, + alphaToCoverageEnable: VK_FALSE, + alphaToOneEnable: VK_FALSE, + ) + colorBlendAttachment = VkPipelineColorBlendAttachmentState( + colorWriteMask: VkColorComponentFlags( + ord(VK_COLOR_COMPONENT_R_BIT) or + ord(VK_COLOR_COMPONENT_G_BIT) or + ord(VK_COLOR_COMPONENT_B_BIT) or + ord(VK_COLOR_COMPONENT_A_BIT) + ), + blendEnable: VK_TRUE, + srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA, + dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + colorBlendOp: VK_BLEND_OP_ADD, + srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, + dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, + alphaBlendOp: VK_BLEND_OP_ADD, + ) + colorBlending = VkPipelineColorBlendStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + logicOpEnable: VK_TRUE, + logicOp: VK_LOGIC_OP_COPY, + attachmentCount: 1, + pAttachments: addr(colorBlendAttachment), + blendConstants: [0.0'f, 0.0'f, 0.0'f, 0.0'f], + ) + + # create pipeline + pipelineLayoutInfo = VkPipelineLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + setLayoutCount: 0, + pSetLayouts: nil, + pushConstantRangeCount: 0, + pPushConstantRanges: nil, + ) + checkVkResult result.vulkan.device.vkCreatePipelineLayout(addr(pipelineLayoutInfo), nil, addr(result.vulkan.pipeline.layout)) + + var pipelineInfo = VkGraphicsPipelineCreateInfo( + sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + stageCount: 2, + pStages: addr(result.vulkan.pipeline.shaderStages[0]), + pVertexInputState: addr(vertexInputInfo), + pInputAssemblyState: addr(inputAssembly), + pViewportState: addr(viewportState), + pRasterizationState: addr(rasterizer), + pMultisampleState: addr(multisampling), + pDepthStencilState: nil, + pColorBlendState: addr(colorBlending), + pDynamicState: addr(dynamicState), + layout: result.vulkan.pipeline.layout, + renderPass: result.vulkan.pipeline.renderPass, + subpass: 0, + basePipelineHandle: VkPipeline(0), + basePipelineIndex: -1, + ) + checkVkResult result.vulkan.device.vkCreateGraphicsPipelines( + VkPipelineCache(0), + 1, + addr(pipelineInfo), + nil, + addr(result.vulkan.pipeline.pipeline) + ) + + # set up framebuffers + result.vulkan.swapFramebuffers = newSeq[VkFramebuffer](result.vulkan.swapImages.len) + + for i, imageview in enumerate(result.vulkan.swapImageViews): + var framebufferInfo = VkFramebufferCreateInfo( + sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + renderPass: result.vulkan.pipeline.renderPass, + attachmentCount: 1, + pAttachments: addr(result.vulkan.swapImageViews[i]), + width: result.vulkan.frameDimension.width, + height: result.vulkan.frameDimension.height, + layers: 1, + ) + checkVkResult result.vulkan.device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result.vulkan.swapFramebuffers[i])) + + # set up command buffer + var poolInfo = VkCommandPoolCreateInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + flags: VkCommandPoolCreateFlags(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT), + queueFamilyIndex: result.vulkan.graphicsQueueFamily, + ) + checkVkResult result.vulkan.device.vkCreateCommandPool(addr(poolInfo), nil, addr(result.vulkan.commandPool)) + + var allocInfo = VkCommandBufferAllocateInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + commandPool: result.vulkan.commandPool, + level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, + commandBufferCount: result.vulkan.commandBuffers.len.uint32, + ) + checkVkResult result.vulkan.device.vkAllocateCommandBuffers(addr(allocInfo), addr(result.vulkan.commandBuffers[0])) + + # create semaphores for syncing rendering + var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) + var fenceInfo = VkFenceCreateInfo( + sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + flags: VkFenceCreateFlags(VK_FENCE_CREATE_SIGNALED_BIT) + ) + for i in 0 ..< MAX_FRAMES_IN_FLIGHT: + checkVkResult result.vulkan.device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vulkan.imageAvailableSemaphores[i])) + checkVkResult result.vulkan.device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result.vulkan.renderFinishedSemaphores[i])) + checkVkResult result.vulkan.device.vkCreateFence(addr(fenceInfo), nil, addr(result.vulkan.inFlightFences[i])) + -proc fullThrottle*(engine: Engine) = - var event: XEvent - while true: - discard XNextEvent(engine.display, addr(event)) - case event.theType - of Expose: - discard - of ClientMessage: - if cast[Atom](event.xclient.data.l[0]) == deleteMessage: - break - of KeyPress: - let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) - if key != 0: - echo "Key ", key, " pressed" - of ButtonPressMask: - echo "Mouse button ", event.xbutton.button, " pressed at ", - event.xbutton.x, ",", event.xbutton.y - else: - discard +proc recordCommandBuffer(vulkan: var Vulkan, commandBuffer: VkCommandBuffer, imageIndex: uint32) = + var beginInfo = VkCommandBufferBeginInfo( + sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + pInheritanceInfo: nil, + ) + checkVkResult commandBuffer.vkBeginCommandBuffer(addr(beginInfo)) + + var + 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, + renderPass: vulkan.pipeline.renderPass, + framebuffer: vulkan.swapFramebuffers[imageIndex], + renderArea: VkRect2D( + offset: VkOffset2D(x: 0, y: 0), + extent: vulkan.frameDimension, + ), + clearValueCount: 1, + pClearValues: addr(clearColor), + ) + commandBuffer.vkCmdBeginRenderPass(addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) + commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vulkan.pipeline.pipeline) + + commandBuffer.vkCmdSetViewport(firstViewport=0, viewportCount=1, addr(vulkan.viewport)) + commandBuffer.vkCmdSetScissor(firstScissor=0, scissorCount=1, addr(vulkan.scissor)) + commandBuffer.vkCmdDraw(vertexCount=3, instanceCount=1, firstVertex=0, firstInstance=0) + commandBuffer.vkCmdEndRenderPass() + checkVkResult commandBuffer.vkEndCommandBuffer() + +proc drawFrame(vulkan: var Vulkan, currentFrame: int) = + checkVkResult vulkan.device.vkWaitForFences(1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) + checkVkResult vulkan.device.vkResetFences(1, addr(vulkan.inFlightFences[currentFrame])) + var bufferImageIndex: uint32 + checkVkResult vulkan.device.vkAcquireNextImageKHR( + vulkan.swapChain, + high(uint64), + vulkan.imageAvailableSemaphores[currentFrame], + VkFence(0), + addr(bufferImageIndex) + ) + + checkVkResult vkResetCommandBuffer(vulkan.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) + recordCommandBuffer(vulkan, vulkan.commandBuffers[currentFrame], bufferImageIndex) + var + waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] + waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] + signalSemaphores = [vulkan.renderFinishedSemaphores[currentFrame]] + submitInfo = VkSubmitInfo( + sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, + waitSemaphoreCount: 1, + pWaitSemaphores: addr(waitSemaphores[0]), + pWaitDstStageMask: addr(waitStages[0]), + commandBufferCount: 1, + pCommandBuffers: addr(vulkan.commandBuffers[currentFrame]), + signalSemaphoreCount: 1, + pSignalSemaphores: addr(signalSemaphores[0]), + ) + checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), vulkan.inFlightFences[currentFrame]) + + var presentInfo = VkPresentInfoKHR( + sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + waitSemaphoreCount: 1, + pWaitSemaphores: addr(signalSemaphores[0]), + swapchainCount: 1, + pSwapchains: addr(vulkan.swapChain), + pImageIndices: addr(bufferImageIndex), + pResults: nil, + ) + checkVkResult vkQueuePresentKHR(vulkan.presentationQueue, addr(presentInfo)) + + +proc fullThrottle*(engine: var Engine) = + var + event: XEvent + killed = false + currentFrame = 0 + + while not killed: + while engine.display.XPending() > 0 and not killed: + discard engine.display.XNextEvent(addr(event)) + case event.theType + of ClientMessage: + if cast[Atom](event.xclient.data.l[0]) == deleteMessage: + killed = true + of KeyPress: + let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) + if key == XK_Escape: + killed = true + else: + discard + drawFrame(engine.vulkan, currentFrame) + currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT; + checkVkResult engine.vulkan.device.vkDeviceWaitIdle() + proc trash*(engine: Engine) = - for shaderStage in engine.pipeline.shaderStages: - vkDestroyShaderModule(engine.vulkan.device, shaderStage.module, nil); + for i in 0 ..< MAX_FRAMES_IN_FLIGHT: + engine.vulkan.device.vkDestroySemaphore(engine.vulkan.imageAvailableSemaphores[i], nil) + engine.vulkan.device.vkDestroySemaphore(engine.vulkan.renderFinishedSemaphores[i], nil) + engine.vulkan.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil) + + engine.vulkan.device.vkDestroyCommandPool(engine.vulkan.commandPool, nil) + for framebuffer in engine.vulkan.swapFramebuffers: + engine.vulkan.device.vkDestroyFramebuffer(framebuffer, nil) + + engine.vulkan.device.vkDestroyPipeline(engine.vulkan.pipeline.pipeline, nil) + engine.vulkan.device.vkDestroyPipelineLayout(engine.vulkan.pipeline.layout, nil) + engine.vulkan.device.vkDestroyRenderPass(engine.vulkan.pipeline.renderPass, nil) + + for shaderStage in engine.vulkan.pipeline.shaderStages: + engine.vulkan.device.vkDestroyShaderModule(shaderStage.module, nil) + glslang_finalize_process() - vkDestroySwapchainKHR(engine.vulkan.device, engine.vulkan.swapChain, nil); - vkDestroySurfaceKHR(engine.vulkan.instance, engine.vulkan.surface, nil); - vkDestroyDevice(engine.vulkan.device, nil) - vkDestroyInstance(engine.vulkan.instance, nil) + engine.vulkan.device.vkDestroySwapchainKHR(engine.vulkan.swapChain, nil) + engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) + engine.vulkan.device.vkDestroyDevice(nil) + engine.vulkan.instance.vkDestroyInstance(nil) checkXlibResult engine.display.XDestroyWindow(engine.window) discard engine.display.XCloseDisplay() # always returns 0 diff -r 0bc8643cfe25 -r 91544fc1afe5 src/glslang/glslang_c_interface.nim --- a/src/glslang/glslang_c_interface.nim Fri Dec 16 00:05:41 2022 +0700 +++ b/src/glslang/glslang_c_interface.nim Mon Dec 19 10:41:20 2022 +0700 @@ -1,9 +1,20 @@ -# required to link the GLSL compiler +import std/strformat + +when defined(linux): + const platform = "linux" +when defined(windows): + const platform = "windows" + + when defined(release): - {.passl: "-Lthirdparty/glslang/lib/release" .} + const libversion = "release" else: - {.passl: "-Lthirdparty/glslang/lib/debug" .} -{.passl: "-Lthirdparty/spirv-tools/lib/" .} + const libversion = "debug" + + +# required to link the GLSL compiler +{.passl: &"-Lthirdparty/lib/glslang/{platform}_{libversion}" .} +{.passl: &"-Lthirdparty/lib/spirv-tools/{platform}_{libversion}" .} {.passl: "-lglslang" .} {.passl: "-lglslang-default-resource-limits" .} @@ -14,7 +25,6 @@ {.passl: "-lOGLCompiler" .} {.passl: "-lSPIRV" .} {.passl: "-lSPIRV-Tools-opt" .} - {.passl: "-lSPIRV-Tools" .} {.passl: "-lSPIRV-Tools-diff" .} {.passl: "-lSPIRV-Tools-fuzz" .} diff -r 0bc8643cfe25 -r 91544fc1afe5 src/test Binary file src/test has changed diff -r 0bc8643cfe25 -r 91544fc1afe5 src/test.nim --- a/src/test.nim Fri Dec 16 00:05:41 2022 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -import engine - - -when isMainModule: - let myengine = igniteEngine() - myengine.fullThrottle() - myengine.trash() diff -r 0bc8643cfe25 -r 91544fc1afe5 src/vulkan.nim --- a/src/vulkan.nim Fri Dec 16 00:05:41 2022 +0700 +++ b/src/vulkan.nim Mon Dec 19 10:41:20 2022 +0700 @@ -91,6 +91,7 @@ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7 VK_IMAGE_LAYOUT_PREINITIALIZED = 8 + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, VkAttachmentLoadOp* {.size: int32.sizeof.} = enum VK_ATTACHMENT_LOAD_OP_LOAD = 0 VK_ATTACHMENT_LOAD_OP_CLEAR = 1 @@ -508,7 +509,9 @@ VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47 VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48 VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000 # added by sam + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001 # added by sam VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000 # added by sam + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004 # added by sam VkSubpassContents* {.size: int32.sizeof.} = enum VK_SUBPASS_CONTENTS_INLINE = 0 VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1 @@ -10635,18 +10638,18 @@ vkCreateMacOSSurfaceMVK = cast[proc(instance: VkInstance, pCreateInfo: ptr VkMacOSSurfaceCreateInfoMVK , pAllocator: ptr VkAllocationCallbacks , pSurface: ptr VkSurfaceKHR ): VkResult {.stdcall.}](vkGetProc("vkCreateMacOSSurfaceMVK")) # Load VK_EXT_debug_utils -proc loadVK_EXT_debug_utils*() = - vkSetDebugUtilsObjectNameEXT = cast[proc(device: VkDevice, pNameInfo: ptr VkDebugUtilsObjectNameInfoEXT ): VkResult {.stdcall.}](vkGetProc("vkSetDebugUtilsObjectNameEXT")) - vkSetDebugUtilsObjectTagEXT = cast[proc(device: VkDevice, pTagInfo: ptr VkDebugUtilsObjectTagInfoEXT ): VkResult {.stdcall.}](vkGetProc("vkSetDebugUtilsObjectTagEXT")) - vkQueueBeginDebugUtilsLabelEXT = cast[proc(queue: VkQueue, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetProc("vkQueueBeginDebugUtilsLabelEXT")) - vkQueueEndDebugUtilsLabelEXT = cast[proc(queue: VkQueue): void {.stdcall.}](vkGetProc("vkQueueEndDebugUtilsLabelEXT")) - vkQueueInsertDebugUtilsLabelEXT = cast[proc(queue: VkQueue, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetProc("vkQueueInsertDebugUtilsLabelEXT")) - vkCmdBeginDebugUtilsLabelEXT = cast[proc(commandBuffer: VkCommandBuffer, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetProc("vkCmdBeginDebugUtilsLabelEXT")) - vkCmdEndDebugUtilsLabelEXT = cast[proc(commandBuffer: VkCommandBuffer): void {.stdcall.}](vkGetProc("vkCmdEndDebugUtilsLabelEXT")) - vkCmdInsertDebugUtilsLabelEXT = cast[proc(commandBuffer: VkCommandBuffer, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetProc("vkCmdInsertDebugUtilsLabelEXT")) - vkCreateDebugUtilsMessengerEXT = cast[proc(instance: VkInstance, pCreateInfo: ptr VkDebugUtilsMessengerCreateInfoEXT , pAllocator: ptr VkAllocationCallbacks , pMessenger: ptr VkDebugUtilsMessengerEXT ): VkResult {.stdcall.}](vkGetProc("vkCreateDebugUtilsMessengerEXT")) - vkDestroyDebugUtilsMessengerEXT = cast[proc(instance: VkInstance, messenger: VkDebugUtilsMessengerEXT, pAllocator: ptr VkAllocationCallbacks ): void {.stdcall.}](vkGetProc("vkDestroyDebugUtilsMessengerEXT")) - vkSubmitDebugUtilsMessageEXT = cast[proc(instance: VkInstance, messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, messageTypes: VkDebugUtilsMessageTypeFlagsEXT, pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT ): void {.stdcall.}](vkGetProc("vkSubmitDebugUtilsMessageEXT")) +proc loadVK_EXT_debug_utils*(instance: VkInstance) = + vkSetDebugUtilsObjectNameEXT = cast[proc(device: VkDevice, pNameInfo: ptr VkDebugUtilsObjectNameInfoEXT ): VkResult {.stdcall.}](vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT")) + vkSetDebugUtilsObjectTagEXT = cast[proc(device: VkDevice, pTagInfo: ptr VkDebugUtilsObjectTagInfoEXT ): VkResult {.stdcall.}](vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectTagEXT")) + vkQueueBeginDebugUtilsLabelEXT = cast[proc(queue: VkQueue, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkQueueBeginDebugUtilsLabelEXT")) + vkQueueEndDebugUtilsLabelEXT = cast[proc(queue: VkQueue): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkQueueEndDebugUtilsLabelEXT")) + vkQueueInsertDebugUtilsLabelEXT = cast[proc(queue: VkQueue, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkQueueInsertDebugUtilsLabelEXT")) + vkCmdBeginDebugUtilsLabelEXT = cast[proc(commandBuffer: VkCommandBuffer, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkCmdBeginDebugUtilsLabelEXT")) + vkCmdEndDebugUtilsLabelEXT = cast[proc(commandBuffer: VkCommandBuffer): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkCmdEndDebugUtilsLabelEXT")) + vkCmdInsertDebugUtilsLabelEXT = cast[proc(commandBuffer: VkCommandBuffer, pLabelInfo: ptr VkDebugUtilsLabelEXT ): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkCmdInsertDebugUtilsLabelEXT")) + vkCreateDebugUtilsMessengerEXT = cast[proc(instance: VkInstance, pCreateInfo: ptr VkDebugUtilsMessengerCreateInfoEXT , pAllocator: ptr VkAllocationCallbacks , pMessenger: ptr VkDebugUtilsMessengerEXT ): VkResult {.stdcall.}](vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")) + vkDestroyDebugUtilsMessengerEXT = cast[proc(instance: VkInstance, messenger: VkDebugUtilsMessengerEXT, pAllocator: ptr VkAllocationCallbacks ): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")) + vkSubmitDebugUtilsMessageEXT = cast[proc(instance: VkInstance, messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, messageTypes: VkDebugUtilsMessageTypeFlagsEXT, pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT ): void {.stdcall.}](vkGetInstanceProcAddr(instance, "vkSubmitDebugUtilsMessageEXT")) # Load VK_ANDROID_external_memory_android_hardware_buffer proc loadVK_ANDROID_external_memory_android_hardware_buffer*() = diff -r 0bc8643cfe25 -r 91544fc1afe5 src/vulkan_helpers.nim --- a/src/vulkan_helpers.nim Fri Dec 16 00:05:41 2022 +0700 +++ b/src/vulkan_helpers.nim Mon Dec 19 10:41:20 2022 +0700 @@ -1,26 +1,43 @@ import std/tables import std/strutils +import std/strformat +import std/logging import ./glslang/glslang import ./vulkan - when defined(release): - const ENABLEVULKANVALIDATIONLAYERS = false + const ENABLEVULKANVALIDATIONLAYERS* = false else: - const ENABLEVULKANVALIDATIONLAYERS = true + const ENABLEVULKANVALIDATIONLAYERS* = true template checkVkResult*(call: untyped) = - let value = call - if value != VK_SUCCESS: - raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value) + when defined(release): + discard call + else: + let value = call + debug(&"CALLING vulkan: {astToStr(call)}") + if value != VK_SUCCESS: + raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value) proc VK_MAKE_API_VERSION*(variant: uint32, major: uint32, minor: uint32, patch: uint32): uint32 {.compileTime.} = (variant shl 29) or (major shl 22) or (minor shl 12) or patch +proc filterForSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = + for format in formats: + if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: + result.add(format) + + +proc cleanString*(str: openArray[char]): string = + for i in 0 ..< len(str): + if str[i] == char(0): + result = join(str[0 ..< i]) + break + proc getInstanceExtensions*(): seq[string] = var extensionCount: uint32 checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), nil) @@ -28,7 +45,7 @@ checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), addr(extensions[0])) for extension in extensions: - result.add(join(extension.extensionName).strip(chars={char(0)})) + result.add(cleanString(extension.extensionName)) proc getDeviceExtensions*(device: VkPhysicalDevice): seq[string] = @@ -38,7 +55,7 @@ checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr(extensionCount), addr(extensions[0])) for extension in extensions: - result.add(join(extension.extensionName).strip(chars={char(0)})) + result.add(cleanString(extension.extensionName)) proc getValidationLayers*(): seq[string] = @@ -48,7 +65,7 @@ checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), addr(layers[0])) for layer in layers: - result.add(join(layer.layerName).strip(chars={char(0)})) + result.add(cleanString(layer.layerName)) proc getVulkanPhysicalDevices*(instance: VkInstance): seq[VkPhysicalDevice] = @@ -88,10 +105,10 @@ proc getPresentMode*(modes: seq[VkPresentModeKHR]): VkPresentModeKHR = let preferredModes = [ - VK_PRESENT_MODE_MAILBOX_KHR, - VK_PRESENT_MODE_FIFO_RELAXED_KHR, - VK_PRESENT_MODE_FIFO_KHR, - VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_MAILBOX_KHR, # triple buffering + VK_PRESENT_MODE_FIFO_RELAXED_KHR, # double duffering + VK_PRESENT_MODE_FIFO_KHR, # double duffering + VK_PRESENT_MODE_IMMEDIATE_KHR, # single buffering ] for preferredMode in preferredModes: for mode in modes: @@ -108,6 +125,7 @@ "VK_KHR_display".cstring, "VK_KHR_surface".cstring, "VK_KHR_xlib_surface".cstring, + "VK_EXT_debug_utils".cstring, ] let availableExtensions = getInstanceExtensions() for extension in requiredExtensions: @@ -122,7 +140,9 @@ if $layer in availableLayers: usableLayers.add(layer) + echo "Available validation layers: ", availableLayers echo "Using validation layers: ", usableLayers + echo "Available extensions: ", availableExtensions echo "Using extensions: ", requiredExtensions var appinfo = VkApplicationInfo( @@ -144,33 +164,40 @@ loadVK_KHR_surface() loadVK_KHR_xlib_surface() loadVK_KHR_swapchain() + when ENABLEVULKANVALIDATIONLAYERS: + loadVK_EXT_debug_utils(result) proc getVulcanDevice*( physicalDevice: var VkPhysicalDevice, features: var VkPhysicalDeviceFeatures, - selectedQueueFamily: uint32, -): (VkDevice, VkQueue) = + graphicsQueueFamily: uint32, + presentationQueueFamily: uint32, +): (VkDevice, VkQueue, VkQueue) = # setup queue and device + # TODO: need check this, possibly wrong logic, see Vulkan tutorial var priority = 1.0'f32 - var queueCreateInfo = VkDeviceQueueCreateInfo( - sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - queueFamilyIndex: uint32(selectedQueueFamily), - queueCount: 1, - pQueuePriorities: addr(priority), - ) + var queueCreateInfo = [ + VkDeviceQueueCreateInfo( + sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueFamilyIndex: graphicsQueueFamily, + queueCount: 1, + pQueuePriorities: addr(priority), + ), + ] var requiredExtensions = ["VK_KHR_swapchain".cstring] var deviceCreateInfo = VkDeviceCreateInfo( sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - pQueueCreateInfos: addr(queueCreateInfo), - queueCreateInfoCount: 1, + queueCreateInfoCount: uint32(queueCreateInfo.len), + pQueueCreateInfos: addr(queueCreateInfo[0]), pEnabledFeatures: addr(features), enabledExtensionCount: requiredExtensions.len.uint32, ppEnabledExtensionNames: cast[ptr UncheckedArray[cstring]](addr(requiredExtensions)) ) checkVkResult vkCreateDevice(physicalDevice, addr(deviceCreateInfo), nil, addr(result[0])) - vkGetDeviceQueue(result[0], selectedQueueFamily, 0'u32, addr(result[1])); + vkGetDeviceQueue(result[0], graphicsQueueFamily, 0'u32, addr(result[1])); + vkGetDeviceQueue(result[0], presentationQueueFamily, 0'u32, addr(result[2])); proc createShaderStage*(device: VkDevice, stage: VkShaderStageFlagBits, shader: string): VkPipelineShaderStageCreateInfo = const VK_GLSL_MAP = { @@ -180,15 +207,24 @@ var code = compileGLSLToSPIRV(VK_GLSL_MAP[stage], shader, "") var createInfo = VkShaderModuleCreateInfo( sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - codeSize: code.len.uint, + codeSize: uint(code.len * sizeof(uint32)), pCode: addr(code[0]), ) var shaderModule: VkShaderModule checkVkResult vkCreateShaderModule(device, addr(createInfo), nil, addr(shaderModule)) - var vertShaderStageInfo = VkPipelineShaderStageCreateInfo( + return VkPipelineShaderStageCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, stage: stage, module: shaderModule, pName: "main", # entry point for shader ) + +proc debugCallback*( + messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, + messageTypes: VkDebugUtilsMessageTypeFlagsEXT, + pCallbackData: VkDebugUtilsMessengerCallbackDataEXT, + userData: pointer +): VkBool32 {.cdecl.} = + echo &"{messageSeverity}: {VkDebugUtilsMessageTypeFlagBitsEXT(messageTypes)}: {pCallbackData.pMessage}" + return VK_FALSE diff -r 0bc8643cfe25 -r 91544fc1afe5 src/xlib_helpers.nim --- a/src/xlib_helpers.nim Fri Dec 16 00:05:41 2022 +0700 +++ b/src/xlib_helpers.nim Mon Dec 19 10:41:20 2022 +0700 @@ -1,7 +1,10 @@ import x11/xlib, x11/xutil, - x11/x + x11/x, + x11/keysym + +export keysym var deleteMessage*: Atom @@ -11,6 +14,7 @@ raise newException(Exception, "Xlib error: " & astToStr(call) & " returned " & $value) proc xlibInit*(): (PDisplay, Window) = + checkXlibResult XInitThreads() let display = XOpenDisplay(nil) if display == nil: quit "Failed to open display" @@ -26,7 +30,7 @@ checkXlibResult XSelectInput(display, window, ButtonPressMask or KeyPressMask or ExposureMask) checkXlibResult XMapWindow(display, window) - deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", false.XBool) + deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false)) checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1) return (display, window)