## Introduction

The geometry we've worked with so far is projected into 3D, but it's still
completely flat. In this chapter we're going to add a Z coordinate to the
position to prepare for 3D meshes. We'll use this third coordinate to place a
square over the current square to see a problem that arises when geometry is not
sorted by depth.

## 3D geometry

Change the `Vertex` struct to use a 3D vector for the position, and update the
`format` in the corresponding `VkVertexInputAttributeDescription`:

```c++
struct Vertex {
    glm::vec3 pos;
    glm::vec3 color;
    glm::vec2 texCoord;

    ...

    static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
        std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions{};

        attributeDescriptions[0].binding = 0;
        attributeDescriptions[0].location = 0;
        attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
        attributeDescriptions[0].offset = offsetof(Vertex, pos);

        ...
    }
};
```

Next, update the vertex shader to accept and transform 3D coordinates as input.
Don't forget to recompile it afterwards!

```glsl
layout(location = 0) in vec3 inPosition;

...

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}
```

Lastly, update the `vertices` container to include Z coordinates:

```c++
const std::vector<Vertex> vertices = {
    {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
    {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
    {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
    {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}
};
```

If you run your application now, then you should see exactly the same result as
before. It's time to add some extra geometry to make the scene more interesting,
and to demonstrate the problem that we're going to tackle in this chapter.
Duplicate the vertices to define positions for a square right under the current
one like this:

![](/images/extra_square.svg)

Use Z coordinates of `-0.5f` and add the appropriate indices for the extra
square:

```c++
const std::vector<Vertex> vertices = {
    {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
    {{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
    {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
    {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},

    {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
    {{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
    {{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
    {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}
};

const std::vector<uint16_t> indices = {
    0, 1, 2, 2, 3, 0,
    4, 5, 6, 6, 7, 4
};
```

Run your program now and you'll see something resembling an Escher illustration:

![](/images/depth_issues.png)

The problem is that the fragments of the lower square are drawn over the
fragments of the upper square, simply because it comes later in the index array.
There are two ways to solve this:

* Sort all of the draw calls by depth from back to front
* Use depth testing with a depth buffer

The first approach is commonly used for drawing transparent objects, because
order-independent transparency is a difficult challenge to solve. However, the
problem of ordering fragments by depth is much more commonly solved using a
*depth buffer*. A depth buffer is an additional attachment that stores the depth
for every position, just like the color attachment stores the color of every
position. Every time the rasterizer produces a fragment, the depth test will
check if the new fragment is closer than the previous one. If it isn't, then the
new fragment is discarded. A fragment that passes the depth test writes its own
depth to the depth buffer. It is possible to manipulate this value from the
fragment shader, just like you can manipulate the color output.

```c++
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
```

The perspective projection matrix generated by GLM will use the OpenGL depth
range of `-1.0` to `1.0` by default. We need to configure it to use the Vulkan
range of `0.0` to `1.0` using the `GLM_FORCE_DEPTH_ZERO_TO_ONE` definition.

## Depth image and view

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.

```c++
VkImage depthImage;
VkDeviceMemory depthImageMemory;
VkImageView depthImageView;
```

Create a new function `createDepthResources` to set up these resources:

```c++
void initVulkan() {
    ...
    createCommandPool();
    createDepthResources();
    createTextureImage();
    ...
}

...

void createDepthResources() {

}
```

Creating a depth image is fairly straightforward. It should have the same
resolution as the color attachment, defined by the swap chain extent, an image
usage appropriate for a depth attachment, optimal tiling and device local
memory. The only question is: what is the right format for a depth image? The
format must contain a depth component, indicated by `_D??_` in the `VK_FORMAT_`.

Unlike the texture image, we don't necessarily need a specific format, because
we won't be directly accessing the texels from the program. It just needs to
have a reasonable accuracy, at least 24 bits is common in real-world
applications. There are several formats that fit this requirement:

* `VK_FORMAT_D32_SFLOAT`: 32-bit float for depth
* `VK_FORMAT_D32_SFLOAT_S8_UINT`: 32-bit signed float for depth and 8 bit
stencil component
* `VK_FORMAT_D24_UNORM_S8_UINT`: 24-bit float for depth and 8 bit stencil
component

The stencil component is used for [stencil tests](https://en.wikipedia.org/wiki/Stencil_buffer),
which is an additional test that can be combined with depth testing. We'll look
at this in a future chapter.

We could simply go for the `VK_FORMAT_D32_SFLOAT` format, because support for it
is extremely common (see the hardware database), but it's nice to add some extra
flexibility to our application where possible. We're going to write a function
`findSupportedFormat` that takes a list of candidate formats in order from most
desirable to least desirable, and checks which is the first one that is
supported:

```c++
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {

}
```

The support of a format depends on the tiling mode and usage, so we must also
include these as parameters. The support of a format can be queried using
the `vkGetPhysicalDeviceFormatProperties` function:

```c++
for (VkFormat format : candidates) {
    VkFormatProperties props;
    vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
}
```

The `VkFormatProperties` struct contains three fields:

* `linearTilingFeatures`: Use cases that are supported with linear tiling
* `optimalTilingFeatures`: Use cases that are supported with optimal tiling
* `bufferFeatures`: Use cases that are supported for buffers

Only the first two are relevant here, and the one we check depends on the
`tiling` parameter of the function:

```c++
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
    return format;
} else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
    return format;
}
```

If none of the candidate formats support the desired usage, then we can either
return a special value or simply throw an exception:

```c++
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
    for (VkFormat format : candidates) {
        VkFormatProperties props;
        vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);

        if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
            return format;
        } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
            return format;
        }
    }

    throw std::runtime_error("failed to find supported format!");
}
```

We'll use this function now to create a `findDepthFormat` helper function to
select a format with a depth component that supports usage as depth attachment:

```c++
VkFormat findDepthFormat() {
    return findSupportedFormat(
        {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
        VK_IMAGE_TILING_OPTIMAL,
        VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
    );
}
```

Make sure to use the `VK_FORMAT_FEATURE_` flag instead of `VK_IMAGE_USAGE_` 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(VkFormat format) {
    return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
}
```

Call the function to find a depth format from `createDepthResources`:

```c++
VkFormat depthFormat = findDepthFormat();
```

We now have all the required information to invoke our `createImage` and
`createImageView` helper functions:

```c++
createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
depthImageView = createImageView(depthImage, depthFormat);
```

However, the `createImageView` function currently assumes that the subresource
is always the `VK_IMAGE_ASPECT_COLOR_BIT`, so we will need to turn that field
into a parameter:

```c++
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) {
    ...
    viewInfo.subresourceRange.aspectMask = aspectFlags;
    ...
}
```

Update all calls to this function to use the right aspect:

```c++
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
...
depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
...
textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
```

That's it for creating the depth image. We don't need to map it or copy another
image to it, because we're going to clear it at the start of the render pass
like the color attachment.

### Explicitly transitioning the depth image

We don't need to explicitly transition the layout of the image to a depth
attachment because we'll take care of this in the render pass. However, for
completeness I'll still describe the process in this section. You may skip it if
you like.

Make a call to `transitionImageLayout` at the end of the `createDepthResources`
function like so:

```c++
transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
```

The undefined layout can be used as initial layout, because there are no
existing depth image contents that matter. We need to update some of the logic
in `transitionImageLayout` to use the right subresource aspect:

```c++
if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;

    if (hasStencilComponent(format)) {
        barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
    }
} else {
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
```

Although we're not using the stencil component, we do need to include it in the
layout transitions of the depth image.

Finally, add the correct access masks and pipeline stages:

```c++
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
    barrier.srcAccessMask = 0;
    barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

    sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

    sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
    destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
    barrier.srcAccessMask = 0;
    barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

    sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
} else {
    throw std::invalid_argument("unsupported layout transition!");
}
```

The depth buffer will be read from to perform depth tests to see if a fragment
is visible, and will be written to when a new fragment is drawn. The reading
happens in the `VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT` stage and the
writing in the `VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT`. You should pick the
earliest pipeline stage that matches the specified operations, so that it is
ready for usage as depth attachment when it needs to be.

## Render pass

We're now going to modify `createRenderPass` to include a depth attachment.
First specify the `VkAttachmentDescription`:

```c++
VkAttachmentDescription depthAttachment{};
depthAttachment.format = findDepthFormat();
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
```

The `format` should be the same as the depth image itself. This time we don't
care about storing the depth data (`storeOp`), because it will not be used after
drawing has finished. This may allow the hardware to perform additional
optimizations. Just like the color buffer, we don't care about the previous
depth contents, so we can use `VK_IMAGE_LAYOUT_UNDEFINED` as `initialLayout`.

```c++
VkAttachmentReference depthAttachmentRef{};
depthAttachmentRef.attachment = 1;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
```

Add a reference to the attachment for the first (and only) subpass:

```c++
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
```

Unlike color attachments, a subpass can only use a single depth (+stencil)
attachment. It wouldn't really make any sense to do depth tests on multiple
buffers.

```c++
std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;
```

Next, update the `VkSubpassDependency` struct to refer to both
attachments.

```c++
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
```

Finally, we need to extend our subpass dependencies to make sure that there is no conflict between the transitioning of the depth image and it being cleared as part of its load operation. 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.

## Framebuffer

The next step is to modify the framebuffer creation to bind the depth image to
the depth attachment. Go to `createFramebuffers` and specify the depth image
view as second attachment:

```c++
std::array<VkImageView, 2> attachments = {
    swapChainImageViews[i],
    depthImageView
};

VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = swapChainExtent.width;
framebufferInfo.height = swapChainExtent.height;
framebufferInfo.layers = 1;
```

The color attachment differs for every swap chain image, but the same depth
image can be used by all of them because only a single subpass is running at the
same time due to our semaphores.

You'll also need to move the call to `createFramebuffers` to make sure that it
is called after the depth image view has actually been created:

```c++
void initVulkan() {
    ...
    createDepthResources();
    createFramebuffers();
    ...
}
```

## Clear values

Because we now have multiple attachments with `VK_ATTACHMENT_LOAD_OP_CLEAR`, we
also need to specify multiple clear values. Go to `recordCommandBuffer` and
create an array of `VkClearValue` structs:

```c++
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clearValues[1].depthStencil = {1.0f, 0};

renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
renderPassInfo.pClearValues = clearValues.data();
```

The range of depths in the depth buffer is `0.0` to `1.0` in Vulkan, where `1.0`
lies at the far view plane and `0.0` at the near view plane. The initial value
at each point in the depth buffer should be the furthest possible depth, which
is `1.0`.

Note that the order of `clearValues` should be identical to the order of your attachments.

## 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
`VkPipelineDepthStencilStateCreateInfo` struct:

```c++
VkPipelineDepthStencilStateCreateInfo depthStencil{};
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencil.depthTestEnable = VK_TRUE;
depthStencil.depthWriteEnable = VK_TRUE;
```

The `depthTestEnable` field specifies if the depth of new fragments should be
compared to the depth buffer to see if they should be discarded. The
`depthWriteEnable` field specifies if the new depth of fragments that pass the
depth test should actually be written to the depth buffer.

```c++
depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
```

The `depthCompareOp` field specifies the comparison that is performed to keep or
discard fragments. We're sticking to the convention of lower depth = closer, so
the depth of new fragments should be *less*.

```c++
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.minDepthBounds = 0.0f; // Optional
depthStencil.maxDepthBounds = 1.0f; // Optional
```

The `depthBoundsTestEnable`, `minDepthBounds` and `maxDepthBounds` fields are
used for the optional depth bound test. Basically, this allows you to only keep
fragments that fall within the specified depth range. We won't be using this
functionality.

```c++
depthStencil.stencilTestEnable = VK_FALSE;
depthStencil.front = {}; // Optional
depthStencil.back = {}; // Optional
```

The last three 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.

```c++
pipelineInfo.pDepthStencilState = &depthStencil;
```

Update the `VkGraphicsPipelineCreateInfo` struct to reference the depth stencil
state we just filled in. A depth stencil state must always be specified if the
render pass contains a depth stencil attachment.

If you run your program now, then you should see that the fragments of the
geometry are now correctly ordered:

![](/images/depth_correct.png)

## Handling window resize

The resolution of the depth buffer should change when the window is resized to
match the new color attachment resolution. Extend the `recreateSwapChain`
function to recreate the depth resources in that case:

```c++
void recreateSwapChain() {
    int width = 0, height = 0;
    while (width == 0 || height == 0) {
        glfwGetFramebufferSize(window, &width, &height);
        glfwWaitEvents();
    }

    vkDeviceWaitIdle(device);

    cleanupSwapChain();

    createSwapChain();
    createImageViews();
    createDepthResources();
    createFramebuffers();
}
```

The cleanup operations should happen in the swap chain cleanup function:

```c++
void cleanupSwapChain() {
    vkDestroyImageView(device, depthImageView, nullptr);
    vkDestroyImage(device, depthImage, nullptr);
    vkFreeMemory(device, depthImageMemory, nullptr);

    ...
}
```

Congratulations, your application is now finally ready to render arbitrary 3D
geometry and have it look right. We're going to try this out in the next chapter
by drawing a textured model!

[C++ code](/code/27_depth_buffering.cpp) /
[Vertex shader](/code/27_shader_depth.vert) /
[Fragment shader](/code/27_shader_depth.frag)
