Mercurial > games > semicongine
diff semiconginev2/rendering.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 21:01:37 +0700 |
parents | semicongine/rendering.nim@04e446a7eb2b |
children | a3fa15c25026 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semiconginev2/rendering.nim Wed Jul 17 21:01:37 2024 +0700 @@ -0,0 +1,311 @@ +# in this file: +# - const defintions for rendering +# - custom pragma defintions for rendering +# - type defintions for rendering +# - some utils code that is used in mutiple rendering files +# - inclusion of all rendering files + + +# const definitions +const INFLIGHTFRAMES* = 2'u32 +const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment +const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 # ca. 100mb per block, seems reasonable +const BUFFER_ALLOCATION_SIZE = 9_000_000'u64 # ca. 9mb per block, seems reasonable, can put 10 buffers into one memory block +const MAX_DESCRIPTORSETS = 4 + +# custom pragmas to classify shader attributes +template VertexAttribute* {.pragma.} +template InstanceAttribute* {.pragma.} +template Pass* {.pragma.} +template PassFlat* {.pragma.} +template ShaderOutput* {.pragma.} +template DescriptorSets* {.pragma.} + +# there is a big, bad global vulkan object +# believe me, this makes everything much, much easier + +include ./platform/window # for NativeWindow +include ./platform/surface # For CreateNativeSurface + +type + VulkanGlobals* = object + # populated through InitVulkan proc + instance*: VkInstance + device*: VkDevice + physicalDevice*: VkPhysicalDevice + surface: VkSurfaceKHR + window: NativeWindow + graphicsQueueFamily*: uint32 + graphicsQueue*: VkQueue + debugMessenger: VkDebugUtilsMessengerEXT + # unclear as of yet + anisotropy*: float32 = 0 # needs to be enable during device creation + Swapchain* = object + # parameters to InitSwapchain, required for swapchain recreation + renderPass: VkRenderPass + vSync: bool + samples*: VkSampleCountFlagBits + # populated through InitSwapchain proc + vk: VkSwapchainKHR + width*: uint32 + height*: uint32 + msaaImage: VkImage + msaaMemory: VkDeviceMemory + msaaImageView: VkImageView + framebuffers: seq[VkFramebuffer] + framebufferViews: seq[VkImageView] + currentFramebufferIndex: uint32 + commandBufferPool: VkCommandPool + # frame-in-flight handling + currentFiF: range[0 .. (INFLIGHTFRAMES - 1).int] + queueFinishedFence*: array[INFLIGHTFRAMES.int, VkFence] + imageAvailableSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] + renderFinishedSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] + commandBuffers: array[INFLIGHTFRAMES.int, VkCommandBuffer] + oldSwapchain: ref Swapchain + oldSwapchainCounter: int # swaps until old swapchain will be destroyed + +var vulkan*: VulkanGlobals + +func currentFiF*(swapchain: Swapchain): int = swapchain.currentFiF + +type + # type aliases + SupportedGPUType = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64] + TextureType = TVec1[uint8] | TVec4[uint8] + + # shader related types + DescriptorSet*[T: object] = object + data*: T + vk: array[INFLIGHTFRAMES.int, VkDescriptorSet] + Pipeline*[TShader] = object + vk: VkPipeline + vertexShaderModule: VkShaderModule + fragmentShaderModule: VkShaderModule + layout: VkPipelineLayout + descriptorSetLayouts*: array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout] + + # memory/buffer related types + MemoryBlock* = object + vk: VkDeviceMemory + size: uint64 + rawPointer: pointer # if not nil, this is mapped memory + offsetNextFree: uint64 + BufferType* = enum + VertexBuffer + VertexBufferMapped + IndexBuffer + IndexBufferMapped + UniformBuffer + UniformBufferMapped + Buffer* = object + vk: VkBuffer + size: uint64 + rawPointer: pointer # if not nil, buffer is using mapped memory + offsetNextFree: uint64 + Texture*[T: TextureType] = object + width*: uint32 + height*: uint32 + interpolation*: VkFilter = VK_FILTER_LINEAR + data*: seq[T] + vk*: VkImage + imageview*: VkImageView + sampler*: VkSampler + isRenderTarget*: bool = false + GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object + data*: seq[T] + buffer*: Buffer + offset*: uint64 + GPUValue*[T: object, TBuffer: static BufferType] = object + data*: T + buffer: Buffer + offset: uint64 + GPUData = GPUArray | GPUValue + + RenderData* = object + descriptorPool: VkDescriptorPool + memory: array[VK_MAX_MEMORY_TYPES.int, seq[MemoryBlock]] + buffers: array[BufferType, seq[Buffer]] + images: seq[VkImage] + imageViews: seq[VkImageView] + samplers: seq[VkSampler] + +template ForDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped = + var `bindingNumber` {.inject.} = 0'u32 + for theFieldname, value in fieldPairs(shader): + when typeof(value) is Texture: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + const `countname` {.inject.} = 1'u32 + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + elif typeof(value) is GPUValue: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + const `countname` {.inject.} = 1'u32 + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + elif typeof(value) is array: + when elementType(value) is Texture: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + const `countname` {.inject.} = uint32(typeof(value).len) + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + elif elementType(value) is GPUValue: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + const `countname` {.inject.} = len(value).uint32 + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + else: + {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).} + else: + {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).} + +include ./rendering/vulkan_wrappers +include ./rendering/renderpasses +include ./rendering/swapchain +include ./rendering/shaders +include ./rendering/renderer + +proc debugCallback( + messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, + messageTypes: VkDebugUtilsMessageTypeFlagsEXT, + pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT, + userData: pointer +): VkBool32 {.cdecl.} = + const LOG_LEVEL_MAPPING = { + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: lvlDebug, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: lvlInfo, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: lvlWarn, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: lvlError, + }.toTable + log LOG_LEVEL_MAPPING[messageSeverity], &"{toEnums messageTypes}: {pCallbackData.pMessage}" + if messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + stderr.writeLine "-----------------------------------" + stderr.write getStackTrace() + stderr.writeLine LOG_LEVEL_MAPPING[messageSeverity], &"{toEnums messageTypes}: {pCallbackData.pMessage}" + stderr.writeLine "-----------------------------------" + let errorMsg = getStackTrace() & &"\n{toEnums messageTypes}: {pCallbackData.pMessage}" + raise newException(Exception, errorMsg) + return false + +proc InitVulkan*(appName: string = "semicongine app") = + + include ./platform/vulkan_extensions # for REQUIRED_PLATFORM_EXTENSIONS + + # instance creation + + # enagle all kind of debug stuff + when not defined(release): + let requiredExtensions = REQUIRED_PLATFORM_EXTENSIONS & @["VK_KHR_surface", "VK_EXT_debug_utils"] + let layers: seq[string] = if hasValidationLayer(): @["VK_LAYER_KHRONOS_validation"] else: @[] + putEnv("VK_LAYER_ENABLES", "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT") + else: + let requiredExtensions = REQUIRED_PLATFORM_EXTENSIONS & @["VK_KHR_surface"] + let layers: seq[string] + + var + layersC = allocCStringArray(layers) + instanceExtensionsC = allocCStringArray(requiredExtensions) + defer: + deallocCStringArray(layersC) + deallocCStringArray(instanceExtensionsC) + + var + appinfo = VkApplicationInfo( + sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, + pApplicationName: appName, + pEngineName: "semicongine", + apiVersion: VK_MAKE_API_VERSION(0, 1, 3, 0), + ) + createinfo = VkInstanceCreateInfo( + sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + pApplicationInfo: addr(appinfo), + enabledLayerCount: layers.len.uint32, + ppEnabledLayerNames: layersC, + enabledExtensionCount: requiredExtensions.len.uint32, + ppEnabledExtensionNames: instanceExtensionsC + ) + checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance)) + loadVulkan(vulkan.instance) + + # load extensions + # + for extension in requiredExtensions: + loadExtension(vulkan.instance, $extension) + vulkan.window = CreateWindow(appName) + vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window) + + # logical device creation + + # TODO: allowing support for physical devices without hasUniformBufferStandardLayout + # would require us to ship different shaders, so we don't support standard layout + # if that will be added, check the function vulkan/shaders.nim:glslUniforms and update accordingly + # let hasUniformBufferStandardLayout = "VK_KHR_uniform_buffer_standard_layout" in physicalDevice.getExtensions() + # var deviceExtensions = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"] + var deviceExtensions = @["VK_KHR_swapchain"] + for extension in deviceExtensions: + loadExtension(vulkan.instance, extension) + + when not defined(release): + var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT( + sType: VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT.items.toSeq.toBits, + messageType: VkDebugUtilsMessageTypeFlagBitsEXT.items.toSeq.toBits, + pfnUserCallback: debugCallback, + pUserData: nil, + ) + checkVkResult vkCreateDebugUtilsMessengerEXT( + vulkan.instance, + addr(debugMessengerCreateInfo), + nil, + addr(vulkan.debugMessenger) + ) + + # get physical device and graphics queue family + vulkan.physicalDevice = GetBestPhysicalDevice(vulkan.instance) + vulkan.graphicsQueueFamily = GetQueueFamily(vulkan.physicalDevice, VK_QUEUE_GRAPHICS_BIT) + + let + priority = cfloat(1) + queueInfo = VkDeviceQueueCreateInfo( + sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueFamilyIndex: vulkan.graphicsQueueFamily, + queueCount: 1, + pQueuePriorities: addr(priority), + ) + deviceExtensionsC = allocCStringArray(deviceExtensions) + defer: deallocCStringArray(deviceExtensionsC) + var createDeviceInfo = VkDeviceCreateInfo( + sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + queueCreateInfoCount: 1, + pQueueCreateInfos: addr(queueInfo), + enabledLayerCount: 0, + ppEnabledLayerNames: nil, + enabledExtensionCount: uint32(deviceExtensions.len), + ppEnabledExtensionNames: deviceExtensionsC, + pEnabledFeatures: nil, + ) + checkVkResult vkCreateDevice( + physicalDevice = vulkan.physicalDevice, + pCreateInfo = addr createDeviceInfo, + pAllocator = nil, + pDevice = addr vulkan.device + ) + vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) + +proc DestroyVulkan*() = + vkDestroyDevice(vulkan.device, nil) + vkDestroySurfaceKHR(vulkan.instance, vulkan.surface, nil) + vkDestroyDebugUtilsMessengerEXT(vulkan.instance, vulkan.debugMessenger, nil) + vkDestroyInstance(vulkan.instance, nil)