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))