1162
|
1 import std/os
|
1159
|
2 import std/macros
|
1161
|
3 import std/strformat
|
1162
|
4 import std/typetraits as tt
|
1159
|
5
|
1161
|
6 import semicongine/core/utils
|
|
7 import semicongine/core/imagetypes
|
1159
|
8 import semicongine/core/vector
|
|
9 import semicongine/core/matrix
|
|
10 import semicongine/core/vulkanapi
|
1161
|
11 import semicongine/vulkan/buffer
|
1159
|
12
|
|
13 template VertexAttribute* {.pragma.}
|
|
14 template InstanceAttribute* {.pragma.}
|
1162
|
15 template Descriptor* {.pragma.}
|
|
16 template Pass* {.pragma.}
|
|
17 template PassFlat* {.pragma.}
|
|
18 template ShaderOutput* {.pragma.}
|
1159
|
19
|
|
20 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]
|
|
22
|
|
23 func VkType[T: SupportedGPUType](value: T): VkFormat =
|
|
24 when T is float32: VK_FORMAT_R32_SFLOAT
|
|
25 elif T is float64: VK_FORMAT_R64_SFLOAT
|
|
26 elif T is int8: VK_FORMAT_R8_SINT
|
|
27 elif T is int16: VK_FORMAT_R16_SINT
|
|
28 elif T is int32: VK_FORMAT_R32_SINT
|
|
29 elif T is int64: VK_FORMAT_R64_SINT
|
|
30 elif T is uint8: VK_FORMAT_R8_UINT
|
|
31 elif T is uint16: VK_FORMAT_R16_UINT
|
|
32 elif T is uint32: VK_FORMAT_R32_UINT
|
|
33 elif T is uint64: VK_FORMAT_R64_UINT
|
|
34 elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT
|
|
35 elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT
|
|
36 elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT
|
|
37 elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT
|
|
38 elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT
|
|
39 elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT
|
|
40 elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT
|
|
41 elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT
|
|
42 elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT
|
|
43 elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT
|
|
44 elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT
|
|
45 elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT
|
|
46 elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT
|
|
47 elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT
|
|
48 elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT
|
|
49 elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT
|
|
50 elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
|
|
51 elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
|
1162
|
52 elif T is TMat2[float32]: VK_FORMAT_R32G32_SFLOAT
|
|
53 elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT
|
|
54 elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT
|
|
55 elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT
|
|
56 elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT
|
|
57 elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT
|
|
58 elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT
|
|
59 elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT
|
|
60 elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
|
|
61 elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
|
|
62 elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT
|
|
63 elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT
|
|
64 elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
|
|
65 elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
|
|
66 else: {.error: "Unsupported data type on GPU".}
|
|
67
|
|
68 func GlslType[T: SupportedGPUType](value: T): string =
|
|
69 when T is float32: "float"
|
|
70 elif T is float64: "double"
|
|
71 elif T is int8, int16, int32, int64: "int"
|
|
72 elif T is uint8, uint16, uint32, uint64: "uint"
|
|
73 elif T is TVec2[int32]: "ivec2"
|
|
74 elif T is TVec2[int64]: "ivec2"
|
|
75 elif T is TVec3[int32]: "ivec3"
|
|
76 elif T is TVec3[int64]: "ivec3"
|
|
77 elif T is TVec4[int32]: "ivec4"
|
|
78 elif T is TVec4[int64]: "ivec4"
|
|
79 elif T is TVec2[uint32]: "uvec2"
|
|
80 elif T is TVec2[uint64]: "uvec2"
|
|
81 elif T is TVec3[uint32]: "uvec3"
|
|
82 elif T is TVec3[uint64]: "uvec3"
|
|
83 elif T is TVec4[uint32]: "uvec4"
|
|
84 elif T is TVec4[uint64]: "uvec4"
|
|
85 elif T is TVec2[float32]: "vec2"
|
|
86 elif T is TVec2[float64]: "dvec2"
|
|
87 elif T is TVec3[float32]: "vec3"
|
|
88 elif T is TVec3[float64]: "dvec3"
|
|
89 elif T is TVec4[float32]: "vec4"
|
|
90 elif T is TVec4[float64]: "dvec4"
|
|
91 elif T is TMat2[float32]: "mat2"
|
|
92 elif T is TMat2[float64]: "dmat2"
|
|
93 elif T is TMat23F32]: "mat23"
|
|
94 elif T is TMat23[float64]: "dmat23"
|
|
95 elif T is TMat32[float32]: "mat32"
|
|
96 elif T is TMat32[float64]: "dmat32"
|
|
97 elif T is TMat3[float32]: "mat3"
|
|
98 elif T is TMat3[float64]: "dmat3"
|
|
99 elif T is TMat34[float32]: "mat34"
|
|
100 elif T is TMat34[float64]: "dmat34"
|
|
101 elif T is TMat43[float32]: "mat43"
|
|
102 elif T is TMat43[float64]: "dmat43"
|
|
103 elif T is TMat4[float32]: "mat4"
|
|
104 elif T is TMat4[float64]: "dmat4"
|
|
105 elif T is Texture: "sampler2D"
|
1159
|
106 else: {.error: "Unsupported data type on GPU".}
|
|
107
|
|
108 template getElementType(field: typed): untyped =
|
|
109 when not (typeof(field) is seq or typeof(field) is array):
|
1162
|
110 typeof(field)
|
|
111 # {.error: "getElementType can only be used with seq or array".}
|
|
112 else:
|
|
113 genericParams(typeof(field)).get(0)
|
1159
|
114
|
1161
|
115 template ForVertexDataFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped =
|
1159
|
116 for theFieldname, value in fieldPairs(inputData):
|
1161
|
117 when hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute):
|
1159
|
118 when not typeof(value) is seq:
|
|
119 {.error: "field '" & theFieldname & "' needs to be a seq".}
|
|
120 when not typeof(value) is SupportedGPUType:
|
|
121 {.error: "field '" & theFieldname & "' is not a supported GPU type".}
|
|
122 block:
|
|
123 let `fieldname` {.inject.} = theFieldname
|
1162
|
124 let `valuename` {.inject.} = value
|
|
125 let `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute)
|
1159
|
126 body
|
|
127
|
1161
|
128 template ForDescriptorFields*(inputData: typed, fieldname, valuename, typename, countname, body: untyped): untyped =
|
|
129 for theFieldname, value in fieldPairs(inputData):
|
1162
|
130 when hasCustomPragma(value, Descriptor):
|
1161
|
131 when not (
|
1162
|
132 typeof(value) is SupportedGPUType or
|
|
133 typeof(value) is Texture or
|
|
134 (typeof(value) is array and getElementType(value) is SupportedGPUType)
|
1161
|
135 ):
|
1162
|
136 {.error: "field '" & theFieldname & "' needs to be a SupportedGPUType or an array of SupportedGPUType or a Texture".}
|
1161
|
137 block:
|
|
138 let `fieldname` {.inject.} = theFieldname
|
|
139 let `valuename` {.inject.} = default(getElementType(value))
|
|
140
|
1162
|
141 when typeof(value) is Texture or (typeof(value) is array and getElementType(value) is Texture):
|
|
142 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
|
|
143 else:
|
|
144 let `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
|
1161
|
145
|
1162
|
146 when typeof(value) is SupportedGPUType or typeof(value) is Texture:
|
|
147 let `countname` {.inject.} = 1'u32
|
1161
|
148 else:
|
1162
|
149 assert typeof(value) is array
|
|
150 let `countname` {.inject.} = uint32(genericParams(typeof(value)).get(0))
|
1161
|
151 body
|
|
152
|
1159
|
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]:
|
|
155 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]:
|
|
157 3
|
|
158 elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]:
|
|
159 4
|
|
160 else:
|
|
161 1
|
|
162
|
1162
|
163 func NLocationSlots[T: SupportedGPUType|Texture](value: T): uint32 =
|
1159
|
164 #[
|
|
165 single location:
|
1162
|
166 - any scalar
|
|
167 - any 16-bit vector
|
|
168 - any 32-bit vector
|
|
169 - any 64-bit vector that has max. 2 components
|
1159
|
170 16-bit scalar and vector types, and
|
|
171 32-bit scalar and vector types, and
|
|
172 64-bit scalar and 2-component vector types.
|
|
173 two locations
|
|
174 64-bit three- and four-component vectors
|
|
175 ]#
|
1162
|
176 when T is TVec3[int64] or
|
|
177 T is TVec4[int64] or
|
|
178 T is TVec3[uint64] or
|
|
179 T is TVec4[uint64] or
|
|
180 T is TVec3[float64] or
|
|
181 T is TVec4[float64] or
|
|
182 T is TMat23[float64] or
|
|
183 T is TMat3[float64] or
|
|
184 T is TMat34[float64] or
|
|
185 T is TMat43[float64] or
|
|
186 T is TMat4[float64]:
|
1159
|
187 return 2
|
|
188 else:
|
|
189 return 1
|
|
190
|
|
191 type
|
1161
|
192 IndexType = enum
|
|
193 None, UInt8, UInt16, UInt32
|
|
194 RenderBuffers = object
|
|
195 deviceBuffers: seq[Buffer] # for fast reads
|
|
196 hostVisibleBuffers: seq[Buffer] # for fast writes
|
|
197 Renderable[TMesh, TInstance] = object
|
|
198 vertexBuffers: seq[VkBuffer]
|
|
199 bufferOffsets: seq[VkDeviceSize]
|
1159
|
200 instanceCount: uint32
|
1161
|
201 case indexType: IndexType
|
|
202 of None:
|
1160
|
203 vertexCount: uint32
|
1161
|
204 else:
|
1160
|
205 indexBuffer: VkBuffer
|
|
206 indexCount: uint32
|
|
207 indexBufferOffset: VkDeviceSize
|
1162
|
208 Pipeline[TShader] = object
|
1159
|
209 pipeline: VkPipeline
|
|
210 layout: VkPipelineLayout
|
|
211 descriptorSets: array[2, seq[VkDescriptorSet]]
|
1162
|
212
|
1161
|
213 converter toVkIndexType(indexType: IndexType): VkIndexType =
|
|
214 case indexType:
|
|
215 of None: VK_INDEX_TYPE_NONE_KHR
|
|
216 of UInt8: VK_INDEX_TYPE_UINT8_EXT
|
|
217 of UInt16: VK_INDEX_TYPE_UINT16
|
|
218 of UInt32: VK_INDEX_TYPE_UINT32
|
1159
|
219
|
1162
|
220 proc compileGlslToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string): seq[uint32] {.compileTime.} =
|
|
221 func stage2string(stage: VkShaderStageFlagBits): string {.compileTime.} =
|
|
222 case stage
|
|
223 of VK_SHADER_STAGE_VERTEX_BIT: "vert"
|
|
224 of VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: "tesc"
|
|
225 of VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: "tese"
|
|
226 of VK_SHADER_STAGE_GEOMETRY_BIT: "geom"
|
|
227 of VK_SHADER_STAGE_FRAGMENT_BIT: "frag"
|
|
228 of VK_SHADER_STAGE_COMPUTE_BIT: "comp"
|
|
229 else: ""
|
1161
|
230
|
1162
|
231 when defined(nimcheck): # will not run if nimcheck is running
|
|
232 return result
|
|
233
|
|
234 let
|
|
235 stagename = stage2string(stage)
|
|
236 shaderHash = hash(shaderSource)
|
|
237 shaderfile = getTempDir() / &"shader_{shaderHash}.{stagename}"
|
|
238
|
|
239
|
|
240 if not shaderfile.fileExists:
|
|
241 echo "shader of type ", stage, ", entrypoint ", entrypoint
|
|
242 for i, line in enumerate(shaderSource.splitlines()):
|
|
243 echo " ", i + 1, " ", line
|
|
244 var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator"
|
|
245 when defined(windows):
|
|
246 glslExe = glslExe & "." & ExeExt
|
|
247 let command = &"{glslExe} --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderfile}"
|
|
248 echo "run: ", command
|
|
249 discard StaticExecChecked(
|
|
250 command = command,
|
|
251 input = shaderSource
|
|
252 )
|
|
253 else:
|
|
254 echo &"shaderfile {shaderfile} is up-to-date"
|
|
255
|
|
256 when defined(mingw) and defined(linux): # required for crosscompilation, path separators get messed up
|
|
257 let shaderbinary = staticRead shaderfile.replace("\\", "/")
|
|
258 else:
|
|
259 let shaderbinary = staticRead shaderfile
|
|
260
|
|
261 var i = 0
|
|
262 while i < shaderbinary.len:
|
|
263 result.add(
|
|
264 (uint32(shaderbinary[i + 0]) shl 0) or
|
|
265 (uint32(shaderbinary[i + 1]) shl 8) or
|
|
266 (uint32(shaderbinary[i + 2]) shl 16) or
|
|
267 (uint32(shaderbinary[i + 3]) shl 24)
|
|
268 )
|
|
269 i += 4
|
|
270
|
|
271 proc generateShaderSource[TShader](shader: TShader): (string, string) {.compileTime.} =
|
|
272 const GLSL_VERSION = "450"
|
|
273 var vsInput: seq[string]
|
|
274 var vsOutput: seq[string]
|
|
275 var fsInput: seq[string]
|
|
276 var fsOutput: seq[string]
|
|
277 var uniforms: seq[string]
|
|
278 var samplers: seq[string]
|
|
279 var vsInputLocation = 0
|
|
280 var passLocation = 0
|
|
281 var fsOutputLocation = 0
|
|
282 var binding = 0
|
|
283
|
|
284 for fieldname, value in fieldPairs(shader):
|
|
285 # vertex shader inputs
|
|
286 if hasCustomPragma(value, VertexAttribute) or hasCustomPragma(value, InstanceAttribute):
|
|
287 assert typeof(value) is SupportedGPUType
|
|
288 vsInput.add &"layout(location = {vsInputLocation}) in {GlslType(value)} {fieldname};"
|
|
289 for j in 0 ..< NumberOfVertexInputAttributeDescriptors(value):
|
|
290 vsInputLocation += NLocationSlots(value)
|
|
291 # intermediate values, passed between shaders
|
|
292 if hasCustomPragma(value, Pass) or hasCustomPragma(value, PassFlat):
|
|
293 let flat = if hasCustomPragma(value, PassFlat): "flat " else ""
|
|
294 vsOutput.add &"layout(location = {passLocation}) {flat}out {GlslType(value)} {fieldname};"
|
|
295 fsInput.add &"layout(location = {passLocation}) {flat}in {GlslType(value)} {fieldname};"
|
|
296 passLocation.inc
|
|
297 if hasCustomPragma(value, ShaderOutput):
|
|
298 fsOutput.add &"layout(location = {fsOutputLocation}) out {GlslType(value)} {fieldname};"
|
|
299 fsOutputLocation.inc
|
|
300 if hasCustomPragma(value, Descriptor):
|
|
301 # TODO; samplers and uniforms
|
|
302 if typeof(value) is Texture:
|
|
303 else:
|
|
304
|
|
305 result[0] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] &
|
|
306 vsInput &
|
|
307 uniforms &
|
|
308 samplers &
|
|
309 vsOutput &
|
|
310 @[shader.vertexCode]).join("\n")
|
|
311
|
|
312 result[1] = (@[&"#version {GLSL_VERSION}", "#extension GL_EXT_scalar_block_layout : require", ""] &
|
|
313 fsInput &
|
|
314 uniforms &
|
|
315 samplers &
|
|
316 fsOutput &
|
|
317 @[shader.fragmentCode]).join("\n")
|
|
318
|
|
319 proc CompileShader[TShader](shader: TShader): (seq[uint32], seq[uint32]) {.compileTime.} =
|
|
320 let (vertexShaderSource, fragmentShaderSource) = generateShaderSource(shader)
|
|
321 (
|
|
322 compileGlslToSPIRV(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderSource),
|
|
323 compileGlslToSPIRV(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderSource)
|
|
324 )
|
|
325
|
|
326
|
|
327 proc CreatePipeline*[TShader](
|
1159
|
328 device: VkDevice,
|
|
329 renderPass: VkRenderPass,
|
1162
|
330 vertexShader: VkShaderModule,
|
|
331 fragmentShader: VkShaderModule,
|
1159
|
332 topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
333 polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL,
|
|
334 cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT,
|
|
335 frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE,
|
1162
|
336 ): Pipeline[TShader] =
|
1159
|
337 # assumptions/limitations:
|
|
338 # - we are only using vertex and fragment shaders (2 stages)
|
|
339 # - we only support one subpass
|
1162
|
340 # = we only support one Uniform-Block
|
1161
|
341
|
|
342 var layoutbindings: seq[VkDescriptorSetLayoutBinding]
|
|
343 var descriptorBindingNumber = 0'u32
|
1162
|
344 ForDescriptorFields(default(TShader), fieldname, value, descriptorType, descriptorCount):
|
|
345 # TODO: Only one binding needed for a Uniforms block
|
1161
|
346 layoutbindings.add VkDescriptorSetLayoutBinding(
|
|
347 binding: descriptorBindingNumber,
|
|
348 descriptorType: descriptorType,
|
|
349 descriptorCount: descriptorCount,
|
1162
|
350 stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS),
|
1161
|
351 pImmutableSamplers: nil,
|
|
352 )
|
|
353 inc descriptorBindingNumber
|
|
354 var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo(
|
|
355 sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
|
356 bindingCount: uint32(layoutbindings.len),
|
|
357 pBindings: layoutbindings.ToCPointer
|
|
358 )
|
|
359 var descriptorSetLayout: VkDescriptorSetLayout
|
1162
|
360 checkVkResult vkCreateDescriptorSetLayout(device, addr(layoutCreateInfo), nil, addr(descriptorSetLayout))
|
1161
|
361 let pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
|
|
362 sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
363 setLayoutCount: 1,
|
|
364 pSetLayouts: addr(descriptorSetLayout),
|
|
365 # pushConstantRangeCount: uint32(pushConstants.len),
|
|
366 # pPushConstantRanges: pushConstants.ToCPointer,
|
|
367 )
|
|
368 checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, addr(result.layout))
|
1159
|
369
|
|
370 let stages = [
|
|
371 VkPipelineShaderStageCreateInfo(
|
|
372 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
373 stage: VK_SHADER_STAGE_VERTEX_BIT,
|
1162
|
374 module: vertexShader,
|
1159
|
375 pName: "main",
|
|
376 ),
|
|
377 VkPipelineShaderStageCreateInfo(
|
|
378 sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
379 stage: VK_SHADER_STAGE_FRAGMENT_BIT,
|
1162
|
380 module: fragmentShader,
|
1159
|
381 pName: "main",
|
|
382 ),
|
|
383 ]
|
1162
|
384 var
|
|
385 bindings: seq[VkVertexInputBindingDescription]
|
|
386 attributes: seq[VkVertexInputAttributeDescription]
|
1159
|
387 var inputBindingNumber = 0'u32
|
1162
|
388 var location = 0'u32
|
|
389 ForVertexDataFields(default(TShader), fieldname, value, isInstanceAttr):
|
1159
|
390 bindings.add VkVertexInputBindingDescription(
|
|
391 binding: inputBindingNumber,
|
|
392 stride: sizeof(value).uint32,
|
|
393 inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX,
|
|
394 )
|
|
395 # allows to submit larger data structures like Mat44, for most other types will be 1
|
|
396 let perDescriptorSize = sizeof(value).uint32 div NumberOfVertexInputAttributeDescriptors(value)
|
|
397 for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value):
|
|
398 attributes.add VkVertexInputAttributeDescription(
|
|
399 binding: inputBindingNumber,
|
1162
|
400 location: location,
|
1159
|
401 format: VkType(value),
|
|
402 offset: i * perDescriptorSize,
|
|
403 )
|
1162
|
404 location += NLocationSlots(value)
|
1159
|
405 inc inputBindingNumber
|
|
406
|
|
407 let
|
|
408 vertexInputInfo = VkPipelineVertexInputStateCreateInfo(
|
|
409 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
410 vertexBindingDescriptionCount: uint32(bindings.len),
|
|
411 pVertexBindingDescriptions: bindings.ToCPointer,
|
|
412 vertexAttributeDescriptionCount: uint32(attributes.len),
|
|
413 pVertexAttributeDescriptions: attributes.ToCPointer,
|
|
414 )
|
|
415 inputAssembly = VkPipelineInputAssemblyStateCreateInfo(
|
|
416 sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
417 topology: topology,
|
|
418 primitiveRestartEnable: false,
|
|
419 )
|
|
420 viewportState = VkPipelineViewportStateCreateInfo(
|
|
421 sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
422 viewportCount: 1,
|
|
423 scissorCount: 1,
|
|
424 )
|
|
425 rasterizer = VkPipelineRasterizationStateCreateInfo(
|
|
426 sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
427 depthClampEnable: VK_FALSE,
|
|
428 rasterizerDiscardEnable: VK_FALSE,
|
|
429 polygonMode: polygonMode,
|
|
430 lineWidth: 1.0,
|
|
431 cullMode: toBits [cullMode],
|
|
432 frontFace: frontFace,
|
|
433 depthBiasEnable: VK_FALSE,
|
|
434 depthBiasConstantFactor: 0.0,
|
|
435 depthBiasClamp: 0.0,
|
|
436 depthBiasSlopeFactor: 0.0,
|
|
437 )
|
|
438 multisampling = VkPipelineMultisampleStateCreateInfo(
|
|
439 sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
440 sampleShadingEnable: VK_FALSE,
|
|
441 rasterizationSamples: VK_SAMPLE_COUNT_1_BIT,
|
|
442 minSampleShading: 1.0,
|
|
443 pSampleMask: nil,
|
|
444 alphaToCoverageEnable: VK_FALSE,
|
|
445 alphaToOneEnable: VK_FALSE,
|
|
446 )
|
|
447 colorBlendAttachment = VkPipelineColorBlendAttachmentState(
|
|
448 colorWriteMask: toBits [VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT],
|
|
449 blendEnable: VK_TRUE,
|
|
450 srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA,
|
|
451 dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
452 colorBlendOp: VK_BLEND_OP_ADD,
|
|
453 srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE,
|
|
454 dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO,
|
|
455 alphaBlendOp: VK_BLEND_OP_ADD,
|
|
456 )
|
|
457 colorBlending = VkPipelineColorBlendStateCreateInfo(
|
|
458 sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
459 logicOpEnable: false,
|
|
460 attachmentCount: 1,
|
|
461 pAttachments: addr(colorBlendAttachment),
|
|
462 )
|
|
463 dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR]
|
|
464 dynamicState = VkPipelineDynamicStateCreateInfo(
|
|
465 sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
466 dynamicStateCount: dynamicStates.len.uint32,
|
|
467 pDynamicStates: dynamicStates.ToCPointer,
|
|
468 )
|
|
469 let createInfo = VkGraphicsPipelineCreateInfo(
|
|
470 sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
471 stageCount: 2,
|
1162
|
472 pStages: stages.ToCPointer,
|
1159
|
473 pVertexInputState: addr(vertexInputInfo),
|
|
474 pInputAssemblyState: addr(inputAssembly),
|
|
475 pViewportState: addr(viewportState),
|
|
476 pRasterizationState: addr(rasterizer),
|
|
477 pMultisampleState: addr(multisampling),
|
|
478 pDepthStencilState: nil,
|
|
479 pColorBlendState: addr(colorBlending),
|
|
480 pDynamicState: addr(dynamicState),
|
|
481 layout: result.layout,
|
|
482 renderPass: renderPass,
|
|
483 subpass: 0,
|
|
484 basePipelineHandle: VkPipeline(0),
|
|
485 basePipelineIndex: -1,
|
|
486 )
|
|
487 checkVkResult vkCreateGraphicsPipelines(
|
|
488 device,
|
|
489 VkPipelineCache(0),
|
|
490 1,
|
|
491 addr(createInfo),
|
|
492 nil,
|
|
493 addr(result.pipeline)
|
|
494 )
|
|
495
|
1161
|
496 proc CreateRenderable[TMesh, TInstance](
|
|
497 mesh: TMesh,
|
|
498 instance: TInstance,
|
|
499 buffers: RenderBuffers,
|
|
500 ): Renderable[TMesh, TInstance] =
|
|
501 result.indexType = None
|
1159
|
502
|
1161
|
503 proc Bind(pipeline: Pipeline, commandBuffer: VkCommandBuffer, currentFrameInFlight: int) =
|
1159
|
504 commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline)
|
|
505 commandBuffer.vkCmdBindDescriptorSets(
|
|
506 VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
507 pipeline.layout,
|
|
508 0,
|
1161
|
509 pipeline.descriptorSets[currentFrameInFlight].len,
|
|
510 pipeline.descriptorSets[currentFrameInFlight],
|
1159
|
511 0,
|
|
512 nil,
|
|
513 )
|
1161
|
514
|
1162
|
515 proc AssertCompatible(TShader, TMesh, TInstance, TGlobals: typedesc) =
|
|
516 # assert seq-fields of TMesh|TInstance == seq-fields of TShader
|
1161
|
517 # assert normal fields of TMesh|Globals == normal fields of TShaderDescriptors
|
1162
|
518 for inputName, inputValue in default(TShader).fieldPairs:
|
1161
|
519 echo "checking shader input '" & inputName & "'"
|
|
520 var foundField = false
|
|
521 when hasCustomPragma(inputValue, VertexAttribute):
|
|
522 echo " is vertex attribute"
|
|
523 for meshName, meshValue in default(TMesh).fieldPairs:
|
|
524 when meshName == inputName:
|
1162
|
525 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)) & "'"
|
1161
|
527 foundField = true
|
1162
|
528 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'"
|
1161
|
529 elif hasCustomPragma(inputValue, InstanceAttribute):
|
|
530 echo " is instance attribute"
|
|
531 for instanceName, instanceValue in default(TInstance).fieldPairs:
|
|
532 when instanceName == inputName:
|
1162
|
533 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)) & "'"
|
1161
|
535 foundField = true
|
1162
|
536 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'"
|
|
537 elif hasCustomPragma(inputValue, Descriptor):
|
1161
|
538 echo " is descriptor attribute"
|
|
539 for meshName, meshValue in default(TMesh).fieldPairs:
|
|
540 when meshName == inputName:
|
1162
|
541 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)) & "'"
|
1161
|
543 foundField = true
|
|
544 for globalName, globalValue in default(TGlobals).fieldPairs:
|
|
545 when globalName == inputName:
|
1162
|
546 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)) & "'"
|
1161
|
548 foundField = true
|
1162
|
549 assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'"
|
1161
|
550 echo " found"
|
|
551
|
|
552
|
1162
|
553 proc Render[TShader, TMesh, TInstance, TGlobals](
|
|
554 pipeline: Pipeline[TShader],
|
1161
|
555 renderable: Renderable[TMesh, TInstance],
|
|
556 globals: TGlobals,
|
|
557 commandBuffer: VkCommandBuffer,
|
|
558 ) =
|
|
559 static:
|
1162
|
560 AssertCompatible(TShader, TMesh, TInstance, TGlobals)
|
1159
|
561 commandBuffer.vkCmdBindVertexBuffers(
|
|
562 firstBinding = 0'u32,
|
1161
|
563 bindingCount = uint32(renderable.vertexBuffers.len),
|
|
564 pBuffers = renderable.vertexBuffers.ToCPointer(),
|
|
565 pOffsets = renderable.bufferOffsets.ToCPointer()
|
1159
|
566 )
|
1161
|
567 if renderable.indexType != None:
|
1159
|
568 commandBuffer.vkCmdBindIndexBuffer(
|
|
569 renderable.indexBuffer,
|
|
570 renderable.indexBufferOffset,
|
1161
|
571 renderable.indexType,
|
1159
|
572 )
|
|
573 commandBuffer.vkCmdDrawIndexed(
|
1161
|
574 indexCount = renderable.indexCount,
|
|
575 instanceCount = renderable.instanceCount,
|
1159
|
576 firstIndex = 0,
|
|
577 vertexOffset = 0,
|
|
578 firstInstance = 0
|
|
579 )
|
|
580 else:
|
|
581 commandBuffer.vkCmdDraw(
|
1161
|
582 vertexCount = renderable.vertexCount,
|
|
583 instanceCount = renderable.instanceCount,
|
1159
|
584 firstVertex = 0,
|
|
585 firstInstance = 0
|
|
586 )
|
1161
|
587
|
|
588 when isMainModule:
|
1162
|
589 import semicongine/platform/window
|
|
590 import semicongine/core/vulkanapi
|
|
591 import semicongine/vulkan/instance
|
|
592 import semicongine/vulkan/device
|
|
593 import semicongine/vulkan/physicaldevice
|
|
594 import semicongine/vulkan/renderpass
|
|
595
|
1161
|
596 type
|
|
597 MeshA = object
|
|
598 position: seq[Vec3f]
|
|
599 transparency: float
|
|
600 InstanceA = object
|
|
601 transform: seq[Mat4]
|
|
602 position: seq[Vec3f]
|
1162
|
603 other: seq[array[3, int32]]
|
1161
|
604 Globals = object
|
1162
|
605 fontAtlas: Texture
|
1161
|
606
|
1162
|
607 ShaderA = object
|
1161
|
608 position {.VertexAttribute.}: Vec3f
|
|
609 transform {.InstanceAttribute.}: Mat4
|
1162
|
610 fontAtlas {.Descriptor.}: Texture
|
|
611 other {.InstanceAttribute.}: array[3, int32]
|
|
612 test {.Pass.}: float32
|
|
613 test1 {.PassFlat.}: Vec3f
|
|
614 color {.ShaderOutput.}: Vec4f
|
|
615 vertexCode: string = "void main() {}"
|
|
616 fragmentCode: string = "void main() {}"
|
1161
|
617
|
1162
|
618 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")
|
|
620 let i = w.CreateInstance(
|
|
621 vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0),
|
|
622 instanceExtensions = @[],
|
|
623 layers = @["VK_LAYER_KHRONOS_validation"],
|
|
624 )
|
|
625
|
|
626 const (a, b) = CompileShader(Shader(A))
|
|
627
|
|
628 let selectedPhysicalDevice = i.GetPhysicalDevices().FilterBestGraphics()
|
|
629 let d = i.CreateDevice(
|
|
630 selectedPhysicalDevice,
|
|
631 enabledExtensions = @[],
|
|
632 selectedPhysicalDevice.FilterForGraphicsPresentationQueues()
|
|
633 )
|
|
634
|
|
635 var p: Pipeline[ShaderA]
|
1161
|
636 var r: Renderable[MeshA, InstanceA]
|
|
637 var g: Globals
|
|
638
|
1162
|
639 let rp = CreateRenderPass(d.vk, d.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat().format)
|
|
640 var p1 = CreatePipeline[ShaderA](device = d.vk, renderPass = rp, VkShaderModule(0), VkShaderModule(0))
|
1161
|
641 Render(p, r, g, VkCommandBuffer(0))
|