Mercurial > games > semicongine
comparison semiconginev2/rendering.nim @ 1227:4d97cfc4888b
merge
| author | sam <sam@basx.dev> |
|---|---|
| date | Wed, 17 Jul 2024 23:45:43 +0700 |
| parents | c8e3037aca66 |
| children | 4e465583ea32 |
comparison
equal
deleted
inserted
replaced
| 1170:2addc5f6804f | 1227:4d97cfc4888b |
|---|---|
| 1 # in this file: | |
| 2 # - const defintions for rendering | |
| 3 # - custom pragma defintions for rendering | |
| 4 # - type defintions for rendering | |
| 5 # - some utils code that is used in mutiple rendering files | |
| 6 # - inclusion of all rendering files | |
| 7 | |
| 8 | |
| 9 # const definitions | |
| 10 const INFLIGHTFRAMES* = 2'u32 | |
| 11 const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment | |
| 12 const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 # ca. 100mb per block, seems reasonable | |
| 13 const BUFFER_ALLOCATION_SIZE = 9_000_000'u64 # ca. 9mb per block, seems reasonable, can put 10 buffers into one memory block | |
| 14 const MAX_DESCRIPTORSETS = 4 | |
| 15 | |
| 16 # custom pragmas to classify shader attributes | |
| 17 template VertexAttribute* {.pragma.} | |
| 18 template InstanceAttribute* {.pragma.} | |
| 19 template Pass* {.pragma.} | |
| 20 template PassFlat* {.pragma.} | |
| 21 template ShaderOutput* {.pragma.} | |
| 22 template DescriptorSets* {.pragma.} | |
| 23 | |
| 24 # there is a big, bad global vulkan object | |
| 25 # believe me, this makes everything much, much easier | |
| 26 | |
| 27 when defined(windows): | |
| 28 include ./rendering/platform/windows | |
| 29 when defined(linux): | |
| 30 include ./rendering/platform/linux | |
| 31 | |
| 32 type | |
| 33 VulkanGlobals* = object | |
| 34 # populated through InitVulkan proc | |
| 35 instance*: VkInstance | |
| 36 device*: VkDevice | |
| 37 physicalDevice*: VkPhysicalDevice | |
| 38 surface: VkSurfaceKHR | |
| 39 window: NativeWindow | |
| 40 graphicsQueueFamily*: uint32 | |
| 41 graphicsQueue*: VkQueue | |
| 42 debugMessenger: VkDebugUtilsMessengerEXT | |
| 43 # unclear as of yet | |
| 44 anisotropy*: float32 = 0 # needs to be enable during device creation | |
| 45 Swapchain* = object | |
| 46 # parameters to InitSwapchain, required for swapchain recreation | |
| 47 renderPass: VkRenderPass | |
| 48 vSync: bool | |
| 49 samples*: VkSampleCountFlagBits | |
| 50 # populated through InitSwapchain proc | |
| 51 vk: VkSwapchainKHR | |
| 52 width*: uint32 | |
| 53 height*: uint32 | |
| 54 msaaImage: VkImage | |
| 55 msaaMemory: VkDeviceMemory | |
| 56 msaaImageView: VkImageView | |
| 57 framebuffers: seq[VkFramebuffer] | |
| 58 framebufferViews: seq[VkImageView] | |
| 59 currentFramebufferIndex: uint32 | |
| 60 commandBufferPool: VkCommandPool | |
| 61 # frame-in-flight handling | |
| 62 currentFiF: range[0 .. (INFLIGHTFRAMES - 1).int] | |
| 63 queueFinishedFence*: array[INFLIGHTFRAMES.int, VkFence] | |
| 64 imageAvailableSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] | |
| 65 renderFinishedSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] | |
| 66 commandBuffers: array[INFLIGHTFRAMES.int, VkCommandBuffer] | |
| 67 oldSwapchain: ref Swapchain | |
| 68 oldSwapchainCounter: int # swaps until old swapchain will be destroyed | |
| 69 | |
| 70 var vulkan*: VulkanGlobals | |
| 71 var fullscreen: bool | |
| 72 | |
| 73 func currentFiF*(swapchain: Swapchain): int = swapchain.currentFiF | |
| 74 | |
| 75 type | |
| 76 # type aliases | |
| 77 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] | |
| 78 TextureType = TVec1[uint8] | TVec4[uint8] | |
| 79 | |
| 80 # shader related types | |
| 81 DescriptorSet*[T: object] = object | |
| 82 data*: T | |
| 83 vk: array[INFLIGHTFRAMES.int, VkDescriptorSet] | |
| 84 Pipeline*[TShader] = object | |
| 85 vk: VkPipeline | |
| 86 vertexShaderModule: VkShaderModule | |
| 87 fragmentShaderModule: VkShaderModule | |
| 88 layout: VkPipelineLayout | |
| 89 descriptorSetLayouts*: array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout] | |
| 90 | |
| 91 # memory/buffer related types | |
| 92 MemoryBlock* = object | |
| 93 vk: VkDeviceMemory | |
| 94 size: uint64 | |
| 95 rawPointer: pointer # if not nil, this is mapped memory | |
| 96 offsetNextFree: uint64 | |
| 97 BufferType* = enum | |
| 98 VertexBuffer | |
| 99 VertexBufferMapped | |
| 100 IndexBuffer | |
| 101 IndexBufferMapped | |
| 102 UniformBuffer | |
| 103 UniformBufferMapped | |
| 104 Buffer* = object | |
| 105 vk: VkBuffer | |
| 106 size: uint64 | |
| 107 rawPointer: pointer # if not nil, buffer is using mapped memory | |
| 108 offsetNextFree: uint64 | |
| 109 Texture*[T: TextureType] = object | |
| 110 width*: uint32 | |
| 111 height*: uint32 | |
| 112 interpolation*: VkFilter = VK_FILTER_LINEAR | |
| 113 data*: seq[T] | |
| 114 vk*: VkImage | |
| 115 imageview*: VkImageView | |
| 116 sampler*: VkSampler | |
| 117 isRenderTarget*: bool = false | |
| 118 GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object | |
| 119 data*: seq[T] | |
| 120 buffer*: Buffer | |
| 121 offset*: uint64 | |
| 122 GPUValue*[T: object, TBuffer: static BufferType] = object | |
| 123 data*: T | |
| 124 buffer: Buffer | |
| 125 offset: uint64 | |
| 126 GPUData = GPUArray | GPUValue | |
| 127 | |
| 128 RenderData* = object | |
| 129 descriptorPool: VkDescriptorPool | |
| 130 memory: array[VK_MAX_MEMORY_TYPES.int, seq[MemoryBlock]] | |
| 131 buffers: array[BufferType, seq[Buffer]] | |
| 132 images: seq[VkImage] | |
| 133 imageViews: seq[VkImageView] | |
| 134 samplers: seq[VkSampler] | |
| 135 | |
| 136 template ForDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped = | |
| 137 var `bindingNumber` {.inject.} = 0'u32 | |
| 138 for theFieldname, value in fieldPairs(shader): | |
| 139 when typeof(value) is Texture: | |
| 140 block: | |
| 141 const `fieldname` {.inject.} = theFieldname | |
| 142 const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER | |
| 143 const `countname` {.inject.} = 1'u32 | |
| 144 let `valuename` {.inject.} = value | |
| 145 body | |
| 146 `bindingNumber`.inc | |
| 147 elif typeof(value) is GPUValue: | |
| 148 block: | |
| 149 const `fieldname` {.inject.} = theFieldname | |
| 150 const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER | |
| 151 const `countname` {.inject.} = 1'u32 | |
| 152 let `valuename` {.inject.} = value | |
| 153 body | |
| 154 `bindingNumber`.inc | |
| 155 elif typeof(value) is array: | |
| 156 when elementType(value) is Texture: | |
| 157 block: | |
| 158 const `fieldname` {.inject.} = theFieldname | |
| 159 const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER | |
| 160 const `countname` {.inject.} = uint32(typeof(value).len) | |
| 161 let `valuename` {.inject.} = value | |
| 162 body | |
| 163 `bindingNumber`.inc | |
| 164 elif elementType(value) is GPUValue: | |
| 165 block: | |
| 166 const `fieldname` {.inject.} = theFieldname | |
| 167 const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER | |
| 168 const `countname` {.inject.} = len(value).uint32 | |
| 169 let `valuename` {.inject.} = value | |
| 170 body | |
| 171 `bindingNumber`.inc | |
| 172 else: | |
| 173 {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).} | |
| 174 else: | |
| 175 {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).} | |
| 176 | |
| 177 include ./rendering/vulkan_wrappers | |
| 178 include ./rendering/renderpasses | |
| 179 include ./rendering/swapchain | |
| 180 include ./rendering/shaders | |
| 181 include ./rendering/renderer | |
| 182 | |
| 183 proc debugCallback( | |
| 184 messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, | |
| 185 messageTypes: VkDebugUtilsMessageTypeFlagsEXT, | |
| 186 pCallbackData: ptr VkDebugUtilsMessengerCallbackDataEXT, | |
| 187 userData: pointer | |
| 188 ): VkBool32 {.cdecl.} = | |
| 189 const LOG_LEVEL_MAPPING = { | |
| 190 VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: lvlDebug, | |
| 191 VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: lvlInfo, | |
| 192 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: lvlWarn, | |
| 193 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: lvlError, | |
| 194 }.toTable | |
| 195 log LOG_LEVEL_MAPPING[messageSeverity], &"{toEnums messageTypes}: {pCallbackData.pMessage}" | |
| 196 if messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: | |
| 197 stderr.writeLine "-----------------------------------" | |
| 198 stderr.write getStackTrace() | |
| 199 stderr.writeLine LOG_LEVEL_MAPPING[messageSeverity], &"{toEnums messageTypes}: {pCallbackData.pMessage}" | |
| 200 stderr.writeLine "-----------------------------------" | |
| 201 let errorMsg = getStackTrace() & &"\n{toEnums messageTypes}: {pCallbackData.pMessage}" | |
| 202 raise newException(Exception, errorMsg) | |
| 203 return false | |
| 204 | |
| 205 proc InitVulkan*(appName: string = "semicongine app") = | |
| 206 | |
| 207 include ./platform/vulkan_extensions # for REQUIRED_PLATFORM_EXTENSIONS | |
| 208 | |
| 209 # instance creation | |
| 210 | |
| 211 # enagle all kind of debug stuff | |
| 212 when not defined(release): | |
| 213 let requiredExtensions = REQUIRED_PLATFORM_EXTENSIONS & @["VK_KHR_surface", "VK_EXT_debug_utils"] | |
| 214 let layers: seq[string] = if hasValidationLayer(): @["VK_LAYER_KHRONOS_validation"] else: @[] | |
| 215 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") | |
| 216 else: | |
| 217 let requiredExtensions = REQUIRED_PLATFORM_EXTENSIONS & @["VK_KHR_surface"] | |
| 218 let layers: seq[string] | |
| 219 | |
| 220 var | |
| 221 layersC = allocCStringArray(layers) | |
| 222 instanceExtensionsC = allocCStringArray(requiredExtensions) | |
| 223 defer: | |
| 224 deallocCStringArray(layersC) | |
| 225 deallocCStringArray(instanceExtensionsC) | |
| 226 | |
| 227 var | |
| 228 appinfo = VkApplicationInfo( | |
| 229 sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
| 230 pApplicationName: appName, | |
| 231 pEngineName: "semicongine", | |
| 232 apiVersion: VK_MAKE_API_VERSION(0, 1, 3, 0), | |
| 233 ) | |
| 234 createinfo = VkInstanceCreateInfo( | |
| 235 sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
| 236 pApplicationInfo: addr(appinfo), | |
| 237 enabledLayerCount: layers.len.uint32, | |
| 238 ppEnabledLayerNames: layersC, | |
| 239 enabledExtensionCount: requiredExtensions.len.uint32, | |
| 240 ppEnabledExtensionNames: instanceExtensionsC | |
| 241 ) | |
| 242 checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance)) | |
| 243 loadVulkan(vulkan.instance) | |
| 244 | |
| 245 # load extensions | |
| 246 # | |
| 247 for extension in requiredExtensions: | |
| 248 loadExtension(vulkan.instance, $extension) | |
| 249 vulkan.window = CreateWindow(appName) | |
| 250 vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window) | |
| 251 | |
| 252 # logical device creation | |
| 253 | |
| 254 # TODO: allowing support for physical devices without hasUniformBufferStandardLayout | |
| 255 # would require us to ship different shaders, so we don't support standard layout | |
| 256 # if that will be added, check the function vulkan/shaders.nim:glslUniforms and update accordingly | |
| 257 # let hasUniformBufferStandardLayout = "VK_KHR_uniform_buffer_standard_layout" in physicalDevice.getExtensions() | |
| 258 # var deviceExtensions = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"] | |
| 259 var deviceExtensions = @["VK_KHR_swapchain"] | |
| 260 for extension in deviceExtensions: | |
| 261 loadExtension(vulkan.instance, extension) | |
| 262 | |
| 263 when not defined(release): | |
| 264 var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT( | |
| 265 sType: VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, | |
| 266 messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT.items.toSeq.toBits, | |
| 267 messageType: VkDebugUtilsMessageTypeFlagBitsEXT.items.toSeq.toBits, | |
| 268 pfnUserCallback: debugCallback, | |
| 269 pUserData: nil, | |
| 270 ) | |
| 271 checkVkResult vkCreateDebugUtilsMessengerEXT( | |
| 272 vulkan.instance, | |
| 273 addr(debugMessengerCreateInfo), | |
| 274 nil, | |
| 275 addr(vulkan.debugMessenger) | |
| 276 ) | |
| 277 | |
| 278 # get physical device and graphics queue family | |
| 279 vulkan.physicalDevice = GetBestPhysicalDevice(vulkan.instance) | |
| 280 vulkan.graphicsQueueFamily = GetQueueFamily(vulkan.physicalDevice, VK_QUEUE_GRAPHICS_BIT) | |
| 281 | |
| 282 let | |
| 283 priority = cfloat(1) | |
| 284 queueInfo = VkDeviceQueueCreateInfo( | |
| 285 sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
| 286 queueFamilyIndex: vulkan.graphicsQueueFamily, | |
| 287 queueCount: 1, | |
| 288 pQueuePriorities: addr(priority), | |
| 289 ) | |
| 290 deviceExtensionsC = allocCStringArray(deviceExtensions) | |
| 291 defer: deallocCStringArray(deviceExtensionsC) | |
| 292 var createDeviceInfo = VkDeviceCreateInfo( | |
| 293 sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
| 294 queueCreateInfoCount: 1, | |
| 295 pQueueCreateInfos: addr(queueInfo), | |
| 296 enabledLayerCount: 0, | |
| 297 ppEnabledLayerNames: nil, | |
| 298 enabledExtensionCount: uint32(deviceExtensions.len), | |
| 299 ppEnabledExtensionNames: deviceExtensionsC, | |
| 300 pEnabledFeatures: nil, | |
| 301 ) | |
| 302 checkVkResult vkCreateDevice( | |
| 303 physicalDevice = vulkan.physicalDevice, | |
| 304 pCreateInfo = addr createDeviceInfo, | |
| 305 pAllocator = nil, | |
| 306 pDevice = addr vulkan.device | |
| 307 ) | |
| 308 vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) | |
| 309 | |
| 310 proc DestroyVulkan*() = | |
| 311 vkDestroyDevice(vulkan.device, nil) | |
| 312 vkDestroySurfaceKHR(vulkan.instance, vulkan.surface, nil) | |
| 313 vkDestroyDebugUtilsMessengerEXT(vulkan.instance, vulkan.debugMessenger, nil) | |
| 314 vkDestroyInstance(vulkan.instance, nil) | |
| 315 | |
| 316 proc ShowSystemCursor*() = vulkan.window.ShowSystemCursor() | |
| 317 proc HideSystemCursor*() = vulkan.window.HideSystemCursor() | |
| 318 proc Fullscreen*(): bool = fullscreen | |
| 319 proc `Fullscreen=`*(enable: bool) = | |
| 320 if enable != fullscreen: | |
| 321 fullscreen = enable | |
| 322 vulkan.window.Fullscreen(fullscreen) | |
| 323 | |
| 324 func GetAspectRatio*(swapchain: Swapchain): float32 = swapchain.width.float32 / swapchain.height.float32 | |
| 325 | |
| 326 proc MaxFramebufferSampleCount*(maxSamples = VK_SAMPLE_COUNT_8_BIT): VkSampleCountFlagBits = | |
| 327 let limits = svkGetPhysicalDeviceProperties().limits | |
| 328 let available = VkSampleCountFlags( | |
| 329 limits.framebufferColorSampleCounts.uint32 and limits.framebufferDepthSampleCounts.uint32 | |
| 330 ).toEnums | |
| 331 return min(max(available), maxSamples) | |
| 332 | |
| 333 | |
| 334 proc `[]`*(texture: Texture, x, y: uint32): auto = | |
| 335 assert x < texture.width, &"{x} < {texture.width} is not true" | |
| 336 assert y < texture.height, &"{y} < {texture.height} is not true" | |
| 337 | |
| 338 texture[].imagedata[y * texture.width + x] | |
| 339 | |
| 340 proc `[]=`*[T](texture: var Texture[T], x, y: uint32, value: T) = | |
| 341 assert x < texture.width | |
| 342 assert y < texture.height | |
| 343 | |
| 344 texture[].imagedata[y * texture.width + x] = value |
