Mercurial > games > semicongine
diff src/engine.nim @ 461:59d861a6a5c4
add: initial version
author | Sam <sam@basx.dev> |
---|---|
date | Wed, 14 Dec 2022 00:49:35 +0700 |
parents | |
children | bb2a7d3a7003 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/engine.nim Wed Dec 14 00:49:35 2022 +0700 @@ -0,0 +1,215 @@ +import std/enumerate + +import ./vulkan +import ./vulkan_helpers +import ./xlib_helpers + +import ./glslang/glslang +import ./glslang/glslang_c_shader_types + + +import + x11/xlib, + x11/x + +const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) + +type + QueueFamily = object + properties*: VkQueueFamilyProperties + hasSurfaceSupport*: bool + PhyscialDevice = object + device*: VkPhysicalDevice + extensions*: seq[string] + properties*: VkPhysicalDeviceProperties + features*: VkPhysicalDeviceFeatures + queueFamilies*: seq[QueueFamily] + surfaceCapabilities*: VkSurfaceCapabilitiesKHR + surfaceFormats: seq[VkSurfaceFormatKHR] + presentModes: seq[VkPresentModeKHR] + Vulkan* = object + instance*: VkInstance + deviceList*: seq[PhyscialDevice] + activePhysicalDevice*: PhyscialDevice + activeQueueFamily*: uint32 + device*: VkDevice + presentationQueue*: VkQueue + surface*: VkSurfaceKHR + selectedSurfaceFormat: VkSurfaceFormatKHR + selectedPresentationMode: VkPresentModeKHR + selectedExtent: VkExtent2D + swapChain: VkSwapchainKHR + swapImages: seq[VkImage] + swapImageViews: seq[VkImageView] + Engine* = object + display*: PDisplay + window*: x.Window + vulkan*: Vulkan + + +proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhyscialDevice] = + for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): + var device = PhyscialDevice(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) + + for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)): + var hasSurfaceSupport: VkBool32 = VkBool32(false) + checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport)) + device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport))) + + result.add(device) + +proc filterForDevice(devices: seq[PhyscialDevice]): seq[(PhyscialDevice, 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))) + +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 getSwapExtent(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D = + if capabilities.currentExtent.width != high(uint32): + return capabilities.currentExtent + else: + let (width, height) = xlibFramebufferSize(display, window) + return VkExtent2D( + width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width), + height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height), + ) + +proc igniteEngine*(): Engine = + vkLoad1_0() + vkLoad1_1() + vkLoad1_2() + + # init X11 window + (result.display, result.window) = xlibInit() + + # create vulkan instance + 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)) + + # 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 + ) + + # determine surface format for swapchain + let usableSurfaceFormats = filterForSurfaceFormat(result.vulkan.activePhysicalDevice.surfaceFormats) + if len(usableSurfaceFormats) == 0: + 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) + + # setup swapchain + var swapchainCreateInfo = VkSwapchainCreateInfoKHR( + sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + surface: result.vulkan.surface, + 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, + 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?) + imageSharingMode: VK_SHARING_MODE_EXCLUSIVE, + preTransform: result.vulkan.activePhysicalDevice.surfaceCapabilities.currentTransform, + compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + presentMode: result.vulkan.selectedPresentationMode, + clipped: VkBool32(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) + + # setup swapchian image views + result.vulkan.swapImageViews = newSeq[VkImageView](result.vulkan.swapImages.len) + for i, image in enumerate(result.vulkan.swapImages): + var imageViewCreateInfo = VkImageViewCreateInfo( + sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + image: image, + viewType: VK_IMAGE_VIEW_TYPE_2D, + format: result.vulkan.selectedSurfaceFormat.format, + components: VkComponentMapping( + r: VK_COMPONENT_SWIZZLE_IDENTITY, + g: VK_COMPONENT_SWIZZLE_IDENTITY, + b: VK_COMPONENT_SWIZZLE_IDENTITY, + a: VK_COMPONENT_SWIZZLE_IDENTITY, + ), + subresourceRange: VkImageSubresourceRange( + aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), + baseMipLevel: 0, + levelCount: 1, + baseArrayLayer: 0, + layerCount: 1, + ), + ) + checkVkResult vkCreateImageView(result.vulkan.device, addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i])) + echo compileShaderToSPIRV_Vulkan(GLSLANG_STAGE_VERTEX, """#version 450 +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); +}""", "<memory-shader>") + + +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 trash*(engine: Engine) = + 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) + checkXlibResult engine.display.XDestroyWindow(engine.window) + discard engine.display.XCloseDisplay() # always returns 0