Mercurial > games > semicongine
comparison static_utils.nim @ 1159:e7cbb13999e4 compiletime-tests
add: changes and static tools
| author | sam <sam@basx.dev> |
|---|---|
| date | Mon, 17 Jun 2024 22:21:59 +0700 |
| parents | |
| children | 836dc1eda5e3 |
comparison
equal
deleted
inserted
replaced
| 1158:f32359ffd882 | 1159:e7cbb13999e4 |
|---|---|
| 1 import std/macros | |
| 2 import std/typetraits | |
| 3 | |
| 4 import semicongine/core/vector | |
| 5 import semicongine/core/matrix | |
| 6 import semicongine/core/vulkanapi | |
| 7 | |
| 8 template VertexAttribute* {.pragma.} | |
| 9 template InstanceAttribute* {.pragma.} | |
| 10 | |
| 11 type | |
| 12 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] | |
| 13 | |
| 14 func VkType[T: SupportedGPUType](value: T): VkFormat = | |
| 15 when T is float32: VK_FORMAT_R32_SFLOAT | |
| 16 elif T is float64: VK_FORMAT_R64_SFLOAT | |
| 17 elif T is int8: VK_FORMAT_R8_SINT | |
| 18 elif T is int16: VK_FORMAT_R16_SINT | |
| 19 elif T is int32: VK_FORMAT_R32_SINT | |
| 20 elif T is int64: VK_FORMAT_R64_SINT | |
| 21 elif T is uint8: VK_FORMAT_R8_UINT | |
| 22 elif T is uint16: VK_FORMAT_R16_UINT | |
| 23 elif T is uint32: VK_FORMAT_R32_UINT | |
| 24 elif T is uint64: VK_FORMAT_R64_UINT | |
| 25 elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT | |
| 26 elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT | |
| 27 elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT | |
| 28 elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT | |
| 29 elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT | |
| 30 elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT | |
| 31 elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT | |
| 32 elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT | |
| 33 elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT | |
| 34 elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT | |
| 35 elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT | |
| 36 elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT | |
| 37 elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT | |
| 38 elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT | |
| 39 elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT | |
| 40 elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT | |
| 41 elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | |
| 42 elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | |
| 43 elif T is Mat2[float32]: VK_FORMAT_R32G32_SFLOAT | |
| 44 elif T is Mat2[float64]: VK_FORMAT_R64G64_SFLOAT | |
| 45 elif T is Mat23[float32]: VK_FORMAT_R32G32B32_SFLOAT | |
| 46 elif T is Mat23[float64]: VK_FORMAT_R64G64B64_SFLOAT | |
| 47 elif T is Mat32[float32]: VK_FORMAT_R32G32_SFLOAT | |
| 48 elif T is Mat32[float64]: VK_FORMAT_R64G64_SFLOAT | |
| 49 elif T is Mat3[float32]: VK_FORMAT_R32G32B32_SFLOAT | |
| 50 elif T is Mat3[float64]: VK_FORMAT_R64G64B64_SFLOAT | |
| 51 elif T is Mat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | |
| 52 elif T is Mat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | |
| 53 elif T is Mat43[float32]: VK_FORMAT_R32G32B32_SFLOAT | |
| 54 elif T is Mat43[float64]: VK_FORMAT_R64G64B64_SFLOAT | |
| 55 elif T is Mat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT | |
| 56 elif T is Mat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT | |
| 57 else: {.error: "Unsupported data type on GPU".} | |
| 58 | |
| 59 template getElementType(field: typed): untyped = | |
| 60 when not (typeof(field) is seq or typeof(field) is array): | |
| 61 {.error: "getElementType can only be used with seq or array".} | |
| 62 genericParams(typeof(field)).get(0) | |
| 63 | |
| 64 proc isVertexAttribute[T](value: T): bool {.compileTime.} = | |
| 65 hasCustomPragma(T, VertexAttribute) | |
| 66 | |
| 67 proc isInstanceAttribute[T](value: T): bool {.compileTime.} = | |
| 68 hasCustomPragma(T, InstanceAttribute) | |
| 69 | |
| 70 template ForAttributeFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = | |
| 71 for theFieldname, value in fieldPairs(inputData): | |
| 72 when isVertexAttribute(value) or isInstanceAttribute(value): | |
| 73 when not typeof(value) is seq: | |
| 74 {.error: "field '" & theFieldname & "' needs to be a seq".} | |
| 75 when not typeof(value) is SupportedGPUType: | |
| 76 {.error: "field '" & theFieldname & "' is not a supported GPU type".} | |
| 77 block: | |
| 78 let `fieldname` {.inject.} = theFieldname | |
| 79 let `valuename` {.inject.} = default(getElementType(value)) | |
| 80 let `isinstancename` {.inject.} = value.isInstanceAttribute() | |
| 81 body | |
| 82 | |
| 83 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = | |
| 84 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: | |
| 85 2 | |
| 86 elif T is TMat32[float32] or T is TMat32[float64] or T is TMat3[float32] or T is TMat3[float64] or T is TMat34[float32] or T is TMat34[float64]: | |
| 87 3 | |
| 88 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: | |
| 89 4 | |
| 90 else: | |
| 91 1 | |
| 92 | |
| 93 func NLocationSlots[T: SupportedGPUType](value: T): uint32 = | |
| 94 #[ | |
| 95 single location: | |
| 96 16-bit scalar and vector types, and | |
| 97 32-bit scalar and vector types, and | |
| 98 64-bit scalar and 2-component vector types. | |
| 99 two locations | |
| 100 64-bit three- and four-component vectors | |
| 101 ]# | |
| 102 when typeof(value) is TVec3 and sizeof(getElementType(value)) == 8: | |
| 103 return 2 | |
| 104 elif typeof(value) is TVec4 and sizeof(getElementType(value)) == 8: | |
| 105 return 2 | |
| 106 else: | |
| 107 return 1 | |
| 108 | |
| 109 | |
| 110 type | |
| 111 Renderable[IndexType: static VkIndexType] = object | |
| 112 buffers: seq[VkBuffer] | |
| 113 offsets: seq[VkDeviceSize] | |
| 114 instanceCount: uint32 | |
| 115 when IndexType == VK_INDEX_TYPE_NONE_KHR: | |
| 116 vertexCount: uint32 | |
| 117 else: | |
| 118 indexBuffer: VkBuffer | |
| 119 indexCount: uint32 | |
| 120 indexBufferOffset: VkDeviceSize | |
| 121 Pipeline = object | |
| 122 pipeline: VkPipeline | |
| 123 layout: VkPipelineLayout | |
| 124 descriptorSets: array[2, seq[VkDescriptorSet]] | |
| 125 ShaderSet[ShaderInputType, ShaderDescriptorType] = object | |
| 126 vertexShader: VkShaderModule | |
| 127 fragmentShader: VkShaderModule | |
| 128 # TODO: I think this needs more fields? | |
| 129 | |
| 130 proc CreatePipeline*[ShaderInputType, ShaderDescriptorType]( | |
| 131 device: VkDevice, | |
| 132 renderPass: VkRenderPass, | |
| 133 shaderSet: ShaderSet[ShaderInputType, ShaderDescriptorType], | |
| 134 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
| 135 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, | |
| 136 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, | |
| 137 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, | |
| 138 ): Pipeline = | |
| 139 # assumptions/limitations: | |
| 140 # - we are only using vertex and fragment shaders (2 stages) | |
| 141 # - we only support one subpass | |
| 142 | |
| 143 # CONTINUE HERE, WITH PIPELINE LAYOUT!!!! | |
| 144 # Rely on ShaderDescriptorType | |
| 145 checkVkResult vkCreatePipelineLayout(device.vk, addr(pipelineLayoutInfo), nil, addr(result.layout)) | |
| 146 | |
| 147 let stages = [ | |
| 148 VkPipelineShaderStageCreateInfo( | |
| 149 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| 150 stage: VK_SHADER_STAGE_VERTEX_BIT, | |
| 151 module: shaderSet.vertexShader, | |
| 152 pName: "main", | |
| 153 ), | |
| 154 VkPipelineShaderStageCreateInfo( | |
| 155 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| 156 stage: VK_SHADER_STAGE_FRAGMENT_BIT, | |
| 157 module: shaderSet.fragmentShader, | |
| 158 pName: "main", | |
| 159 ), | |
| 160 ] | |
| 161 let | |
| 162 bindings: var seq[VkVertexInputBindingDescription] | |
| 163 attributes: var seq[VkVertexInputAttributeDescription] | |
| 164 var inputBindingNumber = 0'u32 | |
| 165 var inputLocationNumber = 0'u32 | |
| 166 ForAttributeFields(default(ShaderInputType), fieldname, value, isInstanceAttr): | |
| 167 bindings.add VkVertexInputBindingDescription( | |
| 168 binding: inputBindingNumber, | |
| 169 stride: sizeof(value).uint32, | |
| 170 inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, | |
| 171 ) | |
| 172 # allows to submit larger data structures like Mat44, for most other types will be 1 | |
| 173 let perDescriptorSize = sizeof(value).uint32 div NumberOfVertexInputAttributeDescriptors(value) | |
| 174 for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value): | |
| 175 attributes.add VkVertexInputAttributeDescription( | |
| 176 binding: inputBindingNumber, | |
| 177 inputLocationNumber: inputLocationNumber, | |
| 178 format: VkType(value), | |
| 179 offset: i * perDescriptorSize, | |
| 180 ) | |
| 181 inputLocationNumber += NLocationSlots(value) | |
| 182 inc inputBindingNumber | |
| 183 | |
| 184 let | |
| 185 vertexInputInfo = VkPipelineVertexInputStateCreateInfo( | |
| 186 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
| 187 vertexBindingDescriptionCount: uint32(bindings.len), | |
| 188 pVertexBindingDescriptions: bindings.ToCPointer, | |
| 189 vertexAttributeDescriptionCount: uint32(attributes.len), | |
| 190 pVertexAttributeDescriptions: attributes.ToCPointer, | |
| 191 ) | |
| 192 inputAssembly = VkPipelineInputAssemblyStateCreateInfo( | |
| 193 sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
| 194 topology: topology, | |
| 195 primitiveRestartEnable: false, | |
| 196 ) | |
| 197 viewportState = VkPipelineViewportStateCreateInfo( | |
| 198 sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
| 199 viewportCount: 1, | |
| 200 scissorCount: 1, | |
| 201 ) | |
| 202 rasterizer = VkPipelineRasterizationStateCreateInfo( | |
| 203 sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
| 204 depthClampEnable: VK_FALSE, | |
| 205 rasterizerDiscardEnable: VK_FALSE, | |
| 206 polygonMode: polygonMode, | |
| 207 lineWidth: 1.0, | |
| 208 cullMode: toBits [cullMode], | |
| 209 frontFace: frontFace, | |
| 210 depthBiasEnable: VK_FALSE, | |
| 211 depthBiasConstantFactor: 0.0, | |
| 212 depthBiasClamp: 0.0, | |
| 213 depthBiasSlopeFactor: 0.0, | |
| 214 ) | |
| 215 multisampling = VkPipelineMultisampleStateCreateInfo( | |
| 216 sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
| 217 sampleShadingEnable: VK_FALSE, | |
| 218 rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, | |
| 219 minSampleShading: 1.0, | |
| 220 pSampleMask: nil, | |
| 221 alphaToCoverageEnable: VK_FALSE, | |
| 222 alphaToOneEnable: VK_FALSE, | |
| 223 ) | |
| 224 colorBlendAttachment = VkPipelineColorBlendAttachmentState( | |
| 225 colorWriteMask: toBits [VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT], | |
| 226 blendEnable: VK_TRUE, | |
| 227 srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA, | |
| 228 dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | |
| 229 colorBlendOp: VK_BLEND_OP_ADD, | |
| 230 srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, | |
| 231 dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, | |
| 232 alphaBlendOp: VK_BLEND_OP_ADD, | |
| 233 ) | |
| 234 colorBlending = VkPipelineColorBlendStateCreateInfo( | |
| 235 sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
| 236 logicOpEnable: false, | |
| 237 attachmentCount: 1, | |
| 238 pAttachments: addr(colorBlendAttachment), | |
| 239 ) | |
| 240 dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] | |
| 241 dynamicState = VkPipelineDynamicStateCreateInfo( | |
| 242 sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | |
| 243 dynamicStateCount: dynamicStates.len.uint32, | |
| 244 pDynamicStates: dynamicStates.ToCPointer, | |
| 245 ) | |
| 246 let createInfo = VkGraphicsPipelineCreateInfo( | |
| 247 sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
| 248 stageCount: 2, | |
| 249 pStages: addr(stages), | |
| 250 pVertexInputState: addr(vertexInputInfo), | |
| 251 pInputAssemblyState: addr(inputAssembly), | |
| 252 pViewportState: addr(viewportState), | |
| 253 pRasterizationState: addr(rasterizer), | |
| 254 pMultisampleState: addr(multisampling), | |
| 255 pDepthStencilState: nil, | |
| 256 pColorBlendState: addr(colorBlending), | |
| 257 pDynamicState: addr(dynamicState), | |
| 258 layout: result.layout, | |
| 259 renderPass: renderPass, | |
| 260 subpass: 0, | |
| 261 basePipelineHandle: VkPipeline(0), | |
| 262 basePipelineIndex: -1, | |
| 263 ) | |
| 264 checkVkResult vkCreateGraphicsPipelines( | |
| 265 device, | |
| 266 VkPipelineCache(0), | |
| 267 1, | |
| 268 addr(createInfo), | |
| 269 nil, | |
| 270 addr(result.pipeline) | |
| 271 ) | |
| 272 | |
| 273 | |
| 274 proc Render*[IndexType: static VkIndexType](renderable: Renderable[IndexType], commandBuffer: VkCommandBuffer, pipeline: Pipeline, frameInFlight: int) = | |
| 275 assert 0 <= frameInFlight and frameInFlight < 2 | |
| 276 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) | |
| 277 commandBuffer.vkCmdBindDescriptorSets( | |
| 278 VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| 279 pipeline.layout, | |
| 280 0, | |
| 281 pipeline.descriptorSets[frameInFlight].len, | |
| 282 pipeline.descriptorSets[frameInFlight], | |
| 283 0, | |
| 284 nil, | |
| 285 ) | |
| 286 commandBuffer.vkCmdBindVertexBuffers( | |
| 287 firstBinding = 0'u32, | |
| 288 bindingCount = uint32(renderable.buffers.len), | |
| 289 pBuffers = renderable.buffers.ToCPointer(), | |
| 290 pOffsets = renderable.offsets.ToCPointer() | |
| 291 ) | |
| 292 when IndexType != VK_INDEX_TYPE_NONE_KHR: | |
| 293 commandBuffer.vkCmdBindIndexBuffer( | |
| 294 renderable.indexBuffer, | |
| 295 renderable.indexBufferOffset, | |
| 296 IndexType, | |
| 297 ) | |
| 298 commandBuffer.vkCmdDrawIndexed( | |
| 299 indexCount = drawable.indexCount, | |
| 300 instanceCount = drawable.instanceCount, | |
| 301 firstIndex = 0, | |
| 302 vertexOffset = 0, | |
| 303 firstInstance = 0 | |
| 304 ) | |
| 305 else: | |
| 306 commandBuffer.vkCmdDraw( | |
| 307 vertexCount = drawable.vertexCount, | |
| 308 instanceCount = drawable.instanceCount, | |
| 309 firstVertex = 0, | |
| 310 firstInstance = 0 | |
| 311 ) |
