First commit
diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp
new file mode 100644
index 0000000..375bcb1
--- /dev/null
+++ b/src/VulkanSample.cpp
@@ -0,0 +1,1724 @@
+//
+// Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define VK_USE_PLATFORM_WIN32_KHR
+#include <vulkan/vulkan.h>
+
+#define VMA_IMPLEMENTATION
+#include "vk_mem_alloc.h"
+
+#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT
+#include <mathfu/glsl_mappings.h>
+#include <mathfu/constants.h>
+
+#include <fstream>
+#include <vector>
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <numeric>
+#include <array>
+#include <type_traits>
+#include <utility>
+
+#include <cmath>
+#include <cassert>
+#include <cstdlib>
+#include <cstdio>
+
+#define ERR_GUARD_VULKAN(Expr) do { VkResult res__ = (Expr); if (res__ < 0) assert(0); } while(0)
+
+static const char* const SHADER_PATH1 = "./";
+static const char* const SHADER_PATH2 = "../bin/";
+static const wchar_t* const WINDOW_CLASS_NAME = L"VULKAN_MEMORY_ALLOCATOR_SAMPLE";
+static const char* const VALIDATION_LAYER_NAME = "VK_LAYER_LUNARG_standard_validation";
+static const char* const APP_TITLE_A = "Vulkan Memory Allocator Sample 1.0";
+static const wchar_t* const APP_TITLE_W = L"Vulkan Memory Allocator Sample 1.0";
+
+static const bool VSYNC = true;
+static const uint32_t COMMAND_BUFFER_COUNT = 2;
+
+static bool g_EnableValidationLayer = true;
+
+static HINSTANCE g_hAppInstance;
+static HWND g_hWnd;
+static LONG g_SizeX = 1280, g_SizeY = 720;
+static VkInstance g_hVulkanInstance;
+static VkSurfaceKHR g_hSurface;
+static VkPhysicalDevice g_hPhysicalDevice;
+static VkQueue g_hPresentQueue;
+static VkSurfaceFormatKHR g_SurfaceFormat;
+static VkExtent2D g_Extent;
+static VkSwapchainKHR g_hSwapchain;
+static std::vector<VkImage> g_SwapchainImages;
+static std::vector<VkImageView> g_SwapchainImageViews;
+static std::vector<VkFramebuffer> g_Framebuffers;
+static VkCommandPool g_hCommandPool;
+static VkCommandBuffer g_MainCommandBuffers[COMMAND_BUFFER_COUNT];
+static VkFence g_MainCommandBufferExecutedFances[COMMAND_BUFFER_COUNT];
+static uint32_t g_NextCommandBufferIndex;
+static VkSemaphore g_hImageAvailableSemaphore;
+static VkSemaphore g_hRenderFinishedSemaphore;
+static uint32_t g_GraphicsQueueFamilyIndex = UINT_MAX;
+static uint32_t g_PresentQueueFamilyIndex = UINT_MAX;
+static VkDescriptorSetLayout g_hDescriptorSetLayout;
+static VkDescriptorPool g_hDescriptorPool;
+static VkDescriptorSet g_hDescriptorSet; // Automatically destroyed with m_DescriptorPool.
+static VkSampler g_hSampler;
+static VkFormat g_DepthFormat;
+static VkImage g_hDepthImage;
+static VkImageView g_hDepthImageView;
+
+static VkSurfaceCapabilitiesKHR g_SurfaceCapabilities;
+static std::vector<VkSurfaceFormatKHR> g_SurfaceFormats;
+static std::vector<VkPresentModeKHR> g_PresentModes;
+
+static PFN_vkCreateDebugReportCallbackEXT g_pvkCreateDebugReportCallbackEXT;
+static PFN_vkDebugReportMessageEXT g_pvkDebugReportMessageEXT;
+static PFN_vkDestroyDebugReportCallbackEXT g_pvkDestroyDebugReportCallbackEXT;
+static VkDebugReportCallbackEXT g_hCallback;
+
+static VkDevice g_hDevice;
+static VmaAllocator g_hAllocator;
+static VkQueue g_hGraphicsQueue;
+static VkCommandBuffer g_hTemporaryCommandBuffer;
+
+static VkPipelineLayout g_hPipelineLayout;
+static VkRenderPass g_hRenderPass;
+static VkPipeline g_hPipeline;
+
+static VkBuffer g_hVertexBuffer;
+static VkBuffer g_hIndexBuffer;
+static uint32_t g_VertexCount;
+static uint32_t g_IndexCount;
+
+static VkImage g_hTextureImage;
+static VkImageView g_hTextureImageView;
+
+static void BeginSingleTimeCommands()
+{
+ VkCommandBufferBeginInfo cmdBufBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
+ cmdBufBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+ ERR_GUARD_VULKAN( vkBeginCommandBuffer(g_hTemporaryCommandBuffer, &cmdBufBeginInfo) );
+}
+
+static void EndSingleTimeCommands()
+{
+ ERR_GUARD_VULKAN( vkEndCommandBuffer(g_hTemporaryCommandBuffer) );
+
+ VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &g_hTemporaryCommandBuffer;
+
+ ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) );
+ ERR_GUARD_VULKAN( vkQueueWaitIdle(g_hGraphicsQueue) );
+}
+
+static void LoadShader(std::vector<char>& out, const char* fileName)
+{
+ std::ifstream file(std::string(SHADER_PATH1) + fileName, std::ios::ate | std::ios::binary);
+ if(file.is_open() == false)
+ file.open(std::string(SHADER_PATH2) + fileName, std::ios::ate | std::ios::binary);
+ assert(file.is_open());
+ size_t fileSize = (size_t)file.tellg();
+ if(fileSize > 0)
+ {
+ out.resize(fileSize);
+ file.seekg(0);
+ file.read(out.data(), fileSize);
+ file.close();
+ }
+ else
+ out.clear();
+}
+
+VKAPI_ATTR VkBool32 VKAPI_CALL MyDebugReportCallback(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objectType,
+ uint64_t object,
+ size_t location,
+ int32_t messageCode,
+ const char* pLayerPrefix,
+ const char* pMessage,
+ void* pUserData)
+{
+ printf("%s \xBA %s\n", pLayerPrefix, pMessage);
+
+ if((flags == VK_DEBUG_REPORT_WARNING_BIT_EXT) ||
+ (flags == VK_DEBUG_REPORT_ERROR_BIT_EXT))
+ {
+ OutputDebugStringA(pMessage);
+ OutputDebugStringA("\n");
+ }
+
+ return VK_FALSE;
+}
+
+static VkSurfaceFormatKHR ChooseSurfaceFormat()
+{
+ assert(!g_SurfaceFormats.empty());
+
+ if((g_SurfaceFormats.size() == 1) && (g_SurfaceFormats[0].format == VK_FORMAT_UNDEFINED))
+ {
+ VkSurfaceFormatKHR result = { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
+ return result;
+ }
+
+ for(const auto& format : g_SurfaceFormats)
+ {
+ if((format.format == VK_FORMAT_B8G8R8A8_UNORM) &&
+ (format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR))
+ {
+ return format;
+ }
+ }
+
+ return g_SurfaceFormats[0];
+}
+
+VkPresentModeKHR ChooseSwapPresentMode()
+{
+ VkPresentModeKHR preferredMode = VSYNC ? VK_PRESENT_MODE_MAILBOX_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR;
+
+ if(std::find(g_PresentModes.begin(), g_PresentModes.end(), preferredMode) !=
+ g_PresentModes.end())
+ {
+ return preferredMode;
+ }
+
+ return VK_PRESENT_MODE_FIFO_KHR;
+}
+
+static VkExtent2D ChooseSwapExtent()
+{
+ if(g_SurfaceCapabilities.currentExtent.width != UINT_MAX)
+ return g_SurfaceCapabilities.currentExtent;
+
+ VkExtent2D result = {
+ std::max(g_SurfaceCapabilities.minImageExtent.width,
+ std::min(g_SurfaceCapabilities.maxImageExtent.width, (uint32_t)g_SizeX)),
+ std::max(g_SurfaceCapabilities.minImageExtent.height,
+ std::min(g_SurfaceCapabilities.maxImageExtent.height, (uint32_t)g_SizeY)) };
+ return result;
+}
+
+struct Vertex
+{
+ float pos[3];
+ float color[3];
+ float texCoord[2];
+};
+
+static void CreateMesh()
+{
+ assert(g_hAllocator);
+
+ static Vertex vertices[] = {
+ // -X
+ { { -1.f, -1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 0.f} },
+ { { -1.f, -1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 0.f} },
+ { { -1.f, 1.f, -1.f}, {1.0f, 0.0f, 0.0f}, {0.f, 1.f} },
+ { { -1.f, 1.f, 1.f}, {1.0f, 0.0f, 0.0f}, {1.f, 1.f} },
+ // +X
+ { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 0.f} },
+ { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 0.f} },
+ { { 1.f, 1.f, 1.f}, {0.0f, 1.0f, 0.0f}, {0.f, 1.f} },
+ { { 1.f, 1.f, -1.f}, {0.0f, 1.0f, 0.0f}, {1.f, 1.f} },
+ // -Z
+ { { 1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 0.f} },
+ { {-1.f, -1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 0.f} },
+ { { 1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {0.f, 1.f} },
+ { {-1.f, 1.f, -1.f}, {0.0f, 0.0f, 1.0f}, {1.f, 1.f} },
+ // +Z
+ { {-1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 0.f} },
+ { { 1.f, -1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 0.f} },
+ { {-1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {0.f, 1.f} },
+ { { 1.f, 1.f, 1.f}, {1.0f, 1.0f, 0.0f}, {1.f, 1.f} },
+ // -Y
+ { {-1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 0.f} },
+ { { 1.f, -1.f, -1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 0.f} },
+ { {-1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {0.f, 1.f} },
+ { { 1.f, -1.f, 1.f}, {0.0f, 1.0f, 1.0f}, {1.f, 1.f} },
+ // +Y
+ { { 1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 0.f} },
+ { {-1.f, 1.f, -1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 0.f} },
+ { { 1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {0.f, 1.f} },
+ { {-1.f, 1.f, 1.f}, {1.0f, 0.0f, 1.0f}, {1.f, 1.f} },
+ };
+ static uint16_t indices[] = {
+ 0, 1, 2, 3, USHRT_MAX,
+ 4, 5, 6, 7, USHRT_MAX,
+ 8, 9, 10, 11, USHRT_MAX,
+ 12, 13, 14, 15, USHRT_MAX,
+ 16, 17, 18, 19, USHRT_MAX,
+ 20, 21, 22, 23, USHRT_MAX,
+ };
+
+ size_t vertexBufferSize = sizeof(Vertex) * _countof(vertices);
+ size_t indexBufferSize = sizeof(uint16_t) * _countof(indices);
+ g_IndexCount = (uint32_t)_countof(indices);
+
+ // Create vertex buffer
+
+ VkBufferCreateInfo vbInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ vbInfo.size = vertexBufferSize;
+ vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ vbInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VmaMemoryRequirements vbMemReq = {};
+ vbMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+ VkMappedMemoryRange stagingVertexBufferMem;
+ VkBuffer stagingVertexBuffer = VK_NULL_HANDLE;
+ ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &stagingVertexBuffer, &stagingVertexBufferMem, nullptr) );
+
+ void* pVbData = nullptr;
+ ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingVertexBufferMem, &pVbData) );
+ memcpy(pVbData, vertices, vertexBufferSize);
+ vmaUnmapMemory(g_hAllocator, &stagingVertexBufferMem);
+
+ vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ vbMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &g_hVertexBuffer, nullptr, nullptr) );
+
+ // Create index buffer
+
+ VkBufferCreateInfo ibInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ ibInfo.size = indexBufferSize;
+ ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ ibInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VmaMemoryRequirements ibMemReq = {};
+ ibMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+ VkMappedMemoryRange stagingIndexBufferMem;
+ VkBuffer stagingIndexBuffer = VK_NULL_HANDLE;
+ ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &stagingIndexBuffer, &stagingIndexBufferMem, nullptr) );
+
+ void* pIbData = nullptr;
+ ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingIndexBufferMem, &pIbData) );
+ memcpy(pIbData, indices, indexBufferSize);
+ vmaUnmapMemory(g_hAllocator, &stagingIndexBufferMem);
+
+ ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ ibMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &g_hIndexBuffer, nullptr, nullptr) );
+
+ // Copy buffers
+
+ BeginSingleTimeCommands();
+
+ VkBufferCopy vbCopyRegion = {};
+ vbCopyRegion.srcOffset = 0;
+ vbCopyRegion.dstOffset = 0;
+ vbCopyRegion.size = vbInfo.size;
+ vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingVertexBuffer, g_hVertexBuffer, 1, &vbCopyRegion);
+
+ VkBufferCopy ibCopyRegion = {};
+ ibCopyRegion.srcOffset = 0;
+ ibCopyRegion.dstOffset = 0;
+ ibCopyRegion.size = ibInfo.size;
+ vkCmdCopyBuffer(g_hTemporaryCommandBuffer, stagingIndexBuffer, g_hIndexBuffer, 1, &ibCopyRegion);
+
+ EndSingleTimeCommands();
+
+ vmaDestroyBuffer(g_hAllocator, stagingIndexBuffer);
+ vmaDestroyBuffer(g_hAllocator, stagingVertexBuffer);
+}
+
+static void CopyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height, uint32_t mipLevel)
+{
+ BeginSingleTimeCommands();
+
+ VkImageCopy imageCopy = {};
+ imageCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageCopy.srcSubresource.baseArrayLayer = 0;
+ imageCopy.srcSubresource.mipLevel = mipLevel;
+ imageCopy.srcSubresource.layerCount = 1;
+ imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageCopy.dstSubresource.baseArrayLayer = 0;
+ imageCopy.dstSubresource.mipLevel = mipLevel;
+ imageCopy.dstSubresource.layerCount = 1;
+ imageCopy.srcOffset.x = 0;
+ imageCopy.srcOffset.y = 0;
+ imageCopy.srcOffset.z = 0;
+ imageCopy.dstOffset.x = 0;
+ imageCopy.dstOffset.y = 0;
+ imageCopy.dstOffset.z = 0;
+ imageCopy.extent.width = width;
+ imageCopy.extent.height = height;
+ imageCopy.extent.depth = 1;
+ vkCmdCopyImage(
+ g_hTemporaryCommandBuffer,
+ srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1, &imageCopy);
+
+ EndSingleTimeCommands();
+}
+
+static void TransitionImageLayout(VkImage image, VkFormat format, uint32_t mipLevelCount, VkImageLayout oldLayout, VkImageLayout newLayout)
+{
+ BeginSingleTimeCommands();
+
+ VkImageMemoryBarrier imgMemBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ imgMemBarrier.oldLayout = oldLayout;
+ imgMemBarrier.newLayout = newLayout;
+ imgMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ imgMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ imgMemBarrier.image = image;
+ imgMemBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imgMemBarrier.subresourceRange.baseMipLevel = 0;
+ imgMemBarrier.subresourceRange.levelCount = mipLevelCount;
+ imgMemBarrier.subresourceRange.baseArrayLayer = 0;
+ imgMemBarrier.subresourceRange.layerCount = 1;
+
+ if((oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) &&
+ (newLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL))
+ {
+ imgMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ }
+ else if((oldLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) &&
+ (newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
+ {
+ imgMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ }
+ else if((oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) &&
+ (newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
+ {
+ imgMemBarrier.srcAccessMask = 0;
+ imgMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ }
+ else if((oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) &&
+ (newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL))
+ {
+ imgMemBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ imgMemBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ }
+ else if ((oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) &&
+ (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL))
+ {
+ imgMemBarrier.srcAccessMask = 0;
+ imgMemBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ }
+ else
+ assert(0);
+
+ if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
+ {
+ imgMemBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ if ((format == VK_FORMAT_D16_UNORM_S8_UINT) ||
+ (format == VK_FORMAT_D24_UNORM_S8_UINT) ||
+ (format == VK_FORMAT_D32_SFLOAT_S8_UINT))
+ {
+ imgMemBarrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
+ }
+ }
+
+ vkCmdPipelineBarrier(
+ g_hTemporaryCommandBuffer,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &imgMemBarrier);
+
+ EndSingleTimeCommands();
+}
+
+static void CreateTexture(uint32_t sizeX, uint32_t sizeY)
+{
+ // Create Image
+
+ const VkDeviceSize imageSize = sizeX * sizeY * 4;
+
+ VkImageCreateInfo stagingImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+ stagingImageInfo.imageType = VK_IMAGE_TYPE_2D;
+ stagingImageInfo.extent.width = sizeX;
+ stagingImageInfo.extent.height = sizeY;
+ stagingImageInfo.extent.depth = 1;
+ stagingImageInfo.mipLevels = 1;
+ stagingImageInfo.arrayLayers = 1;
+ stagingImageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+ stagingImageInfo.tiling = VK_IMAGE_TILING_LINEAR;
+ stagingImageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ stagingImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ stagingImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ stagingImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ stagingImageInfo.flags = 0;
+ VmaMemoryRequirements stagingImageMemReq = {};
+ stagingImageMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+ VkImage stagingImage = VK_NULL_HANDLE;
+ VkMappedMemoryRange stagingImageMem;
+ ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &stagingImageInfo, &stagingImageMemReq, &stagingImage, &stagingImageMem, nullptr) );
+
+ char* pImageData = nullptr;
+ ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingImageMem, (void**)&pImageData) );
+
+ VkImageSubresource imageSubresource = {};
+ imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageSubresource.mipLevel = 0;
+ imageSubresource.arrayLayer = 0;
+
+ VkSubresourceLayout imageLayout = {};
+ vkGetImageSubresourceLayout(g_hDevice, stagingImage, &imageSubresource, &imageLayout);
+
+ char* const pMipLevelData = pImageData + imageLayout.offset;
+ uint8_t* pRowData = (uint8_t*)pMipLevelData;
+ for(uint32_t y = 0; y < sizeY; ++y)
+ {
+ uint32_t* pPixelData = (uint32_t*)pRowData;
+ for(uint32_t x = 0; x < sizeY; ++x)
+ {
+ *pPixelData =
+ ((x & 0x18) == 0x08 ? 0x000000FF : 0x00000000) |
+ ((x & 0x18) == 0x10 ? 0x0000FFFF : 0x00000000) |
+ ((y & 0x18) == 0x08 ? 0x0000FF00 : 0x00000000) |
+ ((y & 0x18) == 0x10 ? 0x00FF0000 : 0x00000000);
+ ++pPixelData;
+ }
+ pRowData += imageLayout.rowPitch;
+ }
+
+ vmaUnmapMemory(g_hAllocator, &stagingImageMem);
+
+ VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.extent.width = sizeX;
+ imageInfo.extent.height = sizeY;
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = 1;
+ imageInfo.arrayLayers = 1;
+ imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+ imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageInfo.flags = 0;
+ VmaMemoryRequirements imageMemReq = {};
+ imageMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageInfo, &imageMemReq, &g_hTextureImage, nullptr, nullptr) );
+
+ TransitionImageLayout(
+ stagingImage,
+ VK_FORMAT_R8G8B8A8_UNORM,
+ 1,
+ VK_IMAGE_LAYOUT_PREINITIALIZED,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+ TransitionImageLayout(
+ g_hTextureImage,
+ VK_FORMAT_R8G8B8A8_UNORM,
+ 1,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+ CopyImage(stagingImage, g_hTextureImage, sizeX, sizeY, 0);
+ TransitionImageLayout(
+ g_hTextureImage,
+ VK_FORMAT_R8G8B8A8_UNORM,
+ 1,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ vmaDestroyImage(g_hAllocator, stagingImage);
+
+ // Create ImageView
+
+ VkImageViewCreateInfo textureImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ textureImageViewInfo.image = g_hTextureImage;
+ textureImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ textureImageViewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+ textureImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ textureImageViewInfo.subresourceRange.baseMipLevel = 0;
+ textureImageViewInfo.subresourceRange.levelCount = 1;
+ textureImageViewInfo.subresourceRange.baseArrayLayer = 0;
+ textureImageViewInfo.subresourceRange.layerCount = 1;
+ ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &textureImageViewInfo, nullptr, &g_hTextureImageView) );
+}
+
+struct UniformBufferObject
+{
+ mathfu::vec4_packed ModelViewProj[4];
+};
+
+static void RegisterDebugCallbacks()
+{
+ g_pvkCreateDebugReportCallbackEXT =
+ reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>
+ (vkGetInstanceProcAddr(g_hVulkanInstance, "vkCreateDebugReportCallbackEXT"));
+ g_pvkDebugReportMessageEXT =
+ reinterpret_cast<PFN_vkDebugReportMessageEXT>
+ (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDebugReportMessageEXT"));
+ g_pvkDestroyDebugReportCallbackEXT =
+ reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>
+ (vkGetInstanceProcAddr(g_hVulkanInstance, "vkDestroyDebugReportCallbackEXT"));
+ assert(g_pvkCreateDebugReportCallbackEXT);
+ assert(g_pvkDebugReportMessageEXT);
+ assert(g_pvkDestroyDebugReportCallbackEXT);
+
+ VkDebugReportCallbackCreateInfoEXT callbackCreateInfo;
+ callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
+ callbackCreateInfo.pNext = nullptr;
+ callbackCreateInfo.flags = //VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
+ VK_DEBUG_REPORT_ERROR_BIT_EXT |
+ VK_DEBUG_REPORT_WARNING_BIT_EXT |
+ VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT /*|
+ VK_DEBUG_REPORT_DEBUG_BIT_EXT*/;
+ callbackCreateInfo.pfnCallback = &MyDebugReportCallback;
+ callbackCreateInfo.pUserData = nullptr;
+
+ ERR_GUARD_VULKAN( g_pvkCreateDebugReportCallbackEXT(g_hVulkanInstance, &callbackCreateInfo, nullptr, &g_hCallback) );
+}
+
+static bool IsLayerSupported(const VkLayerProperties* pProps, size_t propCount, const char* pLayerName)
+{
+ const VkLayerProperties* propsEnd = pProps + propCount;
+ return std::find_if(
+ pProps,
+ propsEnd,
+ [pLayerName](const VkLayerProperties& prop) -> bool {
+ return strcmp(pLayerName, prop.layerName) == 0;
+ }) != propsEnd;
+}
+
+static VkFormat FindSupportedFormat(
+ const std::vector<VkFormat>& candidates,
+ VkImageTiling tiling,
+ VkFormatFeatureFlags features)
+{
+ for (VkFormat format : candidates)
+ {
+ VkFormatProperties props;
+ vkGetPhysicalDeviceFormatProperties(g_hPhysicalDevice, 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;
+ }
+ }
+ return VK_FORMAT_UNDEFINED;
+}
+
+static VkFormat FindDepthFormat()
+{
+ std::vector<VkFormat> formats;
+ formats.push_back(VK_FORMAT_D32_SFLOAT);
+ formats.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT);
+ formats.push_back(VK_FORMAT_D24_UNORM_S8_UINT);
+
+ return FindSupportedFormat(
+ formats,
+ VK_IMAGE_TILING_OPTIMAL,
+ VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
+}
+
+static void CreateSwapchain()
+{
+ // Query surface formats.
+
+ ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_hPhysicalDevice, g_hSurface, &g_SurfaceCapabilities) );
+
+ uint32_t formatCount = 0;
+ ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, nullptr) );
+ g_SurfaceFormats.resize(formatCount);
+ ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfaceFormatsKHR(g_hPhysicalDevice, g_hSurface, &formatCount, g_SurfaceFormats.data()) );
+
+ uint32_t presentModeCount = 0;
+ ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, nullptr) );
+ g_PresentModes.resize(presentModeCount);
+ ERR_GUARD_VULKAN( vkGetPhysicalDeviceSurfacePresentModesKHR(g_hPhysicalDevice, g_hSurface, &presentModeCount, g_PresentModes.data()) );
+
+ // Create swap chain
+
+ g_SurfaceFormat = ChooseSurfaceFormat();
+ VkPresentModeKHR presentMode = ChooseSwapPresentMode();
+ g_Extent = ChooseSwapExtent();
+
+ uint32_t imageCount = g_SurfaceCapabilities.minImageCount + 1;
+ if((g_SurfaceCapabilities.maxImageCount > 0) &&
+ (imageCount > g_SurfaceCapabilities.maxImageCount))
+ {
+ imageCount = g_SurfaceCapabilities.maxImageCount;
+ }
+
+ VkSwapchainCreateInfoKHR swapChainInfo = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
+ swapChainInfo.surface = g_hSurface;
+ swapChainInfo.minImageCount = imageCount;
+ swapChainInfo.imageFormat = g_SurfaceFormat.format;
+ swapChainInfo.imageColorSpace = g_SurfaceFormat.colorSpace;
+ swapChainInfo.imageExtent = g_Extent;
+ swapChainInfo.imageArrayLayers = 1;
+ swapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ swapChainInfo.preTransform = g_SurfaceCapabilities.currentTransform;
+ swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ swapChainInfo.presentMode = presentMode;
+ swapChainInfo.clipped = VK_TRUE;
+ swapChainInfo.oldSwapchain = g_hSwapchain;
+
+ uint32_t queueFamilyIndices[] = { g_GraphicsQueueFamilyIndex, g_PresentQueueFamilyIndex };
+ if(g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex)
+ {
+ swapChainInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
+ swapChainInfo.queueFamilyIndexCount = 2;
+ swapChainInfo.pQueueFamilyIndices = queueFamilyIndices;
+ }
+ else
+ {
+ swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ }
+
+ VkSwapchainKHR hNewSwapchain = VK_NULL_HANDLE;
+ ERR_GUARD_VULKAN( vkCreateSwapchainKHR(g_hDevice, &swapChainInfo, nullptr, &hNewSwapchain) );
+ if(g_hSwapchain != VK_NULL_HANDLE)
+ vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr);
+ g_hSwapchain = hNewSwapchain;
+
+ // Retrieve swapchain images.
+
+ uint32_t swapchainImageCount = 0;
+ ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, nullptr) );
+ g_SwapchainImages.resize(swapchainImageCount);
+ ERR_GUARD_VULKAN( vkGetSwapchainImagesKHR(g_hDevice, g_hSwapchain, &swapchainImageCount, g_SwapchainImages.data()) );
+
+ // Create swapchain image views.
+
+ for(size_t i = g_SwapchainImageViews.size(); i--; )
+ vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr);
+ g_SwapchainImageViews.clear();
+
+ VkImageViewCreateInfo swapchainImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ g_SwapchainImageViews.resize(swapchainImageCount);
+ for(uint32_t i = 0; i < swapchainImageCount; ++i)
+ {
+ swapchainImageViewInfo.image = g_SwapchainImages[i];
+ swapchainImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ swapchainImageViewInfo.format = g_SurfaceFormat.format;
+ swapchainImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ swapchainImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ swapchainImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ swapchainImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+ swapchainImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ swapchainImageViewInfo.subresourceRange.baseMipLevel = 0;
+ swapchainImageViewInfo.subresourceRange.levelCount = 1;
+ swapchainImageViewInfo.subresourceRange.baseArrayLayer = 0;
+ swapchainImageViewInfo.subresourceRange.layerCount = 1;
+ ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &swapchainImageViewInfo, nullptr, &g_SwapchainImageViews[i]) );
+ }
+
+ // Create depth buffer
+
+ g_DepthFormat = FindDepthFormat();
+ assert(g_DepthFormat != VK_FORMAT_UNDEFINED);
+
+ VkImageCreateInfo depthImageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+ depthImageInfo.imageType = VK_IMAGE_TYPE_2D;
+ depthImageInfo.extent.width = g_Extent.width;
+ depthImageInfo.extent.height = g_Extent.height;
+ depthImageInfo.extent.depth = 1;
+ depthImageInfo.mipLevels = 1;
+ depthImageInfo.arrayLayers = 1;
+ depthImageInfo.format = g_DepthFormat;
+ depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
+ depthImageInfo.flags = 0;
+
+ VmaMemoryRequirements depthImageMemReq = {};
+ depthImageMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+
+ ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &depthImageInfo, &depthImageMemReq, &g_hDepthImage, nullptr, nullptr) );
+
+ VkImageViewCreateInfo depthImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
+ depthImageViewInfo.image = g_hDepthImage;
+ depthImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ depthImageViewInfo.format = g_DepthFormat;
+ depthImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ depthImageViewInfo.subresourceRange.baseMipLevel = 0;
+ depthImageViewInfo.subresourceRange.levelCount = 1;
+ depthImageViewInfo.subresourceRange.baseArrayLayer = 0;
+ depthImageViewInfo.subresourceRange.layerCount = 1;
+
+ ERR_GUARD_VULKAN( vkCreateImageView(g_hDevice, &depthImageViewInfo, nullptr, &g_hDepthImageView) );
+
+ TransitionImageLayout(
+ g_hDepthImage,
+ g_DepthFormat,
+ 1,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+
+ // Create pipeline layout
+ {
+ if(g_hPipelineLayout != VK_NULL_HANDLE)
+ {
+ vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr);
+ g_hPipelineLayout = VK_NULL_HANDLE;
+ }
+
+ VkPushConstantRange pushConstantRanges[1];
+ ZeroMemory(&pushConstantRanges, sizeof pushConstantRanges);
+ pushConstantRanges[0].offset = 0;
+ pushConstantRanges[0].size = sizeof(UniformBufferObject);
+ pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+
+ VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout };
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = descriptorSetLayouts;
+ pipelineLayoutInfo.pushConstantRangeCount = 1;
+ pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges;
+ ERR_GUARD_VULKAN( vkCreatePipelineLayout(g_hDevice, &pipelineLayoutInfo, nullptr, &g_hPipelineLayout) );
+ }
+
+ // Create render pass
+ {
+ if(g_hRenderPass != VK_NULL_HANDLE)
+ {
+ vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr);
+ g_hRenderPass = VK_NULL_HANDLE;
+ }
+
+ VkAttachmentDescription attachments[2];
+ ZeroMemory(attachments, sizeof(attachments));
+
+ attachments[0].format = g_SurfaceFormat.format;
+ attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
+ attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ attachments[1].format = g_DepthFormat;
+ attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
+ attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ VkAttachmentReference colorAttachmentRef = {};
+ colorAttachmentRef.attachment = 0;
+ colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+ VkAttachmentReference depthStencilAttachmentRef = {};
+ depthStencilAttachmentRef.attachment = 1;
+ depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ VkSubpassDescription subpassDesc = {};
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.colorAttachmentCount = 1;
+ subpassDesc.pColorAttachments = &colorAttachmentRef;
+ subpassDesc.pDepthStencilAttachment = &depthStencilAttachmentRef;
+
+ VkSubpassDependency subpassDependency = {};
+ subpassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
+ subpassDependency.dstSubpass = 0;
+ subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDependency.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
+ subpassDependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
+ subpassDependency.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
+
+ VkRenderPassCreateInfo renderPassInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
+ renderPassInfo.attachmentCount = (uint32_t)_countof(attachments);
+ renderPassInfo.pAttachments = attachments;
+ renderPassInfo.subpassCount = 1;
+ renderPassInfo.pSubpasses = &subpassDesc;
+ renderPassInfo.dependencyCount = 1;
+ renderPassInfo.pDependencies = &subpassDependency;
+ ERR_GUARD_VULKAN( vkCreateRenderPass(g_hDevice, &renderPassInfo, nullptr, &g_hRenderPass) );
+ }
+
+ // Create pipeline
+ {
+ std::vector<char> vertShaderCode;
+ LoadShader(vertShaderCode, "Shader.vert.spv");
+ VkShaderModuleCreateInfo shaderModuleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
+ shaderModuleInfo.codeSize = vertShaderCode.size();
+ shaderModuleInfo.pCode = (const uint32_t*)vertShaderCode.data();
+ VkShaderModule hVertShaderModule = VK_NULL_HANDLE;
+ ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &hVertShaderModule) );
+
+ std::vector<char> hFragShaderCode;
+ LoadShader(hFragShaderCode, "Shader.frag.spv");
+ shaderModuleInfo.codeSize = hFragShaderCode.size();
+ shaderModuleInfo.pCode = (const uint32_t*)hFragShaderCode.data();
+ VkShaderModule fragShaderModule = VK_NULL_HANDLE;
+ ERR_GUARD_VULKAN( vkCreateShaderModule(g_hDevice, &shaderModuleInfo, nullptr, &fragShaderModule) );
+
+ VkPipelineShaderStageCreateInfo vertPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
+ vertPipelineShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
+ vertPipelineShaderStageInfo.module = hVertShaderModule;
+ vertPipelineShaderStageInfo.pName = "main";
+
+ VkPipelineShaderStageCreateInfo fragPipelineShaderStageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
+ fragPipelineShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ fragPipelineShaderStageInfo.module = fragShaderModule;
+ fragPipelineShaderStageInfo.pName = "main";
+
+ VkPipelineShaderStageCreateInfo pipelineShaderStageInfos[] = {
+ vertPipelineShaderStageInfo,
+ fragPipelineShaderStageInfo
+ };
+
+ VkVertexInputBindingDescription bindingDescription = {};
+ bindingDescription.binding = 0;
+ bindingDescription.stride = sizeof(Vertex);
+ bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+
+ VkVertexInputAttributeDescription attributeDescriptions[3];
+ ZeroMemory(attributeDescriptions, sizeof(attributeDescriptions));
+
+ attributeDescriptions[0].binding = 0;
+ attributeDescriptions[0].location = 0;
+ attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
+ attributeDescriptions[0].offset = offsetof(Vertex, pos);
+
+ attributeDescriptions[1].binding = 0;
+ attributeDescriptions[1].location = 1;
+ attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
+ attributeDescriptions[1].offset = offsetof(Vertex, color);
+
+ attributeDescriptions[2].binding = 0;
+ attributeDescriptions[2].location = 2;
+ attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
+ attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
+
+ VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
+ pipelineVertexInputStateInfo.vertexBindingDescriptionCount = 1;
+ pipelineVertexInputStateInfo.pVertexBindingDescriptions = &bindingDescription;
+ pipelineVertexInputStateInfo.vertexAttributeDescriptionCount = _countof(attributeDescriptions);
+ pipelineVertexInputStateInfo.pVertexAttributeDescriptions = attributeDescriptions;
+
+ VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
+ pipelineInputAssemblyStateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ pipelineInputAssemblyStateInfo.primitiveRestartEnable = VK_TRUE;
+
+ VkViewport viewport = {};
+ viewport.x = 0.f;
+ viewport.y = 0.f;
+ viewport.width = (float)g_Extent.width;
+ viewport.height = (float)g_Extent.height;
+ viewport.minDepth = 0.f;
+ viewport.maxDepth = 1.f;
+
+ VkRect2D scissor = {};
+ scissor.offset.x = 0;
+ scissor.offset.y = 0;
+ scissor.extent = g_Extent;
+
+ VkPipelineViewportStateCreateInfo pipelineViewportStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
+ pipelineViewportStateInfo.viewportCount = 1;
+ pipelineViewportStateInfo.pViewports = &viewport;
+ pipelineViewportStateInfo.scissorCount = 1;
+ pipelineViewportStateInfo.pScissors = &scissor;
+
+ VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
+ pipelineRasterizationStateInfo.depthClampEnable = VK_FALSE;
+ pipelineRasterizationStateInfo.rasterizerDiscardEnable = VK_FALSE;
+ pipelineRasterizationStateInfo.polygonMode = VK_POLYGON_MODE_FILL;
+ pipelineRasterizationStateInfo.lineWidth = 1.f;
+ pipelineRasterizationStateInfo.cullMode = VK_CULL_MODE_BACK_BIT;
+ pipelineRasterizationStateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ pipelineRasterizationStateInfo.depthBiasEnable = VK_FALSE;
+ pipelineRasterizationStateInfo.depthBiasConstantFactor = 0.f;
+ pipelineRasterizationStateInfo.depthBiasClamp = 0.f;
+ pipelineRasterizationStateInfo.depthBiasSlopeFactor = 0.f;
+
+ VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
+ pipelineMultisampleStateInfo.sampleShadingEnable = VK_FALSE;
+ pipelineMultisampleStateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ pipelineMultisampleStateInfo.minSampleShading = 1.f;
+ pipelineMultisampleStateInfo.pSampleMask = nullptr;
+ pipelineMultisampleStateInfo.alphaToCoverageEnable = VK_FALSE;
+ pipelineMultisampleStateInfo.alphaToOneEnable = VK_FALSE;
+
+ VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {};
+ pipelineColorBlendAttachmentState.colorWriteMask =
+ VK_COLOR_COMPONENT_R_BIT |
+ VK_COLOR_COMPONENT_G_BIT |
+ VK_COLOR_COMPONENT_B_BIT |
+ VK_COLOR_COMPONENT_A_BIT;
+ pipelineColorBlendAttachmentState.blendEnable = VK_FALSE;
+ pipelineColorBlendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
+ pipelineColorBlendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
+ pipelineColorBlendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD; // Optional
+ pipelineColorBlendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Optional
+ pipelineColorBlendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Optional
+ pipelineColorBlendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
+
+ VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
+ pipelineColorBlendStateInfo.logicOpEnable = VK_FALSE;
+ pipelineColorBlendStateInfo.logicOp = VK_LOGIC_OP_COPY;
+ pipelineColorBlendStateInfo.attachmentCount = 1;
+ pipelineColorBlendStateInfo.pAttachments = &pipelineColorBlendAttachmentState;
+
+ VkPipelineDepthStencilStateCreateInfo depthStencilStateInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
+ depthStencilStateInfo.depthTestEnable = VK_TRUE;
+ depthStencilStateInfo.depthWriteEnable = VK_TRUE;
+ depthStencilStateInfo.depthCompareOp = VK_COMPARE_OP_LESS;
+ depthStencilStateInfo.depthBoundsTestEnable = VK_FALSE;
+ depthStencilStateInfo.stencilTestEnable = VK_FALSE;
+
+ VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
+ pipelineInfo.stageCount = 2;
+ pipelineInfo.pStages = pipelineShaderStageInfos;
+ pipelineInfo.pVertexInputState = &pipelineVertexInputStateInfo;
+ pipelineInfo.pInputAssemblyState = &pipelineInputAssemblyStateInfo;
+ pipelineInfo.pViewportState = &pipelineViewportStateInfo;
+ pipelineInfo.pRasterizationState = &pipelineRasterizationStateInfo;
+ pipelineInfo.pMultisampleState = &pipelineMultisampleStateInfo;
+ pipelineInfo.pDepthStencilState = &depthStencilStateInfo;
+ pipelineInfo.pColorBlendState = &pipelineColorBlendStateInfo;
+ pipelineInfo.pDynamicState = nullptr;
+ pipelineInfo.layout = g_hPipelineLayout;
+ pipelineInfo.renderPass = g_hRenderPass;
+ pipelineInfo.subpass = 0;
+ pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
+ pipelineInfo.basePipelineIndex = -1;
+ ERR_GUARD_VULKAN( vkCreateGraphicsPipelines(
+ g_hDevice,
+ VK_NULL_HANDLE,
+ 1,
+ &pipelineInfo, nullptr,
+ &g_hPipeline) );
+
+ vkDestroyShaderModule(g_hDevice, fragShaderModule, nullptr);
+ vkDestroyShaderModule(g_hDevice, hVertShaderModule, nullptr);
+ }
+
+ // Create frambuffers
+
+ for(size_t i = g_Framebuffers.size(); i--; )
+ vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr);
+ g_Framebuffers.clear();
+
+ g_Framebuffers.resize(g_SwapchainImageViews.size());
+ for(size_t i = 0; i < g_SwapchainImages.size(); ++i)
+ {
+ VkImageView attachments[] = { g_SwapchainImageViews[i], g_hDepthImageView };
+
+ VkFramebufferCreateInfo framebufferInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
+ framebufferInfo.renderPass = g_hRenderPass;
+ framebufferInfo.attachmentCount = (uint32_t)_countof(attachments);
+ framebufferInfo.pAttachments = attachments;
+ framebufferInfo.width = g_Extent.width;
+ framebufferInfo.height = g_Extent.height;
+ framebufferInfo.layers = 1;
+ ERR_GUARD_VULKAN( vkCreateFramebuffer(g_hDevice, &framebufferInfo, nullptr, &g_Framebuffers[i]) );
+ }
+
+ // Create semaphores
+
+ if(g_hImageAvailableSemaphore != VK_NULL_HANDLE)
+ {
+ vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr);
+ g_hImageAvailableSemaphore = VK_NULL_HANDLE;
+ }
+ if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE)
+ {
+ vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr);
+ g_hRenderFinishedSemaphore = VK_NULL_HANDLE;
+ }
+
+ VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
+ ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hImageAvailableSemaphore) );
+ ERR_GUARD_VULKAN( vkCreateSemaphore(g_hDevice, &semaphoreInfo, nullptr, &g_hRenderFinishedSemaphore) );
+}
+
+static void DestroySwapchain(bool destroyActualSwapchain)
+{
+ if(g_hImageAvailableSemaphore != VK_NULL_HANDLE)
+ {
+ vkDestroySemaphore(g_hDevice, g_hImageAvailableSemaphore, nullptr);
+ g_hImageAvailableSemaphore = VK_NULL_HANDLE;
+ }
+ if(g_hRenderFinishedSemaphore != VK_NULL_HANDLE)
+ {
+ vkDestroySemaphore(g_hDevice, g_hRenderFinishedSemaphore, nullptr);
+ g_hRenderFinishedSemaphore = VK_NULL_HANDLE;
+ }
+
+ for(size_t i = g_Framebuffers.size(); i--; )
+ vkDestroyFramebuffer(g_hDevice, g_Framebuffers[i], nullptr);
+ g_Framebuffers.clear();
+
+ if(g_hDepthImageView != VK_NULL_HANDLE)
+ {
+ vkDestroyImageView(g_hDevice, g_hDepthImageView, nullptr);
+ g_hDepthImageView = VK_NULL_HANDLE;
+ }
+ if(g_hDepthImage != VK_NULL_HANDLE)
+ {
+ vmaDestroyImage(g_hAllocator, g_hDepthImage);
+ g_hDepthImage = VK_NULL_HANDLE;
+ }
+
+ if(g_hPipeline != VK_NULL_HANDLE)
+ {
+ vkDestroyPipeline(g_hDevice, g_hPipeline, nullptr);
+ g_hPipeline = VK_NULL_HANDLE;
+ }
+
+ if(g_hRenderPass != VK_NULL_HANDLE)
+ {
+ vkDestroyRenderPass(g_hDevice, g_hRenderPass, nullptr);
+ g_hRenderPass = VK_NULL_HANDLE;
+ }
+
+ if(g_hPipelineLayout != VK_NULL_HANDLE)
+ {
+ vkDestroyPipelineLayout(g_hDevice, g_hPipelineLayout, nullptr);
+ g_hPipelineLayout = VK_NULL_HANDLE;
+ }
+
+ for(size_t i = g_SwapchainImageViews.size(); i--; )
+ vkDestroyImageView(g_hDevice, g_SwapchainImageViews[i], nullptr);
+ g_SwapchainImageViews.clear();
+
+ if(destroyActualSwapchain && (g_hSwapchain != VK_NULL_HANDLE))
+ {
+ vkDestroySwapchainKHR(g_hDevice, g_hSwapchain, nullptr);
+ g_hSwapchain = VK_NULL_HANDLE;
+ }
+}
+
+static void InitializeApplication()
+{
+ uint32_t instanceLayerPropCount = 0;
+ ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, nullptr) );
+ std::vector<VkLayerProperties> instanceLayerProps(instanceLayerPropCount);
+ if(instanceLayerPropCount > 0)
+ {
+ ERR_GUARD_VULKAN( vkEnumerateInstanceLayerProperties(&instanceLayerPropCount, instanceLayerProps.data()) );
+ }
+
+ if(g_EnableValidationLayer == true)
+ {
+ if(IsLayerSupported(instanceLayerProps.data(), instanceLayerProps.size(), VALIDATION_LAYER_NAME) == false)
+ {
+ printf("Layer \"%s\" not supported.", VALIDATION_LAYER_NAME);
+ g_EnableValidationLayer = false;
+ }
+ }
+
+ std::vector<const char*> instanceExtensions;
+ instanceExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
+ instanceExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
+
+ std::vector<const char*> instanceLayers;
+ if(g_EnableValidationLayer == true)
+ {
+ instanceLayers.push_back(VALIDATION_LAYER_NAME);
+ instanceExtensions.push_back("VK_EXT_debug_report");
+ }
+
+ VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
+ appInfo.pApplicationName = APP_TITLE_A;
+ appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
+ appInfo.pEngineName = "Adam Sawicki Engine";
+ appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
+ appInfo.apiVersion = VK_API_VERSION_1_0;
+
+ VkInstanceCreateInfo instInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
+ instInfo.pApplicationInfo = &appInfo;
+ instInfo.enabledExtensionCount = static_cast<uint32_t>(instanceExtensions.size());
+ instInfo.ppEnabledExtensionNames = instanceExtensions.data();
+ instInfo.enabledLayerCount = static_cast<uint32_t>(instanceLayers.size());
+ instInfo.ppEnabledLayerNames = instanceLayers.data();
+
+ ERR_GUARD_VULKAN( vkCreateInstance(&instInfo, NULL, &g_hVulkanInstance) );
+
+ // Create VkSurfaceKHR.
+ VkWin32SurfaceCreateInfoKHR surfaceInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
+ surfaceInfo.hinstance = g_hAppInstance;
+ surfaceInfo.hwnd = g_hWnd;
+ VkResult result = vkCreateWin32SurfaceKHR(g_hVulkanInstance, &surfaceInfo, NULL, &g_hSurface);
+ assert(result == VK_SUCCESS);
+
+ if(g_EnableValidationLayer == true)
+ RegisterDebugCallbacks();
+
+ // Find physical device
+
+ uint32_t deviceCount = 0;
+ ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, nullptr) );
+ assert(deviceCount > 0);
+
+ std::vector<VkPhysicalDevice> physicalDevices(deviceCount);
+ ERR_GUARD_VULKAN( vkEnumeratePhysicalDevices(g_hVulkanInstance, &deviceCount, physicalDevices.data()) );
+
+ g_hPhysicalDevice = physicalDevices[0];
+
+ // Query for features
+
+ VkPhysicalDeviceProperties physicalDeviceProperties = {};
+ vkGetPhysicalDeviceProperties(g_hPhysicalDevice, &physicalDeviceProperties);
+
+ //VkPhysicalDeviceFeatures physicalDeviceFreatures = {};
+ //vkGetPhysicalDeviceFeatures(g_PhysicalDevice, &physicalDeviceFreatures);
+
+ // Find queue family index
+
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, nullptr);
+ assert(queueFamilyCount > 0);
+ std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(g_hPhysicalDevice, &queueFamilyCount, queueFamilies.data());
+ for(uint32_t i = 0;
+ (i < queueFamilyCount) &&
+ (g_GraphicsQueueFamilyIndex == UINT_MAX || g_PresentQueueFamilyIndex == UINT_MAX);
+ ++i)
+ {
+ if(queueFamilies[i].queueCount > 0)
+ {
+ if((g_GraphicsQueueFamilyIndex != 0) &&
+ ((queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0))
+ {
+ g_GraphicsQueueFamilyIndex = i;
+ }
+
+ VkBool32 surfaceSupported = 0;
+ VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(g_hPhysicalDevice, i, g_hSurface, &surfaceSupported);
+ if((res >= 0) && (surfaceSupported == VK_TRUE))
+ {
+ g_PresentQueueFamilyIndex = i;
+ }
+ }
+ }
+ assert(g_GraphicsQueueFamilyIndex != UINT_MAX);
+
+ // Create logical device
+
+ const float queuePriority = 1.f;
+
+ VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
+ deviceQueueCreateInfo[0].queueFamilyIndex = g_GraphicsQueueFamilyIndex;
+ deviceQueueCreateInfo[0].queueCount = 1;
+ deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority;
+ deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ deviceQueueCreateInfo[1].queueFamilyIndex = g_PresentQueueFamilyIndex;
+ deviceQueueCreateInfo[1].queueCount = 1;
+ deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority;
+
+ VkPhysicalDeviceFeatures deviceFeatures = {};
+ deviceFeatures.fillModeNonSolid = VK_TRUE;
+ deviceFeatures.samplerAnisotropy = VK_TRUE;
+
+ std::vector<const char*> enabledDeviceExtensions;
+ enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
+ VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
+ deviceCreateInfo.enabledLayerCount = 0;
+ deviceCreateInfo.ppEnabledLayerNames = nullptr;
+ deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size();
+ deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
+ deviceCreateInfo.queueCreateInfoCount = g_PresentQueueFamilyIndex != g_GraphicsQueueFamilyIndex ? 2 : 1;
+ deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
+ deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
+
+ ERR_GUARD_VULKAN( vkCreateDevice(g_hPhysicalDevice, &deviceCreateInfo, nullptr, &g_hDevice) );
+
+ // Create memory allocator
+
+ VmaAllocatorCreateInfo allocatorInfo = {};
+ allocatorInfo.physicalDevice = g_hPhysicalDevice;
+ allocatorInfo.device = g_hDevice;
+ ERR_GUARD_VULKAN( vmaCreateAllocator(&allocatorInfo, &g_hAllocator) );
+
+ // Retrieve queue (doesn't need to be destroyed)
+
+ vkGetDeviceQueue(g_hDevice, g_GraphicsQueueFamilyIndex, 0, &g_hGraphicsQueue);
+ vkGetDeviceQueue(g_hDevice, g_PresentQueueFamilyIndex, 0, &g_hPresentQueue);
+ assert(g_hGraphicsQueue);
+ assert(g_hPresentQueue);
+
+ // Create command pool
+
+ VkCommandPoolCreateInfo commandPoolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
+ commandPoolInfo.queueFamilyIndex = g_GraphicsQueueFamilyIndex;
+ commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ ERR_GUARD_VULKAN( vkCreateCommandPool(g_hDevice, &commandPoolInfo, nullptr, &g_hCommandPool) );
+
+ VkCommandBufferAllocateInfo commandBufferInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
+ commandBufferInfo.commandPool = g_hCommandPool;
+ commandBufferInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ commandBufferInfo.commandBufferCount = COMMAND_BUFFER_COUNT;
+ ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, g_MainCommandBuffers) );
+
+ VkFenceCreateInfo fenceInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
+ fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+ for(size_t i = 0; i < COMMAND_BUFFER_COUNT; ++i)
+ {
+ ERR_GUARD_VULKAN( vkCreateFence(g_hDevice, &fenceInfo, nullptr, &g_MainCommandBufferExecutedFances[i]) );
+ }
+
+ commandBufferInfo.commandBufferCount = 1;
+ ERR_GUARD_VULKAN( vkAllocateCommandBuffers(g_hDevice, &commandBufferInfo, &g_hTemporaryCommandBuffer) );
+
+ // Create texture sampler
+
+ VkSamplerCreateInfo samplerInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
+ samplerInfo.magFilter = VK_FILTER_LINEAR;
+ samplerInfo.minFilter = VK_FILTER_LINEAR;
+ samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ samplerInfo.anisotropyEnable = VK_TRUE;
+ samplerInfo.maxAnisotropy = 16;
+ samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+ samplerInfo.unnormalizedCoordinates = VK_FALSE;
+ samplerInfo.compareEnable = VK_FALSE;
+ samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
+ samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ samplerInfo.mipLodBias = 0.f;
+ samplerInfo.minLod = 0.f;
+ samplerInfo.maxLod = FLT_MAX;
+ ERR_GUARD_VULKAN( vkCreateSampler(g_hDevice, &samplerInfo, nullptr, &g_hSampler) );
+
+ CreateTexture(128, 128);
+ CreateMesh();
+
+ VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
+ samplerLayoutBinding.binding = 1;
+ samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ samplerLayoutBinding.descriptorCount = 1;
+ samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+
+ VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
+ descriptorSetLayoutInfo.bindingCount = 1;
+ descriptorSetLayoutInfo.pBindings = &samplerLayoutBinding;
+ ERR_GUARD_VULKAN( vkCreateDescriptorSetLayout(g_hDevice, &descriptorSetLayoutInfo, nullptr, &g_hDescriptorSetLayout) );
+
+ // Create descriptor pool
+
+ VkDescriptorPoolSize descriptorPoolSizes[2];
+ ZeroMemory(descriptorPoolSizes, sizeof(descriptorPoolSizes));
+ descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptorPoolSizes[0].descriptorCount = 1;
+ descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ descriptorPoolSizes[1].descriptorCount = 1;
+
+ VkDescriptorPoolCreateInfo descriptorPoolInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
+ descriptorPoolInfo.poolSizeCount = (uint32_t)_countof(descriptorPoolSizes);
+ descriptorPoolInfo.pPoolSizes = descriptorPoolSizes;
+ descriptorPoolInfo.maxSets = 1;
+ ERR_GUARD_VULKAN( vkCreateDescriptorPool(g_hDevice, &descriptorPoolInfo, nullptr, &g_hDescriptorPool) );
+
+ // Create descriptor set layout
+
+ VkDescriptorSetLayout descriptorSetLayouts[] = { g_hDescriptorSetLayout };
+ VkDescriptorSetAllocateInfo descriptorSetInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
+ descriptorSetInfo.descriptorPool = g_hDescriptorPool;
+ descriptorSetInfo.descriptorSetCount = 1;
+ descriptorSetInfo.pSetLayouts = descriptorSetLayouts;
+ ERR_GUARD_VULKAN( vkAllocateDescriptorSets(g_hDevice, &descriptorSetInfo, &g_hDescriptorSet) );
+
+ VkDescriptorImageInfo descriptorImageInfo = {};
+ descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ descriptorImageInfo.imageView = g_hTextureImageView;
+ descriptorImageInfo.sampler = g_hSampler;
+
+ VkWriteDescriptorSet writeDescriptorSet = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
+ writeDescriptorSet.dstSet = g_hDescriptorSet;
+ writeDescriptorSet.dstBinding = 1;
+ writeDescriptorSet.dstArrayElement = 0;
+ writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ writeDescriptorSet.descriptorCount = 1;
+ writeDescriptorSet.pImageInfo = &descriptorImageInfo;
+
+ vkUpdateDescriptorSets(g_hDevice, 1, &writeDescriptorSet, 0, nullptr);
+
+ CreateSwapchain();
+}
+
+static void FinalizeApplication()
+{
+ vkDeviceWaitIdle(g_hDevice);
+
+ DestroySwapchain(true);
+
+ if(g_hDescriptorPool != VK_NULL_HANDLE)
+ {
+ vkDestroyDescriptorPool(g_hDevice, g_hDescriptorPool, nullptr);
+ g_hDescriptorPool = VK_NULL_HANDLE;
+ }
+
+ if(g_hDescriptorSetLayout != VK_NULL_HANDLE)
+ {
+ vkDestroyDescriptorSetLayout(g_hDevice, g_hDescriptorSetLayout, nullptr);
+ g_hDescriptorSetLayout = VK_NULL_HANDLE;
+ }
+
+ if(g_hTextureImageView != VK_NULL_HANDLE)
+ {
+ vkDestroyImageView(g_hDevice, g_hTextureImageView, nullptr);
+ g_hTextureImageView = VK_NULL_HANDLE;
+ }
+ if(g_hTextureImage != VK_NULL_HANDLE)
+ {
+ vmaDestroyImage(g_hAllocator, g_hTextureImage);
+ g_hTextureImage = VK_NULL_HANDLE;
+ }
+
+ if(g_hIndexBuffer != VK_NULL_HANDLE)
+ {
+ vmaDestroyBuffer(g_hAllocator, g_hIndexBuffer);
+ g_hIndexBuffer = VK_NULL_HANDLE;
+ }
+ if(g_hVertexBuffer != VK_NULL_HANDLE)
+ {
+ vmaDestroyBuffer(g_hAllocator, g_hVertexBuffer);
+ g_hVertexBuffer = VK_NULL_HANDLE;
+ }
+
+ if(g_hSampler != VK_NULL_HANDLE)
+ {
+ vkDestroySampler(g_hDevice, g_hSampler, nullptr);
+ g_hSampler = VK_NULL_HANDLE;
+ }
+
+ for(size_t i = COMMAND_BUFFER_COUNT; i--; )
+ {
+ if(g_MainCommandBufferExecutedFances[i] != VK_NULL_HANDLE)
+ {
+ vkDestroyFence(g_hDevice, g_MainCommandBufferExecutedFances[i], nullptr);
+ g_MainCommandBufferExecutedFances[i] = VK_NULL_HANDLE;
+ }
+ }
+ if(g_MainCommandBuffers[0] != VK_NULL_HANDLE)
+ {
+ vkFreeCommandBuffers(g_hDevice, g_hCommandPool, COMMAND_BUFFER_COUNT, g_MainCommandBuffers);
+ ZeroMemory(g_MainCommandBuffers, sizeof(g_MainCommandBuffers));
+ }
+ if(g_hTemporaryCommandBuffer != VK_NULL_HANDLE)
+ {
+ vkFreeCommandBuffers(g_hDevice, g_hCommandPool, 1, &g_hTemporaryCommandBuffer);
+ g_hTemporaryCommandBuffer = VK_NULL_HANDLE;
+ }
+
+ if(g_hCommandPool != VK_NULL_HANDLE)
+ {
+ vkDestroyCommandPool(g_hDevice, g_hCommandPool, nullptr);
+ g_hCommandPool = VK_NULL_HANDLE;
+ }
+
+ if(g_hAllocator != VK_NULL_HANDLE)
+ {
+ vmaDestroyAllocator(g_hAllocator);
+ g_hAllocator = nullptr;
+ }
+
+ if(g_hDevice != VK_NULL_HANDLE)
+ {
+ vkDestroyDevice(g_hDevice, nullptr);
+ g_hDevice = nullptr;
+ }
+
+ if(g_pvkDestroyDebugReportCallbackEXT && g_hCallback != VK_NULL_HANDLE)
+ {
+ g_pvkDestroyDebugReportCallbackEXT(g_hVulkanInstance, g_hCallback, nullptr);
+ g_hCallback = VK_NULL_HANDLE;
+ }
+
+ if(g_hSurface != VK_NULL_HANDLE)
+ {
+ vkDestroySurfaceKHR(g_hVulkanInstance, g_hSurface, NULL);
+ g_hSurface = VK_NULL_HANDLE;
+ }
+
+ if(g_hVulkanInstance != VK_NULL_HANDLE)
+ {
+ vkDestroyInstance(g_hVulkanInstance, NULL);
+ g_hVulkanInstance = VK_NULL_HANDLE;
+ }
+}
+
+static void PrintAllocatorStats()
+{
+#if VMA_STATS_STRING_ENABLED
+ char* statsString = nullptr;
+ vmaBuildStatsString(g_hAllocator, &statsString, true);
+ printf("%s\n", statsString);
+ vmaFreeStatsString(g_hAllocator, statsString);
+#endif
+}
+
+static void RecreateSwapChain()
+{
+ vkDeviceWaitIdle(g_hDevice);
+ DestroySwapchain(false);
+ CreateSwapchain();
+}
+
+static void DrawFrame()
+{
+ // Begin main command buffer
+ size_t cmdBufIndex = (g_NextCommandBufferIndex++) % COMMAND_BUFFER_COUNT;
+ VkCommandBuffer hCommandBuffer = g_MainCommandBuffers[cmdBufIndex];
+ VkFence hCommandBufferExecutedFence = g_MainCommandBufferExecutedFances[cmdBufIndex];
+
+ ERR_GUARD_VULKAN( vkWaitForFences(g_hDevice, 1, &hCommandBufferExecutedFence, VK_TRUE, UINT64_MAX) );
+ ERR_GUARD_VULKAN( vkResetFences(g_hDevice, 1, &hCommandBufferExecutedFence) );
+
+ VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
+ commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+ ERR_GUARD_VULKAN( vkBeginCommandBuffer(hCommandBuffer, &commandBufferBeginInfo) );
+
+ // Acquire swapchain image
+ uint32_t imageIndex = 0;
+ VkResult res = vkAcquireNextImageKHR(g_hDevice, g_hSwapchain, UINT64_MAX, g_hImageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
+ if(res == VK_ERROR_OUT_OF_DATE_KHR)
+ {
+ RecreateSwapChain();
+ return;
+ }
+ else if(res < 0)
+ {
+ ERR_GUARD_VULKAN(res);
+ }
+
+ // Record geometry pass
+
+ VkClearValue clearValues[2];
+ ZeroMemory(clearValues, sizeof(clearValues));
+ clearValues[0].color.float32[0] = 0.25f;
+ clearValues[0].color.float32[1] = 0.25f;
+ clearValues[0].color.float32[2] = 0.5f;
+ clearValues[0].color.float32[3] = 1.0f;
+ clearValues[1].depthStencil.depth = 1.0f;
+
+ VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
+ renderPassBeginInfo.renderPass = g_hRenderPass;
+ renderPassBeginInfo.framebuffer = g_Framebuffers[imageIndex];
+ renderPassBeginInfo.renderArea.offset.x = 0;
+ renderPassBeginInfo.renderArea.offset.y = 0;
+ renderPassBeginInfo.renderArea.extent = g_Extent;
+ renderPassBeginInfo.clearValueCount = (uint32_t)_countof(clearValues);
+ renderPassBeginInfo.pClearValues = clearValues;
+ vkCmdBeginRenderPass(hCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ vkCmdBindPipeline(
+ hCommandBuffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ g_hPipeline);
+
+ mathfu::mat4 view = mathfu::mat4::LookAt(
+ mathfu::kZeros3f,
+ mathfu::vec3(0.f, -2.f, 4.f),
+ mathfu::kAxisY3f);
+ mathfu::mat4 proj = mathfu::mat4::Perspective(
+ 1.0471975511966f, // 60 degrees
+ (float)g_Extent.width / (float)g_Extent.height,
+ 0.1f,
+ 1000.f,
+ -1.f);
+ //proj[1][1] *= -1.f;
+ mathfu::mat4 viewProj = proj * view;
+
+ vkCmdBindDescriptorSets(
+ hCommandBuffer,
+ VK_PIPELINE_BIND_POINT_GRAPHICS,
+ g_hPipelineLayout,
+ 0,
+ 1,
+ &g_hDescriptorSet,
+ 0,
+ nullptr);
+
+ float rotationAngle = (float)GetTickCount() * 0.001f * (float)M_PI * 0.2f;
+ mathfu::mat3 model_3 = mathfu::mat3::RotationY(rotationAngle);
+ mathfu::mat4 model_4 = mathfu::mat4(
+ model_3(0, 0), model_3(0, 1), model_3(0, 2), 0.f,
+ model_3(1, 0), model_3(1, 1), model_3(1, 2), 0.f,
+ model_3(2, 0), model_3(2, 1), model_3(2, 2), 0.f,
+ 0.f, 0.f, 0.f, 1.f);
+ mathfu::mat4 modelViewProj = viewProj * model_4;
+
+ UniformBufferObject ubo = {};
+ modelViewProj.Pack(ubo.ModelViewProj);
+ vkCmdPushConstants(hCommandBuffer, g_hPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(UniformBufferObject), &ubo);
+
+ VkBuffer vertexBuffers[] = { g_hVertexBuffer };
+ VkDeviceSize offsets[] = { 0 };
+ vkCmdBindVertexBuffers(hCommandBuffer, 0, 1, vertexBuffers, offsets);
+
+ vkCmdBindIndexBuffer(hCommandBuffer, g_hIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
+
+ vkCmdDrawIndexed(hCommandBuffer, g_IndexCount, 1, 0, 0, 0);
+
+ vkCmdEndRenderPass(hCommandBuffer);
+
+ vkEndCommandBuffer(hCommandBuffer);
+
+ // Submit command buffer
+
+ VkSemaphore submitWaitSemaphores[] = { g_hImageAvailableSemaphore };
+ VkPipelineStageFlags submitWaitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
+ VkSemaphore submitSignalSemaphores[] = { g_hRenderFinishedSemaphore };
+ VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pWaitSemaphores = submitWaitSemaphores;
+ submitInfo.pWaitDstStageMask = submitWaitStages;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &hCommandBuffer;
+ submitInfo.signalSemaphoreCount = _countof(submitSignalSemaphores);
+ submitInfo.pSignalSemaphores = submitSignalSemaphores;
+ ERR_GUARD_VULKAN( vkQueueSubmit(g_hGraphicsQueue, 1, &submitInfo, hCommandBufferExecutedFence) );
+
+ VkSemaphore presentWaitSemaphores[] = { g_hRenderFinishedSemaphore };
+
+ VkSwapchainKHR swapchains[] = { g_hSwapchain };
+ VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
+ presentInfo.waitSemaphoreCount = _countof(presentWaitSemaphores);
+ presentInfo.pWaitSemaphores = presentWaitSemaphores;
+ presentInfo.swapchainCount = 1;
+ presentInfo.pSwapchains = swapchains;
+ presentInfo.pImageIndices = &imageIndex;
+ presentInfo.pResults = nullptr;
+ res = vkQueuePresentKHR(g_hPresentQueue, &presentInfo);
+ if(res == VK_ERROR_OUT_OF_DATE_KHR)
+ {
+ RecreateSwapChain();
+ }
+ else
+ ERR_GUARD_VULKAN(res);
+}
+
+static void HandlePossibleSizeChange()
+{
+ RECT clientRect;
+ GetClientRect(g_hWnd, &clientRect);
+ LONG newSizeX = clientRect.right - clientRect.left;
+ LONG newSizeY = clientRect.bottom - clientRect.top;
+ if((newSizeX > 0) &&
+ (newSizeY > 0) &&
+ ((newSizeX != g_SizeX) || (newSizeY != g_SizeY)))
+ {
+ g_SizeX = newSizeX;
+ g_SizeY = newSizeY;
+
+ RecreateSwapChain();
+ }
+}
+
+static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg)
+ {
+ case WM_CREATE:
+ // This is intentionally assigned here because we are now inside CreateWindow, before it returns.
+ g_hWnd = hWnd;
+ InitializeApplication();
+ PrintAllocatorStats();
+ return 0;
+
+ case WM_DESTROY:
+ FinalizeApplication();
+ PostQuitMessage(0);
+ return 0;
+
+ // This prevents app from freezing when left Alt is pressed
+ // (which normally enters modal menu loop).
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ return 0;
+
+ case WM_SIZE:
+ if((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))
+ HandlePossibleSizeChange();
+ return 0;
+
+ case WM_EXITSIZEMOVE:
+ HandlePossibleSizeChange();
+ return 0;
+
+ case WM_KEYDOWN:
+ if(wParam == VK_ESCAPE)
+ PostMessage(hWnd, WM_CLOSE, 0, 0);
+ return 0;
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+int main()
+{
+ g_hAppInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+ WNDCLASSEX wndClassDesc = { sizeof(WNDCLASSEX) };
+ wndClassDesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
+ wndClassDesc.hbrBackground = NULL;
+ wndClassDesc.hCursor = LoadCursor(NULL, IDC_CROSS);
+ wndClassDesc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wndClassDesc.hInstance = g_hAppInstance;
+ wndClassDesc.lpfnWndProc = WndProc;
+ wndClassDesc.lpszClassName = WINDOW_CLASS_NAME;
+
+ const ATOM hWndClass = RegisterClassEx(&wndClassDesc);
+ assert(hWndClass);
+
+ const DWORD style = WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
+ const DWORD exStyle = 0;
+
+ RECT rect = { 0, 0, g_SizeX, g_SizeY };
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+
+ HWND hWnd = CreateWindowEx(
+ exStyle, WINDOW_CLASS_NAME, APP_TITLE_W, style,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, g_hAppInstance, NULL);
+
+ MSG msg;
+ for(;;)
+ {
+ if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if(msg.message == WM_QUIT)
+ break;
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ if(g_hDevice != VK_NULL_HANDLE)
+ DrawFrame();
+ }
+
+ return 0;
+}