comparison static_utils.nim @ 1163:438d32d8b14f compiletime-tests

add: more static compilation stuff, code is getting a bit crazy, but also super nice API
author sam <sam@basx.dev>
date Fri, 21 Jun 2024 00:14:43 +0700
parents 46fae89cffb0
children 7b4d4d85d9f5
comparison
equal deleted inserted replaced
1162:46fae89cffb0 1163:438d32d8b14f
1 import std/os 1 import std/os
2 import std/enumerate
3 import std/hashes
2 import std/macros 4 import std/macros
3 import std/strformat 5 import std/strformat
6 import std/strutils
4 import std/typetraits as tt 7 import std/typetraits as tt
5 8
6 import semicongine/core/utils 9 import semicongine/core/utils
7 import semicongine/core/imagetypes 10 import semicongine/core/imagetypes
8 import semicongine/core/vector 11 import semicongine/core/vector
10 import semicongine/core/vulkanapi 13 import semicongine/core/vulkanapi
11 import semicongine/vulkan/buffer 14 import semicongine/vulkan/buffer
12 15
13 template VertexAttribute* {.pragma.} 16 template VertexAttribute* {.pragma.}
14 template InstanceAttribute* {.pragma.} 17 template InstanceAttribute* {.pragma.}
15 template Descriptor* {.pragma.}
16 template Pass* {.pragma.} 18 template Pass* {.pragma.}
17 template PassFlat* {.pragma.} 19 template PassFlat* {.pragma.}
18 template ShaderOutput* {.pragma.} 20 template ShaderOutput* {.pragma.}
19 21
22 const INFLIGHTFRAMES = 2
20 type 23 type
21 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] 24 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]
25 ShaderObject*[TShader] = object
26 vertexShader: VkShaderModule
27 fragmentShader: VkShaderModule
22 28
23 func VkType[T: SupportedGPUType](value: T): VkFormat = 29 func VkType[T: SupportedGPUType](value: T): VkFormat =
24 when T is float32: VK_FORMAT_R32_SFLOAT 30 when T is float32: VK_FORMAT_R32_SFLOAT
25 elif T is float64: VK_FORMAT_R64_SFLOAT 31 elif T is float64: VK_FORMAT_R64_SFLOAT
26 elif T is int8: VK_FORMAT_R8_SINT 32 elif T is int8: VK_FORMAT_R8_SINT
63 elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT 69 elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT
64 elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT 70 elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
65 elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT 71 elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
66 else: {.error: "Unsupported data type on GPU".} 72 else: {.error: "Unsupported data type on GPU".}
67 73
68 func GlslType[T: SupportedGPUType](value: T): string = 74 func GlslType[T: SupportedGPUType|Texture](value: T): string =
69 when T is float32: "float" 75 when T is float32: "float"
70 elif T is float64: "double" 76 elif T is float64: "double"
71 elif T is int8, int16, int32, int64: "int" 77 elif T is int8 or T is int16 or T is int32 or T is int64: "int"
72 elif T is uint8, uint16, uint32, uint64: "uint" 78 elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint"
73 elif T is TVec2[int32]: "ivec2" 79 elif T is TVec2[int32]: "ivec2"
74 elif T is TVec2[int64]: "ivec2" 80 elif T is TVec2[int64]: "ivec2"
75 elif T is TVec3[int32]: "ivec3" 81 elif T is TVec3[int32]: "ivec3"
76 elif T is TVec3[int64]: "ivec3" 82 elif T is TVec3[int64]: "ivec3"
77 elif T is TVec4[int32]: "ivec4" 83 elif T is TVec4[int32]: "ivec4"
88 elif T is TVec3[float64]: "dvec3" 94 elif T is TVec3[float64]: "dvec3"
89 elif T is TVec4[float32]: "vec4" 95 elif T is TVec4[float32]: "vec4"
90 elif T is TVec4[float64]: "dvec4" 96 elif T is TVec4[float64]: "dvec4"
91 elif T is TMat2[float32]: "mat2" 97 elif T is TMat2[float32]: "mat2"
92 elif T is TMat2[float64]: "dmat2" 98 elif T is TMat2[float64]: "dmat2"
93 elif T is TMat23F32]: "mat23" 99 elif T is TMat23[float32]: "mat23"
94 elif T is TMat23[float64]: "dmat23" 100 elif T is TMat23[float64]: "dmat23"
95 elif T is TMat32[float32]: "mat32" 101 elif T is TMat32[float32]: "mat32"
96 elif T is TMat32[float64]: "dmat32" 102 elif T is TMat32[float64]: "dmat32"
97 elif T is TMat3[float32]: "mat3" 103 elif T is TMat3[float32]: "mat3"
98 elif T is TMat3[float64]: "dmat3" 104 elif T is TMat3[float64]: "dmat3"
103 elif T is TMat4[float32]: "mat4" 109 elif T is TMat4[float32]: "mat4"
104 elif T is TMat4[float64]: "dmat4" 110 elif T is TMat4[float64]: "dmat4"
105 elif T is Texture: "sampler2D" 111 elif T is Texture: "sampler2D"
106 else: {.error: "Unsupported data type on GPU".} 112 else: {.error: "Unsupported data type on GPU".}
107 113
108 template getElementType(field: typed): untyped =
109 when not (typeof(field) is seq or typeof(field) is array):
110 typeof(field)
111 # {.error: "getElementType can only be used with seq or array".}
112 else:
113 genericParams(typeof(field)).get(0)
114
115 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = 114 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped =
116 for theFieldname, value in fieldPairs(inputData): 115 for theFieldname, value in fieldPairs(inputData):
117 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): 116 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute):
118 when not typeof(value) is seq: 117 when not typeof(value) is seq:
119 {.error: "field '" & theFieldname & "' needs to be a seq".} 118 {.error: "field '" & theFieldname & "' needs to be a seq".}
123 let `fieldname` {.inject.} = theFieldname 122 let `fieldname` {.inject.} = theFieldname
124 let `valuename` {.inject.} = value 123 let `valuename` {.inject.} = value
125 let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) 124 let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute)
126 body 125 body
127 126
128 template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped = 127 template ForDescriptorFields*(inputData: typed, typename, countname, body: untyped): untyped =
129 for theFieldname, value in fieldPairs(inputData): 128 for theFieldname, value in fieldPairs(inputData):
130 when hasCustomPragma(value, Descriptor): 129 when typeof(value) is Texture:
131 when not (
132 typeof(value) is SupportedGPUType or
133 typeof(value) is Texture or
134 (typeof(value) is array and getElementType(value) is SupportedGPUType)
135 ):
136 {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType or a Texture".}
137 block: 130 block:
138 let `fieldname` {.inject.} = theFieldname 131 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
139 let `valuename` {.inject.} = default(getElementType(value)) 132 let `countname` {.inject.} = 1'u32
140 133 body
141 when typeof(value) is Texture or (typeof(value) is array and getElementType(value) is Texture): 134 elif typeof(value) is object:
135 block:
136 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
137 let `countname` {.inject.} = 1'u32
138 body
139 elif typeof(value) is array:
140 when elementType(value) is Texture:
141 block:
142 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER 142 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
143 else: 143 let `countname` {.inject.} = uint32(typeof(value).len)
144 body
145 elif elementType(value) is object:
146 block:
144 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER 147 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
145 148 let `countname` {.inject.} = uint32(typeof(value).len)
146 when typeof(value) is SupportedGPUType or typeof(value) is Texture: 149 body
147 let `countname` {.inject.} = 1'u32 150
148 else: 151 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](value: T): uint32 =
149 assert typeof(value) is array
150 let `countname` {.inject.} = uint32(genericParams(typeof(value)).get(0))
151 body
152
153 func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 =
154 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: 152 when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]:
155 2 153 2
156 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]: 154 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]:
157 3 155 3
158 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: 156 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]:
206 indexCount: uint32 204 indexCount: uint32
207 indexBufferOffset: VkDeviceSize 205 indexBufferOffset: VkDeviceSize
208 Pipeline[TShader] = object 206 Pipeline[TShader] = object
209 pipeline: VkPipeline 207 pipeline: VkPipeline
210 layout: VkPipelineLayout 208 layout: VkPipelineLayout
211 descriptorSets: array[2, seq[VkDescriptorSet]] 209 descriptorSets: array[INFLIGHTFRAMES, seq[VkDescriptorSet]]
212 210
213 converter toVkIndexType(indexType: IndexType): VkIndexType = 211 converter toVkIndexType(indexType: IndexType): VkIndexType =
214 case indexType: 212 case indexType:
215 of None: VK_INDEX_TYPE_NONE_KHR 213 of None: VK_INDEX_TYPE_NONE_KHR
216 of UInt8: VK_INDEX_TYPE_UINT8_EXT 214 of UInt8: VK_INDEX_TYPE_UINT8_EXT
234 let 232 let
235 stagename = stage2string(stage) 233 stagename = stage2string(stage)
236 shaderHash = hash(shaderSource) 234 shaderHash = hash(shaderSource)
237 shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}" 235 shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}"
238 236
239
240 if not shaderfile.fileExists: 237 if not shaderfile.fileExists:
241 echo "shader of type ", stage, ", entrypoint ", entrypoint 238 echo "shader of type ", stage
242 for i, line in enumerate(shaderSource.splitlines()): 239 for i, line in enumerate(shaderSource.splitlines()):
243 echo " ", i + 1, " ", line 240 echo " ", i + 1, " ", line
244 var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" 241 # var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator"
242 var glslExe = currentSourcePath.parentDir / "tools" / "glslangValidator"
245 when defined(windows): 243 when defined(windows):
246 glslExe = glslExe & "." & ExeExt 244 glslExe = glslExe & "." & ExeExt
247 let command = &"{glslExe} --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderfile}" 245 let command = &"{glslExe} --entry-point main -V --stdin -S {stagename} -o {shaderfile}"
248 echo "run: ", command 246 echo "run: ", command
249 discard StaticExecChecked( 247 discard StaticExecChecked(
250 command = command, 248 command = command,
251 input = shaderSource 249 input = shaderSource
252 ) 250 )
274 var vsOutput: seq[string] 272 var vsOutput: seq[string]
275 var fsInput: seq[string] 273 var fsInput: seq[string]
276 var fsOutput: seq[string] 274 var fsOutput: seq[string]
277 var uniforms: seq[string] 275 var uniforms: seq[string]
278 var samplers: seq[string] 276 var samplers: seq[string]
279 var vsInputLocation = 0 277 var vsInputLocation = 0'u32
280 var passLocation = 0 278 var passLocation = 0
281 var fsOutputLocation = 0 279 var fsOutputLocation = 0
282 var binding = 0 280 var descriptorBinding = 0
283 281
284 for fieldname, value in fieldPairs(shader): 282 for fieldname, value in fieldPairs(shader):
285 # vertex shader inputs 283 # vertex shader inputs
286 if hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute): 284 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute):
287 assert typeof(value) is SupportedGPUType 285 assert typeof(value) is SupportedGPUType
288 vsInput.add &"layout(location = {vsInputLocation}) in {GlslType(value)} {fieldname};" 286 vsInput.add "layout(location = " & $vsInputLocation & ") in " & GlslType(value) & " " & fieldname & ";"
289 for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value): 287 for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value):
290 vsInputLocation += NLocationSlots(value) 288 vsInputLocation += NLocationSlots(value)
291 # intermediate values, passed between shaders 289 # intermediate values, passed between shaders
292 if hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat): 290 elif hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat):
293 let flat = if hasCustomPragma(value, PassFlat): "flat " else "" 291 let flat = if hasCustomPragma(value, PassFlat): "flat " else: ""
294 vsOutput.add &"layout(location = {passLocation}) {flat}out {GlslType(value)} {fieldname};" 292 vsOutput.add "layout(location = " & $passLocation & ") " & flat & "out " & GlslType(value) & " " & fieldname & ";"
295 fsInput.add &"layout(location = {passLocation}) {flat}in {GlslType(value)} {fieldname};" 293 fsInput.add "layout(location = " & $passLocation & ") " & flat & "in " & GlslType(value) & " " & fieldname & ";"
296 passLocation.inc 294 passLocation.inc
297 if hasCustomPragma(value, ShaderOutput): 295 elif hasCustomPragma(value, ShaderOutput):
298 fsOutput.add &"layout(location = {fsOutputLocation}) out {GlslType(value)} {fieldname};" 296 fsOutput.add &"layout(location = " & $fsOutputLocation & ") out " & GlslType(value) & " " & fieldname & ";"
299 fsOutputLocation.inc 297 fsOutputLocation.inc
300 if hasCustomPragma(value, Descriptor): 298 elif typeof(value) is Texture:
301 # TODO; samplers and uniforms 299 samplers.add "layout(binding = " & $descriptorBinding & ") uniform " & GlslType(value) & " " & fieldname & ";"
302 if typeof(value) is Texture: 300 descriptorBinding.inc
301 elif typeof(value) is object:
302 # TODO
303 uniforms.add ""
304 descriptorBinding.inc
305 elif typeof(value) is array:
306 when elementType(value) is Texture:
307 let arrayDecl = "[" & $typeof(value).len & "]"
308 samplers.add "layout(binding = " & $descriptorBinding & ") uniform " & GlslType(default(elementType(value))) & " " & fieldname & "" & arrayDecl & ";"
309 descriptorBinding.inc
310 elif elementType(value) is object:
311 # TODO
312 let arrayDecl = "[" & $typeof(value).len & "]"
313 # uniforms.add "layout(binding = " & $descriptorBinding & ") uniform " & GlslType(elementType(value)) & " " & fieldname & "" & arrayDecl & ";"
314 descriptorBinding.inc
303 else: 315 else:
316 {.error: "Unsupported shader field " & fieldname.}
317 elif fieldname in ["vertexCode", "fragmentCode"]:
318 discard
319 else:
320 {.error: "Unsupported shader field '" & tt.name(TShader) & "." & fieldname & "' of type " & tt.name(typeof(value)).}
304 321
305 result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] & 322 result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] &
306 vsInput & 323 vsInput &
307 uniforms & 324 uniforms &
308 samplers & 325 samplers &
314 uniforms & 331 uniforms &
315 samplers & 332 samplers &
316 fsOutput & 333 fsOutput &
317 @[shader.fragmentCode]).join("\n") 334 @[shader.fragmentCode]).join("\n")
318 335
319 proc CompileShader[TShader](shader: TShader): (seq[uint32], seq[uint32]) {.compileTime.} = 336 # proc CompileShader[TShader](shader: static TShader): (seq[uint32], seq[uint32]) {.compileTime.}=
320 let (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader) 337 proc CompileShader[TShader](device: VkDevice, shader: static TShader): ShaderObject[TShader] =
321 ( 338 const (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader)
322 compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource), 339
323 compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource) 340 let vertexBinary = compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource)
324 ) 341 let fragmentBinary = compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource)
342
343 var createInfoVertex = VkShaderModuleCreateInfo(
344 sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
345 codeSize: csize_t(vertexBinary.len * sizeof(uint32)),
346 pCode: vertexBinary.ToCPointer,
347 )
348 checkVkResult device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result.vertexShader))
349 var createInfoFragment = VkShaderModuleCreateInfo(
350 sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
351 codeSize: csize_t(fragmentBinary.len * sizeof(uint32)),
352 pCode: fragmentBinary.ToCPointer,
353 )
354 checkVkResult device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result.fragmentShader))
325 355
326 356
327 proc CreatePipeline*[TShader]( 357 proc CreatePipeline*[TShader](
328 device: VkDevice, 358 device: VkDevice,
329 renderPass: VkRenderPass, 359 renderPass: VkRenderPass,
330 vertexShader: VkShaderModule, 360 shader: ShaderObject[TShader],
331 fragmentShader: VkShaderModule,
332 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 361 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
333 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, 362 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL,
334 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, 363 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT,
335 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, 364 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE,
336 ): Pipeline[TShader] = 365 ): Pipeline[TShader] =
339 # - we only support one subpass 368 # - we only support one subpass
340 # = we only support one Uniform-Block 369 # = we only support one Uniform-Block
341 370
342 var layoutbindings: seq[VkDescriptorSetLayoutBinding] 371 var layoutbindings: seq[VkDescriptorSetLayoutBinding]
343 var descriptorBindingNumber = 0'u32 372 var descriptorBindingNumber = 0'u32
344 ForDescriptorFields(default(TShader), fieldname, value, descriptorType, descriptorCount): 373 ForDescriptorFields(default(TShader), descriptorType, descriptorCount):
345 # TODO: Only one binding needed for a Uniforms block
346 layoutbindings.add VkDescriptorSetLayoutBinding( 374 layoutbindings.add VkDescriptorSetLayoutBinding(
347 binding: descriptorBindingNumber, 375 binding: descriptorBindingNumber,
348 descriptorType: descriptorType, 376 descriptorType: descriptorType,
349 descriptorCount: descriptorCount, 377 descriptorCount: descriptorCount,
350 stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), 378 stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS),
369 397
370 let stages = [ 398 let stages = [
371 VkPipelineShaderStageCreateInfo( 399 VkPipelineShaderStageCreateInfo(
372 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 400 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
373 stage: VK_SHADER_STAGE_VERTEX_BIT, 401 stage: VK_SHADER_STAGE_VERTEX_BIT,
374 module: vertexShader, 402 module: shader.vertexShader,
375 pName: "main", 403 pName: "main",
376 ), 404 ),
377 VkPipelineShaderStageCreateInfo( 405 VkPipelineShaderStageCreateInfo(
378 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 406 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
379 stage: VK_SHADER_STAGE_FRAGMENT_BIT, 407 stage: VK_SHADER_STAGE_FRAGMENT_BIT,
380 module: fragmentShader, 408 module: shader.fragmentShader,
381 pName: "main", 409 pName: "main",
382 ), 410 ),
383 ] 411 ]
384 var 412 var
385 bindings: seq[VkVertexInputBindingDescription] 413 bindings: seq[VkVertexInputBindingDescription]
504 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) 532 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline)
505 commandBuffer.vkCmdBindDescriptorSets( 533 commandBuffer.vkCmdBindDescriptorSets(
506 VK_PIPELINE_BIND_POINT_GRAPHICS, 534 VK_PIPELINE_BIND_POINT_GRAPHICS,
507 pipeline.layout, 535 pipeline.layout,
508 0, 536 0,
509 pipeline.descriptorSets[currentFrameInFlight].len, 537 pipeline.descriptorSets[currentFrameInFlight].len.uint32,
510 pipeline.descriptorSets[currentFrameInFlight], 538 pipeline.descriptorSets[currentFrameInFlight].ToCPointer,
511 0, 539 0,
512 nil, 540 nil,
513 ) 541 )
514 542
515 proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) = 543 proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) =
516 # assert seq-fields of TMesh|TInstance == seq-fields of TShader 544 # assert seq-fields of TMesh|TInstance == seq-fields of TShader
517 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors 545 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors
518 for inputName, inputValue in default(TShader).fieldPairs: 546 for inputName, inputValue in default(TShader).fieldPairs:
519 echo "checking shader input '" & inputName & "'"
520 var foundField = false 547 var foundField = false
521 when hasCustomPragma(inputValue, VertexAttribute): 548 when hasCustomPragma(inputValue, VertexAttribute):
522 echo " is vertex attribute" 549 assert typeof(inputValue) is SupportedGPUType
523 for meshName, meshValue in default(TMesh).fieldPairs: 550 for meshName, meshValue in default(TMesh).fieldPairs:
524 when meshName == inputName: 551 when meshName == inputName:
525 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" 552 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
526 assert getElementType(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(getElementType(meshValue)) & "'" 553 assert elementType(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue)) & "'"
527 foundField = true 554 foundField = true
528 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'" 555 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'"
529 elif hasCustomPragma(inputValue, InstanceAttribute): 556 elif hasCustomPragma(inputValue, InstanceAttribute):
530 echo " is instance attribute" 557 assert typeof(inputValue) is SupportedGPUType
531 for instanceName, instanceValue in default(TInstance).fieldPairs: 558 for instanceName, instanceValue in default(TInstance).fieldPairs:
532 when instanceName == inputName: 559 when instanceName == inputName:
533 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" 560 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
534 assert getElementType(instanceValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but instance attribute is of type '" & tt.name(getElementType(instanceValue)) & "'" 561 assert elementType(instanceValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but instance attribute is of type '" & tt.name(elementType(instanceValue)) & "'"
535 foundField = true 562 foundField = true
536 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'" 563 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'"
537 elif hasCustomPragma(inputValue, Descriptor): 564 elif typeof(inputValue) is Texture or typeof(inputValue) is object:
538 echo " is descriptor attribute"
539 for meshName, meshValue in default(TMesh).fieldPairs: 565 for meshName, meshValue in default(TMesh).fieldPairs:
540 when meshName == inputName: 566 when meshName == inputName:
541 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" 567 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
542 assert typeof(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(getElementType(meshValue)) & "'" 568 assert typeof(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue)) & "'"
543 foundField = true 569 foundField = true
544 for globalName, globalValue in default(TGlobals).fieldPairs: 570 for globalName, globalValue in default(TGlobals).fieldPairs:
545 when globalName == inputName: 571 when globalName == inputName:
546 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" 572 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
547 assert typeof(globalValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue)) & "'" 573 assert typeof(globalValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue)) & "'"
548 foundField = true 574 foundField = true
549 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" 575 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'"
550 echo " found" 576 elif typeof(inputValue) is array:
577 when (elementType(inputValue) is Texture or elementType(inputValue) is object):
578 for meshName, meshValue in default(TMesh).fieldPairs:
579 when meshName == inputName:
580 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
581 assert typeof(meshValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue)) & "'"
582 foundField = true
583 for globalName, globalValue in default(TGlobals).fieldPairs:
584 when globalName == inputName:
585 assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once"
586 assert typeof(globalValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue)) & "'"
587 foundField = true
588 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'"
551 589
552 590
553 proc Render[TShader, TMesh, TInstance, TGlobals]( 591 proc Render[TShader, TMesh, TInstance, TGlobals](
554 pipeline: Pipeline[TShader], 592 pipeline: Pipeline[TShader],
555 renderable: Renderable[TMesh, TInstance], 593 renderable: Renderable[TMesh, TInstance],
585 firstInstance = 0 623 firstInstance = 0
586 ) 624 )
587 625
588 when isMainModule: 626 when isMainModule:
589 import semicongine/platform/window 627 import semicongine/platform/window
590 import semicongine/core/vulkanapi
591 import semicongine/vulkan/instance 628 import semicongine/vulkan/instance
592 import semicongine/vulkan/device 629 import semicongine/vulkan/device
593 import semicongine/vulkan/physicaldevice 630 import semicongine/vulkan/physicaldevice
594 import semicongine/vulkan/renderpass 631 import semicongine/vulkan/renderpass
632 import semicongine/vulkan/commandbuffer
633 import std/options
595 634
596 type 635 type
636 MaterialA = object
637 reflection: float32
638 baseColor: Vec3f
639 ShaderSettings = object
640 brightness: float32
597 MeshA = object 641 MeshA = object
598 position: seq[Vec3f] 642 position: seq[Vec3f]
599 transparency: float 643 transparency: float
644 material: array[3, MaterialA]
645 materialTextures: array[3, Texture]
600 InstanceA = object 646 InstanceA = object
601 transform: seq[Mat4] 647 transform: seq[Mat4]
602 position: seq[Vec3f] 648 position: seq[Vec3f]
603 other: seq[array[3, int32]]
604 Globals = object 649 Globals = object
605 fontAtlas: Texture 650 fontAtlas: Texture
651 settings: ShaderSettings
606 652
607 ShaderA = object 653 ShaderA = object
654 # vertex input
608 position {.VertexAttribute.}: Vec3f 655 position {.VertexAttribute.}: Vec3f
609 transform {.InstanceAttribute.}: Mat4 656 transform {.InstanceAttribute.}: Mat4
610 fontAtlas {.Descriptor.}: Texture 657 # intermediate
611 other {.InstanceAttribute.}: array[3, int32]
612 test {.Pass.}: float32 658 test {.Pass.}: float32
613 test1 {.PassFlat.}: Vec3f 659 test1 {.PassFlat.}: Vec3f
660 # output
614 color {.ShaderOutput.}: Vec4f 661 color {.ShaderOutput.}: Vec4f
662 # uniforms
663 material: array[3, MaterialA]
664 settings: ShaderSettings
665 # textures
666 fontAtlas: Texture
667 materialTextures: array[3, Texture]
668 # code
615 vertexCode: string = "void main() {}" 669 vertexCode: string = "void main() {}"
616 fragmentCode: string = "void main() {}" 670 fragmentCode: string = "void main() {}"
617 671
618 let w = CreateWindow("test2") 672 let w = CreateWindow("test2")
619 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") 673 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")
621 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), 675 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
622 instanceExtensions = @[], 676 instanceExtensions = @[],
623 layers = @["VK_LAYER_KHRONOS_validation"], 677 layers = @["VK_LAYER_KHRONOS_validation"],
624 ) 678 )
625 679
626 const (a, b) = CompileShader(Shader(A))
627 680
628 let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics() 681 let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics()
629 let d = i.CreateDevice( 682 let d = i.CreateDevice(
630 selectedPhysicalDevice, 683 selectedPhysicalDevice,
631 enabledExtensions = @[], 684 enabledExtensions = @[],
632 selectedPhysicalDevice.FilterForGraphicsPresentationQueues() 685 selectedPhysicalDevice.FilterForGraphicsPresentationQueues()
633 ) 686 )
634 687
635 var p: Pipeline[ShaderA]
636 var r: Renderable[MeshA, InstanceA] 688 var r: Renderable[MeshA, InstanceA]
637 var g: Globals 689 var g: Globals
638 690
639 let rp = CreateRenderPass(d.vk, d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format) 691 const shader = ShaderA()
640 var p1 = CreatePipeline[ShaderA](device = d.vk, renderPass = rp, VkShaderModule(0), VkShaderModule(0)) 692 let shaderObject = d.vk.CompileShader(shader)
641 Render(p, r, g, VkCommandBuffer(0)) 693 let rp = d.vk.CreateRenderPass(d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format)
694 var p = CreatePipeline(d.vk, renderPass = rp, shaderObject)
695
696 let commandBufferPool = d.CreateCommandBufferPool(d.FirstGraphicsQueue().get().family, INFLIGHTFRAMES)
697 let cmd = commandBufferPool.buffers[0]
698
699 checkVkResult cmd.vkResetCommandBuffer(VkCommandBufferResetFlags(0))
700 let beginInfo = VkCommandBufferBeginInfo(
701 sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
702 flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT),
703 )
704 checkVkResult cmd.vkBeginCommandBuffer(addr(beginInfo))
705 p.Bind(cmd, currentFrameInFlight = 0)
706 p.Render(r, g, cmd)
707
708 checkVkResult cmd.vkEndCommandBuffer()