Mercurial > games > semicongine
comparison semiconginev2/old/renderer.nim @ 1218:56781cc0fc7c compiletime-tests
did: renamge main package
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 21:01:37 +0700 |
parents | semicongine/old/renderer.nim@a3eb305bcac2 |
children |
comparison
equal
deleted
inserted
replaced
1217:f819a874058f | 1218:56781cc0fc7c |
---|---|
1 import std/options | |
2 import std/tables | |
3 import std/strformat | |
4 import std/sequtils | |
5 import std/strutils | |
6 import std/logging | |
7 | |
8 import ./core | |
9 import ./vulkan/commandbuffer | |
10 import ./vulkan/buffer | |
11 import ./vulkan/device | |
12 import ./vulkan/drawable | |
13 import ./vulkan/physicaldevice | |
14 import ./vulkan/pipeline | |
15 import ./vulkan/renderpass | |
16 import ./vulkan/swapchain | |
17 import ./vulkan/shader | |
18 import ./vulkan/descriptor | |
19 import ./vulkan/image | |
20 | |
21 import ./scene | |
22 import ./mesh | |
23 import ./material | |
24 | |
25 const VERTEX_ATTRIB_ALIGNMENT = 4 # used for buffer alignment | |
26 | |
27 type | |
28 ShaderData = ref object | |
29 descriptorPool: DescriptorPool | |
30 descriptorSets: seq[DescriptorSet] # len = n swapchain images | |
31 uniformBuffers: seq[Buffer] | |
32 textures: Table[string, seq[VulkanTexture]] | |
33 | |
34 SceneData = ref object | |
35 drawables: seq[tuple[drawable: Drawable, mesh: Mesh]] | |
36 vertexBuffers: Table[MemoryPerformanceHint, Buffer] | |
37 indexBuffer: Buffer | |
38 attributeLocation: Table[string, MemoryPerformanceHint] | |
39 vertexBufferOffsets: Table[(Mesh, string), uint64] | |
40 materials: Table[MaterialType, seq[MaterialData]] | |
41 shaderData: Table[VkPipeline, ShaderData] | |
42 Renderer* = object | |
43 device: Device | |
44 renderPass: RenderPass | |
45 swapchain: Swapchain | |
46 scenedata: Table[Scene, SceneData] | |
47 emptyTexture: VulkanTexture | |
48 queue: Queue | |
49 commandBufferPool: CommandBufferPool | |
50 nextFrameReady: bool = false | |
51 | |
52 proc currentFrameCommandBuffer(renderer: Renderer): VkCommandBuffer = | |
53 renderer.commandBufferPool.buffers[renderer.swapchain.currentInFlight] | |
54 | |
55 proc HasScene*(renderer: Renderer, scene: Scene): bool = | |
56 scene in renderer.scenedata | |
57 | |
58 proc InitRenderer*( | |
59 device: Device, | |
60 shaders: openArray[(MaterialType, ShaderConfiguration)], | |
61 clearColor = NewVec4f(0, 0, 0, 0), | |
62 backFaceCulling = true, | |
63 vSync = false, | |
64 inFlightFrames = 2, | |
65 samples = VK_SAMPLE_COUNT_1_BIT, | |
66 ): Renderer = | |
67 assert device.vk.Valid | |
68 | |
69 result.device = device | |
70 result.renderPass = device.CreateRenderPass( | |
71 shaders, | |
72 clearColor = clearColor, | |
73 backFaceCulling = backFaceCulling, | |
74 samples = samples | |
75 ) | |
76 let swapchain = device.CreateSwapchain( | |
77 result.renderPass.vk, | |
78 device.physicalDevice.GetSurfaceFormats().FilterSurfaceFormat(), | |
79 vSync = vSync, | |
80 inFlightFrames = inFlightFrames, | |
81 samples = samples, | |
82 ) | |
83 if not swapchain.isSome: | |
84 raise newException(Exception, "Unable to create swapchain") | |
85 | |
86 result.queue = device.FirstGraphicsQueue().get() | |
87 result.commandBufferPool = device.CreateCommandBufferPool(result.queue.family, swapchain.get().inFlightFrames) | |
88 result.swapchain = swapchain.get() | |
89 result.emptyTexture = device.UploadTexture(result.queue, EMPTY_TEXTURE) | |
90 | |
91 func shadersForScene(renderer: Renderer, scene: Scene): seq[(MaterialType, ShaderPipeline)] = | |
92 for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: | |
93 if scene.UsesMaterial(materialType): | |
94 result.add (materialType, shaderPipeline) | |
95 | |
96 func vertexInputsForScene(renderer: Renderer, scene: Scene): seq[ShaderAttribute] = | |
97 var found: Table[string, ShaderAttribute] | |
98 for (materialType, shaderPipeline) in renderer.shadersForScene(scene): | |
99 for input in shaderPipeline.Inputs: | |
100 if found.contains(input.name): | |
101 assert input.name == found[input.name].name, &"{input.name}: {input.name} != {found[input.name].name}" | |
102 assert input.theType == found[input.name].theType, &"{input.name}: {input.theType} != {found[input.name].theType}" | |
103 assert input.arrayCount == found[input.name].arrayCount, &"{input.name}: {input.arrayCount} != {found[input.name].arrayCount}" | |
104 assert input.memoryPerformanceHint == found[input.name].memoryPerformanceHint, &"{input.name}: {input.memoryPerformanceHint} != {found[input.name].memoryPerformanceHint}" | |
105 else: | |
106 result.add input | |
107 found[input.name] = input | |
108 | |
109 proc SetupDrawableBuffers*(renderer: var Renderer, scene: var Scene) = | |
110 assert not (scene in renderer.scenedata) | |
111 | |
112 var scenedata = SceneData() | |
113 | |
114 # find all material data and group it by material type | |
115 for mesh in scene.meshes: | |
116 assert mesh.material != nil, "Mesh {mesh} has no material assigned" | |
117 if not scenedata.materials.contains(mesh.material.theType): | |
118 scenedata.materials[mesh.material.theType] = @[] | |
119 if not scenedata.materials[mesh.material.theType].contains(mesh.material): | |
120 scenedata.materials[mesh.material.theType].add mesh.material | |
121 | |
122 # automatically populate material and tranform attributes | |
123 for mesh in scene.meshes: | |
124 if not (TRANSFORM_ATTRIB in mesh[].Attributes): | |
125 mesh[].InitInstanceAttribute(TRANSFORM_ATTRIB, Unit4) | |
126 if not (MATERIALINDEX_ATTRIBUTE in mesh[].Attributes): | |
127 mesh[].InitInstanceAttribute(MATERIALINDEX_ATTRIBUTE, uint16(scenedata.materials[mesh.material.theType].find(mesh.material))) | |
128 | |
129 # create index buffer if necessary | |
130 var indicesBufferSize = 0'u64 | |
131 for mesh in scene.meshes: | |
132 if mesh[].indexType != MeshIndexType.None: | |
133 let indexAlignment = case mesh[].indexType | |
134 of MeshIndexType.None: 0'u64 | |
135 of Tiny: 1'u64 | |
136 of Small: 2'u64 | |
137 of Big: 4'u64 | |
138 # index value alignment required by Vulkan | |
139 if indicesBufferSize mod indexAlignment != 0: | |
140 indicesBufferSize += indexAlignment - (indicesBufferSize mod indexAlignment) | |
141 indicesBufferSize += mesh[].IndexSize | |
142 if indicesBufferSize > 0: | |
143 scenedata.indexBuffer = renderer.device.CreateBuffer( | |
144 size = indicesBufferSize, | |
145 usage = [VK_BUFFER_USAGE_INDEX_BUFFER_BIT], | |
146 requireMappable = false, | |
147 preferVRAM = true, | |
148 ) | |
149 | |
150 # calculcate offsets for attributes in vertex buffers | |
151 # trying to use one buffer per memory type | |
152 var perLocationSizes: Table[MemoryPerformanceHint, uint64] | |
153 for hint in MemoryPerformanceHint: | |
154 perLocationSizes[hint] = 0 | |
155 | |
156 let sceneVertexInputs = renderer.vertexInputsForScene(scene) | |
157 let sceneShaders = renderer.shadersForScene(scene) | |
158 | |
159 for (materialType, shaderPipeline) in sceneShaders: | |
160 scenedata.shaderData[shaderPipeline.vk] = ShaderData() | |
161 | |
162 for vertexAttribute in sceneVertexInputs: | |
163 scenedata.attributeLocation[vertexAttribute.name] = vertexAttribute.memoryPerformanceHint | |
164 # setup one buffer per vertexAttribute-location-type | |
165 for mesh in scene.meshes: | |
166 # align size to VERTEX_ATTRIB_ALIGNMENT bytes (the important thing is the correct alignment of the offsets, but | |
167 # we need to expand the buffer size as well, therefore considering alignment already here as well | |
168 if perLocationSizes[vertexAttribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT != 0: | |
169 perLocationSizes[vertexAttribute.memoryPerformanceHint] += VERTEX_ATTRIB_ALIGNMENT - (perLocationSizes[vertexAttribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT) | |
170 perLocationSizes[vertexAttribute.memoryPerformanceHint] += mesh[].AttributeSize(vertexAttribute.name) | |
171 | |
172 # create vertex buffers | |
173 for memoryPerformanceHint, bufferSize in perLocationSizes.pairs: | |
174 if bufferSize > 0: | |
175 scenedata.vertexBuffers[memoryPerformanceHint] = renderer.device.CreateBuffer( | |
176 size = bufferSize, | |
177 usage = [VK_BUFFER_USAGE_VERTEX_BUFFER_BIT], | |
178 requireMappable = memoryPerformanceHint == PreferFastWrite, | |
179 preferVRAM = true, | |
180 ) | |
181 | |
182 # calculate offset of each attribute for all meshes | |
183 var perLocationOffsets: Table[MemoryPerformanceHint, uint64] | |
184 var indexBufferOffset = 0'u64 | |
185 for hint in MemoryPerformanceHint: | |
186 perLocationOffsets[hint] = 0 | |
187 | |
188 for mesh in scene.meshes: | |
189 for attribute in sceneVertexInputs: | |
190 scenedata.vertexBufferOffsets[(mesh, attribute.name)] = perLocationOffsets[attribute.memoryPerformanceHint] | |
191 if mesh[].Attributes.contains(attribute.name): | |
192 perLocationOffsets[attribute.memoryPerformanceHint] += mesh[].AttributeSize(attribute.name) | |
193 if perLocationOffsets[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT != 0: | |
194 perLocationOffsets[attribute.memoryPerformanceHint] += VERTEX_ATTRIB_ALIGNMENT - (perLocationOffsets[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT) | |
195 | |
196 # fill offsets per shaderPipeline (as sequence corresponds to shader input binding) | |
197 var offsets: Table[VkPipeline, seq[(string, MemoryPerformanceHint, uint64)]] | |
198 for (materialType, shaderPipeline) in sceneShaders: | |
199 offsets[shaderPipeline.vk] = newSeq[(string, MemoryPerformanceHint, uint64)]() | |
200 for attribute in shaderPipeline.Inputs: | |
201 offsets[shaderPipeline.vk].add (attribute.name, attribute.memoryPerformanceHint, scenedata.vertexBufferOffsets[(mesh, attribute.name)]) | |
202 | |
203 # create drawables | |
204 let indexed = mesh.indexType != MeshIndexType.None | |
205 var drawable = Drawable( | |
206 name: mesh.name, | |
207 elementCount: if indexed: mesh[].IndicesCount else: mesh[].vertexCount, | |
208 bufferOffsets: offsets, | |
209 instanceCount: mesh[].InstanceCount, | |
210 indexed: indexed, | |
211 ) | |
212 if indexed: | |
213 let indexAlignment = case mesh.indexType | |
214 of MeshIndexType.None: 0'u64 | |
215 of Tiny: 1'u64 | |
216 of Small: 2'u64 | |
217 of Big: 4'u64 | |
218 # index value alignment required by Vulkan | |
219 if indexBufferOffset mod indexAlignment != 0: | |
220 indexBufferOffset += indexAlignment - (indexBufferOffset mod indexAlignment) | |
221 drawable.indexBufferOffset = indexBufferOffset | |
222 drawable.indexType = mesh.indexType | |
223 var (pdata, size) = mesh[].GetRawIndexData() | |
224 scenedata.indexBuffer.SetData(renderer.queue, pdata, size, indexBufferOffset) | |
225 indexBufferOffset += size | |
226 scenedata.drawables.add (drawable, mesh) | |
227 | |
228 # setup uniforms and textures (anything descriptor) | |
229 var uploadedTextures: Table[Texture, VulkanTexture] | |
230 for (materialType, shaderPipeline) in sceneShaders: | |
231 # gather textures | |
232 for textureAttribute in shaderPipeline.Samplers: | |
233 scenedata.shaderData[shaderPipeline.vk].textures[textureAttribute.name] = newSeq[VulkanTexture]() | |
234 if scene.shaderGlobals.contains(textureAttribute.name): | |
235 for textureValue in scene.shaderGlobals[textureAttribute.name][Texture][]: | |
236 if not uploadedTextures.contains(textureValue): | |
237 uploadedTextures[textureValue] = renderer.device.UploadTexture(renderer.queue, textureValue) | |
238 scenedata.shaderData[shaderPipeline.vk].textures[textureAttribute.name].add uploadedTextures[textureValue] | |
239 else: | |
240 var foundTexture = false | |
241 for material in scene.GetMaterials(materialType): | |
242 if material.HasMatchingAttribute(textureAttribute): | |
243 foundTexture = true | |
244 let value = material[textureAttribute.name, Texture][] | |
245 assert value.len == 1, &"Mesh material attribute '{textureAttribute.name}' has texture-array, but only single textures are allowed" | |
246 if not uploadedTextures.contains(value[0]): | |
247 uploadedTextures[value[0]] = renderer.device.UploadTexture(renderer.queue, value[0]) | |
248 scenedata.shaderData[shaderPipeline.vk].textures[textureAttribute.name].add uploadedTextures[value[0]] | |
249 assert foundTexture, &"No texture found in shaderGlobals or materials for '{textureAttribute.name}'" | |
250 let nTextures = scenedata.shaderData[shaderPipeline.vk].textures[textureAttribute.name].len.uint32 | |
251 assert (textureAttribute.arrayCount == 0 and nTextures == 1) or textureAttribute.arrayCount >= nTextures, &"Shader assigned to render '{materialType}' expected {textureAttribute.arrayCount} textures for '{textureAttribute.name}' but got {nTextures}" | |
252 if textureAttribute.arrayCount < nTextures: | |
253 warn &"Shader assigned to render '{materialType}' expected {textureAttribute.arrayCount} textures for '{textureAttribute.name}' but got {nTextures}" | |
254 | |
255 # gather uniform sizes | |
256 var uniformBufferSize = 0'u64 | |
257 for uniform in shaderPipeline.Uniforms: | |
258 uniformBufferSize += uniform.Size | |
259 if uniformBufferSize > 0: | |
260 for frame_i in 0 ..< renderer.swapchain.inFlightFrames: | |
261 scenedata.shaderData[shaderPipeline.vk].uniformBuffers.add renderer.device.CreateBuffer( | |
262 size = uniformBufferSize, | |
263 usage = [VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT], | |
264 requireMappable = true, | |
265 preferVRAM = true, | |
266 ) | |
267 | |
268 # TODO: rework the whole descriptor/pool/layout stuff, a bit unclear | |
269 var poolsizes = @[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, renderer.swapchain.inFlightFrames.uint32)] | |
270 var nTextures = 0'u32 | |
271 for descriptor in shaderPipeline.descriptorSetLayout.descriptors: | |
272 if descriptor.thetype == ImageSampler: | |
273 nTextures += descriptor.count | |
274 if nTextures > 0: | |
275 poolsizes.add (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nTextures * renderer.swapchain.inFlightFrames.uint32) | |
276 scenedata.shaderData[shaderPipeline.vk].descriptorPool = renderer.device.CreateDescriptorSetPool(poolsizes) | |
277 | |
278 scenedata.shaderData[shaderPipeline.vk].descriptorSets = shaderPipeline.SetupDescriptors( | |
279 scenedata.shaderData[shaderPipeline.vk].descriptorPool, | |
280 scenedata.shaderData[shaderPipeline.vk].uniformBuffers, | |
281 scenedata.shaderData[shaderPipeline.vk].textures, | |
282 inFlightFrames = renderer.swapchain.inFlightFrames, | |
283 emptyTexture = renderer.emptyTexture, | |
284 ) | |
285 for frame_i in 0 ..< renderer.swapchain.inFlightFrames: | |
286 scenedata.shaderData[shaderPipeline.vk].descriptorSets[frame_i].WriteDescriptorSet() | |
287 | |
288 renderer.scenedata[scene] = scenedata | |
289 | |
290 proc UpdateMeshData*(renderer: var Renderer, scene: var Scene, forceAll = false) = | |
291 assert scene in renderer.scenedata | |
292 | |
293 var addedBarrier = false; | |
294 for (drawable, mesh) in renderer.scenedata[scene].drawables.mitems: | |
295 if mesh[].Attributes.contains(TRANSFORM_ATTRIB): | |
296 mesh[].UpdateInstanceTransforms(TRANSFORM_ATTRIB) | |
297 let attrs = (if forceAll: mesh[].Attributes else: mesh[].DirtyAttributes) | |
298 for attribute in attrs: | |
299 # ignore attributes that are not used in this scene | |
300 if attribute in renderer.scenedata[scene].attributeLocation: | |
301 debug &"Update mesh attribute {attribute}" | |
302 let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute] | |
303 # if we have to do a vkCmdCopyBuffer (not buffer.canMap), then we want to added a barrier to | |
304 # not infer with the current frame that is being renderer (relevant when we have multiple frames in flight) | |
305 # (remark: ...I think..., I am pretty new to this sync stuff) | |
306 if not renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].CanMap and not addedBarrier: | |
307 WithSingleUseCommandBuffer(renderer.device, renderer.queue, commandBuffer): | |
308 let barrier = VkMemoryBarrier( | |
309 sType: VK_STRUCTURE_TYPE_MEMORY_BARRIER, | |
310 srcAccessMask: [VK_ACCESS_MEMORY_READ_BIT].toBits, | |
311 dstAccessMask: [VK_ACCESS_MEMORY_WRITE_BIT].toBits, | |
312 ) | |
313 commandBuffer.PipelineBarrier( | |
314 srcStages = [VK_PIPELINE_STAGE_VERTEX_INPUT_BIT], | |
315 dstStages = [VK_PIPELINE_STAGE_TRANSFER_BIT], | |
316 memoryBarriers = [barrier] | |
317 ) | |
318 addedBarrier = true | |
319 renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].SetData( | |
320 renderer.queue, | |
321 mesh[].GetPointer(attribute), | |
322 mesh[].AttributeSize(attribute), | |
323 renderer.scenedata[scene].vertexBufferOffsets[(mesh, attribute)] | |
324 ) | |
325 mesh[].ClearDirtyAttributes() | |
326 | |
327 proc UpdateUniformData*(renderer: var Renderer, scene: var Scene, forceAll = false) = | |
328 assert scene in renderer.scenedata | |
329 | |
330 let dirty = scene.DirtyShaderGlobals | |
331 | |
332 if forceAll: | |
333 debug "Update uniforms because 'forceAll' was given" | |
334 elif dirty.len > 0: | |
335 debug &"Update uniforms because of dirty scene globals: {dirty}" | |
336 | |
337 # loop over all used shaders/pipelines | |
338 for (materialType, shaderPipeline) in renderer.shadersForScene(scene): | |
339 if renderer.scenedata[scene].shaderData[shaderPipeline.vk].uniformBuffers.len > 0: | |
340 var dirtyMaterialAttribs: seq[string] | |
341 for material in renderer.scenedata[scene].materials[materialType].mitems: | |
342 dirtyMaterialAttribs.add material.DirtyAttributes | |
343 material.ClearDirtyAttributes() | |
344 assert renderer.scenedata[scene].shaderData[shaderPipeline.vk].uniformBuffers[renderer.swapchain.currentInFlight].vk.Valid | |
345 if forceAll: | |
346 for buffer in renderer.scenedata[scene].shaderData[shaderPipeline.vk].uniformBuffers: | |
347 assert buffer.vk.Valid | |
348 | |
349 var offset = 0'u64 | |
350 # loop over all uniforms of the shader-shaderPipeline | |
351 for uniform in shaderPipeline.Uniforms: | |
352 if dirty.contains(uniform.name) or dirtyMaterialAttribs.contains(uniform.name) or forceAll: # only update uniforms if necessary | |
353 var value = InitDataList(uniform.theType) | |
354 if scene.shaderGlobals.hasKey(uniform.name): | |
355 assert scene.shaderGlobals[uniform.name].thetype == uniform.thetype | |
356 value = scene.shaderGlobals[uniform.name] | |
357 else: | |
358 var foundValue = false | |
359 for material in renderer.scenedata[scene].materials[materialType]: | |
360 if material.HasMatchingAttribute(uniform): | |
361 value.AppendValues(material[uniform.name]) | |
362 foundValue = true | |
363 assert foundValue, &"Uniform '{uniform.name}' not found in scene shaderGlobals or materials" | |
364 assert (uniform.arrayCount == 0 and value.len == 1) or value.len.uint <= uniform.arrayCount, &"Uniform '{uniform.name}' found has wrong length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})" | |
365 if value.len.uint <= uniform.arrayCount: | |
366 debug &"Uniform '{uniform.name}' found has short length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})" | |
367 assert value.Size <= uniform.Size, &"During uniform update: gathered value has size {value.Size} but uniform expects size {uniform.Size}" | |
368 if value.Size < uniform.Size: | |
369 debug &"During uniform update: gathered value has size {value.Size} but uniform expects size {uniform.Size}" | |
370 debug &" update uniform '{uniform.name}' with value: {value}" | |
371 # TODO: technically we would only need to update the uniform buffer of the current | |
372 # frameInFlight (I think), but we don't track for which frame the shaderglobals are no longer dirty | |
373 # therefore we have to update the uniform values in all buffers, of all inFlightframes (usually 2) | |
374 for buffer in renderer.scenedata[scene].shaderData[shaderPipeline.vk].uniformBuffers: | |
375 buffer.SetData(renderer.queue, value.GetPointer(), value.Size, offset) | |
376 offset += uniform.Size | |
377 scene.ClearDirtyShaderGlobals() | |
378 | |
379 proc StartNewFrame*(renderer: var Renderer): bool = | |
380 # first, we need to await the next free frame from the swapchain | |
381 if not renderer.swapchain.AcquireNextFrame(): | |
382 # so, there was a problem while acquiring the frame | |
383 # lets first take a break (not sure if this helps anything) | |
384 checkVkResult renderer.device.vk.vkDeviceWaitIdle() | |
385 # now, first thing is, we recreate the swapchain, because a invalid swapchain | |
386 # is a common reason for the inability to acquire the next frame | |
387 let res = renderer.swapchain.Recreate() | |
388 if res.isSome: | |
389 # okay, swapchain recreation worked | |
390 # Now we can swap old and new swapchain | |
391 # the vkDeviceWaitIdle makes the resizing of windows not super smooth, | |
392 # but things seem to be more stable this way | |
393 var oldSwapchain = renderer.swapchain | |
394 renderer.swapchain = res.get() | |
395 checkVkResult renderer.device.vk.vkDeviceWaitIdle() | |
396 oldSwapchain.Destroy() | |
397 # NOW, we still have to acquire that next frame with the NEW swapchain | |
398 # if that fails, I don't know what to smart to do... | |
399 if not renderer.swapchain.AcquireNextFrame(): | |
400 return false | |
401 else: | |
402 # dang, swapchain could not be recreated. Some bigger issues is at hand... | |
403 return false | |
404 renderer.nextFrameReady = true | |
405 return true | |
406 | |
407 proc Render*(renderer: var Renderer, scene: Scene) = | |
408 assert scene in renderer.scenedata | |
409 assert renderer.nextFrameReady, "startNewFrame() must be called before calling render()" | |
410 | |
411 # preparation | |
412 renderer.currentFrameCommandBuffer.BeginRenderCommands(renderer.renderPass, renderer.swapchain.CurrentFramebuffer(), oneTimeSubmit = true) | |
413 | |
414 # debug output | |
415 debug "Scene buffers:" | |
416 for (location, buffer) in renderer.scenedata[scene].vertexBuffers.pairs: | |
417 debug " ", location, ": ", buffer | |
418 debug " Index buffer: ", renderer.scenedata[scene].indexBuffer | |
419 | |
420 # draw all meshes | |
421 for (materialType, shaderPipeline) in renderer.renderPass.shaderPipelines: | |
422 if scene.UsesMaterial(materialType): | |
423 debug &"Start shaderPipeline for '{materialType}'" | |
424 renderer.currentFrameCommandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, shaderPipeline.vk) | |
425 renderer.currentFrameCommandBuffer.vkCmdBindDescriptorSets( | |
426 VK_PIPELINE_BIND_POINT_GRAPHICS, | |
427 shaderPipeline.layout, | |
428 0, | |
429 1, | |
430 addr(renderer.scenedata[scene].shaderData[shaderPipeline.vk].descriptorSets[renderer.swapchain.currentInFlight].vk), | |
431 0, | |
432 nil | |
433 ) | |
434 for (drawable, mesh) in renderer.scenedata[scene].drawables.filterIt(it[1].visible and it[1].material.theType == materialType): | |
435 drawable.Draw(renderer.currentFrameCommandBuffer, vertexBuffers = renderer.scenedata[scene].vertexBuffers, indexBuffer = renderer.scenedata[scene].indexBuffer, shaderPipeline.vk) | |
436 | |
437 # done rendering | |
438 renderer.currentFrameCommandBuffer.EndRenderCommands() | |
439 | |
440 # swap framebuffer | |
441 if not renderer.swapchain.Swap(renderer.queue, renderer.currentFrameCommandBuffer): | |
442 let res = renderer.swapchain.Recreate() | |
443 if res.isSome: | |
444 var oldSwapchain = renderer.swapchain | |
445 renderer.swapchain = res.get() | |
446 checkVkResult renderer.device.vk.vkDeviceWaitIdle() | |
447 oldSwapchain.Destroy() | |
448 renderer.swapchain.currentInFlight = (renderer.swapchain.currentInFlight + 1) mod renderer.swapchain.inFlightFrames | |
449 renderer.nextFrameReady = false | |
450 | |
451 func Valid*(renderer: Renderer): bool = | |
452 renderer.device.vk.Valid | |
453 | |
454 proc Destroy*(renderer: var Renderer, scene: Scene) = | |
455 checkVkResult renderer.device.vk.vkDeviceWaitIdle() | |
456 var scenedata = renderer.scenedata[scene] | |
457 | |
458 for buffer in scenedata.vertexBuffers.mvalues: | |
459 assert buffer.vk.Valid | |
460 buffer.Destroy() | |
461 | |
462 if scenedata.indexBuffer.vk.Valid: | |
463 assert scenedata.indexBuffer.vk.Valid | |
464 scenedata.indexBuffer.Destroy() | |
465 | |
466 var destroyedTextures: seq[VkImage] | |
467 | |
468 for (vkPipeline, shaderData) in scenedata.shaderData.mpairs: | |
469 | |
470 for buffer in shaderData.uniformBuffers.mitems: | |
471 assert buffer.vk.Valid | |
472 buffer.Destroy() | |
473 | |
474 for textures in shaderData.textures.mvalues: | |
475 for texture in textures.mitems: | |
476 if not destroyedTextures.contains(texture.image.vk): | |
477 destroyedTextures.add texture.image.vk | |
478 texture.Destroy() | |
479 | |
480 shaderData.descriptorPool.Destroy() | |
481 | |
482 renderer.scenedata.del(scene) | |
483 | |
484 proc Destroy*(renderer: var Renderer) = | |
485 for scene in renderer.scenedata.keys.toSeq: | |
486 renderer.Destroy(scene) | |
487 assert renderer.scenedata.len == 0 | |
488 renderer.emptyTexture.Destroy() | |
489 renderer.renderPass.Destroy() | |
490 renderer.commandBufferPool.Destroy() | |
491 renderer.swapchain.Destroy() |