Mercurial > games > semicongine
comparison static_utils.nim @ 1161:dbca0528c714 compiletime-tests
add: more stuff
author | sam <sam@basx.dev> |
---|---|
date | Wed, 19 Jun 2024 13:50:18 +0700 |
parents | 836dc1eda5e3 |
children | 46fae89cffb0 |
comparison
equal
deleted
inserted
replaced
1160:836dc1eda5e3 | 1161:dbca0528c714 |
---|---|
1 import std/macros | 1 import std/macros |
2 import std/strformat | |
2 import std/typetraits | 3 import std/typetraits |
3 | 4 |
5 import semicongine/core/utils | |
6 import semicongine/core/imagetypes | |
4 import semicongine/core/vector | 7 import semicongine/core/vector |
5 import semicongine/core/matrix | 8 import semicongine/core/matrix |
6 import semicongine/core/vulkanapi | 9 import semicongine/core/vulkanapi |
10 import semicongine/vulkan/buffer | |
7 | 11 |
8 template VertexAttribute* {.pragma.} | 12 template VertexAttribute* {.pragma.} |
9 template InstanceAttribute* {.pragma.} | 13 template InstanceAttribute* {.pragma.} |
14 template DescriptorAttribute* {.pragma.} | |
15 | |
10 | 16 |
11 type | 17 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] | 18 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 | 19 |
14 func VkType[T: SupportedGPUType](value: T): VkFormat = | 20 func VkType[T: SupportedGPUType](value: T): VkFormat = |
59 template getElementType(field: typed): untyped = | 65 template getElementType(field: typed): untyped = |
60 when not (typeof(field) is seq or typeof(field) is array): | 66 when not (typeof(field) is seq or typeof(field) is array): |
61 {.error: "getElementType can only be used with seq or array".} | 67 {.error: "getElementType can only be used with seq or array".} |
62 genericParams(typeof(field)).get(0) | 68 genericParams(typeof(field)).get(0) |
63 | 69 |
64 proc isVertexAttribute[T](value: T): bool {.compileTime.} = | 70 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = |
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): | 71 for theFieldname, value in fieldPairs(inputData): |
72 when isVertexAttribute(value) or isInstanceAttribute(value): | 72 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): |
73 when not typeof(value) is seq: | 73 when not typeof(value) is seq: |
74 {.error: "field '" & theFieldname & "' needs to be a seq".} | 74 {.error: "field '" & theFieldname & "' needs to be a seq".} |
75 when not typeof(value) is SupportedGPUType: | 75 when not typeof(value) is SupportedGPUType: |
76 {.error: "field '" & theFieldname & "' is not a supported GPU type".} | 76 {.error: "field '" & theFieldname & "' is not a supported GPU type".} |
77 block: | 77 block: |
78 let `fieldname` {.inject.} = theFieldname | 78 let `fieldname` {.inject.} = theFieldname |
79 let `valuename` {.inject.} = default(getElementType(value)) | 79 let `valuename` {.inject.} = default(getElementType(value)) |
80 let `isinstancename` {.inject.} = value.isInstanceAttribute() | 80 let `isinstancename` {.inject.} = value.isInstanceAttribute() |
81 body | |
82 | |
83 template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped = | |
84 for theFieldname, value in fieldPairs(inputData): | |
85 when hasCustomPragma(value, DescriptorAttribute): | |
86 when not ( | |
87 typeof(value) is SupportedGPUType | |
88 or (typeof(value) is array and elementType(value) is SupportedGPUType) | |
89 or typeof(value) is Texture | |
90 ): | |
91 {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType".} | |
92 block: | |
93 let `fieldname` {.inject.} = theFieldname | |
94 let `valuename` {.inject.} = default(getElementType(value)) | |
95 | |
96 # TODO | |
97 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER | |
98 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER | |
99 | |
100 when typeof(value) is array: | |
101 let `countname` {.inject.} = genericParams(typeof(value)).get(0) | |
102 else: | |
103 let `countname` {.inject.} = 1 | |
81 body | 104 body |
82 | 105 |
83 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = | 106 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]: | 107 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: |
85 2 | 108 2 |
105 return 2 | 128 return 2 |
106 else: | 129 else: |
107 return 1 | 130 return 1 |
108 | 131 |
109 type | 132 type |
110 Renderable = object | 133 IndexType = enum |
111 buffers: seq[VkBuffer] | 134 None, UInt8, UInt16, UInt32 |
112 offsets: seq[VkDeviceSize] | 135 RenderBuffers = object |
136 deviceBuffers: seq[Buffer] # for fast reads | |
137 hostVisibleBuffers: seq[Buffer] # for fast writes | |
138 Renderable[TMesh, TInstance] = object | |
139 vertexBuffers: seq[VkBuffer] | |
140 bufferOffsets: seq[VkDeviceSize] | |
113 instanceCount: uint32 | 141 instanceCount: uint32 |
114 case indexType: VkIndexType | 142 case indexType: IndexType |
115 of VK_INDEX_TYPE_NONE_KHR: | 143 of None: |
116 vertexCount: uint32 | 144 vertexCount: uint32 |
117 of VK_INDEX_TYPE_UINT8_EXT, VK_INDEX_TYPE_UINT16, VK_INDEX_TYPE_UINT32: | 145 else: |
118 indexBuffer: VkBuffer | 146 indexBuffer: VkBuffer |
119 indexCount: uint32 | 147 indexCount: uint32 |
120 indexBufferOffset: VkDeviceSize | 148 indexBufferOffset: VkDeviceSize |
121 Pipeline = object | 149 Pipeline[TShaderInputs] = object |
122 pipeline: VkPipeline | 150 pipeline: VkPipeline |
123 layout: VkPipelineLayout | 151 layout: VkPipelineLayout |
124 descriptorSets: array[2, seq[VkDescriptorSet]] | 152 descriptorSets: array[2, seq[VkDescriptorSet]] |
125 ShaderSet[ShaderInputType, ShaderDescriptorType] = object | 153 ShaderSet[TShaderInputs] = object |
126 vertexShader: VkShaderModule | 154 vertexShader: VkShaderModule |
127 fragmentShader: VkShaderModule | 155 fragmentShader: VkShaderModule |
128 # TODO: I think this needs more fields? | 156 converter toVkIndexType(indexType: IndexType): VkIndexType = |
129 | 157 case indexType: |
130 proc CreatePipeline*[ShaderInputType, ShaderDescriptorType]( | 158 of None: VK_INDEX_TYPE_NONE_KHR |
159 of UInt8: VK_INDEX_TYPE_UINT8_EXT | |
160 of UInt16: VK_INDEX_TYPE_UINT16 | |
161 of UInt32: VK_INDEX_TYPE_UINT32 | |
162 | |
163 | |
164 proc CreatePipeline*[TShaderInputs]( | |
131 device: VkDevice, | 165 device: VkDevice, |
132 renderPass: VkRenderPass, | 166 renderPass: VkRenderPass, |
133 shaderSet: ShaderSet[ShaderInputType, ShaderDescriptorType], | 167 shaderSet: ShaderSet[TShaderInputs], |
134 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | 168 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, |
135 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, | 169 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, |
136 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, | 170 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, |
137 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, | 171 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, |
138 ): Pipeline = | 172 ): Pipeline[TShaderInputs] = |
139 # assumptions/limitations: | 173 # assumptions/limitations: |
140 # - we are only using vertex and fragment shaders (2 stages) | 174 # - we are only using vertex and fragment shaders (2 stages) |
141 # - we only support one subpass | 175 # - we only support one subpass |
142 | 176 |
143 # CONTINUE HERE, WITH PIPELINE LAYOUT!!!! | 177 # CONTINUE HERE, WITH PIPELINE LAYOUT!!!! |
144 # Rely on ShaderDescriptorType | 178 # Rely on TShaderInputs |
145 checkVkResult vkCreatePipelineLayout(device.vk, addr(pipelineLayoutInfo), nil, addr(result.layout)) | 179 |
180 var layoutbindings: seq[VkDescriptorSetLayoutBinding] | |
181 let descriptors = [ | |
182 (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1), # more than 1 for arrays | |
183 (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1), | |
184 ] | |
185 var descriptorBindingNumber = 0'u32 | |
186 ForDescriptorFields(default(TShaderInputs), fieldname, value, descriptorCount): | |
187 layoutbindings.add VkDescriptorSetLayoutBinding( | |
188 binding: descriptorBindingNumber, | |
189 descriptorType: descriptorType, | |
190 descriptorCount: descriptorCount, | |
191 stageFlags: VK_SHADER_STAGE_ALL_GRAPHICS, | |
192 pImmutableSamplers: nil, | |
193 ) | |
194 inc descriptorBindingNumber | |
195 var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( | |
196 sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | |
197 bindingCount: uint32(layoutbindings.len), | |
198 pBindings: layoutbindings.ToCPointer | |
199 ) | |
200 var descriptorSetLayout: VkDescriptorSetLayout | |
201 checkVkResult vkCreateDescriptorSetLayout(device.vk, addr(layoutCreateInfo), nil, addr(descriptorSetLayout)) | |
202 let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( | |
203 sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
204 setLayoutCount: 1, | |
205 pSetLayouts: addr(descriptorSetLayout), | |
206 # pushConstantRangeCount: uint32(pushConstants.len), | |
207 # pPushConstantRanges: pushConstants.ToCPointer, | |
208 ) | |
209 checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, addr(result.layout)) | |
146 | 210 |
147 let stages = [ | 211 let stages = [ |
148 VkPipelineShaderStageCreateInfo( | 212 VkPipelineShaderStageCreateInfo( |
149 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 213 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
150 stage: VK_SHADER_STAGE_VERTEX_BIT, | 214 stage: VK_SHADER_STAGE_VERTEX_BIT, |
161 let | 225 let |
162 bindings: var seq[VkVertexInputBindingDescription] | 226 bindings: var seq[VkVertexInputBindingDescription] |
163 attributes: var seq[VkVertexInputAttributeDescription] | 227 attributes: var seq[VkVertexInputAttributeDescription] |
164 var inputBindingNumber = 0'u32 | 228 var inputBindingNumber = 0'u32 |
165 var inputLocationNumber = 0'u32 | 229 var inputLocationNumber = 0'u32 |
166 ForAttributeFields(default(ShaderInputType), fieldname, value, isInstanceAttr): | 230 ForVertexDataFields(default(TShaderInputs), fieldname, value, isInstanceAttr): |
167 bindings.add VkVertexInputBindingDescription( | 231 bindings.add VkVertexInputBindingDescription( |
168 binding: inputBindingNumber, | 232 binding: inputBindingNumber, |
169 stride: sizeof(value).uint32, | 233 stride: sizeof(value).uint32, |
170 inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, | 234 inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, |
171 ) | 235 ) |
268 addr(createInfo), | 332 addr(createInfo), |
269 nil, | 333 nil, |
270 addr(result.pipeline) | 334 addr(result.pipeline) |
271 ) | 335 ) |
272 | 336 |
273 | 337 proc CreateRenderable[TMesh, TInstance]( |
274 proc Render*(renderable: Renderable, commandBuffer: VkCommandBuffer, pipeline: Pipeline, frameInFlight: int) = | 338 mesh: TMesh, |
275 assert 0 <= frameInFlight and frameInFlight < 2 | 339 instance: TInstance, |
340 buffers: RenderBuffers, | |
341 ): Renderable[TMesh, TInstance] = | |
342 result.indexType = None | |
343 | |
344 proc Bind(pipeline: Pipeline, commandBuffer: VkCommandBuffer, currentFrameInFlight: int) = | |
276 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) | 345 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) |
277 commandBuffer.vkCmdBindDescriptorSets( | 346 commandBuffer.vkCmdBindDescriptorSets( |
278 VK_PIPELINE_BIND_POINT_GRAPHICS, | 347 VK_PIPELINE_BIND_POINT_GRAPHICS, |
279 pipeline.layout, | 348 pipeline.layout, |
280 0, | 349 0, |
281 pipeline.descriptorSets[frameInFlight].len, | 350 pipeline.descriptorSets[currentFrameInFlight].len, |
282 pipeline.descriptorSets[frameInFlight], | 351 pipeline.descriptorSets[currentFrameInFlight], |
283 0, | 352 0, |
284 nil, | 353 nil, |
285 ) | 354 ) |
355 | |
356 proc AssertCompatible(TShaderInputs, TMesh, TInstance, TGlobals: typedesc) = | |
357 # assert seq-fields of TMesh|TInstance == seq-fields of TShaderInputs | |
358 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors | |
359 for inputName, inputValue in default(TShaderInputs).fieldPairs: | |
360 echo "checking shader input '" & inputName & "'" | |
361 var foundField = false | |
362 when hasCustomPragma(inputValue, VertexAttribute): | |
363 echo " is vertex attribute" | |
364 for meshName, meshValue in default(TMesh).fieldPairs: | |
365 when meshName == inputName: | |
366 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | |
367 assert getElementType(meshValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but mesh attribute is of type '" & getElementType(meshValue).name & "'" | |
368 foundField = true | |
369 assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TMesh.name & "'" | |
370 elif hasCustomPragma(inputValue, InstanceAttribute): | |
371 echo " is instance attribute" | |
372 for instanceName, instanceValue in default(TInstance).fieldPairs: | |
373 when instanceName == inputName: | |
374 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | |
375 assert getElementType(instanceValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but instance attribute is of type '" & getElementType(instanceValue).name & "'" | |
376 foundField = true | |
377 assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TInstance.name & "'" | |
378 elif hasCustomPragma(inputValue, DescriptorAttribute): | |
379 echo " is descriptor attribute" | |
380 for meshName, meshValue in default(TMesh).fieldPairs: | |
381 when meshName == inputName: | |
382 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | |
383 assert typeof(meshValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but mesh attribute is of type '" & getElementType(meshValue).name & "'" | |
384 foundField = true | |
385 for globalName, globalValue in default(TGlobals).fieldPairs: | |
386 when globalName == inputName: | |
387 assert foundField == false, "Shader input '" & TShaderInputs.name & "." & inputName & "' has been found more than once" | |
388 assert typeof(globalValue) is typeof(inputValue), "Shader input " & TShaderInputs.name & "." & inputName & " is of type '" & typeof(inputValue).name & "' but global attribute is of type '" & typeof(globalValue).name & "'" | |
389 foundField = true | |
390 assert foundField, "Shader input '" & TShaderInputs.name & "." & inputName & ": " & typeof(inputValue).name & "' not found in '" & TMesh.name & "|" & TGlobals.name & "'" | |
391 echo " found" | |
392 | |
393 | |
394 proc Render[TShaderInputs, TMesh, TInstance, TGlobals]( | |
395 pipeline: Pipeline[TShaderInputs], | |
396 renderable: Renderable[TMesh, TInstance], | |
397 globals: TGlobals, | |
398 commandBuffer: VkCommandBuffer, | |
399 ) = | |
400 static: | |
401 AssertCompatible(TShaderInputs, TMesh, TInstance, TGlobals) | |
286 commandBuffer.vkCmdBindVertexBuffers( | 402 commandBuffer.vkCmdBindVertexBuffers( |
287 firstBinding = 0'u32, | 403 firstBinding = 0'u32, |
288 bindingCount = uint32(renderable.buffers.len), | 404 bindingCount = uint32(renderable.vertexBuffers.len), |
289 pBuffers = renderable.buffers.ToCPointer(), | 405 pBuffers = renderable.vertexBuffers.ToCPointer(), |
290 pOffsets = renderable.offsets.ToCPointer() | 406 pOffsets = renderable.bufferOffsets.ToCPointer() |
291 ) | 407 ) |
292 if renderable.indexType != VK_INDEX_TYPE_NONE_KHR: | 408 if renderable.indexType != None: |
293 commandBuffer.vkCmdBindIndexBuffer( | 409 commandBuffer.vkCmdBindIndexBuffer( |
294 renderable.indexBuffer, | 410 renderable.indexBuffer, |
295 renderable.indexBufferOffset, | 411 renderable.indexBufferOffset, |
296 IndexType, | 412 renderable.indexType, |
297 ) | 413 ) |
298 commandBuffer.vkCmdDrawIndexed( | 414 commandBuffer.vkCmdDrawIndexed( |
299 indexCount = drawable.indexCount, | 415 indexCount = renderable.indexCount, |
300 instanceCount = drawable.instanceCount, | 416 instanceCount = renderable.instanceCount, |
301 firstIndex = 0, | 417 firstIndex = 0, |
302 vertexOffset = 0, | 418 vertexOffset = 0, |
303 firstInstance = 0 | 419 firstInstance = 0 |
304 ) | 420 ) |
305 else: | 421 else: |
306 commandBuffer.vkCmdDraw( | 422 commandBuffer.vkCmdDraw( |
307 vertexCount = drawable.vertexCount, | 423 vertexCount = renderable.vertexCount, |
308 instanceCount = drawable.instanceCount, | 424 instanceCount = renderable.instanceCount, |
309 firstVertex = 0, | 425 firstVertex = 0, |
310 firstInstance = 0 | 426 firstInstance = 0 |
311 ) | 427 ) |
428 | |
429 when isMainModule: | |
430 type | |
431 MeshA = object | |
432 position: seq[Vec3f] | |
433 transparency: float | |
434 InstanceA = object | |
435 transform: seq[Mat4] | |
436 position: seq[Vec3f] | |
437 Globals = object | |
438 color: Vec4f | |
439 | |
440 ShaderInputsA = object | |
441 position {.VertexAttribute.}: Vec3f | |
442 transform {.InstanceAttribute.}: Mat4 | |
443 color {.DescriptorAttribute.}: Vec4f | |
444 | |
445 var p: Pipeline[ShaderInputsA] | |
446 var r: Renderable[MeshA, InstanceA] | |
447 var g: Globals | |
448 var s: ShaderSet[ShaderInputsA] | |
449 | |
450 var p1 = CreatePipeline(device = VkDevice(0), renderPass = VkRenderPass(0), shaderSet = s) | |
451 Render(p, r, g, VkCommandBuffer(0)) |