Mercurial > games > semicongine
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() |