Mercurial > games > semicongine
changeset 566:cc7ba46fe3c4
add: descriptors, better swapchain implementation
author | Sam <sam@basx.dev> |
---|---|
date | Fri, 24 Mar 2023 00:11:42 +0700 |
parents | a0b01e84607d |
children | 05ac2455ff60 |
files | src/semicongine/engine.nim src/semicongine/vulkan.nim src/semicongine/vulkan/buffer.nim src/semicongine/vulkan/descriptor.nim src/semicongine/vulkan/framebuffer.nim src/semicongine/vulkan/pipeline.nim src/semicongine/vulkan/shader.nim src/semicongine/vulkan/swapchain.nim src/semicongine/vulkan/syncing.nim src/semicongine/vulkan/vertex.nim tests/test_vulkan_wrapper.nim |
diffstat | 11 files changed, 250 insertions(+), 76 deletions(-) [+] |
line wrap: on
line diff
--- a/src/semicongine/engine.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/engine.nim Fri Mar 24 00:11:42 2023 +0700 @@ -656,8 +656,7 @@ vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil) + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil) for (vertexBufferSet, indexed, indexBuffer, count, indexType) in pipeline.vertexBuffers: var @@ -718,7 +717,9 @@ checkVkResult vkEndCommandBuffer(commandBuffer) proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, pipeline: var RenderPipeline) = + checkVkResult vkWaitForFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64)) + var bufferImageIndex: uint32 let nextImageResult = vkAcquireNextImageKHR( vulkan.device.device, @@ -729,18 +730,19 @@ addr(bufferImageIndex) ) if nextImageResult == VK_ERROR_OUT_OF_DATE_KHR: - vulkan.frameSize = window.getFrameDimension( - vulkan.device.physicalDevice.device, vulkan.surface) + vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain() elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]): - raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & - $nextImageResult) + raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult) checkVkResult vkResetFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame])) - checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame], - VkCommandBufferResetFlags(0)) - vulkan.renderPass.recordCommandBuffer(pipeline, vulkan.device.commandBuffers[ - currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameSize, currentFrame) + checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame], VkCommandBufferResetFlags(0)) + vulkan.renderPass.recordCommandBuffer( + pipeline, + vulkan.device.commandBuffers[currentFrame], + vulkan.framebuffers[bufferImageIndex], vulkan.frameSize, + currentFrame + ) var waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]] waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)] @@ -767,11 +769,8 @@ pResults: nil, ) let presentResult = vkQueuePresentKHR(vulkan.device.presentationQueue, addr(presentInfo)) - - if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == - VK_SUBOPTIMAL_KHR or resized: - vulkan.frameSize = window.getFrameDimension( - vulkan.device.physicalDevice.device, vulkan.surface) + if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR or resized: + vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface) (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain()
--- a/src/semicongine/vulkan.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan.nim Fri Mar 24 00:11:42 2023 +0700 @@ -31,6 +31,9 @@ import ./vulkan/vertex export vertex +import ./vulkan/descriptor +export descriptor + import ./vulkan/pipeline export pipeline
--- a/src/semicongine/vulkan/buffer.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/buffer.nim Fri Mar 24 00:11:42 2023 +0700 @@ -2,10 +2,10 @@ import ./device type - Buffer = object - device: Device - vk: VkBuffer - size: uint64 + Buffer* = object + device*: Device + vk*: VkBuffer + size*: uint64 # currently no support for extended structure and concurrent/shared use # (shardingMode = VK_SHARING_MODE_CONCURRENT not supported)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/semicongine/vulkan/descriptor.nim Fri Mar 24 00:11:42 2023 +0700 @@ -0,0 +1,132 @@ +import std/enumerate + +import ./api +import ./device +import ./buffer +import ./utils + +type + Descriptor* = object # "fields" of a DescriptorSet + thetype*: VkDescriptorType + count*: uint32 + stages*: seq[VkShaderStageFlagBits] + itemsize*: uint32 + DescriptorSetLayout* = object # "type description of a DescriptorSet + device: Device + vk*: VkDescriptorSetLayout + descriptors: seq[Descriptor] + DescriptorSet* = object # "instance" of a DescriptorSetLayout + vk*: VkDescriptorSet + layout: DescriptorSetLayout + DescriptorPool* = object # required for allocation of DescriptorSet + device: Device + vk*: VkDescriptorPool + maxSets*: uint32 # maximum number of allocatable descriptor sets + counts*: seq[(VkDescriptorType, uint32)] # maximum number for each descriptor type to allocate + + +proc createDescriptorSetLayout*(device: Device, descriptors: seq[Descriptor]): DescriptorSetLayout = + assert device.vk.valid + + result.device = device + result.descriptors = descriptors + + var layoutbindings: seq[VkDescriptorSetLayoutBinding] + for i, descriptor in enumerate(descriptors): + layoutbindings.add VkDescriptorSetLayoutBinding( + binding: uint32(i), + descriptorType: descriptor.thetype, + descriptorCount: descriptor.count, + stageFlags: toBits descriptor.stages, + pImmutableSamplers: nil, + ) + var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + bindingCount: uint32(layoutbindings.len), + pBindings: layoutbindings.toCPointer + ) + checkVkResult vkCreateDescriptorSetLayout(device.vk, addr(layoutCreateInfo), nil, addr(result.vk)) + +proc destroy*(descriptorSetLayout: var DescriptorSetLayout) = + assert descriptorSetLayout.device.vk.valid + assert descriptorSetLayout.vk.valid + descriptorSetLayout.device.vk.vkDestroyDescriptorSetLayout(descriptorSetLayout.vk, nil) + descriptorSetLayout.vk.reset + + +proc createDescriptorSetPool*(device: Device, counts: seq[(VkDescriptorType, uint32)], maxSets = 1000'u32): DescriptorPool = + assert device.vk.valid + + result.device = device + result.maxSets = maxSets + result.counts = counts + + var poolSizes: seq[VkDescriptorPoolSize] + for (thetype, count) in result.counts: + poolSizes.add VkDescriptorPoolSize(thetype: thetype, descriptorCount: count) + var poolInfo = VkDescriptorPoolCreateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + poolSizeCount: uint32(poolSizes.len), + pPoolSizes: poolSizes.toCPointer, + maxSets: result.maxSets, + ) + checkVkResult vkCreateDescriptorPool(result.device.vk, addr(poolInfo), nil, addr(result.vk)) + +proc reset*(pool: DescriptorPool) = + assert pool.device.vk.valid + assert pool.vk.valid + checkVkResult vkResetDescriptorPool(pool.device.vk, pool.vk, VkDescriptorPoolResetFlags(0)) + +proc destroy*(pool: var DescriptorPool) = + assert pool.device.vk.valid + assert pool.vk.valid + pool.device.vk.vkDestroyDescriptorPool(pool.vk, nil) + pool.vk.reset + +proc allocateDescriptorSet*(pool: DescriptorPool, layout: DescriptorSetLayout, nframes: uint32): seq[DescriptorSet] = + assert pool.device.vk.valid + assert pool.vk.valid + assert layout.device.vk.valid + assert layout.vk.valid + + var layouts: seq[VkDescriptorSetLayout] + var descriptorSets = newSeq[VkDescriptorSet](nframes) + for i in 0 ..< nframes: + layouts.add layout.vk + var allocInfo = VkDescriptorSetAllocateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + descriptorPool: pool.vk, + descriptorSetCount: uint32(layouts.len), + pSetLayouts: layouts.toCPointer, + ) + + checkVkResult vkAllocateDescriptorSets(pool.device.vk, addr(allocInfo), descriptorSets.toCPointer) + for descriptorSet in descriptorSets: + result.add DescriptorSet(vk: descriptorSet, layout: layout) + +proc setDescriptorSet*(descriptorSet: DescriptorSet, buffer: Buffer, bindingBase=0'u32) = + # assumes descriptors of the descriptorSet are arranged interleaved in buffer + assert descriptorSet.layout.device.vk.valid + assert descriptorSet.layout.vk.valid + assert descriptorSet.vk.valid + assert buffer.device.vk.valid + assert buffer.vk.valid + + var descriptorSetWrites: seq[VkWriteDescriptorSet] + + var offset = VkDeviceSize(0) + var i = bindingBase + for descriptor in descriptorSet.layout.descriptors: + let length = VkDeviceSize(descriptor.itemsize * descriptor.count) + var bufferInfo = VkDescriptorBufferInfo(buffer: buffer.vk, offset: offset, range: length) + descriptorSetWrites.add VkWriteDescriptorSet( + sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + dstSet: descriptorSet.vk, + dstBinding: i, + dstArrayElement: 0, + descriptorType: descriptor.thetype, + descriptorCount: descriptor.count, + pBufferInfo: addr(bufferInfo), + ) + offset += length + descriptorSet.layout.device.vk.vkUpdateDescriptorSets(uint32(descriptorSetWrites.len), descriptorSetWrites.toCPointer, 0, nil)
--- a/src/semicongine/vulkan/framebuffer.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/framebuffer.nim Fri Mar 24 00:11:42 2023 +0700 @@ -1,14 +1,15 @@ import ./api import ./device -import ./renderpass import ./utils import ./image +import ./renderpass + import ../math type Framebuffer* = object + device*: Device vk*: VkFramebuffer - device*: Device proc createFramebuffer*(device: Device, renderPass: RenderPass, attachments: openArray[ImageView], dimension: TVec2[uint32]): Framebuffer = assert device.vk.valid
--- a/src/semicongine/vulkan/pipeline.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/pipeline.nim Fri Mar 24 00:11:42 2023 +0700 @@ -1,16 +1,16 @@ import ./api import ./utils import ./renderpass -import ./vertex import ./device import ./shader +import ./descriptor type - Pipeline = object + Pipeline* = object device: Device vk*: VkPipeline layout: VkPipelineLayout - descriptorLayout: VkDescriptorSetLayout + descriptorSetLayout*: DescriptorSetLayout proc createPipeline*[VertexShader: Shader, FragmentShader: Shader](renderPass: RenderPass, vertexShader: VertexShader, fragmentShader: FragmentShader): Pipeline = @@ -18,42 +18,46 @@ assert renderPass.device.vk.valid assert vertexShader.stage == VK_SHADER_STAGE_VERTEX_BIT assert fragmentShader.stage == VK_SHADER_STAGE_FRAGMENT_BIT - result.device = renderPass.device - var descriptorType: VkDescriptorType - var bindingNumber = 0'u32 - var arrayLen = 1 - var shaderStage: seq[VkShaderStageFlagBits] - var layoutbinding = VkDescriptorSetLayoutBinding( - binding: bindingNumber, - descriptorType: descriptorType, - descriptorCount: uint32(arrayLen), - stageFlags: toBits shaderStage, - pImmutableSamplers: nil, - ) - var descriptorLayoutBinding: seq[VkDescriptorSetLayoutBinding] = @[layoutbinding] - var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( - sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - bindingCount: uint32(descriptorLayoutBinding.len), - pBindings: descriptorLayoutBinding.toCPointer - ) - checkVkResult vkCreateDescriptorSetLayout( - renderPass.device.vk, - addr(layoutCreateInfo), - nil, - addr(result.descriptorLayout), - ) + result.device = renderPass.device + + var descriptors = @[Descriptor( + thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + count: 1, + stages: @[VK_SHADER_STAGE_VERTEX_BIT], + itemsize: uint32(sizeof(shaderUniforms(vertexShader))) + )] + when shaderUniforms(vertexShader) is shaderUniforms(fragmentShader): + descriptors[0].stages.add VK_SHADER_STAGE_FRAGMENT_BIT + else: + descriptors.add Descriptor( + thetype: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + count: 1, + stages: @[VK_SHADER_STAGE_FRAGMENT_BIT], + itemsize: uint32(sizeof(shaderUniforms(fragmentShader))) + ) + result.descriptorSetLayout = renderPass.device.createDescriptorSetLayout(descriptors) + + #[ + Descriptor + thetype: VkDescriptorType + count: uint32 + stages: seq[VkShaderStageFlagBits] + itemsize: uint32 + ]# + + # TODO: Push constants # var pushConstant = VkPushConstantRange( # stageFlags: toBits shaderStage, # offset: 0, # size: 0, # ) - var descriptorSets: seq[VkDescriptorSetLayout] = @[result.descriptorLayout] + var descriptorSetLayouts: seq[VkDescriptorSetLayout] = @[result.descriptorSetLayout.vk] # var pushConstants: seq[VkPushConstantRange] = @[pushConstant] var pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - setLayoutCount: uint32(descriptorSets.len), - pSetLayouts: descriptorSets.toCPointer, + setLayoutCount: uint32(descriptorSetLayouts.len), + pSetLayouts: descriptorSetLayouts.toCPointer, # pushConstantRangeCount: uint32(pushConstants.len), # pPushConstantRanges: pushConstants.toCPointer, ) @@ -151,11 +155,11 @@ assert pipeline.device.vk.valid assert pipeline.vk.valid assert pipeline.layout.valid - assert pipeline.descriptorLayout.valid + assert pipeline.descriptorSetLayout.vk.valid - pipeline.device.vk.vkDestroyDescriptorSetLayout(pipeline.descriptorLayout, nil) + pipeline.descriptorSetLayout.destroy() pipeline.device.vk.vkDestroyPipelineLayout(pipeline.layout, nil) pipeline.device.vk.vkDestroyPipeline(pipeline.vk, nil) - pipeline.descriptorLayout.reset() + pipeline.descriptorSetLayout.reset() pipeline.layout.reset() pipeline.vk.reset()
--- a/src/semicongine/vulkan/shader.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/shader.nim Fri Mar 24 00:11:42 2023 +0700 @@ -1,3 +1,4 @@ +import std/typetraits import std/macros import std/os import std/enumerate @@ -24,6 +25,10 @@ vk*: VkShaderModule entrypoint*: string +template shaderInput*[Inputs, Uniforms, Outputs](shader: Shader[Inputs, Uniforms, Outputs]): typedesc = Inputs +template shaderOutputs*[Inputs, Uniforms, Outputs](shader: Shader[Inputs, Uniforms, Outputs]): typedesc = Outputs +template shaderUniforms*[Inputs, Uniforms, Outputs](shader: Shader[Inputs, Uniforms, Outputs]): typedesc = Uniforms + proc compileGLSLToSPIRV*(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} =
--- a/src/semicongine/vulkan/swapchain.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/swapchain.nim Fri Mar 24 00:11:42 2023 +0700 @@ -3,28 +3,42 @@ import ./device import ./physicaldevice import ./image +import ./renderpass +import ./framebuffer +import ./syncing + import ../math type Swapchain = object + device*: Device vk*: VkSwapchainKHR - device*: Device - imageviews*: seq[ImageView] format*: VkFormat dimension*: TVec2[uint32] + nImages*: uint32 + imageviews*: seq[ImageView] + framebuffers*: seq[Framebuffer] + nInFlight*: uint32 + inFlightFences: seq[Fence] + imageAvailableSemaphores*: seq[Semaphore] + renderFinishedSemaphores*: seq[Semaphore] proc createSwapchain*( device: Device, + renderPass: RenderPass, surfaceFormat: VkSurfaceFormatKHR, - nBuffers=3'u32, + desiredNumberOfImages=3'u32, + framesInFlight=2'u32, presentationMode: VkPresentModeKHR=VK_PRESENT_MODE_MAILBOX_KHR ): (Swapchain, VkResult) = assert device.vk.valid assert device.physicalDevice.vk.valid + assert renderPass.vk.valid + var capabilities = device.physicalDevice.getSurfaceCapabilities() - var imageCount = nBuffers + var imageCount = desiredNumberOfImages # following is according to vulkan specs if presentationMode in [VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR]: imageCount = 1 @@ -53,17 +67,26 @@ swapchain = Swapchain( device: device, format: surfaceFormat.format, - dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height])) + nInFlight: framesInFlight, + dimension: TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height]) + ) createResult = device.vk.vkCreateSwapchainKHR(addr(createInfo), nil, addr(swapchain.vk)) if createResult == VK_SUCCESS: var nImages: uint32 checkVkResult device.vk.vkGetSwapchainImagesKHR(swapChain.vk, addr(nImages), nil) + swapchain.nImages = nImages var images = newSeq[VkImage](nImages) checkVkResult device.vk.vkGetSwapchainImagesKHR(swapChain.vk, addr(nImages), images.toCPointer) for vkimage in images: let image = Image(vk: vkimage, format: surfaceFormat.format, device: device) - swapChain.imageviews.add image.createImageView() + let imageview = image.createImageView() + swapChain.imageviews.add imageview + swapChain.framebuffers.add swapchain.device.createFramebuffer(renderPass, [imageview], swapchain.dimension) + for i in 0 ..< swapchain.nInFlight: + swapchain.inFlightFences.add device.createFence() + swapchain.imageAvailableSemaphores.add device.createSemaphore() + swapchain.renderFinishedSemaphores.add device.createSemaphore() return (swapchain, createResult) @@ -77,6 +100,17 @@ proc destroy*(swapchain: var Swapchain) = assert swapchain.vk.valid for imageview in swapchain.imageviews.mitems: + assert imageview.vk.valid imageview.destroy() + for framebuffer in swapchain.framebuffers.mitems: + assert framebuffer.vk.valid + framebuffer.destroy() + for i in 0 ..< swapchain.nInFlight: + assert swapchain.inFlightFences[i].vk.valid + assert swapchain.imageAvailableSemaphores[i].vk.valid + assert swapchain.renderFinishedSemaphores[i].vk.valid + swapchain.inFlightFences[i].destroy() + swapchain.imageAvailableSemaphores[i].destroy() + swapchain.renderFinishedSemaphores[i].destroy() swapchain.device.vk.vkDestroySwapchainKHR(swapchain.vk, nil) swapchain.vk.reset()
--- a/src/semicongine/vulkan/syncing.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/syncing.nim Fri Mar 24 00:11:42 2023 +0700 @@ -2,10 +2,10 @@ import ./device type - Semaphore = object + Semaphore* = object vk*: VkSemaphore device: Device - Fence = object + Fence* = object vk*: VkFence device: Device
--- a/src/semicongine/vulkan/vertex.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/src/semicongine/vulkan/vertex.nim Fri Mar 24 00:11:42 2023 +0700 @@ -1,10 +1,3 @@ -import std/tables -import std/macros - -import ../math -import ./api - - # add pragma to fields of the VertexType that represent per instance attributes template PerInstance*() {.pragma.}
--- a/tests/test_vulkan_wrapper.nim Mon Mar 20 23:48:46 2023 +0700 +++ b/tests/test_vulkan_wrapper.nim Fri Mar 24 00:11:42 2023 +0700 @@ -65,13 +65,14 @@ ) # setup render pipeline - var (swapchain, res) = device.createSwapchain(device.physicalDevice.getSurfaceFormats().filterSurfaceFormat()) + var surfaceFormat = device.physicalDevice.getSurfaceFormats().filterSurfaceFormat() + var renderpass = device.simpleForwardRenderPass(surfaceFormat.format) + var (swapchain, res) = device.createSwapchain(renderpass, surfaceFormat) if res != VK_SUCCESS: raise newException(Exception, "Unable to create swapchain") - var renderpass = device.simpleForwardRenderPass(swapchain.format) - var framebuffers: seq[Framebuffer] - for imageview in swapchain.imageviews: - framebuffers.add device.createFramebuffer(renderpass, [imageview], swapchain.dimension) + # var framebuffers: seq[Framebuffer] + # for imageview in swapchain.imageviews: + # framebuffers.add device.createFramebuffer(renderpass, [imageview], swapchain.dimension) # todo: could be create inside "device", but it would be nice to have nim v2 with support for circular dependencies first var @@ -86,11 +87,15 @@ var fragmentshader = createShader[FragmentInput, void, Pixel](device, VK_SHADER_STAGE_FRAGMENT_BIT, "main", fragmentBinary) var pipeline = renderpass.createPipeline(vertexshader, fragmentshader) + var descriptorPool = device.createDescriptorSetPool(@[(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1'u32)]) + var descriptorSet = descriptorPool.allocateDescriptorSet(pipeline.descriptorSetLayout, 1) echo "All successfull" echo "Start cleanup" # cleanup + checkVkResult device.vk.vkDeviceWaitIdle() + descriptorPool.destroy() vertexshader.destroy() fragmentshader.destroy() pipeline.destroy() @@ -98,8 +103,6 @@ imageAvailable.destroy() renderFinished.destroy() commandPool.destroy() - for fb in framebuffers.mitems: - fb.destroy() renderpass.destroy() swapchain.destroy() device.destroy()