diff --git a/attachments/27_depth_buffering.cpp b/attachments/27_depth_buffering.cpp index ed9d9244..dfb24250 100644 --- a/attachments/27_depth_buffering.cpp +++ b/attachments/27_depth_buffering.cpp @@ -49,15 +49,14 @@ struct Vertex static vk::VertexInputBindingDescription getBindingDescription() { - return {0, sizeof(Vertex), vk::VertexInputRate::eVertex}; + return {.binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex}; } static std::array getAttributeDescriptions() { - return { - vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos)), - vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color)), - vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, texCoord))}; + return {{{.location = 0, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, pos)}, + {.location = 1, .binding = 0, .format = vk::Format::eR32G32B32Sfloat, .offset = offsetof(Vertex, color)}, + {.location = 2, .binding = 0, .format = vk::Format::eR32G32Sfloat, .offset = offsetof(Vertex, texCoord)}}}; } }; @@ -164,7 +163,7 @@ class HelloTriangleApplication static void framebufferResizeCallback(GLFWwindow *window, int width, int height) { - auto app = static_cast(glfwGetWindowUserPointer(window)); + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->framebufferResized = true; } @@ -210,7 +209,7 @@ class HelloTriangleApplication swapChain = nullptr; } - void cleanup() const + void cleanup() { glfwDestroyWindow(window); @@ -220,7 +219,6 @@ class HelloTriangleApplication void recreateSwapChain() { int width = 0, height = 0; - glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { glfwGetFramebufferSize(window, &width, &height); @@ -294,7 +292,7 @@ class HelloTriangleApplication vk::DebugUtilsMessageSeverityFlagsEXT severityFlags(vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError); vk::DebugUtilsMessageTypeFlagsEXT messageTypeFlags( - vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation); + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation); vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfoEXT{.messageSeverity = severityFlags, .messageType = messageTypeFlags, .pfnUserCallback = &debugCallback}; @@ -331,7 +329,6 @@ class HelloTriangleApplication // Check if the physicalDevice supports the required features auto features = physicalDevice.template getFeatures2(); bool supportsRequiredFeatures = features.template get().features.samplerAnisotropy && @@ -426,21 +423,18 @@ class HelloTriangleApplication { assert(swapChainImageViews.empty()); - vk::ImageViewCreateInfo imageViewCreateInfo{.viewType = vk::ImageViewType::e2D, - .format = swapChainSurfaceFormat.format, - .subresourceRange = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}}; + swapChainImageViews.reserve(swapChainImages.size()); for (auto &image : swapChainImages) { - imageViewCreateInfo.image = image; - swapChainImageViews.emplace_back(device, imageViewCreateInfo); + swapChainImageViews.emplace_back(createImageView(image, swapChainSurfaceFormat.format, vk::ImageAspectFlagBits::eColor)); } } void createDescriptorSetLayout() { - std::array bindings = { - vk::DescriptorSetLayoutBinding(0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex, nullptr), - vk::DescriptorSetLayoutBinding(1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment, nullptr)}; + std::array bindings{ + {{.binding = 0, .descriptorType = vk::DescriptorType::eUniformBuffer, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eVertex}, + {.binding = 1, .descriptorType = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eFragment}}}; vk::DescriptorSetLayoutCreateInfo layoutInfo{.bindingCount = static_cast(bindings.size()), .pBindings = bindings.data()}; descriptorSetLayout = vk::raii::DescriptorSetLayout(device, layoutInfo); @@ -454,30 +448,25 @@ class HelloTriangleApplication vk::PipelineShaderStageCreateInfo fragShaderStageInfo{.stage = vk::ShaderStageFlagBits::eFragment, .module = shaderModule, .pName = "fragMain"}; vk::PipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; - auto bindingDescription = Vertex::getBindingDescription(); - auto attributeDescriptions = Vertex::getAttributeDescriptions(); - vk::PipelineVertexInputStateCreateInfo vertexInputInfo{ - .vertexBindingDescriptionCount = 1, - .pVertexBindingDescriptions = &bindingDescription, - .vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()), - .pVertexAttributeDescriptions = attributeDescriptions.data()}; - vk::PipelineInputAssemblyStateCreateInfo inputAssembly{ - .topology = vk::PrimitiveTopology::eTriangleList, - .primitiveRestartEnable = vk::False}; - vk::PipelineViewportStateCreateInfo viewportState{ - .viewportCount = 1, - .scissorCount = 1}; - vk::PipelineRasterizationStateCreateInfo rasterizer{ - .depthClampEnable = vk::False, - .rasterizerDiscardEnable = vk::False, - .polygonMode = vk::PolygonMode::eFill, - .cullMode = vk::CullModeFlagBits::eBack, - .frontFace = vk::FrontFace::eCounterClockwise, - .depthBiasEnable = vk::False, - .lineWidth = 1.0f}; - vk::PipelineMultisampleStateCreateInfo multisampling{ - .rasterizationSamples = vk::SampleCountFlagBits::e1, - .sampleShadingEnable = vk::False}; + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + vk::PipelineVertexInputStateCreateInfo vertexInputInfo{.vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &bindingDescription, + .vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()), + .pVertexAttributeDescriptions = attributeDescriptions.data()}; + vk::PipelineInputAssemblyStateCreateInfo inputAssembly{.topology = vk::PrimitiveTopology::eTriangleList}; + vk::PipelineViewportStateCreateInfo viewportState{.viewportCount = 1, .scissorCount = 1}; + + vk::PipelineRasterizationStateCreateInfo rasterizer{.depthClampEnable = vk::False, + .rasterizerDiscardEnable = vk::False, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .frontFace = vk::FrontFace::eCounterClockwise, + .depthBiasEnable = vk::False, + .lineWidth = 1.0f}; + + vk::PipelineMultisampleStateCreateInfo multisampling{.rasterizationSamples = vk::SampleCountFlagBits::e1, .sampleShadingEnable = vk::False}; + vk::PipelineDepthStencilStateCreateInfo depthStencil{ .depthTestEnable = vk::True, .depthWriteEnable = vk::True, @@ -487,18 +476,14 @@ class HelloTriangleApplication vk::PipelineColorBlendAttachmentState colorBlendAttachment{ .blendEnable = vk::False, .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA}; + vk::PipelineColorBlendStateCreateInfo colorBlending{ - .logicOpEnable = vk::False, - .logicOp = vk::LogicOp::eCopy, - .attachmentCount = 1, - .pAttachments = &colorBlendAttachment}; - std::vector dynamicStates = { - vk::DynamicState::eViewport, - vk::DynamicState::eScissor}; + .logicOpEnable = vk::False, .logicOp = vk::LogicOp::eCopy, .attachmentCount = 1, .pAttachments = &colorBlendAttachment}; + + std::vector dynamicStates = {vk::DynamicState::eViewport, vk::DynamicState::eScissor}; vk::PipelineDynamicStateCreateInfo dynamicState{.dynamicStateCount = static_cast(dynamicStates.size()), .pDynamicStates = dynamicStates.data()}; vk::PipelineLayoutCreateInfo pipelineLayoutInfo{.setLayoutCount = 1, .pSetLayouts = &*descriptorSetLayout, .pushConstantRangeCount = 0}; - pipelineLayout = vk::raii::PipelineLayout(device, pipelineLayoutInfo); vk::Format depthFormat = findDepthFormat(); @@ -523,9 +508,8 @@ class HelloTriangleApplication void createCommandPool() { - vk::CommandPoolCreateInfo poolInfo{ - .flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, - .queueFamilyIndex = queueIndex}; + vk::CommandPoolCreateInfo poolInfo{.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer, + .queueFamilyIndex = queueIndex}; commandPool = vk::raii::CommandPool(device, poolInfo); } @@ -533,35 +517,30 @@ class HelloTriangleApplication { vk::Format depthFormat = findDepthFormat(); - createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, depthImage, depthImageMemory); - depthImageView = createImageView(depthImage, depthFormat, vk::ImageAspectFlagBits::eDepth); + std::tie(depthImage, depthImageMemory) = createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal); + depthImageView = createImageView(depthImage, depthFormat, vk::ImageAspectFlagBits::eDepth); } vk::Format findSupportedFormat(const std::vector &candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features) { - auto formatIt = std::ranges::find_if(candidates, [&](auto const format) { - vk::FormatProperties props = physicalDevice.getFormatProperties(format); - return (((tiling == vk::ImageTiling::eLinear) && ((props.linearTilingFeatures & features) == features)) || - ((tiling == vk::ImageTiling::eOptimal) && ((props.optimalTilingFeatures & features) == features))); - }); - if (formatIt == candidates.end()) + for (const auto format : candidates) { - throw std::runtime_error("failed to find supported format!"); + vk::FormatProperties props = physicalDevice.getFormatProperties(format); + if (((tiling == vk::ImageTiling::eLinear) && ((props.linearTilingFeatures & features) == features)) || + ((tiling == vk::ImageTiling::eOptimal) && ((props.optimalTilingFeatures & features) == features))) + { + return format; + } } - return *formatIt; - } - vk::Format findDepthFormat() - { - return findSupportedFormat( - {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint}, - vk::ImageTiling::eOptimal, - vk::FormatFeatureFlagBits::eDepthStencilAttachment); + throw std::runtime_error("failed to find supported format!"); } - bool hasStencilComponent(vk::Format format) + vk::Format findDepthFormat() { - return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint; + return findSupportedFormat({vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint}, + vk::ImageTiling::eOptimal, + vk::FormatFeatureFlagBits::eDepthStencilAttachment); } void createTextureImage() @@ -575,9 +554,8 @@ class HelloTriangleApplication throw std::runtime_error("failed to load texture image!"); } - vk::raii::Buffer stagingBuffer({}); - vk::raii::DeviceMemory stagingBufferMemory({}); - createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); + auto [stagingBuffer, stagingBufferMemory] = + createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); void *data = stagingBufferMemory.mapMemory(0, imageSize); memcpy(data, pixels, imageSize); @@ -585,78 +563,83 @@ class HelloTriangleApplication stbi_image_free(pixels); - createImage(texWidth, texHeight, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, textureImage, textureImageMemory); - - transitionImageLayout(textureImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); - copyBufferToImage(stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); - transitionImageLayout(textureImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); + std::tie(textureImage, textureImageMemory) = createImage(texWidth, + texHeight, + vk::Format::eR8G8B8A8Srgb, + vk::ImageTiling::eOptimal, + vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, + vk::MemoryPropertyFlagBits::eDeviceLocal); + + vk::raii::CommandBuffer commandBuffer = beginSingleTimeCommands(); + transitionImageLayout(commandBuffer, textureImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); + copyBufferToImage(commandBuffer, stagingBuffer, textureImage, static_cast(texWidth), static_cast(texHeight)); + transitionImageLayout(commandBuffer, textureImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); + endSingleTimeCommands(std::move(commandBuffer)); } void createTextureImageView() { - textureImageView = createImageView(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor); + textureImageView = createImageView(*textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor); } void createTextureSampler() { vk::PhysicalDeviceProperties properties = physicalDevice.getProperties(); - vk::SamplerCreateInfo samplerInfo{ - .magFilter = vk::Filter::eLinear, - .minFilter = vk::Filter::eLinear, - .mipmapMode = vk::SamplerMipmapMode::eLinear, - .addressModeU = vk::SamplerAddressMode::eRepeat, - .addressModeV = vk::SamplerAddressMode::eRepeat, - .addressModeW = vk::SamplerAddressMode::eRepeat, - .mipLodBias = 0.0f, - .anisotropyEnable = vk::True, - .maxAnisotropy = properties.limits.maxSamplerAnisotropy, - .compareEnable = vk::False, - .compareOp = vk::CompareOp::eAlways}; + vk::SamplerCreateInfo samplerInfo{.magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eLinear, + .addressModeU = vk::SamplerAddressMode::eRepeat, + .addressModeV = vk::SamplerAddressMode::eRepeat, + .addressModeW = vk::SamplerAddressMode::eRepeat, + .mipLodBias = 0.0f, + .anisotropyEnable = vk::True, + .maxAnisotropy = properties.limits.maxSamplerAnisotropy, + .compareEnable = vk::False, + .compareOp = vk::CompareOp::eAlways}; textureSampler = vk::raii::Sampler(device, samplerInfo); } - vk::raii::ImageView createImageView(vk::raii::Image &image, vk::Format format, vk::ImageAspectFlags aspectFlags) + vk::raii::ImageView createImageView(vk::Image const &image, vk::Format format, vk::ImageAspectFlags aspectFlags) { vk::ImageViewCreateInfo viewInfo{ .image = image, .viewType = vk::ImageViewType::e2D, .format = format, - .subresourceRange = {aspectFlags, 0, 1, 0, 1}}; + .subresourceRange = {.aspectMask = aspectFlags, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; return vk::raii::ImageView(device, viewInfo); } - void createImage(uint32_t width, uint32_t height, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties, vk::raii::Image &image, vk::raii::DeviceMemory &imageMemory) + std::pair createImage(uint32_t width, uint32_t height, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties) { - vk::ImageCreateInfo imageInfo{ - .imageType = vk::ImageType::e2D, - .format = format, - .extent = {width, height, 1}, - .mipLevels = 1, - .arrayLayers = 1, - .samples = vk::SampleCountFlagBits::e1, - .tiling = tiling, - .usage = usage, - .sharingMode = vk::SharingMode::eExclusive, - .initialLayout = vk::ImageLayout::eUndefined}; - image = vk::raii::Image(device, imageInfo); + vk::ImageCreateInfo imageInfo{.imageType = vk::ImageType::e2D, + .format = format, + .extent = {width, height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .tiling = tiling, + .usage = usage, + .sharingMode = vk::SharingMode::eExclusive}; + + vk::raii::Image image = vk::raii::Image(device, imageInfo); vk::MemoryRequirements memRequirements = image.getMemoryRequirements(); - vk::MemoryAllocateInfo allocInfo{ - .allocationSize = memRequirements.size, - .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)}; - imageMemory = vk::raii::DeviceMemory(device, allocInfo); + vk::MemoryAllocateInfo allocInfo{.allocationSize = memRequirements.size, + .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)}; + vk::raii::DeviceMemory imageMemory = vk::raii::DeviceMemory(device, allocInfo); image.bindMemory(imageMemory, 0); + + return {std::move(image), std::move(imageMemory)}; } - void transitionImageLayout(const vk::raii::Image &image, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) + void transitionImageLayout(vk::raii::CommandBuffer &commandBuffer, const vk::raii::Image &image, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { - auto commandBuffer = beginSingleTimeCommands(); - - vk::ImageMemoryBarrier barrier{ - .oldLayout = oldLayout, - .newLayout = newLayout, - .image = image, - .subresourceRange = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}}; + vk::ImageMemoryBarrier barrier{.oldLayout = oldLayout, + .newLayout = newLayout, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, + .image = image, + .subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eColor, .levelCount = 1, .layerCount = 1}}; vk::PipelineStageFlags sourceStage; vk::PipelineStageFlags destinationStage; @@ -681,85 +664,86 @@ class HelloTriangleApplication { throw std::invalid_argument("unsupported layout transition!"); } - commandBuffer->pipelineBarrier(sourceStage, destinationStage, {}, {}, nullptr, barrier); - endSingleTimeCommands(*commandBuffer); + commandBuffer.pipelineBarrier(sourceStage, destinationStage, {}, {}, {}, barrier); } - void copyBufferToImage(const vk::raii::Buffer &buffer, vk::raii::Image &image, uint32_t width, uint32_t height) + void copyBufferToImage(vk::raii::CommandBuffer &commandBuffer, const vk::raii::Buffer &buffer, vk::raii::Image &image, uint32_t width, uint32_t height) { - std::unique_ptr commandBuffer = beginSingleTimeCommands(); - vk::BufferImageCopy region{ - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1}, - .imageOffset = {0, 0, 0}, - .imageExtent = {width, height, 1}}; - commandBuffer->copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, {region}); - endSingleTimeCommands(*commandBuffer); + vk::BufferImageCopy region{.bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = {.aspectMask = vk::ImageAspectFlagBits::eColor, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1}, + .imageOffset = {0, 0, 0}, + .imageExtent = {width, height, 1}}; + commandBuffer.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal, region); } void createVertexBuffer() { - vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - vk::raii::Buffer stagingBuffer({}); - vk::raii::DeviceMemory stagingBufferMemory({}); - createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); + vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); + + auto [stagingBuffer, stagingBufferMemory] = + createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); void *dataStaging = stagingBufferMemory.mapMemory(0, bufferSize); memcpy(dataStaging, vertices.data(), bufferSize); stagingBufferMemory.unmapMemory(); - createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal, vertexBuffer, vertexBufferMemory); + std::tie(vertexBuffer, vertexBufferMemory) = + createBuffer(bufferSize, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal); copyBuffer(stagingBuffer, vertexBuffer, bufferSize); } + std::pair createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties) + { + vk::BufferCreateInfo bufferInfo{.size = size, .usage = usage, .sharingMode = vk::SharingMode::eExclusive}; + vk::raii::Buffer buffer = vk::raii::Buffer(device, bufferInfo); + vk::MemoryRequirements memRequirements = buffer.getMemoryRequirements(); + vk::MemoryAllocateInfo allocInfo{.allocationSize = memRequirements.size, .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)}; + vk::raii::DeviceMemory bufferMemory = vk::raii::DeviceMemory(device, allocInfo); + buffer.bindMemory(*bufferMemory, 0); + return {std::move(buffer), std::move(bufferMemory)}; + } + void createIndexBuffer() { vk::DeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - vk::raii::Buffer stagingBuffer({}); - vk::raii::DeviceMemory stagingBufferMemory({}); - createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); + auto [stagingBuffer, stagingBufferMemory] = + createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); void *data = stagingBufferMemory.mapMemory(0, bufferSize); - memcpy(data, indices.data(), bufferSize); + memcpy(data, indices.data(), (size_t) bufferSize); stagingBufferMemory.unmapMemory(); - createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal, indexBuffer, indexBufferMemory); + std::tie(indexBuffer, indexBufferMemory) = + createBuffer(bufferSize, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal); copyBuffer(stagingBuffer, indexBuffer, bufferSize); } void createUniformBuffers() { - uniformBuffers.clear(); - uniformBuffersMemory.clear(); - uniformBuffersMapped.clear(); - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vk::DeviceSize bufferSize = sizeof(UniformBufferObject); - vk::raii::Buffer buffer({}); - vk::raii::DeviceMemory bufferMem({}); - createBuffer(bufferSize, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, buffer, bufferMem); + vk::DeviceSize bufferSize = sizeof(UniformBufferObject); + auto [buffer, bufferMem] = createBuffer( + bufferSize, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); uniformBuffers.emplace_back(std::move(buffer)); uniformBuffersMemory.emplace_back(std::move(bufferMem)); - uniformBuffersMapped.emplace_back(uniformBuffersMemory[i].mapMemory(0, bufferSize)); + uniformBuffersMapped.emplace_back(uniformBuffersMemory.back().mapMemory(0, bufferSize)); } } void createDescriptorPool() { - std::array poolSize{ - vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, MAX_FRAMES_IN_FLIGHT), - vk::DescriptorPoolSize(vk::DescriptorType::eCombinedImageSampler, MAX_FRAMES_IN_FLIGHT)}; - vk::DescriptorPoolCreateInfo poolInfo{ - .flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, - .maxSets = MAX_FRAMES_IN_FLIGHT, - .poolSizeCount = static_cast(poolSize.size()), - .pPoolSizes = poolSize.data()}; + std::array poolSize{{{.type = vk::DescriptorType::eUniformBuffer, .descriptorCount = MAX_FRAMES_IN_FLIGHT}, + {.type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = MAX_FRAMES_IN_FLIGHT}}}; + vk::DescriptorPoolCreateInfo poolInfo{.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, + .maxSets = MAX_FRAMES_IN_FLIGHT, + .poolSizeCount = static_cast(poolSize.size()), + .pPoolSizes = poolSize.data()}; descriptorPool = vk::raii::DescriptorPool(device, poolInfo); } @@ -767,73 +751,46 @@ class HelloTriangleApplication { std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); vk::DescriptorSetAllocateInfo allocInfo{ - .descriptorPool = descriptorPool, - .descriptorSetCount = static_cast(layouts.size()), - .pSetLayouts = layouts.data()}; + .descriptorPool = descriptorPool, + .descriptorSetCount = static_cast(layouts.size()), + .pSetLayouts = layouts.data()}; descriptorSets.clear(); descriptorSets = device.allocateDescriptorSets(allocInfo); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vk::DescriptorBufferInfo bufferInfo{ - .buffer = uniformBuffers[i], - .offset = 0, - .range = sizeof(UniformBufferObject)}; - vk::DescriptorImageInfo imageInfo{ - .sampler = textureSampler, - .imageView = textureImageView, - .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal}; - std::array descriptorWrites{ - vk::WriteDescriptorSet{ - .dstSet = descriptorSets[i], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &bufferInfo}, - vk::WriteDescriptorSet{ - .dstSet = descriptorSets[i], - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &imageInfo}}; + vk::DescriptorBufferInfo bufferInfo{.buffer = uniformBuffers[i], .offset = 0, .range = sizeof(UniformBufferObject)}; + vk::DescriptorImageInfo imageInfo{.sampler = textureSampler, .imageView = textureImageView, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal}; + + std::array descriptorWrites{{{.dstSet = descriptorSets[i], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &bufferInfo}, + {.dstSet = descriptorSets[i], + .dstBinding = 1, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &imageInfo}}}; device.updateDescriptorSets(descriptorWrites, {}); } } - void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::raii::Buffer &buffer, vk::raii::DeviceMemory &bufferMemory) + vk::raii::CommandBuffer beginSingleTimeCommands() { - vk::BufferCreateInfo bufferInfo{ - .size = size, - .usage = usage, - .sharingMode = vk::SharingMode::eExclusive}; - buffer = vk::raii::Buffer(device, bufferInfo); - vk::MemoryRequirements memRequirements = buffer.getMemoryRequirements(); - vk::MemoryAllocateInfo allocInfo{ - .allocationSize = memRequirements.size, - .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties)}; - bufferMemory = vk::raii::DeviceMemory(device, allocInfo); - buffer.bindMemory(bufferMemory, 0); - } - - std::unique_ptr beginSingleTimeCommands() - { - vk::CommandBufferAllocateInfo allocInfo{ - .commandPool = commandPool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1}; - std::unique_ptr commandBuffer = std::make_unique(std::move(vk::raii::CommandBuffers(device, allocInfo).front())); + vk::CommandBufferAllocateInfo allocInfo{.commandPool = commandPool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1}; + vk::raii::CommandBuffer commandBuffer = std::move(vk::raii::CommandBuffers(device, allocInfo).front()); - vk::CommandBufferBeginInfo beginInfo{ - .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; - commandBuffer->begin(beginInfo); + vk::CommandBufferBeginInfo beginInfo{.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + commandBuffer.begin(beginInfo); - return commandBuffer; + return std::move(commandBuffer); } - void endSingleTimeCommands(vk::raii::CommandBuffer &commandBuffer) + void endSingleTimeCommands(vk::raii::CommandBuffer &&commandBuffer) { commandBuffer.end(); @@ -844,13 +801,9 @@ class HelloTriangleApplication void copyBuffer(vk::raii::Buffer &srcBuffer, vk::raii::Buffer &dstBuffer, vk::DeviceSize size) { - vk::CommandBufferAllocateInfo allocInfo{.commandPool = commandPool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1}; - vk::raii::CommandBuffer commandCopyBuffer = std::move(device.allocateCommandBuffers(allocInfo).front()); - commandCopyBuffer.begin(vk::CommandBufferBeginInfo{.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}); + vk::raii::CommandBuffer commandCopyBuffer = beginSingleTimeCommands(); commandCopyBuffer.copyBuffer(*srcBuffer, *dstBuffer, vk::BufferCopy{.size = size}); - commandCopyBuffer.end(); - queue.submit(vk::SubmitInfo{.commandBufferCount = 1, .pCommandBuffers = &*commandCopyBuffer}, nullptr); - queue.waitIdle(); + endSingleTimeCommands(std::move(commandCopyBuffer)); } uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) @@ -879,7 +832,8 @@ class HelloTriangleApplication { auto &commandBuffer = commandBuffers[frameIndex]; commandBuffer.begin({}); - // Before starting rendering, transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL + + // Before starting rendering, transition the swapchain image to vk::ImageLayout::eColorAttachmentOptimal transition_image_layout( swapChainImages[imageIndex], vk::ImageLayout::eUndefined, @@ -889,6 +843,7 @@ class HelloTriangleApplication vk::PipelineStageFlagBits2::eColorAttachmentOutput, // srcStage vk::PipelineStageFlagBits2::eColorAttachmentOutput, // dstStage vk::ImageAspectFlagBits::eColor); + // Transition depth image to depth attachment optimal layout transition_image_layout( *depthImage, @@ -923,16 +878,18 @@ class HelloTriangleApplication .colorAttachmentCount = 1, .pColorAttachments = &colorAttachmentInfo, .pDepthAttachment = &depthAttachmentInfo}; + commandBuffer.beginRendering(renderingInfo); commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, *graphicsPipeline); commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, static_cast(swapChainExtent.width), static_cast(swapChainExtent.height), 0.0f, 1.0f)); commandBuffer.setScissor(0, vk::Rect2D(vk::Offset2D(0, 0), swapChainExtent)); commandBuffer.bindVertexBuffers(0, *vertexBuffer, {0}); - commandBuffer.bindIndexBuffer(*indexBuffer, 0, vk::IndexType::eUint16); + commandBuffer.bindIndexBuffer(*indexBuffer, 0, vk::IndexTypeValue::value); commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, *descriptorSets[frameIndex], nullptr); - commandBuffer.drawIndexed(indices.size(), 1, 0, 0, 0); + commandBuffer.drawIndexed(static_cast(indices.size()), 1, 0, 0, 0); commandBuffer.endRendering(); - // After rendering, transition the swapchain image to PRESENT_SRC + + // After rendering, transition the swapchain image to vk::ImageLayout::ePresentSrcKHR transition_image_layout( swapChainImages[imageIndex], vk::ImageLayout::eColorAttachmentOptimal, @@ -966,11 +923,11 @@ class HelloTriangleApplication .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = { - .aspectMask = image_aspect_flags, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1}}; + .aspectMask = image_aspect_flags, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; vk::DependencyInfo dependency_info = { .dependencyFlags = {}, .imageMemoryBarrierCount = 1, @@ -1004,7 +961,8 @@ class HelloTriangleApplication UniformBufferObject ubo{}; ubo.model = rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); ubo.view = lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); - ubo.proj = glm::perspective(glm::radians(45.0f), static_cast(swapChainExtent.width) / static_cast(swapChainExtent.height), 0.1f, 10.0f); + ubo.proj = + glm::perspective(glm::radians(45.0f), static_cast(swapChainExtent.width) / static_cast(swapChainExtent.height), 0.1f, 10.0f); ubo.proj[1][1] *= -1; memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); diff --git a/en/07_Depth_buffering.adoc b/en/07_Depth_buffering.adoc index 9fd44b60..a512d7e8 100644 --- a/en/07_Depth_buffering.adoc +++ b/en/07_Depth_buffering.adoc @@ -129,20 +129,21 @@ We need to configure it to use the Vulkan range of `0.0` to `1.0` using the `GLM A depth attachment is based on an image, just like the color attachment. The difference is that the swap chain will not automatically create depth images for us. We only need a single depth image, because only one draw operation is running at once. -The depth image will again require the trifecta of resources: image, memory and image view. +The depth image will again require the triplet of resources: image, memory and image view. [,c++] ---- -vk::raii::Image depthImage = nullptr; +vk::raii::Image depthImage = nullptr; vk::raii::DeviceMemory depthImageMemory = nullptr; -vk::raii::ImageView depthImageView = nullptr; +vk::raii::ImageView depthImageView = nullptr; ---- Create a new function `createDepthResources` to set up these resources: [,c++] ---- -void initVulkan() { +void initVulkan() +{ ... createCommandPool(); createDepthResources(); @@ -152,8 +153,8 @@ void initVulkan() { ... -void createDepthResources() { - +void createDepthResources() +{ } ---- @@ -178,8 +179,8 @@ We're going to write a function `findSupportedFormat` that takes a list of candi [,c++] ---- -vk::Format findSupportedFormat(const std::vector& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features) { - +vk::Format findSupportedFormat(const std::vector& candidates, vk::ImageTiling tiling, vk::FormatFeatureFlags features) +{ } ---- @@ -203,10 +204,9 @@ Only the first two are relevant here, and the one we check depends on the `tilin [,c++] ---- -if (tiling == vk::ImageTiling::eLinear && (props.linearTilingFeatures & features) == features) { - return format; -} -if (tiling == vk::ImageTiling::eOptimal && (props.optimalTilingFeatures & features) == features) { +if (((tiling == vk::ImageTiling::eLinear) && ((props.linearTilingFeatures & features) == features)) || + ((tiling == vk::ImageTiling::eOptimal) && ((props.optimalTilingFeatures & features) == features))) +{ return format; } ---- @@ -219,11 +219,10 @@ vk::Format findSupportedFormat(const std::vector& candidates, vk::Im for (const auto format : candidates) { vk::FormatProperties props = physicalDevice.getFormatProperties(format); - if (tiling == vk::ImageTiling::eLinear && (props.linearTilingFeatures & features) == features) { - return format; - } - if (tiling == vk::ImageTiling::eOptimal && (props.optimalTilingFeatures & features) == features) { - return format; + if (((tiling == vk::ImageTiling::eLinear) && ((props.linearTilingFeatures & features) == features)) || + ((tiling == vk::ImageTiling::eOptimal) && ((props.optimalTilingFeatures & features) == features))) + { + return format; } } @@ -235,26 +234,18 @@ We'll use this function now to create a `findDepthFormat` helper function to sel [,c++] ---- -vk::Format findDepthFormat() { - return findSupportedFormat( - {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint}, - vk::ImageTiling::eOptimal, - vk::FormatFeatureFlagBits::eDepthStencilAttachment - ); +vk::Format findDepthFormat() +{ + return findSupportedFormat({vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint}, + vk::ImageTiling::eOptimal, + vk::FormatFeatureFlagBits::eDepthStencilAttachment); } ---- Make sure to use the `vk::FormatFeatureFlagBits` instead of `vk::ImageUsageFlagBits` in this case. All of these candidate formats contain a depth component, but the latter two also contain a stencil component. We won't be using that yet, but we do need to take that into account when performing layout transitions on images with these formats. -Add a simple helper function that tells us if the chosen depth format contains a stencil component: - -[,c++] ----- -bool hasStencilComponent(vk::Format format) { - return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint; -} ----- +There are simple helper functions like `vk::hasDepthComponent` or `vk::hasStencilComponent` available in the `vulkan/vulkan_format_traits.hpp` header. Call the function to find a depth format from `createDepthResources`: @@ -267,19 +258,20 @@ We now have all the required information to invoke our `createImage` and `create [,c++] ---- -createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, depthImage, depthImageMemory); -depthImageView = createImageView(depthImage, depthFormat, vk::ImageAspectFlagBits::eDepth); +std::tie(depthImage, depthImageMemory) = createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal); +depthImageView = createImageView(depthImage, depthFormat, vk::ImageAspectFlagBits::eDepth); ---- However, the `createImageView` function currently assumes that the subresource is always the `vk::ImageAspectFlagBits::eColor`, so we will need to turn that field into a parameter: [,c++] ---- -vk::raii::ImageView createImageView(vk::raii::Image& image, vk::Format format, vk::ImageAspectFlags aspectFlags) { +vk::raii::ImageView createImageView(vk::raii::Image& image, vk::Format format, vk::ImageAspectFlags aspectFlags) +{ ... - vk::ImageViewCreateInfo viewInfo{ - ... - .subresourceRange = {aspectFlags, 0, 1, 0, 1}}; + vk::ImageViewCreateInfo viewInfo{ + ... + .subresourceRange = {.aspectMask = aspectFlags, ...}}; ... } ---- @@ -288,11 +280,11 @@ Update all calls to this function to use the right aspect: [,c++] ---- -swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, vk::ImageAspectFlagBits::eColor); +swapChainImageViews.emplace_back(createImageView(image, swapChainSurfaceFormat.format, vk::ImageAspectFlagBits::eColor)); ... depthImageView = createImageView(depthImage, depthFormat, vk::ImageAspectFlagBits::eDepth); ... -textureImageView = createImageView(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor); +textureImageView = createImageView(*textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor); ---- That's it for creating the depth image. @@ -342,7 +334,7 @@ vk::RenderingInfo renderingInfo = { === Explicitly transitioning the depth image -Just like the color attachment, the depth attachment needs to be in the correct layout for the intended use case. For this we need to issue an additional barriers to ensure that the depth image can be used as a depth attachment during rendering. +Just like the color attachment, the depth attachment needs to be in the correct layout for the intended use case. For this we need to issue an additional barrier to ensure that the depth image can be used as a depth attachment during rendering. The depth image is first accessed in the early fragment test pipeline stage and because we have a load operation that _clears_, we should specify the access mask for writes. As we now deal with an additional image type (depth), first add a new argument to the `transition_image_layout` function for the image aspect: @@ -369,11 +361,12 @@ Now add new image layout transition at the start of the command buffer in `recor [,c++] ---- commandBuffers[currentFrame].begin({}); -// Transition for the color attachment +// Before starting rendering, transition the swapchain image to vk::ImageLayout::eColorAttachmentOptimal transition_image_layout( ... vk::ImageAspectFlagBits::eColor); -// New transition for the depth image + +// Transition depth image to depth attachment optimal layout transition_image_layout( *depthImage, vk::ImageLayout::eUndefined, @@ -401,7 +394,7 @@ transition_image_layout( == Depth and stencil state The depth attachment is ready to be used now, but depth testing still needs to be enabled in the graphics pipeline. -It is configured through the `PipelineDepthStencilStateCreateInfo` struct: +It is configured through the `vk::PipelineDepthStencilStateCreateInfo` struct: [,c++] ---- @@ -423,7 +416,7 @@ The `depthBoundsTestEnable`, `minDepthBounds` and `maxDepthBounds` fields are us Basically, this allows you to only keep fragments that fall within the specified depth range. We won't be using this functionality. -The last three fields configure stencil buffer operations, which we also won't be using in this tutorial. +The `stencilTestEnable`, `front`, and `back` fields configure stencil buffer operations, which we also won't be using in this tutorial. If you want to use these operations, then you will have to make sure that the format of the depth/stencil image contains a stencil component. A depth stencil state must always be specified if the dynamic rendering setup contains a depth stencil attachment: @@ -434,9 +427,9 @@ Update the `pipelineCreateInfoChain` structure chain to reference the depth sten ---- vk::StructureChain pipelineCreateInfoChain = { {.stageCount = 2, - ... - .pDepthStencilState = &depthStencil, - ... + ... + .pDepthStencilState = &depthStencil, + ...}, {.colorAttachmentCount = 1, .pColorAttachmentFormats = &swapChainSurfaceFormat.format, .depthAttachmentFormat = depthFormat}}; ---- @@ -451,12 +444,14 @@ Extend the `recreateSwapChain` function to recreate the depth resources in that [,c++] ---- -void recreateSwapChain() { +void recreateSwapChain() +{ int width = 0, height = 0; - while (width == 0 || height == 0) { + while (width == 0 || height == 0) + { glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); - } + } device.waitIdle(device);