Mercurial > games > semicongine
changeset 608:1698c8ab9203
del: legacy
author | Sam <sam@basx.dev> |
---|---|
date | Thu, 27 Apr 2023 00:24:10 +0700 |
parents | 64eb53f81cf6 |
children | 23e41f5adbaf |
files | src/semicongine/legacy/buffer.nim src/semicongine/legacy/descriptor.nim src/semicongine/legacy/engine.nim src/semicongine/legacy/glsl.nim src/semicongine/legacy/glsl_helpers.nim src/semicongine/legacy/image.nim src/semicongine/legacy/shader.nim src/semicongine/legacy/thing.nim src/semicongine/legacy/vertex.nim src/semicongine/legacy/vulkan_helpers.nim |
diffstat | 10 files changed, 0 insertions(+), 2005 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/legacy/buffer.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -import std/typetraits - -import ./vulkan -import ./vulkan_helpers - -type - BufferType* = enum - None = 0 - TransferSrc = VK_BUFFER_USAGE_TRANSFER_SRC_BIT - TransferDst = VK_BUFFER_USAGE_TRANSFER_DST_BIT - UniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT - IndexBuffer = VK_BUFFER_USAGE_INDEX_BUFFER_BIT - VertexBuffer = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT - MemoryProperty* = enum - DeviceLocal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT - HostVisible = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT - HostCoherent = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT - MemoryProperties* = set[MemoryProperty] - Buffer* = object - device*: VkDevice - vkBuffer*: VkBuffer - size*: uint64 - memoryRequirements*: VkMemoryRequirements - memoryProperties*: MemoryProperties - memory*: VkDeviceMemory - bufferTypes*: set[BufferType] - data*: pointer - -proc trash*(buffer: var Buffer) = - if int64(buffer.vkBuffer) != 0 and int64(buffer.memory) != 0: - vkUnmapMemory(buffer.device, buffer.memory) - if int64(buffer.vkBuffer) != 0: - vkDestroyBuffer(buffer.device, buffer.vkBuffer, nil) - buffer.vkBuffer = VkBuffer(0) - if int64(buffer.memory) != 0: - vkFreeMemory(buffer.device, buffer.memory, nil) - buffer.memory = VkDeviceMemory(0) - -proc findMemoryType*(memoryRequirements: VkMemoryRequirements, - physicalDevice: VkPhysicalDevice, properties: MemoryProperties): uint32 = - var physicalProperties: VkPhysicalDeviceMemoryProperties - vkGetPhysicalDeviceMemoryProperties(physicalDevice, addr(physicalProperties)) - - for i in 0'u32 ..< physicalProperties.memoryTypeCount: - if bool(memoryRequirements.memoryTypeBits and (1'u32 shl i)): - if (uint32(physicalProperties.memoryTypes[i].propertyFlags) and cast[ - uint32](properties)) == cast[uint32](properties): - return i - -proc InitBuffer*( - device: VkDevice, - physicalDevice: VkPhysicalDevice, - size: uint64, - bufferTypes: set[BufferType], - properties: MemoryProperties, -): Buffer = - result = Buffer(device: device, size: size, bufferTypes: bufferTypes, memoryProperties: properties) - var usageFlags = 0 - for usage in bufferTypes: - usageFlags = ord(usageFlags) or ord(usage) - var bufferInfo = VkBufferCreateInfo( - sType: VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - size: VkDeviceSize(result.size), - usage: VkBufferUsageFlags(usageFlags), - sharingMode: VK_SHARING_MODE_EXCLUSIVE, - ) - checkVkResult vkCreateBuffer(result.device, addr(bufferInfo), nil, addr(result.vkBuffer)) - vkGetBufferMemoryRequirements(result.device, result.vkBuffer, addr(result.memoryRequirements)) - - var allocInfo = VkMemoryAllocateInfo( - sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - allocationSize: result.memoryRequirements.size, - memoryTypeIndex: result.memoryRequirements.findMemoryType(physicalDevice, properties) - ) - if result.size > 0: - checkVkResult result.device.vkAllocateMemory(addr(allocInfo), nil, addr(result.memory)) - checkVkResult result.device.vkBindBufferMemory(result.vkBuffer, result.memory, VkDeviceSize(0)) - checkVkResult vkMapMemory( - result.device, - result.memory, - offset = VkDeviceSize(0), - VkDeviceSize(result.size), - VkMemoryMapFlags(0), - addr(result.data) - ) - - -proc transferBuffer*(commandPool: VkCommandPool, queue: VkQueue, src, dst: Buffer, size: uint64) = - assert uint64(src.device) == uint64(dst.device) - assert TransferSrc in src.bufferTypes - assert TransferDst in dst.bufferTypes - var - allocInfo = VkCommandBufferAllocateInfo( - sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, - commandPool: commandPool, - commandBufferCount: 1, - ) - commandBuffer: VkCommandBuffer - checkVkResult vkAllocateCommandBuffers(src.device, addr(allocInfo), addr(commandBuffer)) - - var beginInfo = VkCommandBufferBeginInfo( - sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), - ) - checkVkResult vkBeginCommandBuffer(commandBuffer, addr(beginInfo)) - var copyRegion = VkBufferCopy(size: VkDeviceSize(size)) - vkCmdCopyBuffer(commandBuffer, src.vkBuffer, dst.vkBuffer, 1, addr(copyRegion)) - checkVkResult vkEndCommandBuffer(commandBuffer) - - var submitInfo = VkSubmitInfo( - sType: VK_STRUCTURE_TYPE_SUBMIT_INFO, - commandBufferCount: 1, - pCommandBuffers: addr(commandBuffer), - ) - - checkVkResult vkQueueSubmit(queue, 1, addr(submitInfo), VkFence(0)) - checkVkResult vkQueueWaitIdle(queue) - vkFreeCommandBuffers(src.device, commandPool, 1, addr(commandBuffer)) -
--- a/src/semicongine/legacy/descriptor.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -import std/strutils -import std/unicode -import std/strformat -import std/typetraits - -import ./vulkan -import ./vulkan_helpers -import ./math/vector -import ./math/matrix -import ./buffer -import ./glsl_helpers - -# TODO: check for alignment in uniform blocks -# -type - DescriptorType = SomeNumber|TVec|TMat - Descriptor*[T: DescriptorType] = object - value*: T - ViewProjectionTransform* = Descriptor[Mat44] - -proc createUniformDescriptorLayout*(device: VkDevice, - shaderStage: VkShaderStageFlags, binding: uint32): VkDescriptorSetLayout = - var - layoutbinding = VkDescriptorSetLayoutBinding( - binding: binding, - descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - descriptorCount: 1, - stageFlags: shaderStage, - pImmutableSamplers: nil, - ) - layoutInfo = VkDescriptorSetLayoutCreateInfo( - sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - bindingCount: 1, - pBindings: addr(layoutbinding) - ) - checkVkResult device.vkCreateDescriptorSetLayout(addr(layoutInfo), nil, addr(result)) - -proc createUniformBuffers*[nBuffers: static int, Uniforms](device: VkDevice, - physicalDevice: VkPhysicalDevice): array[nBuffers, Buffer] = - let size = sizeof(Uniforms) - for i in 0 ..< nBuffers: - var buffer = InitBuffer( - device, - physicalDevice, - uint64(size), - {UniformBuffer}, - {HostVisible, HostCoherent}, - ) - result[i] = buffer - -template getDescriptorType*(v: Descriptor): auto = get(genericParams(typeof(v)), 0) - -func generateGLSLUniformDeclarations*[Uniforms](binding: int = 0): string {.compileTime.} = - var stmtList: seq[string] - - when not (Uniforms is void): - let uniformTypeName = name(Uniforms).toUpper() - let uniformInstanceName = name(Uniforms).toLower() - stmtList.add(&"layout(binding = {binding}) uniform {uniformTypeName} {{") - for fieldname, value in Uniforms().fieldPairs: - when typeof(value) is Descriptor: - let glsltype = getGLSLType[getDescriptorType(value)]() - let n = fieldname - stmtList.add(&" {glsltype} {n};") - stmtList.add(&"}} {uniformInstanceName};") - - return stmtList.join("\n")
--- a/src/semicongine/legacy/engine.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,870 +0,0 @@ -import std/options -import std/os -import std/times -import std/typetraits -import std/strformat -import std/enumerate -import std/logging - - -import ./math/vector -import ./vulkan -import ./vulkan_helpers -import ./platform/window -import ./events -import ./shader -import ./vertex -import ./buffer -import ./thing -import ./descriptor -import ./mesh - -const MAX_FRAMES_IN_FLIGHT = 2 -const DEBUG_LOG = not defined(release) - -var logger = newConsoleLogger() -addHandler(logger) - - -const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32) -const ENGINE_NAME = "zamkongine" -const ENGINE_VERSION = "0.1" -const BUILD_VERSION = ENGINE_VERSION & '-' & gorge("git log -1 --format=format:'%H'") -echo "Engine: " & ENGINE_NAME & " " & BUILD_VERSION - -type - Device = object - device*: VkDevice - physicalDevice*: PhysicalDevice - graphicsQueueFamily*: uint32 - presentationQueueFamily*: uint32 - graphicsQueue*: VkQueue - presentationQueue*: VkQueue - commandPool*: VkCommandPool - commandBuffers*: array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer] - Swapchain = object - swapchain: VkSwapchainKHR - images: seq[VkImage] - imageviews: seq[VkImageView] - RenderPipeline*[VertexType, Uniforms] = object - device*: VkDevice - shaders*: seq[ShaderProgram[VertexType, Uniforms]] - layout*: VkPipelineLayout - pipeline*: VkPipeline - vertexBuffers*: seq[(seq[Buffer], bool, Buffer, uint32, VkIndexType)] - descriptorSetLayout*: VkDescriptorSetLayout - uniformBuffers*: array[MAX_FRAMES_IN_FLIGHT, Buffer] - descriptorPool*: VkDescriptorPool - descriptors: array[MAX_FRAMES_IN_FLIGHT, VkDescriptorSet] - clearColor*: Vec4 - QueueFamily = object - properties*: VkQueueFamilyProperties - hasSurfaceSupport*: bool - PhysicalDevice = object - device*: VkPhysicalDevice - extensions*: seq[string] - properties*: VkPhysicalDeviceProperties - features*: VkPhysicalDeviceFeatures - queueFamilies*: seq[QueueFamily] - formats: seq[VkSurfaceFormatKHR] - presentModes: seq[VkPresentModeKHR] - Vulkan* = object - debugMessenger*: VkDebugUtilsMessengerEXT - instance*: VkInstance - deviceList*: seq[PhysicalDevice] - device*: Device - surface*: VkSurfaceKHR - surfaceFormat*: VkSurfaceFormatKHR - frameSize*: TVec2[uint32] - swapchain*: Swapchain - framebuffers*: seq[VkFramebuffer] - renderPass*: VkRenderPass - imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] - renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] - inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] - Input* = object - keysDown*: set[Key] - keysPressed*: set[Key] - keysReleased*: set[Key] - mouseDown*: set[MouseButton] - mousePressed*: set[MouseButton] - mouseReleased*: set[MouseButton] - mousePos*: Vec2 - Engine* = object - vulkan*: Vulkan - window*: NativeWindow - currentscenedata*: Thing - input*: Input - maxFPS*: uint - - -method update*(thing: Thing, engine: Engine, t, dt: float32) {.base.} = discard -method update*(part: Part, engine: Engine, t, dt: float32) {.base.} = discard - -method update*[T, U](mesh: Mesh[T, U], engine: Engine, t, dt: float32) = - let transform = @[mesh.thing.getModelTransform().transposed()] - for name, value in mesh.vertexData.fieldPairs: - when value is ModelTransformAttribute: - value.data = transform - engine.vulkan.device.updateVertexData(value) - -proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[ - PhysicalDevice] = - for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): - var device = PhysicalDevice(device: vulkanPhysicalDevice, extensions: vulkan.getDeviceExtensions(vulkanPhysicalDevice)) - vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties)) - vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features)) - 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)): - 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[PhysicalDevice]): seq[(PhysicalDevice, uint32, uint32)] = - for device in devices: - 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) - 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)) - - for (device, graphicsQueueFamily, presentationQueueFamily) in result: - debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})") - - -proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice, - surface: VkSurfaceKHR): TVec2[uint32] = - let capabilities = device.getSurfaceCapabilities(surface) - if capabilities.currentExtent.width != high(uint32): - return TVec2[uint32]([capabilities.currentExtent.width, - capabilities.currentExtent.height]) - else: - let (width, height) = window.size() - return TVec2[uint32]([ - min(max(uint32(width), capabilities.minImageExtent.width), - capabilities.maxImageExtent.width), - min(max(uint32(height), capabilities.minImageExtent.height), - capabilities.maxImageExtent.height), - ]) - -when DEBUG_LOG: - proc setupDebugLog(instance: VkInstance): VkDebugUtilsMessengerEXT = - 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 instance.vkCreateDebugUtilsMessengerEXT(addr(createInfo), nil, addr(result)) - -proc setupVulkanDeviceAndQueues(instance: VkInstance, surface: VkSurfaceKHR): Device = - let usableDevices = instance.getAllPhysicalDevices(surface).filterForDevice() - if len(usableDevices) == 0: - raise newException(Exception, "No suitable graphics device found") - result.physicalDevice = usableDevices[0][0] - result.graphicsQueueFamily = usableDevices[0][1] - result.presentationQueueFamily = usableDevices[0][2] - - debug(&"Chose device {cleanString(result.physicalDevice.properties.deviceName)}") - - (result.device, result.graphicsQueue, result.presentationQueue) = getVulcanDevice( - result.physicalDevice.device, - result.physicalDevice.features, - result.graphicsQueueFamily, - result.presentationQueueFamily, - ) - -proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice, surface: VkSurfaceKHR, dimension: TVec2[uint32], surfaceFormat: VkSurfaceFormatKHR): Swapchain = - - let capabilities = physicalDevice.device.getSurfaceCapabilities(surface) - var selectedPresentationMode = getPresentMode(physicalDevice.presentModes) - var imageCount = capabilities.minImageCount + 1 - if capabilities.maxImageCount > 0: - imageCount = min(capabilities.maxImageCount, imageCount) - # TODO: something not working on window..., likely the extent - var extent = VkExtent2D( - width: if dimension[0] > 0: dimension[0] else: 1, - height: if dimension[1] > 0: dimension[1] else: 1, - ) - # setup swapchain - var swapchainCreateInfo = VkSwapchainCreateInfoKHR( - sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - surface: surface, - minImageCount: imageCount, - imageFormat: surfaceFormat.format, - imageColorSpace: surfaceFormat.colorSpace, - imageExtent: extent, - 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: capabilities.currentTransform, - compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - presentMode: selectedPresentationMode, - clipped: VK_TRUE, - oldSwapchain: VkSwapchainKHR(0), - ) - checkVkResult device.vkCreateSwapchainKHR(addr(swapchainCreateInfo), nil, - addr(result.swapchain)) - result.images = device.getSwapChainImages(result.swapchain) - - # setup swapchian image views - - result.imageviews = newSeq[VkImageView](result.images.len) - for i, image in enumerate(result.images): - var imageViewCreateInfo = VkImageViewCreateInfo( - sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - image: image, - viewType: VK_IMAGE_VIEW_TYPE_2D, - format: surfaceFormat.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 device.vkCreateImageView(addr(imageViewCreateInfo), nil, addr(result.imageviews[i])) - -proc setupRenderPass(device: VkDevice, format: VkFormat): VkRenderPass = - var - colorAttachment = VkAttachmentDescription( - format: 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 device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result)) - -proc initRenderPipeline[VertexType, Uniforms](device: VkDevice, frameSize: TVec2[uint32], renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, Uniforms] = - # load shaders - result.device = device - result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_VERTEX_BIT, vertexShader)) - result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader)) - - 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]), - ) - vertexbindings = generateInputVertexBinding[VertexType]() - attributebindings = generateInputAttributeBinding[VertexType]() - - # define input data format - vertexInputInfo = VkPipelineVertexInputStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - vertexBindingDescriptionCount: uint32(vertexbindings.len), - pVertexBindingDescriptions: addr(vertexbindings[0]), - vertexAttributeDescriptionCount: uint32(attributebindings.len), - pVertexAttributeDescriptions: addr(attributebindings[0]), - ) - inputAssembly = VkPipelineInputAssemblyStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - primitiveRestartEnable: VK_FALSE, - ) - - # setup viewport - var viewportState = VkPipelineViewportStateCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - viewportCount: 1, - scissorCount: 1, - ) - - # 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], - ) - - result.descriptorSetLayout = device.createUniformDescriptorLayout( - VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), 0) - var - # "globals" that go into the shader, uniforms etc. - pipelineLayoutInfo = VkPipelineLayoutCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - setLayoutCount: 1, - pSetLayouts: addr(result.descriptorSetLayout), - pushConstantRangeCount: 0, - pPushConstantRanges: nil, - ) - checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, addr(result.layout)) - - var stages: seq[VkPipelineShaderStageCreateInfo] - for shader in result.shaders: - stages.add(shader.shader) - var pipelineInfo = VkGraphicsPipelineCreateInfo( - sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - stageCount: uint32(stages.len), - pStages: addr(stages[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.layout, - renderPass: renderPass, - subpass: 0, - basePipelineHandle: VkPipeline(0), - basePipelineIndex: -1, - ) - checkVkResult vkCreateGraphicsPipelines( - device, - VkPipelineCache(0), - 1, - addr(pipelineInfo), - nil, - addr(result.pipeline) - ) - -proc setupFramebuffers(device: VkDevice, swapchain: var Swapchain, - renderPass: VkRenderPass, dimension: TVec2[uint32]): seq[VkFramebuffer] = - result = newSeq[VkFramebuffer](swapchain.images.len) - for i, imageview in enumerate(swapchain.imageviews): - var framebufferInfo = VkFramebufferCreateInfo( - sType: VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - renderPass: renderPass, - attachmentCount: 1, - pAttachments: addr(swapchain.imageviews[i]), - width: dimension[0], - height: dimension[1], - layers: 1, - ) - 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]) = - if vulkan.frameSize.x == 0 or vulkan.frameSize.y == 0: - return (vulkan.swapchain, vulkan.framebuffers) - debug(&"Recreate swapchain with dimension {vulkan.frameSize}") - checkVkResult vulkan.device.device.vkDeviceWaitIdle() - - vulkan.device.device.trash(vulkan.swapchain, vulkan.framebuffers) - - result[0] = vulkan.device.device.setupSwapChain( - vulkan.device.physicalDevice, - vulkan.surface, - vulkan.frameSize, - vulkan.surfaceFormat - ) - result[1] = vulkan.device.device.setupFramebuffers( - result[0], - vulkan.renderPass, - vulkan.frameSize - ) - - -proc setupCommandBuffers(device: VkDevice, graphicsQueueFamily: uint32): ( - VkCommandPool, array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer]) = - # 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: graphicsQueueFamily, - ) - checkVkResult device.vkCreateCommandPool(addr(poolInfo), nil, addr(result[0])) - - var allocInfo = VkCommandBufferAllocateInfo( - sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - commandPool: result[0], - level: VK_COMMAND_BUFFER_LEVEL_PRIMARY, - commandBufferCount: result[1].len.uint32, - ) - checkVkResult device.vkAllocateCommandBuffers(addr(allocInfo), addr(result[1][0])) - -proc setupSyncPrimitives(device: VkDevice): ( - array[MAX_FRAMES_IN_FLIGHT, VkSemaphore], - array[MAX_FRAMES_IN_FLIGHT, VkSemaphore], - array[MAX_FRAMES_IN_FLIGHT, VkFence], -) = - 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 device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr( - result[0][i])) - checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr( - result[1][i])) - checkVkResult device.vkCreateFence(addr(fenceInfo), nil, addr(result[2][i])) - -proc igniteEngine*(windowTitle: string): Engine = - - result.window = createWindow(windowTitle) - let mousepos = result.window.getMousePosition() - if mousepos.isSome(): - result.input.mousePos = mousePos.get() - - - # create vulkan instance - result.vulkan.instance = createVulkanInstance(VULKAN_VERSION) - - # setup vulkan functions - loadVulkan(result.vulkan.instance) - - when DEBUG_LOG: - result.vulkan.debugMessenger = result.vulkan.instance.setupDebugLog() - result.vulkan.surface = result.vulkan.instance.createVulkanSurface(result.window) - result.vulkan.device = result.vulkan.instance.setupVulkanDeviceAndQueues(result.vulkan.surface) - - # get basic frame information - result.vulkan.surfaceFormat = result.vulkan.device.physicalDevice.formats.getSuitableSurfaceFormat() - result.vulkan.frameSize = result.window.getFrameDimension(result.vulkan.device.physicalDevice.device, result.vulkan.surface) - - # setup swapchain and render pipeline - result.vulkan.swapchain = result.vulkan.device.device.setupSwapChain( - result.vulkan.device.physicalDevice, - result.vulkan.surface, - result.vulkan.frameSize, - result.vulkan.surfaceFormat - ) - result.vulkan.renderPass = result.vulkan.device.device.setupRenderPass( - result.vulkan.surfaceFormat.format) - result.vulkan.framebuffers = result.vulkan.device.device.setupFramebuffers( - result.vulkan.swapchain, - result.vulkan.renderPass, - result.vulkan.frameSize - ) - ( - result.vulkan.device.commandPool, - result.vulkan.device.commandBuffers, - ) = result.vulkan.device.device.setupCommandBuffers( - result.vulkan.device.graphicsQueueFamily) - - ( - result.vulkan.imageAvailableSemaphores, - result.vulkan.renderFinishedSemaphores, - result.vulkan.inFlightFences, - ) = result.vulkan.device.device.setupSyncPrimitives() - - -proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](engine: var Engine, scenedata: Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] = - engine.currentscenedata = scenedata - result = initRenderPipeline[VertexType, UniformType]( - engine.vulkan.device.device, - engine.vulkan.frameSize, - engine.vulkan.renderPass, - vertexShader, - fragmentShader, - ) - - for mesh in allPartsOfType[Mesh[VertexType, IndexType]]( - engine.currentscenedata): - result.vertexBuffers.add createIndexedVertexBuffers(mesh, - result.device, engine.vulkan.device.physicalDevice.device, - engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue) - - # uniform buffers - when not (UniformType is void): - result.uniformBuffers = createUniformBuffers[MAX_FRAMES_IN_FLIGHT, UniformType]( - result.device, - engine.vulkan.device.physicalDevice.device - ) - - var - poolSize = VkDescriptorPoolSize( - thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - descriptorCount: uint32(MAX_FRAMES_IN_FLIGHT), - ) - poolInfo = VkDescriptorPoolCreateInfo( - sType: VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - poolSizeCount: 1, - pPoolSizes: addr(poolSize), - maxSets: uint32(MAX_FRAMES_IN_FLIGHT), - ) - checkVkResult vkCreateDescriptorPool(result.device, addr(poolInfo), nil, addr(result.descriptorPool)) - - var layouts: array[MAX_FRAMES_IN_FLIGHT, VkDescriptorSetLayout] - for i in 0 ..< MAX_FRAMES_IN_FLIGHT: - layouts[i] = result.descriptorSetLayout - var allocInfo = VkDescriptorSetAllocateInfo( - sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - descriptorPool: result.descriptorPool, - descriptorSetCount: uint32(MAX_FRAMES_IN_FLIGHT), - pSetLayouts: addr(layouts[0]), - ) - - checkVkResult vkAllocateDescriptorSets(result.device, addr(allocInfo), addr(result.descriptors[0])) - - when not (UniformType is void): - var bufferInfos: array[MAX_FRAMES_IN_FLIGHT, array[1, VkDescriptorBufferInfo]] # because we use only one Uniform atm - for i in 0 ..< MAX_FRAMES_IN_FLIGHT: - bufferInfos[i][0] = VkDescriptorBufferInfo( - buffer: result.uniformBuffers[i].vkBuffer, - offset: VkDeviceSize(0), - range: VkDeviceSize(sizeof(UniformType)), - ) - var descriptorWrite = VkWriteDescriptorSet( - sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - dstSet: result.descriptors[i], - dstBinding: 0, - dstArrayElement: 0, - descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - descriptorCount: 1, - pBufferInfo: addr(bufferInfos[i][0]), - ) - vkUpdateDescriptorSets(result.device, 1, addr(descriptorWrite), 0, nil) - -proc updateBufferData*[T](device: Device, buffer: Buffer, data: var T) = - when stripGenericParams(T) is seq: # seq needs special treatment for automated data uploading - assert data.len > 0 - let size = data.len * sizeof(get(genericParams(typeof(data)), 0)) - let dataptr = addr(data[0]) - else: - let size = sizeof(data) - let dataptr = addr(data) - if not (HostVisible in buffer.memoryProperties): - if not (TransferDst in buffer.bufferTypes): - raise newException(Exception, "Buffer cannot be updated") - var stagingBuffer = device.device.InitBuffer(device.physicalDevice.device, - uint64(size), {TransferSrc}, {HostVisible, HostCoherent}) - copyMem(stagingBuffer.data, dataptr, size) - transferBuffer(device.commandPool, device.graphicsQueue, stagingBuffer, - buffer, uint64(size)) - stagingBuffer.trash() - else: - copyMem(buffer.data, dataptr, size) - -proc updateVertexData*[T: VertexAttribute](device: Device, - vertexAttribute: var T) = - device.updateBufferData(vertexAttribute.buffer, vertexAttribute.data) - -proc updateUniformData*[VertexType, Uniforms](device: Device, - pipeline: RenderPipeline[VertexType, Uniforms], data: var Uniforms) = - for buffer in pipeline.uniformBuffers: - device.updateBufferData(buffer, data) - - -proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer, pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) = - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil) - - for (vertexBufferSet, indexed, indexBuffer, count, indexType) in pipeline.vertexBuffers: - var - vertexBuffers: seq[VkBuffer] - offsets: seq[VkDeviceSize] - for buffer in vertexBufferSet: - vertexBuffers.add buffer.vkBuffer - offsets.add VkDeviceSize(0) - - vkCmdBindVertexBuffers(commandBuffer, firstBinding = 0'u32, bindingCount = uint32(vertexBuffers.len), pBuffers = addr(vertexBuffers[ 0]), pOffsets = addr(offsets[0])) - if indexed: - vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType) - vkCmdDrawIndexed(commandBuffer, count, 1, 0, 0, 0) - else: - vkCmdDraw(commandBuffer, vertexCount = count, instanceCount = 1, firstVertex = 0, firstInstance = 0) - -proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline, - commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, - frameSize: TVec2[uint32], currentFrame: int) = - var - beginInfo = VkCommandBufferBeginInfo( - sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - pInheritanceInfo: nil, - ) - clearColor = VkClearValue(color: VkClearColorValue(float32: pipeline.clearColor)) - renderPassInfo = VkRenderPassBeginInfo( - sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - renderPass: renderPass, - framebuffer: framebuffer, - renderArea: VkRect2D( - offset: VkOffset2D(x: 0, y: 0), - extent: VkExtent2D(width: frameSize.x, height: frameSize.y), - ), - clearValueCount: 1, - pClearValues: addr(clearColor), - ) - viewport = VkViewport( - x: 0.0, - y: 0.0, - width: (float)frameSize.x, - height: (float)frameSize.y, - minDepth: 0.0, - maxDepth: 1.0, - ) - scissor = VkRect2D( - offset: VkOffset2D(x: 0, y: 0), - extent: VkExtent2D(width: frameSize.x, height: frameSize.y) - ) - checkVkResult vkBeginCommandBuffer(commandBuffer, addr(beginInfo)) - block: - vkCmdBeginRenderPass(commandBuffer, addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE) - vkCmdSetViewport(commandBuffer, firstViewport = 0, viewportCount = 1, addr(viewport)) - vkCmdSetScissor(commandBuffer, firstScissor = 0, scissorCount = 1, addr(scissor)) - runPipeline(commandBuffer, pipeline, currentFrame) - vkCmdEndRenderPass(commandBuffer) - checkVkResult vkEndCommandBuffer(commandBuffer) - -proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, pipeline: var RenderPipeline) = - - checkVkResult vkWaitForFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) - - var bufferImageIndex: uint32 - let nextImageResult = vkAcquireNextImageKHR( - vulkan.device.device, - vulkan.swapchain.swapchain, - high(uint64), - vulkan.imageAvailableSemaphores[currentFrame], - VkFence(0), - addr(bufferImageIndex) - ) - if nextImageResult == VK_ERROR_OUT_OF_DATE_KHR: - vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) - (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() - elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): - raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) - checkVkResult vkResetFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame])) - - checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) - vulkan.renderPass.recordCommandBuffer( - pipeline, - vulkan.device.commandBuffers[currentFrame], - vulkan.framebuffers[bufferImageIndex], vulkan.frameSize, - currentFrame - ) - 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.device.commandBuffers[currentFrame]), - signalSemaphoreCount: 1, - pSignalSemaphores: addr(signalSemaphores[0]), - ) - checkVkResult vkQueueSubmit(vulkan.device.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.swapchain), - pImageIndices: addr(bufferImageIndex), - pResults: nil, - ) - let presentResult = vkQueuePresentKHR(vulkan.device.presentationQueue, addr(presentInfo)) - if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR or resized: - vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) - (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() - - -func frametime(engine: Engine): auto = - if engine.maxFPS == 0: 0'f - else: 1'f / float32(engine.maxFPS) - -proc run*(engine: var Engine, pipeline: var RenderPipeline, globalUpdate: proc( - engine: var Engine, t, dt: float32)) = - var - currentFrame = 0 - resized = false - lastUpdate = cpuTime() - lastframe = 0'f - - while true: - # process input - engine.input.keysPressed = {} - engine.input.keysReleased = {} - engine.input.mousePressed = {} - engine.input.mouseReleased = {} - var killed = false - for event in engine.window.pendingEvents(): - case event.eventType: - of Quit: - killed = true - of ResizedWindow: - resized = true - of KeyPressed: - engine.input.keysPressed.incl event.key - engine.input.keysDown.incl event.key - of KeyReleased: - engine.input.keysReleased.incl event.key - engine.input.keysDown.excl event.key - of MousePressed: - engine.input.mousePressed.incl event.button - engine.input.mouseDown.incl event.button - of MouseReleased: - engine.input.mouseReleased.incl event.button - engine.input.mouseDown.excl event.button - of MouseMoved: - engine.input.mousePos = Vec2([float32(event.x), float32(event.y)]) - if killed: # at least on windows we should return immediately as swapchain recreation will fail after kill - break - - # game logic update - let - now = cpuTime() - dt = now - lastUpdate - lastUpdate = now - engine.globalUpdate(now, dt) - for thing in allThings(engine.currentscenedata): - for part in thing.parts: - update(part, engine, now, dt) - update(thing, engine, now, dt) - - # submit frame for drawing - if engine.maxFPS == 0 or (now - lastframe >= engine.frametime): # framerate limit - engine.window.drawFrame(engine.vulkan, currentFrame, resized, pipeline) - lastframe = now - currentFrame = (currentFrame + 1) mod MAX_FRAMES_IN_FLIGHT - resized = false - - checkVkResult vkDeviceWaitIdle(engine.vulkan.device.device) - -proc trash*(pipeline: var RenderPipeline) = - vkDestroyDescriptorPool(pipeline.device, pipeline.descriptorPool, nil) - vkDestroyDescriptorSetLayout(pipeline.device, pipeline.descriptorSetLayout, nil) - vkDestroyPipeline(pipeline.device, pipeline.pipeline, nil) - vkDestroyPipelineLayout(pipeline.device, pipeline.layout, nil) - for shader in pipeline.shaders: - vkDestroyShaderModule(pipeline.device, shader.shader.module, nil) - - for (bufferset, indexed, indexbuffer, cnt, t) in - pipeline.vertexBuffers.mitems: - if indexed: - indexbuffer.trash() - for buffer in bufferset.mitems: - buffer.trash() - for buffer in pipeline.uniformBuffers.mitems: - buffer.trash() - -proc trash*(engine: var Engine) = - checkVkResult vkDeviceWaitIdle(engine.vulkan.device.device) - engine.vulkan.device.device.trash(engine.vulkan.swapchain, - engine.vulkan.framebuffers) - - 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.vkDestroyRenderPass(engine.vulkan.renderPass, nil) - engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.device.commandPool, nil) - - engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) - engine.vulkan.device.device.vkDestroyDevice(nil) - when DEBUG_LOG: - engine.vulkan.instance.vkDestroyDebugUtilsMessengerEXT(engine.vulkan.debugMessenger, nil) - engine.window.trash() - # needs to happen after window is trashed as the driver might have a hook registered for the window destruction - engine.vulkan.instance.vkDestroyInstance(nil)
--- a/src/semicongine/legacy/glsl.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -import std/typetraits -import std/strformat -import ../math/vector -import ../math/matrix - - -func getGLSLType*[T](t: T): string {.compileTime.} = - # todo: likely not correct as we would need to enable some - # extensions somewhere (Vulkan/GLSL compiler?) to have - # everything work as intended. Or maybe the GPU driver does - # some automagic conversion stuf.. - when T is uint8: "uint" - elif T is int8: "int" - elif T is uint16: "uint" - elif T is int16: "int" - elif T is uint32: "uint" - elif T is int32: "int" - elif T is uint64: "uint" - elif T is int64: "int" - elif T is float32: "float" - elif T is float64: "double" - - elif T is TVec2[uint8]: "uvec2" - elif T is TVec2[int8]: "ivec2" - elif T is TVec2[uint16]: "uvec2" - elif T is TVec2[int16]: "ivec2" - elif T is TVec2[uint32]: "uvec2" - elif T is TVec2[int32]: "ivec2" - elif T is TVec2[uint64]: "uvec2" - elif T is TVec2[int64]: "ivec2" - elif T is TVec2[float32]: "vec2" - elif T is TVec2[float64]: "dvec2" - - elif T is TVec3[uint8]: "uvec3" - elif T is TVec3[int8]: "ivec3" - elif T is TVec3[uint16]: "uvec3" - elif T is TVec3[int16]: "ivec3" - elif T is TVec3[uint32]: "uvec3" - elif T is TVec3[int32]: "ivec3" - elif T is TVec3[uint64]: "uvec3" - elif T is TVec3[int64]: "ivec3" - elif T is TVec3[float32]: "vec3" - elif T is TVec3[float64]: "dvec3" - - elif T is TVec4[uint8]: "uvec4" - elif T is TVec4[int8]: "ivec4" - elif T is TVec4[uint16]: "uvec4" - elif T is TVec4[int16]: "ivec4" - elif T is TVec4[uint32]: "uvec4" - elif T is TVec4[int32]: "ivec4" - elif T is TVec4[uint64]: "uvec4" - elif T is TVec4[int64]: "ivec4" - elif T is TVec4[float32]: "vec4" - elif T is TVec4[float64]: "dvec4" - - elif T is TMat22[float32]: "mat2" - elif T is TMat23[float32]: "mat32" - elif T is TMat32[float32]: "mat23" - elif T is TMat33[float32]: "mat3" - elif T is TMat34[float32]: "mat43" - elif T is TMat43[float32]: "mat34" - elif T is TMat44[float32]: "mat4" - - elif T is TMat22[float64]: "dmat2" - elif T is TMat23[float64]: "dmat32" - elif T is TMat32[float64]: "dmat23" - elif T is TMat33[float64]: "dmat3" - elif T is TMat34[float64]: "dmat43" - elif T is TMat43[float64]: "dmat34" - elif T is TMat44[float64]: "dmat4" - - -# return the number of elements into which larger types are divided -func compositeAttributesNumber*[T](value: T): int = - when T is TMat33[float32]: - 3 - elif T is TMat44[float32]: - 4 - else: - 1 - - -# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html -func nLocationSlots*[T](value: T): uint32 = - when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]): - return 2 - elif T is SomeNumber or T is TVec: - return 1 - else: - raise newException(Exception, "Unsupported vertex attribute type") - - -# return the type into which larger types are divided -func compositeAttribute*[T](value: T): auto = - when T is TMat33[float32]: - Vec3() - elif T is TMat44[float32]: - Vec4() - else: - value - -func glslInput*[T](): seq[string] {.compileTime.} = - when not (T is void): - var i = 0'u32 - for fieldname, value in default(T).fieldPairs: - let glsltype = getGLSLType(value) - let thename = fieldname - result.add &"layout(location = {i}) in {glsltype} {thename};" - for j in 0 ..< compositeAttributesNumber(value): - i += nLocationSlots(compositeAttribute(value)) - -func glslUniforms*[T](): seq[string] {.compileTime.} = - # currently only a single uniform block supported, therefore binding = 0 - when not (T is void): - let uniformName = name(T) - result.add(&"layout(binding = 0) uniform T{uniformName} {{") - for fieldname, value in default(T).fieldPairs: - let glsltype = getGLSLType(value) - let thename = fieldname - result.add(&" {glsltype} {thename};") - result.add(&"}} {uniformName};") - -func glslOutput*[T](): seq[string] {.compileTime.} = - when not (T is void): - var i = 0'u32 - for fieldname, value in default(T).fieldPairs: - let glsltype = getGLSLType(value) - let thename = fieldname - result.add &"layout(location = {i}) out {glsltype} {thename};" - i += 1 - else: - result
--- a/src/semicongine/legacy/glsl_helpers.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -import ./math/vector -import ./math/matrix - -func getGLSLType*[T](): string = - # todo: likely not correct as we would need to enable some - # extensions somewhere (Vulkan/GLSL compiler?) to have - # everything work as intended. Or maybe the GPU driver does - # some automagic conversion stuf.. - when T is uint8: "uint" - elif T is int8: "int" - elif T is uint16: "uint" - elif T is int16: "int" - elif T is uint32: "uint" - elif T is int32: "int" - elif T is uint64: "uint" - elif T is int64: "int" - elif T is float32: "float" - elif T is float64: "double" - - elif T is TVec2[uint8]: "uvec2" - elif T is TVec2[int8]: "ivec2" - elif T is TVec2[uint16]: "uvec2" - elif T is TVec2[int16]: "ivec2" - elif T is TVec2[uint32]: "uvec2" - elif T is TVec2[int32]: "ivec2" - elif T is TVec2[uint64]: "uvec2" - elif T is TVec2[int64]: "ivec2" - elif T is TVec2[float32]: "vec2" - elif T is TVec2[float64]: "dvec2" - - elif T is TVec3[uint8]: "uvec3" - elif T is TVec3[int8]: "ivec3" - elif T is TVec3[uint16]: "uvec3" - elif T is TVec3[int16]: "ivec3" - elif T is TVec3[uint32]: "uvec3" - elif T is TVec3[int32]: "ivec3" - elif T is TVec3[uint64]: "uvec3" - elif T is TVec3[int64]: "ivec3" - elif T is TVec3[float32]: "vec3" - elif T is TVec3[float64]: "dvec3" - - elif T is TVec4[uint8]: "uvec4" - elif T is TVec4[int8]: "ivec4" - elif T is TVec4[uint16]: "uvec4" - elif T is TVec4[int16]: "ivec4" - elif T is TVec4[uint32]: "uvec4" - elif T is TVec4[int32]: "ivec4" - elif T is TVec4[uint64]: "uvec4" - elif T is TVec4[int64]: "ivec4" - elif T is TVec4[float32]: "vec4" - elif T is TVec4[float64]: "dvec4" - - elif T is TMat22[float32]: "mat2" - elif T is TMat23[float32]: "mat32" - elif T is TMat32[float32]: "mat23" - elif T is TMat33[float32]: "mat3" - elif T is TMat34[float32]: "mat43" - elif T is TMat43[float32]: "mat34" - elif T is TMat44[float32]: "mat4" - - elif T is TMat22[float64]: "dmat2" - elif T is TMat23[float64]: "dmat32" - elif T is TMat32[float64]: "dmat23" - elif T is TMat33[float64]: "dmat3" - elif T is TMat34[float64]: "dmat43" - elif T is TMat43[float64]: "dmat34" - elif T is TMat44[float64]: "dmat4"
--- a/src/semicongine/legacy/image.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -import ./buffer -import ./math/vector -import ./vulkan -import ./vulkan_helpers - -type - ImageUsage* = enum - TransferDst = VK_IMAGE_USAGE_TRANSFER_DST_BIT - SampledBit = VK_IMAGE_USAGE_SAMPLED_BIT - ImageUsages = set[ImageUsage] - Image = object - buffer: Buffer - image: VkImage - memory: VkDeviceMemory - -proc InitImage(data: var seq[byte], size: TVec2[uint32], format: VkFormat, - tiling: VkImageTiling, usage: ImageUsages, properties: MemoryProperties, - device: VkDevice, - physicalDevice: VkPhysicalDevice): Image = - result.buffer = InitBuffer(device, physicalDevice, uint64(data.len), { - TransferSrc}, {HostVisible, HostCoherent}) - copyMem(result.buffer.data, addr(data[0]), data.len) - - var imageInfo = VkImageCreateInfo( - sType: VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - imageType: VK_IMAGE_TYPE_2D, - extent: VkExtent3D(width: size.x, height: size.y, depth: 1), - mipLevels: 1, - arrayLayers: 1, - format: format, - tiling: tiling, - initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, - usage: cast[VkImageUsageFlags](usage), - sharingMode: VK_SHARING_MODE_EXCLUSIVE, - samples: VK_SAMPLE_COUNT_1_BIT, - ) - checkVkResult vkCreateImage(device, addr(imageInfo), nil, addr(result.image)) - - var memRequirements: VkMemoryRequirements - vkGetImageMemoryRequirements(device, result.image, addr(memRequirements)) - - var allocInfo = VkMemoryAllocateInfo( - sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - allocationSize: memRequirements.size, - memoryTypeIndex: memRequirements.findMemoryType(physicalDevice, properties) - ) - - checkVkResult vkAllocateMemory(device, addr(allocInfo), nil, addr(result.memory)) - checkVkResult vkBindImageMemory(device, result.image, result.memory, - VkDeviceSize(0))
--- a/src/semicongine/legacy/shader.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -import std/os -import std/typetraits -import std/hashes -import std/strformat -import std/strutils -import std/tables -import std/compilesettings - -import ./vulkan_helpers -import ./glsl_helpers -import ./vulkan -import ./vertex -import ./descriptor -import ./math/vector - -type - AllowedUniformType = SomeNumber|TVec - UniformSlot *[T: AllowedUniformType] = object - ShaderProgram*[VertexType, Uniforms] = object - entryPoint*: string - programType*: VkShaderStageFlagBits - shader*: VkPipelineShaderStageCreateInfo - uniforms*: Uniforms - -proc staticExecChecked(command: string, input = ""): string {.compileTime.} = - let (output, exitcode) = gorgeEx( - command = command, - input = input) - if exitcode != 0: - raise newException(Exception, &"Running '{command}' produced exit code: {exitcode}" & output) - return output - - -func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} = - case stage - of VK_SHADER_STAGE_VERTEX_BIT: "vert" - of VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: "tesc" - of VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: "tese" - of VK_SHADER_STAGE_GEOMETRY_BIT: "geom" - of VK_SHADER_STAGE_FRAGMENT_BIT: "frag" - of VK_SHADER_STAGE_COMPUTE_BIT: "comp" - else: "" - -proc compileGLSLToSPIRV(stage: static VkShaderStageFlagBits, shaderSource: static string, entrypoint: string): seq[uint32] {.compileTime.} = - when defined(nimcheck): # will not run if nimcheck is running - return result - const - stagename = stage2string(stage) - shaderHash = hash(shaderSource) - # cross compilation for windows workaround, sorry computer - shaderfile = getTempDir() / fmt"shader_{shaderHash}.{stagename}" - projectPath = querySetting(projectPath) - - discard staticExecChecked( - command = fmt"{projectPath}/glslangValidator --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderfile}", - input = shaderSource - ) - - when defined(mingw) and defined(linux): # required for crosscompilation, path separators get messed up - let shaderbinary = staticRead shaderfile.replace("\\", "/") - else: - let shaderbinary = staticRead shaderfile - when defined(linux): - discard staticExecChecked(command = fmt"rm {shaderfile}") - elif defined(windows): - discard staticExecChecked(command = fmt"cmd.exe /c del {shaderfile}") - else: - raise newException(Exception, "Unsupported operating system") - - var i = 0 - while i < shaderbinary.len: - result.add( - (uint32(shaderbinary[i + 0]) shl 0) or - (uint32(shaderbinary[i + 1]) shl 8) or - (uint32(shaderbinary[i + 2]) shl 16) or - (uint32(shaderbinary[i + 3]) shl 24) - ) - i += 4 - -proc initShaderProgram*[VertexType, Uniforms](device: VkDevice, programType: static VkShaderStageFlagBits, shader: static string, entryPoint: static string = "main"): ShaderProgram[VertexType, Uniforms] = - result.entryPoint = entryPoint - result.programType = programType - - const constcode = compileGLSLToSPIRV(programType, shader, entryPoint) - var code = constcode - var createInfo = VkShaderModuleCreateInfo( - sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - codeSize: uint(code.len * sizeof(uint32)), - pCode: if code.len > 0: addr(code[0]) else: nil, - ) - var shaderModule: VkShaderModule - checkVkResult vkCreateShaderModule(device, addr(createInfo), nil, addr(shaderModule)) - - result.shader = VkPipelineShaderStageCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - stage: programType, - module: shaderModule, - pName: cstring(result.entryPoint), # entry point for shader - ) - -func generateVertexShaderCode*[VertexType, Uniforms]( - shaderBody: static string = "", - entryPoint: static string = "main", - glslVersion: static string = "450" -): string {.compileTime.} = - var lines: seq[string] - lines.add "#version " & glslVersion - lines.add "layout(row_major) uniform;" - lines.add generateGLSLUniformDeclarations[Uniforms]() - lines.add generateGLSLVertexDeclarations[VertexType]() - lines.add "layout(location = 0) out vec4 fragColor;" - lines.add "void " & entryPoint & "() {" - - var viewprojection = "" - - var hasPosition = 0 - var hasColor = 0 - for attrname, value in VertexType().fieldPairs: - when typeof(value) is PositionAttribute: - let glsltype = getGLSLType[getAttributeType(value)]() - lines.add &" {glsltype} in_position = " & attrname & ";" - if getAttributeType(value) is TVec2: - lines.add " vec4 out_position = vec4(in_position, 0.0, 1.0);" - elif getAttributeType(value) is TVec3: - lines.add " vec4 out_position = vec4(in_position, 1.0);" - elif getAttributeType(value) is TVec4: - lines.add " vec4 out_position = in_position;" - hasPosition += 1 - when typeof(value) is ModelTransformAttribute: - lines.add &" out_position = " & attrname & " * out_position;" - when typeof(value) is ColorAttribute: - let glsltype = getGLSLType[getAttributeType(value)]() - lines.add &" {glsltype} in_color = " & attrname & ";" - if getAttributeType(value) is TVec3: - lines.add &" vec4 out_color = vec4(in_color, 1);"; - elif getAttributeType(value) is TVec4: - lines.add &" vec4 out_color = in_color;"; - hasColor += 1 - when not (Uniforms is void): - let uniformBlockName = name(Uniforms).toLower() - for attrname, value in Uniforms().fieldPairs: - when typeof(value) is ViewProjectionTransform: - lines.add "out_position = " & uniformBlockName & "." & attrname & " * out_position;" - - lines.add shaderBody - lines.add " gl_Position = out_position;" - lines.add " fragColor = out_color;" - lines.add "}" - if hasPosition != 1: - raise newException(Exception, fmt"VertexType needs to have exactly one attribute of type PositionAttribute (has {hasPosition})") - if hasColor != 1: - raise newException(Exception, fmt"VertexType needs to have exactly one attribute of type ColorAttribute (has {hasColor})") - return lines.join("\n") - -func generateFragmentShaderCode*[VertexType]( - shaderBody: static string = "", - entryPoint: static string = "main", - glslVersion: static string = "450" -): string {.compileTime.} = - var lines: seq[string] - lines.add "#version " & glslVersion - lines.add "layout(row_major) uniform;" - lines.add "layout(location = 0) in vec4 fragColor;" - lines.add "layout(location = 0) out vec4 outColor;" - lines.add "void " & entryPoint & "() {" - lines.add " vec4 in_color = fragColor;" - lines.add " vec4 out_color = in_color;" - lines.add shaderBody - lines.add " outColor = out_color;" - lines.add "}" - - return lines.join("\n")
--- a/src/semicongine/legacy/thing.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -import std/strformat -import std/typetraits - -import ./math/matrix - -type - Part* = ref object of RootObj - thing*: Thing - - Thing* = ref object of RootObj - name*: string - transform*: Mat44 # todo: cache transform + only update VBO when transform changed - parent*: Thing - children*: seq[Thing] - parts*: seq[Part] - - -func `$`*(thing: Thing): string = thing.name -method `$`*(part: Part): string {.base.} = - if part.thing != nil: - &"{part.thing} -> Part" - else: - &"Standalone Part" - -proc add*(thing: Thing, child: Thing) = - child.parent = thing - thing.children.add child -proc add*(thing: Thing, part: Part) = - part.thing = thing - thing.parts.add part -proc add*(thing: Thing, children: seq[Thing]) = - for child in children: - child.parent = thing - thing.children.add child -proc add*(thing: Thing, parts: seq[Part]) = - for part in parts: - part.thing = thing - thing.parts.add part - -func newThing*(name: string = ""): Thing = - result = new Thing - result.name = name - result.transform = Unit44 - if result.name == "": - result.name = &"Thing[{$(cast[ByteAddress](result))}]" - -func newThing*(name: string, firstChild: Thing, children: varargs[ - Thing]): Thing = - result = new Thing - result.add firstChild - for child in children: - result.add child - result.name = name - result.transform = Unit44 - if result.name == "": - result.name = &"Thing[{$(cast[ByteAddress](result))}]" - -proc newThing*(name: string, firstPart: Part, parts: varargs[Part]): Thing = - result = new Thing - result.name = name - result.add firstPart - for part in parts: - result.add part - if result.name == "": - result.name = &"Thing[{$(cast[ByteAddress](result))}]" - result.transform = Unit44 - -func getModelTransform*(thing: Thing): Mat44 = - result = Unit44 - var currentThing = thing - while currentThing != nil: - result = currentThing.transform * result - currentThing = currentThing.parent - -iterator allPartsOfType*[T: Part](root: Thing): T = - var queue = @[root] - while queue.len > 0: - let thing = queue.pop - for part in thing.parts: - if part of T: - yield T(part) - for i in countdown(thing.children.len - 1, 0): - queue.add thing.children[i] - -func firstWithName*(root: Thing, name: string): Thing = - var queue = @[root] - while queue.len > 0: - let next = queue.pop - for child in next.children: - if child.name == name: - return child - queue.add child - -func firstPartWithName*[T: Part](root: Thing, name: string): T = - var queue = @[root] - while queue.len > 0: - let next = queue.pop - for child in next.children: - if child.name == name: - for part in child.parts: - if part of T: - return T(part) - queue.add child - -func allWithName*(root: Thing, name: string): seq[Thing] = - var queue = @[root] - while queue.len > 0: - let next = queue.pop - for child in next.children: - if child.name == name: - result.add child - queue.add child - -func allPartsWithName*[T: Part](root: Thing, name: string): seq[T] = - var queue = @[root] - while queue.len > 0: - let next = queue.pop - for child in next.children: - if child.name == name: - for part in child.parts: - if part of T: - result.add T(part) - queue.add child - -iterator allThings*(root: Thing): Thing = - var queue = @[root] - while queue.len > 0: - let next = queue.pop - for child in next.children: - queue.add child - yield next
--- a/src/semicongine/legacy/vertex.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -import std/options -import std/macros -import std/strutils -import std/strformat -import std/typetraits - -import ./math/vector -import ./math/matrix -import ./vulkan -import ./buffer -import ./glsl_helpers - -type - VertexAttributeType = SomeNumber|TVec|TMat - # useOnDeviceMemory can be used to make sure the attribute buffer memory will - # be on the device. Data will be faster to access but much slower to update - GenericAttribute*[T: VertexAttributeType] = object - data*: seq[T] - buffer*: Buffer - useOnDeviceMemory*: bool - PositionAttribute*[T: TVec] = object - data*: seq[T] - buffer*: Buffer - useOnDeviceMemory*: bool - ColorAttribute*[T: TVec] = object - data*: seq[T] - buffer*: Buffer - useOnDeviceMemory*: bool - GenericInstanceAttribute*[T: VertexAttributeType] = object - data*: seq[T] - buffer*: Buffer - useOnDeviceMemory*: bool - ModelTransformAttribute* = GenericInstanceAttribute[Mat44] - InstanceAttribute* = GenericInstanceAttribute|ModelTransformAttribute - VertexAttribute* = GenericAttribute|PositionAttribute|ColorAttribute|InstanceAttribute - -template getAttributeType*(v: VertexAttribute): auto = get(genericParams(typeof(v)), 0) - -func datasize*(attribute: VertexAttribute): uint64 = - uint64(sizeof(getAttributeType(attribute))) * uint64(attribute.data.len) - -# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html -func nLocationSlots[T: VertexAttributeType](): int = - when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or - T is TVec4[float64]): - 2 - elif T is SomeNumber or T is TVec: - 1 - else: - {.error: "Unsupported vertex attribute type".} - -# numbers -func getVkFormat[T: VertexAttributeType](): VkFormat = - when T is uint8: VK_FORMAT_R8_UINT - elif T is int8: VK_FORMAT_R8_SINT - elif T is uint16: VK_FORMAT_R16_UINT - elif T is int16: VK_FORMAT_R16_SINT - elif T is uint32: VK_FORMAT_R32_UINT - elif T is int32: VK_FORMAT_R32_SINT - elif T is uint64: VK_FORMAT_R64_UINT - elif T is int64: VK_FORMAT_R64_SINT - elif T is float32: VK_FORMAT_R32_SFLOAT - elif T is float64: VK_FORMAT_R64_SFLOAT - elif T is TVec2[uint8]: VK_FORMAT_R8G8_UINT - elif T is TVec2[int8]: VK_FORMAT_R8G8_SINT - elif T is TVec2[uint16]: VK_FORMAT_R16G16_UINT - elif T is TVec2[int16]: VK_FORMAT_R16G16_SINT - elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT - elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT - elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT - elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT - elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT - elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT - elif T is TVec3[uint8]: VK_FORMAT_R8G8B8_UINT - elif T is TVec3[int8]: VK_FORMAT_R8G8B8_SINT - elif T is TVec3[uint16]: VK_FORMAT_R16G16B16_UINT - elif T is TVec3[int16]: VK_FORMAT_R16G16B16_SINT - elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT - elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT - elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT - elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT - elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is TVec4[uint8]: VK_FORMAT_R8G8B8A8_UINT - elif T is TVec4[int8]: VK_FORMAT_R8G8B8A8_SINT - elif T is TVec4[uint16]: VK_FORMAT_R16G16B16A16_UINT - elif T is TVec4[int16]: VK_FORMAT_R16G16B16A16_SINT - elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT - elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT - elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT - elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT - elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT - elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT - else: {.error: "Unsupported vertex attribute type".} - - -func VertexCount*[T](t: T): uint32 = - for name, value in t.fieldPairs: - when typeof(value) is VertexAttribute and not (typeof( - value) is InstanceAttribute): - if result == 0: - result = uint32(value.data.len) - else: - assert result == uint32(value.data.len) - - -func hasAttributeType*[T, AT](t: T): uint32 = - for name, value in t.fieldPairs: - when typeof(value) is AT: - return true - return false - -# helper function to make sure matrices are correctly -# divided into multiple vector attributes -template compositeAttributeType[T]() = - when T is TMat33[float32]: - Vec3 - elif T is TMat44[float32]: - Vec4 - else: - T -func compositeAttributesNumber[T](): int = - when T is TMat33[float32]: - 3 - elif T is TMat44[float32]: - 4 - else: - 1 - - -func generateGLSLVertexDeclarations*[T](): string = - var stmtList: seq[string] - var i = 0 - for name, value in T().fieldPairs: - when typeof(value) is VertexAttribute: - let glsltype = getGLSLType[getAttributeType(value)]() - let n = name - stmtList.add(&"layout(location = {i}) in {glsltype} {n};") - for j in 0 ..< compositeAttributesNumber[getAttributeType(value)](): - i += nLocationSlots[compositeAttributeType(getAttributeType(value))]() - - return stmtList.join("\n") - - -func generateInputVertexBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputBindingDescription] = - # packed attribute data, not interleaved (aks "struct of arrays") - var binding = bindingoffset - for name, value in T().fieldPairs: - when typeof(value) is InstanceAttribute: - let inputRate = VK_VERTEX_INPUT_RATE_INSTANCE - elif typeof(value) is VertexAttribute: - let inputRate = VK_VERTEX_INPUT_RATE_VERTEX - else: - {.error: "Unsupported vertex attribute type".} - result.add( - VkVertexInputBindingDescription( - binding: uint32(binding), - stride: uint32(sizeof(getAttributeType(value))), - inputRate: inputRate, - ) - ) - binding += 1 - - -func generateInputAttributeBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputAttributeDescription] = - # TODO: - var location = 0 - var binding = bindingoffset - for name, value in T().fieldPairs: - when typeof(value) is VertexAttribute: - for i in 0 ..< compositeAttributesNumber[getAttributeType(value)](): - result.add( - VkVertexInputAttributeDescription( - binding: uint32(binding), - location: uint32(location), - format: getVkFormat[compositeAttributeType(getAttributeType(value))](), - offset: uint32(i * sizeof(compositeAttributeType(getAttributeType(value)))), - ) - ) - location += nLocationSlots[compositeAttributeType(getAttributeType(value))]() - binding += 1
--- a/src/semicongine/legacy/vulkan_helpers.nim Thu Apr 27 00:23:23 2023 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -import std/tables -import std/strutils -import std/strformat -import std/logging -import std/macros - -import ./vulkan -import ./window - -# the included code need checkVkResult, therefore having the template above -when defined(linux): - include ./platform/linux/vulkan -when defined(windows): - include ./platform/windows/vulkan - -const ENABLEVULKANVALIDATIONLAYERS* = not defined(release) - -func addrOrNil[T](obj: var openArray[T]): ptr T = - if obj.len > 0: addr(obj[0]) else: nil - -func 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) - -func getSuitableSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR = - let usableSurfaceFormats = filterForSurfaceFormat(formats) - if len(usableSurfaceFormats) == 0: - raise newException(Exception, "No suitable surface formats found") - return usableSurfaceFormats[0] - - -func 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) - var extensions = newSeq[VkExtensionProperties](extensionCount) - checkVkResult vkEnumerateInstanceExtensionProperties(nil, addr( extensionCount), addrOrNil(extensions)) - - for extension in extensions: - result.add(cleanString(extension.extensionName)) - - -proc getDeviceExtensions*(device: VkPhysicalDevice): seq[string] = - var extensionCount: uint32 - checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr( - extensionCount), nil) - var extensions = newSeq[VkExtensionProperties](extensionCount) - checkVkResult vkEnumerateDeviceExtensionProperties(device, nil, addr( - extensionCount), addrOrNil(extensions)) - - for extension in extensions: - result.add(cleanString(extension.extensionName)) - - -proc getValidationLayers*(): seq[string] = - var n_layers: uint32 - checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) - var layers = newSeq[VkLayerProperties](n_layers) - checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), addrOrNil(layers)) - - for layer in layers: - result.add(cleanString(layer.layerName)) - - -proc getVulkanPhysicalDevices*(instance: VkInstance): seq[VkPhysicalDevice] = - var n_devices: uint32 - checkVkResult vkEnumeratePhysicalDevices(instance, addr(n_devices), nil) - result = newSeq[VkPhysicalDevice](n_devices) - 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), 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), 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), 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])) - - -func getPresentMode*(modes: seq[VkPresentModeKHR]): VkPresentModeKHR = - let preferredModes = [ - 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: - if preferredMode == mode: - return mode - # should never be reached, but seems to be garuanteed by vulkan specs to always be available - return VK_PRESENT_MODE_FIFO_KHR - - -proc createVulkanInstance*(vulkanVersion: uint32): VkInstance = - - var requiredExtensions = @["VK_KHR_surface".cstring] & REQUIRED_PLATFORM_EXTENSIONS - when ENABLEVULKANVALIDATIONLAYERS: - requiredExtensions.add("VK_EXT_debug_utils".cstring) - - let availableExtensions = getInstanceExtensions() - for extension in requiredExtensions: - assert $extension in availableExtensions, $extension - - let availableLayers = getValidationLayers() - var usableLayers = newSeq[cstring]() - - when ENABLEVULKANVALIDATIONLAYERS: - const desiredLayers = ["VK_LAYER_KHRONOS_validation".cstring, "VK_LAYER_MESA_overlay".cstring] - else: - const desiredLayers: array[0, string] = [] - for layer in desiredLayers: - if $layer in availableLayers: - usableLayers.add(layer) - - echo "Available validation layers: ", availableLayers - echo "Using validation layers: ", usableLayers - echo "Available extensions: ", availableExtensions - echo "Using instance extensions: ", requiredExtensions - - var appinfo = VkApplicationInfo( - sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, - pApplicationName: "Hello Triangle", - pEngineName: "Custom engine", - apiVersion: vulkanVersion, - ) - var createinfo = VkInstanceCreateInfo( - sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - pApplicationInfo: addr(appinfo), - enabledLayerCount: usableLayers.len.uint32, - ppEnabledLayerNames: cast[ptr UncheckedArray[cstring]](addrOrNil( - usableLayers)), - enabledExtensionCount: requiredExtensions.len.uint32, - ppEnabledExtensionNames: cast[ptr UncheckedArray[cstring]](addr( - requiredExtensions[0])) - ) - checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result)) - let other_extensions = @["VK_KHR_swapchain".cstring] - for extension in requiredExtensions & other_extensions: - result.loadExtension($extension) - -proc getVulcanDevice*( - physicalDevice: var VkPhysicalDevice, - features: var VkPhysicalDeviceFeatures, - 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: graphicsQueueFamily, - queueCount: 1, - pQueuePriorities: addr(priority), - ), - ] - - var requiredExtensions = ["VK_KHR_swapchain".cstring] - var deviceCreateInfo = VkDeviceCreateInfo( - sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - queueCreateInfoCount: uint32(queueCreateInfo.len), - pQueueCreateInfos: addrOrNil(queueCreateInfo), - 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], graphicsQueueFamily, 0'u32, addr(result[1])) - vkGetDeviceQueue(result[0], presentationQueueFamily, 0'u32, addr(result[2])) - -proc debugCallback*( - messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, - messageTypes: VkDebugUtilsMessageTypeFlagsEXT, - pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT, - userData: pointer -): VkBool32 {.cdecl.} = - echo &"{messageSeverity}: {VkDebugUtilsMessageTypeFlagBitsEXT(messageTypes)}: {pCallbackData.pMessage}" - return false - -proc getSurfaceCapabilities*(device: VkPhysicalDevice, - surface: VkSurfaceKHR): VkSurfaceCapabilitiesKHR = - checkVkResult device.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, addr(result))