# HG changeset patch # User Sam # Date 1671642400 -25200 # Node ID 4ed9cb0983154c45a3b74d38422d134f3385cb93 # Parent af9183acb1737611f3b0ec1ef8909bd708edc6bf add: structure code for crossplatform, add some input handling + bugfixes diff -r af9183acb173 -r 4ed9cb098315 Makefile --- a/Makefile Tue Dec 20 00:28:05 2022 +0700 +++ b/Makefile Thu Dec 22 00:06:40 2022 +0700 @@ -3,43 +3,67 @@ 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} +# build +build/debug/linux/test: ${SOURCES} + mkdir -p $$( dirname $@ ) nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -o:$@ examples/test.nim - -build/release/linux: - mkdir -p $@ -build/release/linux/test: build/release/linux ${SOURCES} +build/release/linux/test: ${SOURCES} + mkdir -p $$( dirname $@ ) + nim c ${COMPILE_OPTIONS} ${RELEASE_OPTIONS} -o:$@ examples/test.nim +build/debug/windows/test: ${SOURCES} + mkdir -p $$( dirname $@ ) + nim c ${COMPILE_OPTIONS} ${DEBUG_OPTIONS} -o:$@ examples/test.nim +build/release/windows/test: ${SOURCES} + mkdir -p $$( dirname $@ ) 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 -thirdparty: - echo https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/windows-msvc-2017-release/continuous/1885/20221216-081805/install.zip - echo https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/windows-msvc-2017-release/continuous/1885/20221216-081805/install.zip +build_all: build/debug/linux/test build/release/linux/test build/debug/windows/test build/release/windows/test + +# publish +publish_linux_debug: build/debug/linux/test + scp $< basx.dev:/var/www/public.basx.dev/joni/linux/debug/ +publish_linux_release: build/release/linux/test + scp $< basx.dev:/var/www/public.basx.dev/joni/linux/release/ +publish_windows_debug: build/debug/linux/test + scp $< basx.dev:/var/www/public.basx.dev/joni/windows/debug/ +publish_windows_release: build/release/linux/test + scp $< basx.dev:/var/www/public.basx.dev/joni/windows/release/ + +publish_all: publish_linux_debug publish_linux_release publish_windows_debug publish_windows_release + + +# download thirdparty-libraries -SPIRV_TOOLS_LINUX_DEBUG: - wget https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/linux-gcc-release/continuous/1889/20221216-081754/install.tgz -SPIRV_TOOLS_LINUX_DEBUG: - wget https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/linux-gcc-debug/continuous/1899/20221216-081758/install.tgz -SPIRV_TOOLS_WINDOWS_DEBUG: - wget https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/windows-msvc-2017-debug/continuous/1599/20221216-081803/install.zip -SPIRV_TOOLS_WINDOWS_RELEASE: - wget https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/windows-msvc-2017-release/continuous/1885/20221216-081805/install.zip +thirdparty/lib/glslang/linux_debug: + mkdir -p $@ + wget --directory-prefix=$@ https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Debug.zip + uzip glslang-master-linux-Debug.zip +thirdparty/lib/glslang/linux_release: + mkdir -p $@ + wget --directory-prefix=$@ https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip + unzip glslang-master-linux-Release.zip +thirdparty/lib/glslang/windows_debug: + mkdir -p $@ + wget --directory-prefix=$@ https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Debug.zip + unzip glslang-master-windows-x64-Debug.zip +thirdparty/lib/glslang/windows_release: + mkdir -p $@ + wget --directory-prefix=$@ https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip + unzip glslang-master-windows-x64-Release.zip -GLSL_LINUX_DEBUG: - wget -GLSL_LINUX_RELEASE: - wget -GLSL_WINDOWS_DEBUG: - wget -GLSL_WINDOWS_RELEASE: - wget +thirdparty/lib/spirv-tools/linux_debug: + mkdir -p $@ + wget --directory-prefix=$@ https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/linux-gcc-debug/continuous/1899/20221216-081758/install.tgz + tar -xf $@/install.tgz +thirdparty/lib/spirv-tools/linux_release: + mkdir -p $@ + wget --directory-prefix=$@ https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/linux-gcc-release/continuous/1889/20221216-081754/install.tgz + tar -xf $@/install.tgz +thirdparty/lib/spirv-tools/windows_debug: + mkdir -p $@ + wget --directory-prefix=$@ https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/windows-msvc-2017-debug/continuous/1599/20221216-081803/install.zip + unzip $@/install.zip +thirdparty/lib/spirv-tools/windows_release: + mkdir -p $@ + wget --directory-prefix=$@ https://storage.googleapis.com/spirv-tools/artifacts/prod/graphics_shader_compiler/spirv-tools/windows-msvc-2017-release/continuous/1885/20221216-081805/install.zip + unzip $@/install.zip diff -r af9183acb173 -r 4ed9cb098315 src/engine.nim --- 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() diff -r af9183acb173 -r 4ed9cb098315 src/events.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/events.nim Thu Dec 22 00:06:40 2022 +0700 @@ -0,0 +1,20 @@ +type + EventType* = enum + Quit + ResizedWindow + KeyDown + KeyUp + Key* = enum + UNKNOWN + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z + a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z + `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `0` + Minus, Plus, Underscore, Equals, Space, Enter, Backspace, Tab + Comma, Period, Semicolon, Colon, + Escape, CtrlL, ShirtL, AltL, CtrlR, ShirtR, AltR + Event* = object + case eventType*: EventType + of KeyDown, KeyUp: + key*: Key + else: + discard diff -r af9183acb173 -r 4ed9cb098315 src/notes --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/notes Thu Dec 22 00:06:40 2022 +0700 @@ -0,0 +1,2 @@ +For implementation of font rendering: +https://developer.apple.com/fonts/TrueType-Reference-Manual/ diff -r af9183acb173 -r 4ed9cb098315 src/platform/linux/symkey_map.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/platform/linux/symkey_map.nim Thu Dec 22 00:06:40 2022 +0700 @@ -0,0 +1,16 @@ +import std/tables +export tables + +import x11/keysym +import x11/x + +import ../../events + +const KeyTypeMap* = { + XK_A: A, XK_B: B, XK_C: C, XK_D: D, XK_E: E, XK_F: F, XK_G: G, XK_H: H, XK_I: I, XK_J: J, XK_K: K, XK_L: L, XK_M: M, XK_N: N, XK_O: O, XK_P: P, XK_Q: Q, XK_R: R, XK_S: S, XK_T: T, XK_U: U, XK_V: V, XK_W: W, XK_X: X, XK_Y: Y, XK_Z: Z, + XK_a: a, XK_b: b, XK_c: c, XK_d: d, XK_e: e, XK_f: f, XK_g: g, XK_h: h, XK_i: i, XK_j: j, XK_k: k, XK_l: l, XK_m: m, XK_n: n, XK_o: o, XK_p: p, XK_q: q, XK_r: r, XK_s: s, XK_t: t, XK_u: u, XK_v: v, XK_w: w, XK_x: Key.x, XK_y: y, XK_z: z, + XK_1: `1`, XK_2: `2`, XK_3: `3`, XK_4: `4`, XK_5: `5`, XK_6: `6`, XK_7: `7`, XK_8: `8`, XK_9: `9`, XK_0: `0`, + XK_minus: Minus, XK_plus: Plus, XK_underscore: Underscore, XK_equal: Equals, XK_space: Space, XK_Return: Enter, XK_BackSpace: Backspace, XK_Tab: Tab, + XK_comma: Comma, XK_period: Period, XK_semicolon: Semicolon, XK_colon: Colon, + XK_Escape: Escape, XK_Control_L: CtrlL, XK_Shift_L: ShirtL, XK_Alt_L: AltL, XK_Control_R: CtrlR, XK_Shift_R: ShirtR, XK_Alt_R: AltR +}.toTable diff -r af9183acb173 -r 4ed9cb098315 src/platform/linux/xlib.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/platform/linux/xlib.nim Thu Dec 22 00:06:40 2022 +0700 @@ -0,0 +1,73 @@ +import + x11/xlib, + x11/xutil, + x11/keysym +import x11/x + +import ../../events + +import ./symkey_map + +export keysym + +var deleteMessage*: Atom + +type + NativeWindow* = object + display*: PDisplay + window*: Window + +template checkXlibResult*(call: untyped) = + let value = call + if value == 0: + raise newException(Exception, "Xlib error: " & astToStr(call) & " returned " & $value) + +proc createWindow*(title: string): NativeWindow = + checkXlibResult XInitThreads() + let display = XOpenDisplay(nil) + if display == nil: + quit "Failed to open display" + + let + screen = XDefaultScreen(display) + rootWindow = XRootWindow(display, screen) + foregroundColor = XBlackPixel(display, screen) + backgroundColor = XWhitePixel(display, screen) + + let window = XCreateSimpleWindow(display, rootWindow, -1, -1, 800, 600, 0, foregroundColor, backgroundColor) + checkXlibResult XSetStandardProperties(display, window, title, "window", 0, nil, 0, nil) + checkXlibResult XSelectInput(display, window, ButtonPressMask or KeyPressMask or ExposureMask) + checkXlibResult XMapWindow(display, window) + + deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false)) + checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1) + + return NativeWindow(display: display, window: window) + +proc trash*(window: NativeWindow) = + checkXlibResult window.display.XDestroyWindow(window.window) + discard window.display.XCloseDisplay() # always returns 0 + +proc size*(window: NativeWindow): (int, int) = + var attribs: XWindowAttributes + checkXlibResult XGetWindowAttributes(window.display, window.window, addr(attribs)) + return (int(attribs.width), int(attribs.height)) + +proc pendingEvents*(window: NativeWindow): seq[Event] = + var event: XEvent + while window.display.XPending() > 0: + discard window.display.XNextEvent(addr(event)) + case event.theType + of ClientMessage: + if cast[Atom](event.xclient.data.l[0]) == deleteMessage: + result.add(Event(eventType: Quit)) + of KeyPress: + let xkey: KeySym = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) + result.add(Event(eventType: KeyDown, key: KeyTypeMap.getOrDefault(xkey, UNKNOWN))) + of KeyRelease: + let xkey: KeySym = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) + result.add(Event(eventType: KeyUp, key: KeyTypeMap.getOrDefault(xkey, UNKNOWN))) + of ConfigureNotify: + result.add(Event(eventType: ResizedWindow)) + else: + discard diff -r af9183acb173 -r 4ed9cb098315 src/platform/windows/win32.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/platform/windows/win32.nim Thu Dec 22 00:06:40 2022 +0700 @@ -0,0 +1,58 @@ +import winim + +import ../../events + +type + NativeWindow* = object + hinstance*: HINSTANCE + hwnd*: HWND + +template checkWin32Result*(call: untyped) = + let value = call + if value != 0: + raise newException(Exception, "Win32 error: " & astToStr(call) & " returned " & $value) + +proc WindowHandler(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT {.stdcall.} = + case uMsg + of WM_DESTROY: + discard + else: + return DefWindowProc(hwnd, uMsg, wParam, lParam) + + +proc createWindow*(title: string): NativeWindow = + result.hInstance = HINSTANCE(GetModuleHandle(nil)) + var + windowClassName = T"EngineWindowClass" + windowName = T(title) + windowClass = WNDCLASS( + lpfnWndProc: WindowHandler, + hInstance: result.hInstance, + lpszClassName: windowClassName, + ) + RegisterClass(addr(windowClass)) + + result.hwnd = CreateWindowEx( + DWORD(0), + windowClassName, + windowName, + DWORD(WS_OVERLAPPEDWINDOW), + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + HMENU(0), + HINSTANCE(0), + result.hInstance, + nil + ) + + discard ShowWindow(result.hwnd, 0) + +proc trash*(window: NativeWindow) = + PostQuitMessage(0) + +proc size*(window: NativeWindow): (int, int) = + var rect: RECT + checkWin32Result GetWindowRect(window.hwnd, addr(rect)) + (int(rect.right - rect.left), int(rect.bottom - rect.top)) + +proc pendingEvents*(window: NativeWindow): seq[Event] = + result diff -r af9183acb173 -r 4ed9cb098315 src/vulkan.nim --- a/src/vulkan.nim Tue Dec 20 00:28:05 2022 +0700 +++ b/src/vulkan.nim Thu Dec 22 00:06:40 2022 +0700 @@ -4,8 +4,12 @@ ## ==== ## WARNING: This is a generated file. Do not edit ## Any edits will be overwritten by the generator. -import x11/xlib -import x11/x + +when defined(linux): + import x11/x + import x11/xlib +when defined(windows): + import winim var vkGetProc: proc(procName: cstring): pointer {.cdecl.} @@ -13,6 +17,7 @@ when defined(windows): {. emit: """#define VK_USE_PLATFORM_WIN32_KHR""" .} + # {.passl: gorge("pkg-config --libs vulkan").} const vkDLL = "vulkan-1.dll" elif defined(linux): {.passl: gorge("pkg-config --libs vulkan").} @@ -511,6 +516,7 @@ 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_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000 # 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 @@ -1191,20 +1197,26 @@ # Types +# stub types if we are on the wrong platform, so we don't need to "when" all platform functions +when not defined(linux): + type + Display* = ptr object + VisualID* = ptr object + Window* = ptr object +when not defined(windows): + type + HINSTANCE* = ptr object + HWND* = ptr object + HMONITOR* = ptr object + HANDLE* = ptr object + SECURITY_ATTRIBUTES* = ptr object + DWORD* = ptr object + LPCWSTR* = ptr object + type - # Display* = ptr object - VisualID* = ptr object - # Window* = ptr object RROutput* = ptr object wl_display* = ptr object wl_surface* = ptr object - HINSTANCE* = ptr object - HWND* = ptr object - HMONITOR* = ptr object - HANDLE* = ptr object - SECURITY_ATTRIBUTES* = ptr object - DWORD* = ptr object - LPCWSTR* = ptr object xcb_connection_t* = ptr object xcb_visualid_t* = ptr object xcb_window_t* = ptr object diff -r af9183acb173 -r 4ed9cb098315 src/vulkan_helpers.nim --- a/src/vulkan_helpers.nim Tue Dec 20 00:28:05 2022 +0700 +++ b/src/vulkan_helpers.nim Thu Dec 22 00:06:40 2022 +0700 @@ -5,6 +5,7 @@ import ./glslang/glslang import ./vulkan +import ./window when defined(release): const ENABLEVULKANVALIDATIONLAYERS* = false @@ -21,6 +22,8 @@ if value != VK_SUCCESS: raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value) +func addrOrNil[T](obj: var openArray[T]): ptr T = + if obj.len > 0: addr(obj[0]) else: nil 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 @@ -48,7 +51,7 @@ var extensionCount: uint32 checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), nil) var extensions = newSeq[VkExtensionProperties](extensionCount) - checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), addr(extensions[0])) + checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr(extensionCount), addrOrNil(extensions)) for extension in extensions: result.add(cleanString(extension.extensionName)) @@ -58,7 +61,7 @@ var extensionCount: uint32 checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr(extensionCount), nil) var extensions = newSeq[VkExtensionProperties](extensionCount) - checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr(extensionCount), addr(extensions[0])) + checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr(extensionCount), addrOrNil(extensions)) for extension in extensions: result.add(cleanString(extension.extensionName)) @@ -68,7 +71,7 @@ var n_layers: uint32 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) var layers = newSeq[VkLayerProperties](n_layers) - checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), addr(layers[0])) + checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), addrOrNil(layers)) for layer in layers: result.add(cleanString(layer.layerName)) @@ -78,35 +81,35 @@ var n_devices: uint32 checkVkResult vkEnumeratePhysicalDevices(instance, addr(n_devices), nil) result = newSeq[VkPhysicalDevice](n_devices) - checkVkResult vkEnumeratePhysicalDevices(instance, addr(n_devices), addr(result[0])) + checkVkResult vkEnumeratePhysicalDevices(instance, addr(n_devices), addrOrNil(result)) proc getQueueFamilies*(device: VkPhysicalDevice): seq[VkQueueFamilyProperties] = var n_queuefamilies: uint32 vkGetPhysicalDeviceQueueFamilyProperties(device, addr(n_queuefamilies), nil) result = newSeq[VkQueueFamilyProperties](n_queuefamilies) - vkGetPhysicalDeviceQueueFamilyProperties(device, addr(n_queuefamilies), addr(result[0])) + vkGetPhysicalDeviceQueueFamilyProperties(device, addr(n_queuefamilies), addrOrNil(result)) proc getDeviceSurfaceFormats*(device: VkPhysicalDevice, surface: VkSurfaceKHR): seq[VkSurfaceFormatKHR] = var n_formats: uint32 checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, addr(n_formats), nil); result = newSeq[VkSurfaceFormatKHR](n_formats) - checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, addr(n_formats), addr(result[0])) + checkVkResult vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, addr(n_formats), addrOrNil(result)) proc getDeviceSurfacePresentModes*(device: VkPhysicalDevice, surface: VkSurfaceKHR): seq[VkPresentModeKHR] = var n_modes: uint32 checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, addr(n_modes), nil); result = newSeq[VkPresentModeKHR](n_modes) - checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, addr(n_modes), addr(result[0])) + checkVkResult vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, addr(n_modes), addrOrNil(result)) proc getSwapChainImages*(device: VkDevice, swapChain: VkSwapchainKHR): seq[VkImage] = var n_images: uint32 checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), nil); result = newSeq[VkImage](n_images) - checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), addr(result[0])); + checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), addrOrNil(result)); proc getPresentMode*(modes: seq[VkPresentModeKHR]): VkPresentModeKHR = @@ -161,7 +164,7 @@ sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, pApplicationInfo: addr(appinfo), enabledLayerCount: usableLayers.len.uint32, - ppEnabledLayerNames: cast[ptr UncheckedArray[cstring]](addr(usableLayers[0])), + ppEnabledLayerNames: cast[ptr UncheckedArray[cstring]](addrOrNil(usableLayers)), enabledExtensionCount: requiredExtensions.len.uint32, ppEnabledExtensionNames: cast[ptr UncheckedArray[cstring]](addr(requiredExtensions)) ) @@ -196,7 +199,7 @@ var deviceCreateInfo = VkDeviceCreateInfo( sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, queueCreateInfoCount: uint32(queueCreateInfo.len), - pQueueCreateInfos: addr(queueCreateInfo[0]), + pQueueCreateInfos: addrOrNil(queueCreateInfo), pEnabledFeatures: addr(features), enabledExtensionCount: requiredExtensions.len.uint32, ppEnabledExtensionNames: cast[ptr UncheckedArray[cstring]](addr(requiredExtensions)) @@ -214,7 +217,7 @@ var createInfo = VkShaderModuleCreateInfo( sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, codeSize: uint(code.len * sizeof(uint32)), - pCode: addr(code[0]), + pCode: addrOrNil(code), ) var shaderModule: VkShaderModule checkVkResult vkCreateShaderModule(device, addr(createInfo), nil, addr(shaderModule)) @@ -234,3 +237,23 @@ ): VkBool32 {.cdecl.} = echo &"{messageSeverity}: {VkDebugUtilsMessageTypeFlagBitsEXT(messageTypes)}: {pCallbackData.pMessage}" return VK_FALSE + +proc getSurfaceCapabilities*(device: VkPhysicalDevice, surface: VkSurfaceKHR): VkSurfaceCapabilitiesKHR = + checkVkResult device.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, addr(result)) + +when defined(linux): + proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): 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)) +when defined(windows): + proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = + var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR( + sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + hinstance: window.hinstance, + hwnd: window.hwnd, + ) + checkVkResult vkCreateWin32SurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) diff -r af9183acb173 -r 4ed9cb098315 src/window.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/window.nim Thu Dec 22 00:06:40 2022 +0700 @@ -0,0 +1,6 @@ +when defined(linux): + import ./platform/linux/xlib + export xlib +elif defined(windows): + import ./platform/windows/win32 + export win32 diff -r af9183acb173 -r 4ed9cb098315 src/xlib_helpers.nim --- a/src/xlib_helpers.nim Tue Dec 20 00:28:05 2022 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -import - x11/xlib, - x11/xutil, - x11/x, - x11/keysym - -export keysym - -var deleteMessage*: Atom - -template checkXlibResult*(call: untyped) = - let value = call - if value == 0: - 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" - - let - screen = XDefaultScreen(display) - rootWindow = XRootWindow(display, screen) - foregroundColor = XBlackPixel(display, screen) - backgroundColor = XWhitePixel(display, screen) - - let window = XCreateSimpleWindow(display, rootWindow, -1, -1, 800, 600, 0, foregroundColor, backgroundColor) - checkXlibResult XSetStandardProperties(display, window, "Nim X11", "window", 0, nil, 0, nil) - checkXlibResult XSelectInput(display, window, ButtonPressMask or KeyPressMask or ExposureMask) - checkXlibResult XMapWindow(display, window) - - deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false)) - checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1) - - return (display, window) - -proc xlibFramebufferSize*(display: PDisplay, window: Window): (int, int) = - var attribs: XWindowAttributes - checkXlibResult XGetWindowAttributes(display, window, addr(attribs)) - return (int(attribs.width), int(attribs.height))