# HG changeset patch # User sam # Date 1721313161 -25200 # Node ID 5dcb503ef0c03118d88182380b59b61073fbe828 # Parent 4e465583ea32784e735dde45071efcc305d7981c did: refactor renderpass a bit, enable depth buffering and msaa on offscreen-rendering diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/rendering.nim --- a/semiconginev2/rendering.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/rendering.nim Thu Jul 18 21:32:41 2024 +0700 @@ -12,6 +12,8 @@ const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 # ca. 100mb per block, seems reasonable const BUFFER_ALLOCATION_SIZE = 9_000_000'u64 # ca. 9mb per block, seems reasonable, can put 10 buffers into one memory block const MAX_DESCRIPTORSETS = 4 +const SURFACE_FORMAT* = VK_FORMAT_B8G8R8A8_SRGB +const DEPTH_FORMAT* = VK_FORMAT_D32_SFLOAT # custom pragmas to classify shader attributes template VertexAttribute* {.pragma.} @@ -42,29 +44,37 @@ debugMessenger: VkDebugUtilsMessengerEXT # unclear as of yet anisotropy*: float32 = 0 # needs to be enable during device creation - Swapchain* = object + Renderpass* = ref object + vk*: VkRenderPass + samples*: VkSampleCountFlagBits + depthBuffer*: bool + Swapchain* = ref object # parameters to InitSwapchain, required for swapchain recreation - renderPass: VkRenderPass + renderPass*: RenderPass vSync: bool - samples*: VkSampleCountFlagBits # populated through InitSwapchain proc vk: VkSwapchainKHR width*: uint32 height*: uint32 - msaaImage: VkImage - msaaMemory: VkDeviceMemory - msaaImageView: VkImageView framebuffers: seq[VkFramebuffer] framebufferViews: seq[VkImageView] currentFramebufferIndex: uint32 commandBufferPool: VkCommandPool + # depth buffer stuff, if enabled + depthImage: VkImage + depthImageView*: VkImageView + depthMemory: VkDeviceMemory + # MSAA stuff, if enabled + msaaImage: VkImage + msaaImageView*: VkImageView + msaaMemory: VkDeviceMemory # frame-in-flight handling currentFiF: range[0 .. (INFLIGHTFRAMES - 1).int] queueFinishedFence*: array[INFLIGHTFRAMES.int, VkFence] imageAvailableSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] renderFinishedSemaphore*: array[INFLIGHTFRAMES.int, VkSemaphore] commandBuffers: array[INFLIGHTFRAMES.int, VkCommandBuffer] - oldSwapchain: ref Swapchain + oldSwapchain: Swapchain oldSwapchainCounter: int # swaps until old swapchain will be destroyed var vulkan*: VulkanGlobals @@ -115,6 +125,7 @@ imageview*: VkImageView sampler*: VkSampler isRenderTarget*: bool = false + samples*: VkSampleCountFlagBits = VK_SAMPLE_COUNT_1_BIT GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object data*: seq[T] buffer*: Buffer diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/rendering/renderer.nim --- a/semiconginev2/rendering/renderer.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/rendering/renderer.nim Thu Jul 18 21:32:41 2024 +0700 @@ -444,7 +444,7 @@ usage.add VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT let format = GetVkFormat(elementType(image.data) is TVec1[uint8], usage = usage) - image.vk = svkCreate2DImage(image.width, image.height, format, usage) + image.vk = svkCreate2DImage(image.width, image.height, format, usage, image.samples) renderData.images.add image.vk image.sampler = createSampler(magFilter = image.interpolation, minFilter = image.interpolation) renderData.samplers.add image.sampler @@ -485,12 +485,13 @@ # data transfer and layout transition TransitionImageLayout(image.vk, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) - WithStagingBuffer( - (image.vk, image.width, image.height), - memoryRequirements.size, - stagingPtr - ): - copyMem(stagingPtr, image.data.ToCPointer, image.size) + if image.data.len > 0: + WithStagingBuffer( + (image.vk, image.width, image.height), + memoryRequirements.size, + stagingPtr + ): + copyMem(stagingPtr, image.data.ToCPointer, image.size) TransitionImageLayout(image.vk, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/rendering/renderpasses.nim --- a/semiconginev2/rendering/renderpasses.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/rendering/renderpasses.nim Thu Jul 18 21:32:41 2024 +0700 @@ -1,9 +1,9 @@ -proc CreateDirectPresentationRenderPass*(samples = VK_SAMPLE_COUNT_1_BIT): VkRenderPass = +proc CreateDirectPresentationRenderPass*(depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT): RenderPass = assert vulkan.instance.Valid, "Vulkan not initialized" + result = RenderPass(depthBuffer: depthBuffer, samples: samples) - let format = DefaultSurfaceFormat() var attachments = @[VkAttachmentDescription( - format: format, + format: SURFACE_FORMAT, samples: samples, loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp: VK_ATTACHMENT_STORE_OP_STORE, @@ -12,9 +12,20 @@ initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, finalLayout: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, )] + if depthBuffer: + attachments.add VkAttachmentDescription( + format: DEPTH_FORMAT, + samples: samples, + loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, + storeOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + finalLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + ) if samples != VK_SAMPLE_COUNT_1_BIT: attachments.add VkAttachmentDescription( - format: format, + format: SURFACE_FORMAT, samples: VK_SAMPLE_COUNT_1_BIT, loadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, storeOp: VK_ATTACHMENT_STORE_OP_STORE, @@ -30,49 +41,82 @@ dstSubpass: 0, srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT], dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT], - srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT], - dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT], + srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT], + dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT], ) ] colorAttachment = VkAttachmentReference( attachment: 0, layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) + depthAttachment = VkAttachmentReference( + attachment: 1, + layout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + ) resolveAttachment = VkAttachmentReference( - attachment: 1, + attachment: (attachments.len - 1).uint32, # depending on whether depthBuffer is used or not layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) - if samples == VK_SAMPLE_COUNT_1_BIT: - return svkCreateRenderPass(attachments, [colorAttachment], [], dependencies) - else: - return svkCreateRenderPass(attachments, [colorAttachment], [resolveAttachment], dependencies) + result.vk = svkCreateRenderPass( + attachments = attachments, + colorAttachments = [colorAttachment], + depthAttachments = if depthBuffer: @[depthAttachment] else: @[], + resolveAttachments = if samples > VK_SAMPLE_COUNT_1_BIT: @[resolveAttachment] else: @[], + dependencies = dependencies, + ) -proc CreateIndirectPresentationRenderPass*(): (VkRenderPass, VkRenderPass) = +proc CreateIndirectPresentationRenderPass*(depthBuffer: bool, samples = VK_SAMPLE_COUNT_1_BIT): (RenderPass, RenderPass) = assert vulkan.instance.Valid, "Vulkan not initialized" + result[0] = RenderPass(depthBuffer: depthBuffer, samples: samples) + result[1] = RenderPass(depthBuffer: false, samples: VK_SAMPLE_COUNT_1_BIT) + # first renderpass, drawing - let format = DefaultSurfaceFormat() var attachments = @[VkAttachmentDescription( - format: format, - samples: VK_SAMPLE_COUNT_1_BIT, + format: SURFACE_FORMAT, # not strictly necessary + samples: samples, loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp: VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, - finalLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + # finalLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + finalLayout: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, )] + if depthBuffer: + attachments.add VkAttachmentDescription( + format: DEPTH_FORMAT, + samples: samples, + loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, + storeOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + finalLayout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + ) + if samples != VK_SAMPLE_COUNT_1_BIT: + attachments.add VkAttachmentDescription( + format: SURFACE_FORMAT, + samples: VK_SAMPLE_COUNT_1_BIT, + loadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + storeOp: VK_ATTACHMENT_STORE_OP_STORE, + stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + finalLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + ) + var dependencies = @[ VkSubpassDependency( srcSubpass: VK_SUBPASS_EXTERNAL, dstSubpass: 0, - srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT], - dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT], - srcAccessMask: VkAccessFlags(0), - dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT], + srcStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT], + dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT], + srcAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT], + dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT], ), VkSubpassDependency( srcSubpass: VK_SUBPASS_EXTERNAL, @@ -95,11 +139,26 @@ attachment: 0, layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) + depthAttachment = VkAttachmentReference( + attachment: 1, + layout: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + ) + resolveAttachment = VkAttachmentReference( + attachment: (attachments.len - 1).uint32, # depending on whether depthBuffer is used or not + layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + ) + result[0].vk = svkCreateRenderPass( + attachments = attachments, + colorAttachments = [colorAttachment], + depthAttachments = if depthBuffer: @[depthAttachment] else: @[], + resolveAttachments = if samples > VK_SAMPLE_COUNT_1_BIT: @[resolveAttachment] else: @[], + dependencies = dependencies + ) # second renderpass, presentation var presentAttachments = @[VkAttachmentDescription( - format: format, + format: SURFACE_FORMAT, samples: VK_SAMPLE_COUNT_1_BIT, loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp: VK_ATTACHMENT_STORE_OP_STORE, @@ -121,13 +180,16 @@ layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) - result = ( - svkCreateRenderPass(attachments, [colorAttachment], [], dependencies), - svkCreateRenderPass(presentAttachments, [presentColorAttachment], [], presentDependencies) + result[1].vk = svkCreateRenderPass( + attachments = presentAttachments, + colorAttachments = [presentColorAttachment], + depthAttachments = [], + resolveAttachments = [], + dependencies = presentDependencies ) template WithRenderPass*( - theRenderpass: VkRenderPass, + theRenderpass: RenderPass, theFramebuffer: VkFramebuffer, commandbuffer: VkCommandBuffer, renderWidth: uint32, @@ -136,16 +198,19 @@ body: untyped ): untyped = var - clearColors = [VkClearValue(color: VkClearColorValue(float32: clearColor))] + clearColors = [ + VkClearValue(color: VkClearColorValue(float32: clearColor)), + VkClearValue(depthStencil: VkClearDepthStencilValue(depth: 1'f32, stencil: 0)) + ] renderPassInfo = VkRenderPassBeginInfo( sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - renderPass: theRenderpass, + renderPass: theRenderpass.vk, framebuffer: theFramebuffer, renderArea: VkRect2D( offset: VkOffset2D(x: 0, y: 0), extent: VkExtent2D(width: renderWidth, height: renderHeight), ), - clearValueCount: uint32(clearColors.len), + clearValueCount: clearColors.len.uint32, pClearValues: clearColors.ToCPointer(), ) viewport = VkViewport( diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/rendering/shaders.nim --- a/semiconginev2/rendering/shaders.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/rendering/shaders.nim Thu Jul 18 21:32:41 2024 +0700 @@ -344,13 +344,12 @@ inc setNumber proc CreatePipeline*[TShader]( - renderPass: VkRenderPass, + renderPass: RenderPass, topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, descriptorPoolLimit = 1024, - samples = VK_SAMPLE_COUNT_1_BIT, ): Pipeline[TShader] = # create pipeline @@ -439,12 +438,23 @@ multisampling = VkPipelineMultisampleStateCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, sampleShadingEnable: VK_FALSE, - rasterizationSamples: samples, + rasterizationSamples: renderPass.samples, minSampleShading: 1.0, pSampleMask: nil, alphaToCoverageEnable: VK_FALSE, alphaToOneEnable: VK_FALSE, ) + # will only be enabled it the renderpass actually uses a depth buffer + depthStencil = VkPipelineDepthStencilStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + depthTestEnable: true, + depthWriteEnable: true, + depthCompareOp: VK_COMPARE_OP_LESS, + depthBoundsTestEnable: false, + stencilTestEnable: false, + minDepthBounds: 0'f32, + maxDepthBounds: 0'f32, + ) colorBlendAttachment = VkPipelineColorBlendAttachmentState( colorWriteMask: toBits [VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT], blendEnable: VK_TRUE, @@ -476,11 +486,11 @@ pViewportState: addr(viewportState), pRasterizationState: addr(rasterizer), pMultisampleState: addr(multisampling), - pDepthStencilState: nil, + pDepthStencilState: if renderPass.depthBuffer: addr(depthStencil) else: nil, pColorBlendState: addr(colorBlending), pDynamicState: addr(dynamicState), layout: result.layout, - renderPass: renderPass, + renderPass: renderPass.vk, subpass: 0, basePipelineHandle: VkPipeline(0), basePipelineIndex: -1, diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/rendering/swapchain.nim --- a/semiconginev2/rendering/swapchain.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/rendering/swapchain.nim Thu Jul 18 21:32:41 2024 +0700 @@ -1,17 +1,15 @@ const N_FRAMEBUFFERS = 3'u32 proc InitSwapchain*( - renderPass: VkRenderPass, + renderPass: RenderPass, vSync: bool = false, - samples = VK_SAMPLE_COUNT_1_BIT, - oldSwapchain: ref Swapchain = nil, + oldSwapchain: Swapchain = nil, ): Option[Swapchain] = assert vulkan.instance.Valid, "Vulkan not initialized" var capabilities: VkSurfaceCapabilitiesKHR checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.physicalDevice, vulkan.surface, addr(capabilities)) let - format = DefaultSurfaceFormat() width = capabilities.currentExtent.width height = capabilities.currentExtent.height @@ -30,7 +28,7 @@ sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, surface: vulkan.surface, minImageCount: minFramebufferCount, - imageFormat: format, + imageFormat: SURFACE_FORMAT, imageColorSpace: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, # only one supported without special extensions imageExtent: capabilities.currentExtent, imageArrayLayers: 1, @@ -42,27 +40,54 @@ clipped: true, oldSwapchain: if oldSwapchain != nil: oldSwapchain.vk else: VkSwapchainKHR(0), ) - var swapchain: Swapchain + var swapchain = Swapchain( + width: width, + height: height, + renderPass: renderPass, + vSync: vSync, + oldSwapchain: oldSwapchain, + ) + if vkCreateSwapchainKHR(vulkan.device, addr(swapchainCreateInfo), nil, addr(swapchain.vk)) != VK_SUCCESS: return none(Swapchain) - swapchain.width = width - swapchain.height = height - swapchain.renderPass = renderPass - swapchain.vSync = vSync - swapchain.samples = samples - swapchain.oldSwapchain = oldSwapchain if swapchain.oldSwapchain != nil: swapchain.oldSwapchainCounter = INFLIGHTFRAMES.int * 2 + # create depth buffer image+view if desired + if renderPass.depthBuffer: + swapchain.depthImage = svkCreate2DImage( + width = width, + height = height, + format = DEPTH_FORMAT, + usage = [VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT], + samples = renderPass.samples, + ) + let requirements = svkGetImageMemoryRequirements(swapchain.depthImage) + swapchain.depthMemory = svkAllocateMemory( + requirements.size, + BestMemory(mappable = false, filter = requirements.memoryTypes) + ) + checkVkResult vkBindImageMemory( + vulkan.device, + swapchain.depthImage, + swapchain.depthMemory, + 0, + ) + swapchain.depthImageView = svkCreate2DImageView( + image = swapchain.depthImage, + format = DEPTH_FORMAT, + aspect = VK_IMAGE_ASPECT_DEPTH_BIT + ) + # create msaa image+view if desired - if samples != VK_SAMPLE_COUNT_1_BIT: + if renderPass.samples != VK_SAMPLE_COUNT_1_BIT: swapchain.msaaImage = svkCreate2DImage( width = width, height = height, - format = format, + format = SURFACE_FORMAT, usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT], - samples = samples, + samples = renderPass.samples, ) let requirements = svkGetImageMemoryRequirements(swapchain.msaaImage) swapchain.msaaMemory = svkAllocateMemory( @@ -75,7 +100,7 @@ swapchain.msaaMemory, 0, ) - swapchain.msaaImageView = svkCreate2DImageView(swapchain.msaaImage, format) + swapchain.msaaImageView = svkCreate2DImageView(image = swapchain.msaaImage, format = SURFACE_FORMAT) # create framebuffers var actualNFramebuffers: uint32 @@ -84,11 +109,25 @@ checkVkResult vkGetSwapchainImagesKHR(vulkan.device, swapchain.vk, addr(actualNFramebuffers), framebuffers.ToCPointer) for framebuffer in framebuffers: - swapchain.framebufferViews.add svkCreate2DImageView(framebuffer, format) - if samples == VK_SAMPLE_COUNT_1_BIT: - swapchain.framebuffers.add svkCreateFramebuffer(renderPass, width, height, [swapchain.framebufferViews[^1]]) + swapchain.framebufferViews.add svkCreate2DImageView(framebuffer, SURFACE_FORMAT) + var attachments: seq[VkImageView] + if renderPass.samples == VK_SAMPLE_COUNT_1_BIT: + if renderPass.depthBuffer: + attachments = @[swapchain.framebufferViews[^1], swapchain.depthImageView] + else: + attachments = @[swapchain.framebufferViews[^1]] else: - swapchain.framebuffers.add svkCreateFramebuffer(renderPass, width, height, [swapchain.msaaImageView, swapchain.framebufferViews[^1]]) + if renderPass.depthBuffer: + attachments = @[swapchain.msaaImageView, swapchain.depthImageView, swapchain.framebufferViews[^1]] + else: + attachments = @[swapchain.msaaImageView, swapchain.framebufferViews[^1]] + + swapchain.framebuffers.add svkCreateFramebuffer( + renderpass = renderPass.vk, + width = width, + height = height, + attachments = attachments, + ) # create sync primitives for i in 0 ..< INFLIGHTFRAMES: @@ -115,11 +154,16 @@ proc DestroySwapchain*(swapchain: Swapchain) = - if swapchain.samples != VK_SAMPLE_COUNT_1_BIT: + if swapchain.msaaImage.Valid: vkDestroyImageView(vulkan.device, swapchain.msaaImageView, nil) vkDestroyImage(vulkan.device, swapchain.msaaImage, nil) vkFreeMemory(vulkan.device, swapchain.msaaMemory, nil) + if swapchain.depthImage.Valid: + vkDestroyImageView(vulkan.device, swapchain.depthImageView, nil) + vkDestroyImage(vulkan.device, swapchain.depthImage, nil) + vkFreeMemory(vulkan.device, swapchain.depthMemory, nil) + for fence in swapchain.queueFinishedFence: vkDestroyFence(vulkan.device, fence, nil) @@ -139,7 +183,7 @@ vkDestroySwapchainKHR(vulkan.device, swapchain.vk, nil) -proc TryAcquireNextImage(swapchain: var Swapchain): Option[VkFramebuffer] = +proc TryAcquireNextImage(swapchain: Swapchain): Option[VkFramebuffer] = if not swapchain.queueFinishedFence[swapchain.currentFiF].Await(100_000_000): return none(VkFramebuffer) @@ -158,7 +202,7 @@ return none(VkFramebuffer) return some(swapchain.framebuffers[swapchain.currentFramebufferIndex]) -proc Swap(swapchain: var Swapchain, commandBuffer: VkCommandBuffer): bool = +proc Swap(swapchain: Swapchain, commandBuffer: VkCommandBuffer): bool = var waitStage = VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) submitInfo = VkSubmitInfo( @@ -192,7 +236,7 @@ if swapchain.oldSwapchain != nil: dec swapchain.oldSwapchainCounter if swapchain.oldSwapchainCounter <= 0: - DestroySwapchain(swapchain.oldSwapchain[]) + DestroySwapchain(swapchain.oldSwapchain) swapchain.oldSwapchain = nil if presentResult != VK_SUCCESS: @@ -202,21 +246,18 @@ return true proc Recreate(swapchain: Swapchain): Option[Swapchain] = - var oldSwapchain = new Swapchain - oldSwapchain[] = swapchain InitSwapchain( renderPass = swapchain.renderPass, vSync = swapchain.vSync, - samples = swapchain.samples, - oldSwapchain = oldSwapchain, + oldSwapchain = swapchain, ) -template WithNextFrame*(swapchain: var Swapchain, framebufferName, commandBufferName, body: untyped): untyped = - var maybeFramebuffer = TryAcquireNextImage(swapchain) +template WithNextFrame*(theSwapchain: var Swapchain, framebufferName, commandBufferName, body: untyped): untyped = + var maybeFramebuffer = TryAcquireNextImage(theSwapchain) if maybeFramebuffer.isSome: block: let `framebufferName` {.inject.} = maybeFramebuffer.get - let `commandBufferName` {.inject.} = swapchain.commandBuffers[swapchain.currentFiF] + let `commandBufferName` {.inject.} = theSwapchain.commandBuffers[theSwapchain.currentFiF] let beginInfo = VkCommandBufferBeginInfo( sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, flags: VkCommandBufferUsageFlags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT), @@ -227,9 +268,9 @@ body checkVkResult vkEndCommandBuffer(`commandBufferName`) - discard Swap(swapchain = swapchain, commandBuffer = `commandBufferName`) + discard Swap(swapchain = theSwapchain, commandBuffer = `commandBufferName`) else: - let maybeNewSwapchain = Recreate(swapchain) + let maybeNewSwapchain = Recreate(theSwapchain) if maybeNewSwapchain.isSome: - swapchain = maybeNewSwapchain.get + theSwapchain = maybeNewSwapchain.get diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/rendering/vulkan_wrappers.nim --- a/semiconginev2/rendering/vulkan_wrappers.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/rendering/vulkan_wrappers.nim Thu Jul 18 21:32:41 2024 +0700 @@ -49,10 +49,6 @@ addr(result), ) -proc DefaultSurfaceFormat(): VkFormat = - # EVERY windows driver and almost every linux driver should support this - VK_FORMAT_B8G8R8A8_SRGB - func size(format: VkFormat): uint64 = const formatSize = [ VK_FORMAT_B8G8R8A8_SRGB.int: 4'u64, @@ -140,7 +136,7 @@ ) checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result)) -proc svkCreate2DImageView(image: VkImage, format: VkFormat): VkImageView = +proc svkCreate2DImageView*(image: VkImage, format: VkFormat, aspect = VK_IMAGE_ASPECT_COLOR_BIT): VkImageView = var createInfo = VkImageViewCreateInfo( sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, image: image, @@ -153,7 +149,7 @@ a: VK_COMPONENT_SWIZZLE_IDENTITY, ), subresourceRange: VkImageSubresourceRange( - aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT), + aspectMask: toBits [aspect], baseMipLevel: 0, levelCount: 1, baseArrayLayer: 0, @@ -229,6 +225,7 @@ proc svkCreateRenderPass( attachments: openArray[VkAttachmentDescription], colorAttachments: openArray[VkAttachmentReference], + depthAttachments: openArray[VkAttachmentReference], resolveAttachments: openArray[VkAttachmentReference], dependencies: openArray[VkSubpassDependency], ): VkRenderPass = @@ -241,7 +238,7 @@ colorAttachmentCount: colorAttachments.len.uint32, pColorAttachments: colorAttachments.ToCPointer, pResolveAttachments: resolveAttachments.ToCPointer, - pDepthStencilAttachment: nil, + pDepthStencilAttachment: depthAttachments.ToCPointer, preserveAttachmentCount: 0, pPreserveAttachments: nil, ) diff -r 4e465583ea32 -r 5dcb503ef0c0 semiconginev2/resources.nim --- a/semiconginev2/resources.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/semiconginev2/resources.nim Thu Jul 18 21:32:41 2024 +0700 @@ -96,8 +96,6 @@ elif thebundletype == Exe: - import std/tables - const BUILD_RESOURCEROOT* {.strdefine.}: string = "" proc loadResources(): Table[string, Table[string, string]] {.compileTime.} = diff -r 4e465583ea32 -r 5dcb503ef0c0 tests/test_rendering.nim --- a/tests/test_rendering.nim Thu Jul 18 16:33:24 2024 +0700 +++ b/tests/test_rendering.nim Thu Jul 18 21:32:41 2024 +0700 @@ -3,11 +3,7 @@ import ../semiconginev2 -var - mainRenderpass: VkRenderPass - swapchain: Swapchain - -proc test_01_triangle(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) = +proc test_01_triangle(nFrames: int, swapchain: var Swapchain) = var renderdata = InitRenderData() type @@ -33,12 +29,12 @@ renderdata.FlushAllMemory() var - pipeline = CreatePipeline[TrianglShader](renderPass = renderPass, samples = samples) + pipeline = CreatePipeline[TrianglShader](renderPass = swapchain.renderPass) var c = 0 while UpdateInputs() and c < nFrames: WithNextFrame(swapchain, framebuffer, commandbuffer): - WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): + WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): WithPipeline(commandbuffer, pipeline): Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh) inc c @@ -49,7 +45,7 @@ DestroyRenderData(renderdata) -proc test_02_triangle_quad_instanced(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) = +proc test_02_triangle_quad_instanced(nFrames: int, swapchain: var Swapchain) = var renderdata = InitRenderData() type @@ -101,12 +97,12 @@ AssignBuffers(renderdata, instancesB) renderdata.FlushAllMemory() - var pipeline = CreatePipeline[SomeShader](renderPass = renderPass, samples = samples) + var pipeline = CreatePipeline[SomeShader](renderPass = swapchain.renderPass) var c = 0 while UpdateInputs() and c < nFrames: WithNextFrame(swapchain, framebuffer, commandbuffer): - WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): + WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): WithPipeline(commandbuffer, pipeline): Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad, instances = instancesA) Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad, instances = instancesB) @@ -119,7 +115,7 @@ DestroyPipeline(pipeline) DestroyRenderData(renderdata) -proc test_03_simple_descriptorset(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) = +proc test_03_simple_descriptorset(nFrames: int, swapchain: var Swapchain) = var renderdata = InitRenderData() type @@ -178,7 +174,7 @@ UploadImages(renderdata, uniforms2) renderdata.FlushAllMemory() - var pipeline = CreatePipeline[QuadShader](renderPass = renderPass, samples = samples) + var pipeline = CreatePipeline[QuadShader](renderPass = swapchain.renderPass) InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], uniforms1) InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], uniforms2) @@ -186,7 +182,7 @@ var c = 0 while UpdateInputs() and c < nFrames: WithNextFrame(swapchain, framebuffer, commandbuffer): - WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): + WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): WithPipeline(commandbuffer, pipeline): WithBind(commandbuffer, (uniforms1, ), pipeline, swapchain.currentFiF): Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad) @@ -199,7 +195,7 @@ DestroyPipeline(pipeline) DestroyRenderData(renderdata) -proc test_04_multiple_descriptorsets(nFrames: int, renderPass: VkRenderPass, samples = VK_SAMPLE_COUNT_1_BIT) = +proc test_04_multiple_descriptorsets(nFrames: int, swapchain: var Swapchain) = var renderdata = InitRenderData() type @@ -286,7 +282,7 @@ UploadImages(renderdata, mainset) renderdata.FlushAllMemory() - var pipeline = CreatePipeline[QuadShader](renderPass = renderPass, samples = samples) + var pipeline = CreatePipeline[QuadShader](renderPass = swapchain.renderPass) InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], constset) InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[1], mainset) @@ -297,7 +293,7 @@ while UpdateInputs() and c < nFrames: TimeAndLog: WithNextFrame(swapchain, framebuffer, commandbuffer): - WithRenderPass(renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): + WithRenderPass(swapchain.renderPass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): WithPipeline(commandbuffer, pipeline): WithBind(commandbuffer, (constset, mainset, otherset1), pipeline, swapchain.currentFiF): Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad) @@ -315,9 +311,9 @@ DestroyPipeline(pipeline) DestroyRenderData(renderdata) -proc test_05_triangle_2pass(nFrames: int, samples = VK_SAMPLE_COUNT_1_BIT) = +proc test_05_triangle_2pass(nFrames: int) = var - (offscreenRP, presentRP) = CreateIndirectPresentationRenderPass() + (offscreenRP, presentRP) = CreateIndirectPresentationRenderPass(depthBuffer = true, samples = VK_SAMPLE_COUNT_4_BIT) swapchain = InitSwapchain(renderpass = presentRP).get() var renderdata = InitRenderData() @@ -376,25 +372,91 @@ frameTexture: Image[TVec4[uint8]](width: swapchain.width, height: swapchain.height, isRenderTarget: true), ) ) - var uniforms2 = asDescriptorSet( - Uniforms( - frameTexture: Image[TVec4[uint8]](width: swapchain.width, height: swapchain.height, isRenderTarget: true), - ) - ) AssignBuffers(renderdata, mesh) AssignBuffers(renderdata, quad) UploadImages(renderdata, uniforms1) - UploadImages(renderdata, uniforms2) renderdata.FlushAllMemory() var - drawPipeline = CreatePipeline[TriangleShader](renderPass = offscreenRP, samples = samples) - presentPipeline = CreatePipeline[PresentShader](renderPass = presentRP, samples = samples) + drawPipeline = CreatePipeline[TriangleShader](renderPass = offscreenRP) + presentPipeline = CreatePipeline[PresentShader](renderPass = presentRP) InitDescriptorSet(renderdata, presentPipeline.descriptorSetLayouts[0], uniforms1) - InitDescriptorSet(renderdata, presentPipeline.descriptorSetLayouts[0], uniforms2) + + # create depth buffer images (will not use the one in the swapchain + var + depthImage: VkImage + depthImageView: VkImageView + depthMemory: VkDeviceMemory + if offscreenRP.depthBuffer: + depthImage = svkCreate2DImage( + width = swapchain.width, + height = swapchain.height, + format = DEPTH_FORMAT, + usage = [VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT], + samples = offscreenRP.samples, + ) + let requirements = svkGetImageMemoryRequirements(depthImage) + depthMemory = svkAllocateMemory( + requirements.size, + BestMemory(mappable = false, filter = requirements.memoryTypes) + ) + checkVkResult vkBindImageMemory( + vulkan.device, + depthImage, + depthMemory, + 0, + ) + depthImageView = svkCreate2DImageView( + image = depthImage, + format = DEPTH_FORMAT, + aspect = VK_IMAGE_ASPECT_DEPTH_BIT + ) - var offscreenFB = svkCreateFramebuffer(offscreenRP, swapchain.width, swapchain.height, [uniforms1.data.frameTexture.imageview]) + # create msaa images (will not use the one in the swapchain + var + msaaImage: VkImage + msaaImageView: VkImageView + msaaMemory: VkDeviceMemory + if offscreenRP.samples != VK_SAMPLE_COUNT_1_BIT: + msaaImage = svkCreate2DImage( + width = swapchain.width, + height = swapchain.height, + format = SURFACE_FORMAT, + usage = [VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT], + samples = offscreenRP.samples, + ) + let requirements = svkGetImageMemoryRequirements(msaaImage) + msaaMemory = svkAllocateMemory( + requirements.size, + BestMemory(mappable = false, filter = requirements.memoryTypes) + ) + checkVkResult vkBindImageMemory( + vulkan.device, + msaaImage, + msaaMemory, + 0, + ) + msaaImageView = svkCreate2DImageView(image = msaaImage, format = SURFACE_FORMAT) + + var attachments: seq[VkImageView] + if offscreenRP.samples == VK_SAMPLE_COUNT_1_BIT: + if offscreenRP.depthBuffer: + attachments = @[uniforms1.data.frameTexture.imageview, depthImageView] + else: + attachments = @[uniforms1.data.frameTexture.imageview] + else: + if offscreenRP.depthBuffer: + attachments = @[msaaImageView, depthImageView, uniforms1.data.frameTexture.imageview] + else: + attachments = @[msaaImageView, uniforms1.data.frameTexture.imageview] + echo attachments + var offscreenFB = svkCreateFramebuffer( + offscreenRP.vk, + swapchain.width, + swapchain.height, + attachments + ) var c = 0 while UpdateInputs() and c < nFrames: @@ -417,50 +479,55 @@ DestroyPipeline(presentPipeline) DestroyPipeline(drawPipeline) DestroyRenderData(renderdata) - vkDestroyRenderPass(vulkan.device, offscreenRP, nil) - vkDestroyRenderPass(vulkan.device, presentRP, nil) + if depthImage.Valid: + vkDestroyImageView(vulkan.device, depthImageView, nil) + vkDestroyImage(vulkan.device, depthImage, nil) + vkFreeMemory(vulkan.device, depthMemory, nil) + if msaaImage.Valid: + vkDestroyImageView(vulkan.device, msaaImageView, nil) + vkDestroyImage(vulkan.device, msaaImage, nil) + vkFreeMemory(vulkan.device, msaaMemory, nil) + vkDestroyRenderPass(vulkan.device, offscreenRP.vk, nil) + vkDestroyRenderPass(vulkan.device, presentRP.vk, nil) vkDestroyFramebuffer(vulkan.device, offscreenFB, nil) DestroySwapchain(swapchain) when isMainModule: - var nFrames = 2000 + var nFrames = 1000 InitVulkan() + var mainRenderpass: RenderPass + var renderPasses = [ + (depthBuffer: false, samples: VK_SAMPLE_COUNT_1_BIT), + (depthBuffer: false, samples: VK_SAMPLE_COUNT_4_BIT), + (depthBuffer: true, samples: VK_SAMPLE_COUNT_1_BIT), + (depthBuffer: true, samples: VK_SAMPLE_COUNT_4_BIT), + ] # test normal - block: - mainRenderpass = CreateDirectPresentationRenderPass() - swapchain = InitSwapchain(renderpass = mainRenderpass).get() + if false: + for i, (depthBuffer, samples) in renderPasses: + var renderpass = CreateDirectPresentationRenderPass(depthBuffer = depthBuffer, samples = samples) + var swapchain = InitSwapchain(renderpass = renderpass).get() - # tests a simple triangle with minimalistic shader and vertex format - test_01_triangle(nFrames, renderPass = mainRenderpass) - - # tests instanced triangles and quads, mixing meshes and instances - test_02_triangle_quad_instanced(nFrames, renderPass = mainRenderpass) - - # teste descriptor sets - test_03_simple_descriptorset(nFrames, renderPass = mainRenderpass) + # tests a simple triangle with minimalistic shader and vertex format + test_01_triangle(nFrames, swapchain) - # tests multiple descriptor sets and arrays - test_04_multiple_descriptorsets(nFrames, renderPass = mainRenderpass) + # tests instanced triangles and quads, mixing meshes and instances + test_02_triangle_quad_instanced(nFrames, swapchain) - checkVkResult vkDeviceWaitIdle(vulkan.device) - vkDestroyRenderPass(vulkan.device, mainRenderpass, nil) - DestroySwapchain(swapchain) + # teste descriptor sets + test_03_simple_descriptorset(nFrames, swapchain) - # test MSAA - block: - mainRenderpass = CreateDirectPresentationRenderPass(samples = VK_SAMPLE_COUNT_4_BIT) - swapchain = InitSwapchain(renderpass = mainRenderpass, samples = VK_SAMPLE_COUNT_4_BIT).get() + # tests multiple descriptor sets and arrays + test_04_multiple_descriptorsets(nFrames, swapchain) - test_01_triangle(nFrames, renderPass = mainRenderpass, VK_SAMPLE_COUNT_4_BIT) - - checkVkResult vkDeviceWaitIdle(vulkan.device) - vkDestroyRenderPass(vulkan.device, mainRenderpass, nil) - DestroySwapchain(swapchain) + checkVkResult vkDeviceWaitIdle(vulkan.device) + vkDestroyRenderPass(vulkan.device, renderpass.vk, nil) + DestroySwapchain(swapchain) # test multiple render passes block: - test_05_triangle_2pass(999999999) + test_05_triangle_2pass(nFrames) DestroyVulkan()