Index: shaders/shader.vert
===================================================================
--- shaders/shader.vert	(revision 2c875042218c87beadd17e3e6a00ecd8ea2dc260)
+++ shaders/shader.vert	(revision bba12e780ad69819ef680fe90385139da9b372d1)
@@ -8,5 +8,5 @@
 } ubo;
 
-layout(location = 0) in vec2 inPosition;
+layout(location = 0) in vec3 inPosition;
 layout(location = 1) in vec3 inColor;
 layout(location = 2) in vec2 inTexCoord;
@@ -16,5 +16,5 @@
 
 void main() {
-   gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
+   gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 2c875042218c87beadd17e3e6a00ecd8ea2dc260)
+++ vulkan-game.cpp	(revision bba12e780ad69819ef680fe90385139da9b372d1)
@@ -67,5 +67,5 @@
 
 struct Vertex {
-   glm::vec2 pos;
+   glm::vec3 pos;
    glm::vec3 color;
    glm::vec2 texCoord;
@@ -86,5 +86,5 @@
       attributeDescriptions[0].binding = 0;
       attributeDescriptions[0].location = 0;
-      attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
+      attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
       attributeDescriptions[0].offset = offsetof(Vertex, pos);
 
@@ -110,12 +110,18 @@
 
 const vector<Vertex> vertices = {
-   {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
-   {{ 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
-   {{ 0.5f,  0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
-   {{-0.5f,  0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
+   {{-0.5f, -0.5f,  0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
+   {{ 0.5f, -0.5f,  0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
+   {{ 0.5f,  0.5f,  0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+   {{-0.5f,  0.5f,  0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
+
+   {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
+   {{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
+   {{ 0.5f,  0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
+   {{-0.5f,  0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
 };
 
 const vector<uint16_t> indices = {
-   0, 1, 2, 2, 3, 0
+   0, 1, 2, 2, 3, 0,
+   4, 5, 6, 6, 7, 4
 };
 
@@ -184,4 +190,8 @@
 
       VkCommandPool commandPool;
+
+      VkImage depthImage;
+      VkDeviceMemory depthImageMemory;
+      VkImageView depthImageView;
 
       VkImage textureImage;
@@ -236,6 +246,7 @@
          createDescriptorSetLayout();
          createGraphicsPipeline();
+         createCommandPool();
+         createDepthResources();
          createFramebuffers();
-         createCommandPool();
          createTextureImage();
          createTextureImageView();
@@ -596,5 +607,5 @@
 
          for (size_t i = 0; i < swapChainImages.size(); i++) {
-            swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
+            swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
          }
       }
@@ -615,8 +626,23 @@
          colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
+         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;
+
+         VkAttachmentReference depthAttachmentRef = {};
+         depthAttachmentRef.attachment = 1;
+         depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
          VkSubpassDescription subpass = {};
          subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
          subpass.colorAttachmentCount = 1;
          subpass.pColorAttachments = &colorAttachmentRef;
+         subpass.pDepthStencilAttachment = &depthAttachmentRef;
 
          VkSubpassDependency dependency = {};
@@ -628,8 +654,9 @@
          dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
 
+         array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
          VkRenderPassCreateInfo renderPassInfo = {};
          renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
-         renderPassInfo.attachmentCount = 1;
-         renderPassInfo.pAttachments = &colorAttachment;
+         renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+         renderPassInfo.pAttachments = attachments.data();
          renderPassInfo.subpassCount = 1;
          renderPassInfo.pSubpasses = &subpass;
@@ -754,4 +781,16 @@
          colorBlending.blendConstants[3] = 0.0f;
 
+         VkPipelineDepthStencilStateCreateInfo depthStencil = {};
+         depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+         depthStencil.depthTestEnable = VK_TRUE;
+         depthStencil.depthWriteEnable = VK_TRUE;
+         depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
+         depthStencil.depthBoundsTestEnable = VK_FALSE;
+         depthStencil.minDepthBounds = 0.0f;
+         depthStencil.maxDepthBounds = 1.0f;
+         depthStencil.stencilTestEnable = VK_FALSE;
+         depthStencil.front = {};
+         depthStencil.back = {};
+
          VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
          pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
@@ -773,5 +812,5 @@
          pipelineInfo.pRasterizationState = &rasterizer;
          pipelineInfo.pMultisampleState = &multisampling;
-         pipelineInfo.pDepthStencilState = nullptr;
+         pipelineInfo.pDepthStencilState = &depthStencil;
          pipelineInfo.pColorBlendState = &colorBlending;
          pipelineInfo.pDynamicState = nullptr;
@@ -808,6 +847,7 @@
 
          for (size_t i = 0; i < swapChainImageViews.size(); i++) {
-            VkImageView attachments[] = {
-               swapChainImageViews[i]
+            array <VkImageView, 2> attachments = {
+               swapChainImageViews[i],
+               depthImageView
             };
 
@@ -815,6 +855,6 @@
             framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
             framebufferInfo.renderPass = renderPass;
-            framebufferInfo.attachmentCount = 1;
-            framebufferInfo.pAttachments = attachments;
+            framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
+            framebufferInfo.pAttachments = attachments.data();
             framebufferInfo.width = swapChainExtent.width;
             framebufferInfo.height = swapChainExtent.height;
@@ -870,4 +910,44 @@
 
          return indices;
+      }
+
+      void createDepthResources() {
+         VkFormat depthFormat = findDepthFormat();
+
+         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, VK_IMAGE_ASPECT_DEPTH_BIT);
+
+         transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+      }
+
+      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
+         );
+      }
+
+      VkFormat findSupportedFormat(const 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 runtime_error("failed to find supported format!");
+      }
+
+      bool hasStencilComponent(VkFormat format) {
+         return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
       }
 
@@ -955,5 +1035,15 @@
          barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
          barrier.image = image;
-         barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+
+         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;
+         }
+
          barrier.subresourceRange.baseMipLevel = 0;
          barrier.subresourceRange.levelCount = 1;
@@ -976,4 +1066,10 @@
             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 invalid_argument("unsupported layout transition!");
@@ -1019,8 +1115,8 @@
 
       void createTextureImageView() {
-         textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM);
-      }
-
-      VkImageView createImageView(VkImage image, VkFormat format) {
+         textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
+      }
+
+      VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) {
          VkImageViewCreateInfo viewInfo = {};
          viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -1034,5 +1130,5 @@
          viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
 
-         viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+         viewInfo.subresourceRange.aspectMask = aspectFlags;
          viewInfo.subresourceRange.baseMipLevel = 0;
          viewInfo.subresourceRange.levelCount = 1;
@@ -1314,7 +1410,10 @@
             renderPassInfo.renderArea.extent = swapChainExtent;
 
-            VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
-            renderPassInfo.clearValueCount = 1;
-            renderPassInfo.pClearValues = &clearColor;
+            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();
 
             vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
@@ -1495,4 +1594,5 @@
          createRenderPass();
          createGraphicsPipeline();
+         createDepthResources();
          createFramebuffers();
          createUniformBuffers();
@@ -1541,4 +1641,8 @@
 
       void cleanupSwapChain() {
+         vkDestroyImageView(device, depthImageView, nullptr);
+         vkDestroyImage(device, depthImage, nullptr);
+         vkFreeMemory(device, depthImageMemory, nullptr);
+
          for (auto framebuffer : swapChainFramebuffers) {
             vkDestroyFramebuffer(device, framebuffer, nullptr);
