|
|
@ -1,57 +1,101 @@ |
|
|
|
#define GLFW_INCLUDE_VULKAN |
|
|
|
#include <GLFW/glfw3.h> |
|
|
|
|
|
|
|
#include <heck/log.h> |
|
|
|
#include <iostream> |
|
|
|
#include <stdexcept> |
|
|
|
#include <cstdlib> |
|
|
|
#include <vector> |
|
|
|
#include <optional> |
|
|
|
#include <set> |
|
|
|
#include <fstream> |
|
|
|
|
|
|
|
|
|
|
|
const uint32_t WIDTH = 800; |
|
|
|
const uint32_t HEIGHT = 600; |
|
|
|
|
|
|
|
class HelloTriangleApplication { |
|
|
|
public: |
|
|
|
void run() |
|
|
|
namespace Heck { |
|
|
|
struct VulkanBase { |
|
|
|
VulkanBase(GLFWwindow& window) : window(window) |
|
|
|
{ |
|
|
|
initWindow(); |
|
|
|
initVulkan(); |
|
|
|
mainLoop(); |
|
|
|
cleanup(); |
|
|
|
deviceExtensions.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
|
|
|
} |
|
|
|
VulkanBase() = delete; |
|
|
|
VulkanBase(VulkanBase& other) = delete; |
|
|
|
VulkanBase(VulkanBase&& other) = delete; |
|
|
|
VulkanBase& operator=(VulkanBase& rhs) = delete; |
|
|
|
VulkanBase& operator=(VulkanBase&& rhs) = delete; |
|
|
|
~VulkanBase() = default; |
|
|
|
|
|
|
|
private: |
|
|
|
GLFWwindow* window; |
|
|
|
VkInstance instance; |
|
|
|
struct QueueFamilyIndices { |
|
|
|
std::optional<uint32_t> graphicsFamily; |
|
|
|
std::optional<uint32_t> presentFamily; |
|
|
|
|
|
|
|
void initWindow() |
|
|
|
bool isComplete() |
|
|
|
{ |
|
|
|
glfwInit(); |
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); |
|
|
|
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
return graphicsFamily.has_value() && presentFamily.has_value(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
void initVulkan() |
|
|
|
{ |
|
|
|
createInstance(); |
|
|
|
} |
|
|
|
struct SwapChainSupportDetails { |
|
|
|
VkSurfaceCapabilitiesKHR capabilities; |
|
|
|
std::vector<VkSurfaceFormatKHR> formats; |
|
|
|
std::vector<VkPresentModeKHR> presentModes; |
|
|
|
}; |
|
|
|
|
|
|
|
void mainLoop() |
|
|
|
{ |
|
|
|
while (!glfwWindowShouldClose(window)) { |
|
|
|
glfwPollEvents(); |
|
|
|
} |
|
|
|
} |
|
|
|
std::vector<const char*> deviceExtensions{}; |
|
|
|
|
|
|
|
void cleanup() |
|
|
|
GLFWwindow& window; |
|
|
|
VkInstance instance{}; |
|
|
|
VkSurfaceKHR surface{}; |
|
|
|
|
|
|
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; |
|
|
|
VkDevice device{}; |
|
|
|
|
|
|
|
VkQueue graphicsQueue{}; |
|
|
|
VkQueue presentQueue{}; |
|
|
|
|
|
|
|
VkSwapchainKHR swapChain{}; |
|
|
|
std::vector<VkImage> swapChainImages{}; |
|
|
|
VkFormat swapChainImageFormat{}; |
|
|
|
VkExtent2D swapChainExtent{}; |
|
|
|
std::vector<VkImageView> swapChainImageViews{}; |
|
|
|
|
|
|
|
VkPipelineLayout pipelineLayout{}; |
|
|
|
VkRenderPass renderPass{}; |
|
|
|
VkPipeline graphicsPipeline{}; |
|
|
|
std::vector<VkFramebuffer> swapChainFramebuffers{}; |
|
|
|
|
|
|
|
VkCommandPool commandPool{}; |
|
|
|
VkCommandBuffer commandBuffer{}; |
|
|
|
|
|
|
|
VkSemaphore imageAvailableSemaphore{}; |
|
|
|
VkSemaphore renderFinishedSemaphore{}; |
|
|
|
VkFence inFlightFence{}; |
|
|
|
|
|
|
|
|
|
|
|
void init() |
|
|
|
{ |
|
|
|
vkDestroyInstance(instance, nullptr); |
|
|
|
glfwDestroyWindow(window); |
|
|
|
glfwTerminate(); |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
createInstance(); |
|
|
|
createSurface(); |
|
|
|
pickPhysicalDevice(); |
|
|
|
createLogicalDevice(); |
|
|
|
createSwapChain(); |
|
|
|
createImageViews(); |
|
|
|
createRenderPass(); |
|
|
|
createGraphicsPipeline(); |
|
|
|
createFramebuffers(); |
|
|
|
createCommandPool(); |
|
|
|
createCommandBuffer(); |
|
|
|
createSyncObjects(); |
|
|
|
} |
|
|
|
|
|
|
|
void createInstance() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
VkInstanceCreateInfo createInfo{}; |
|
|
|
|
|
|
|
VkApplicationInfo appInfo{}; |
|
|
|
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
|
|
|
appInfo.pApplicationName = "Hello Triangle"; |
|
|
@ -59,10 +103,9 @@ private: |
|
|
|
appInfo.pEngineName = "No Engine"; |
|
|
|
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); |
|
|
|
appInfo.apiVersion = VK_API_VERSION_1_0; |
|
|
|
createInfo.pApplicationInfo = &appInfo; |
|
|
|
|
|
|
|
VkInstanceCreateInfo createInfo{}; |
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
|
|
|
createInfo.pApplicationInfo = &appInfo; |
|
|
|
|
|
|
|
uint32_t glfwExtensionCount = 0; |
|
|
|
const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); |
|
|
@ -84,18 +127,775 @@ private: |
|
|
|
throw std::runtime_error("failed to create instance!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createSurface() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
if (glfwCreateWindowSurface(instance, &window, nullptr, &surface) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create window surface!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
QueueFamilyIndices indices{}; |
|
|
|
|
|
|
|
uint32_t queueFamilyCount = 0; |
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); |
|
|
|
|
|
|
|
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); |
|
|
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); |
|
|
|
|
|
|
|
int i = 0; |
|
|
|
for (const auto& queueFamily : queueFamilies) { |
|
|
|
HECK_LOG_INFO("Checking queue family nr:" << 0); |
|
|
|
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
|
|
|
indices.graphicsFamily = i; |
|
|
|
} |
|
|
|
|
|
|
|
VkBool32 presentSupport = false; |
|
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); |
|
|
|
if (presentSupport) { |
|
|
|
indices.presentFamily = i; |
|
|
|
} |
|
|
|
|
|
|
|
if (indices.isComplete()) { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
i++; |
|
|
|
} |
|
|
|
|
|
|
|
return indices; |
|
|
|
} |
|
|
|
|
|
|
|
void pickPhysicalDevice() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
uint32_t deviceCount = 0; |
|
|
|
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); |
|
|
|
|
|
|
|
HECK_LOG_INFO("devices found:" << deviceCount); |
|
|
|
if (deviceCount == 0) { |
|
|
|
throw std::runtime_error("failed to find GPUs with Vulkan support!"); |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<VkPhysicalDevice> devices{ deviceCount }; |
|
|
|
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); |
|
|
|
|
|
|
|
for (const VkPhysicalDevice& device : devices) { |
|
|
|
if (isDeviceSuitable(device)) { |
|
|
|
physicalDevice = device; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (physicalDevice == VK_NULL_HANDLE) { |
|
|
|
throw std::runtime_error("failed to find a suitable GPU!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createLogicalDevice() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
|
|
|
|
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; |
|
|
|
std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), |
|
|
|
indices.presentFamily.value() }; |
|
|
|
|
|
|
|
float queuePriority = 1.0f; |
|
|
|
for (uint32_t queueFamily : uniqueQueueFamilies) { |
|
|
|
VkDeviceQueueCreateInfo queueCreateInfo{}; |
|
|
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
|
|
|
queueCreateInfo.queueFamilyIndex = queueFamily; |
|
|
|
queueCreateInfo.queueCount = 1; |
|
|
|
queueCreateInfo.pQueuePriorities = &queuePriority; |
|
|
|
queueCreateInfos.push_back(queueCreateInfo); |
|
|
|
} |
|
|
|
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures{}; |
|
|
|
|
|
|
|
VkDeviceCreateInfo createInfo{}; |
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
|
|
|
|
|
|
|
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); |
|
|
|
createInfo.pQueueCreateInfos = queueCreateInfos.data(); |
|
|
|
|
|
|
|
createInfo.pEnabledFeatures = &deviceFeatures; |
|
|
|
|
|
|
|
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size()); |
|
|
|
createInfo.ppEnabledExtensionNames = deviceExtensions.data(); |
|
|
|
|
|
|
|
createInfo.enabledLayerCount = 0; |
|
|
|
|
|
|
|
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create logical device!"); |
|
|
|
} |
|
|
|
|
|
|
|
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); |
|
|
|
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); |
|
|
|
} |
|
|
|
|
|
|
|
bool isDeviceSuitable(VkPhysicalDevice device) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(device); |
|
|
|
bool ret = false; |
|
|
|
QueueFamilyIndices indices = findQueueFamilies(device); |
|
|
|
|
|
|
|
bool extensionsSupported = checkDeviceExtensionSupport(device); |
|
|
|
bool swapChainAdequate = false; |
|
|
|
if (extensionsSupported) { |
|
|
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); |
|
|
|
swapChainAdequate = !swapChainSupport.formats.empty() && |
|
|
|
!swapChainSupport.presentModes.empty(); |
|
|
|
} |
|
|
|
|
|
|
|
if (indices.isComplete() && extensionsSupported && swapChainAdequate) { |
|
|
|
HECK_LOG_INFO("device is suitable"); |
|
|
|
ret = true; |
|
|
|
} else { |
|
|
|
HECK_LOG_INFO("device not suitable"); |
|
|
|
ret = false; |
|
|
|
} |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void createSwapChain() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); |
|
|
|
|
|
|
|
VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); |
|
|
|
VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); |
|
|
|
VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); |
|
|
|
|
|
|
|
uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; |
|
|
|
if (swapChainSupport.capabilities.maxImageCount > 0 && |
|
|
|
imageCount > swapChainSupport.capabilities.maxImageCount) { |
|
|
|
imageCount = swapChainSupport.capabilities.maxImageCount; |
|
|
|
} |
|
|
|
|
|
|
|
VkSwapchainCreateInfoKHR createInfo{}; |
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; |
|
|
|
createInfo.surface = surface; |
|
|
|
|
|
|
|
createInfo.minImageCount = imageCount; |
|
|
|
createInfo.imageFormat = surfaceFormat.format; |
|
|
|
createInfo.imageColorSpace = surfaceFormat.colorSpace; |
|
|
|
createInfo.imageExtent = extent; |
|
|
|
createInfo.imageArrayLayers = 1; |
|
|
|
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
|
|
|
|
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
|
uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), |
|
|
|
indices.presentFamily.value() }; |
|
|
|
|
|
|
|
if (indices.graphicsFamily != indices.presentFamily) { |
|
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; |
|
|
|
createInfo.queueFamilyIndexCount = 2; |
|
|
|
createInfo.pQueueFamilyIndices = queueFamilyIndices; |
|
|
|
} else { |
|
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
|
|
|
} |
|
|
|
|
|
|
|
createInfo.preTransform = swapChainSupport.capabilities.currentTransform; |
|
|
|
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
|
|
|
createInfo.presentMode = presentMode; |
|
|
|
createInfo.clipped = VK_TRUE; |
|
|
|
|
|
|
|
createInfo.oldSwapchain = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create swap chain!"); |
|
|
|
} |
|
|
|
|
|
|
|
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); |
|
|
|
swapChainImages.resize(imageCount); |
|
|
|
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); |
|
|
|
|
|
|
|
swapChainImageFormat = surfaceFormat.format; |
|
|
|
swapChainExtent = extent; |
|
|
|
} |
|
|
|
|
|
|
|
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
for (const auto& availableFormat : availableFormats) { |
|
|
|
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && |
|
|
|
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { |
|
|
|
return availableFormat; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return availableFormats[0]; |
|
|
|
} |
|
|
|
|
|
|
|
VkPresentModeKHR chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
for (const auto& availablePresentMode : availablePresentModes) { |
|
|
|
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { |
|
|
|
return availablePresentMode; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return VK_PRESENT_MODE_FIFO_KHR; |
|
|
|
} |
|
|
|
|
|
|
|
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) { |
|
|
|
return capabilities.currentExtent; |
|
|
|
} else { |
|
|
|
int width; |
|
|
|
int height; |
|
|
|
glfwGetFramebufferSize(&window, &width, &height); |
|
|
|
|
|
|
|
VkExtent2D actualExtent = { static_cast<uint32_t>(width), |
|
|
|
static_cast<uint32_t>(height) }; |
|
|
|
|
|
|
|
actualExtent.width = std::clamp( |
|
|
|
actualExtent.width, |
|
|
|
capabilities.minImageExtent.width, |
|
|
|
capabilities.maxImageExtent.width); |
|
|
|
actualExtent.height = std::clamp( |
|
|
|
actualExtent.height, |
|
|
|
capabilities.minImageExtent.height, |
|
|
|
capabilities.maxImageExtent.height); |
|
|
|
|
|
|
|
return actualExtent; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool checkDeviceExtensionSupport(VkPhysicalDevice device) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
uint32_t extensionCount; |
|
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); |
|
|
|
|
|
|
|
std::vector<VkExtensionProperties> availableExtensions(extensionCount); |
|
|
|
vkEnumerateDeviceExtensionProperties( |
|
|
|
device, |
|
|
|
nullptr, |
|
|
|
&extensionCount, |
|
|
|
availableExtensions.data()); |
|
|
|
|
|
|
|
std::set<std::string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); |
|
|
|
|
|
|
|
for (const auto& extension : availableExtensions) { |
|
|
|
requiredExtensions.erase(extension.extensionName); |
|
|
|
} |
|
|
|
|
|
|
|
return requiredExtensions.empty(); |
|
|
|
} |
|
|
|
|
|
|
|
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
SwapChainSupportDetails details; |
|
|
|
|
|
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); |
|
|
|
|
|
|
|
uint32_t formatCount; |
|
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); |
|
|
|
|
|
|
|
if (formatCount != 0) { |
|
|
|
details.formats.resize(formatCount); |
|
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR( |
|
|
|
device, |
|
|
|
surface, |
|
|
|
&formatCount, |
|
|
|
details.formats.data()); |
|
|
|
} |
|
|
|
|
|
|
|
uint32_t presentModeCount; |
|
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); |
|
|
|
|
|
|
|
if (presentModeCount != 0) { |
|
|
|
details.presentModes.resize(presentModeCount); |
|
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR( |
|
|
|
device, |
|
|
|
surface, |
|
|
|
&presentModeCount, |
|
|
|
details.presentModes.data()); |
|
|
|
} |
|
|
|
|
|
|
|
return details; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void createImageViews() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
swapChainImageViews.resize(swapChainImages.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < swapChainImages.size(); i++) { |
|
|
|
HECK_LOG_INFO("Creating swap chain image nr: " << i); |
|
|
|
VkImageViewCreateInfo createInfo{}; |
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; |
|
|
|
createInfo.image = swapChainImages[i]; |
|
|
|
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
|
|
|
createInfo.format = swapChainImageFormat; |
|
|
|
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; |
|
|
|
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
|
|
|
createInfo.subresourceRange.baseMipLevel = 0; |
|
|
|
createInfo.subresourceRange.levelCount = 1; |
|
|
|
createInfo.subresourceRange.baseArrayLayer = 0; |
|
|
|
createInfo.subresourceRange.layerCount = 1; |
|
|
|
|
|
|
|
if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != |
|
|
|
VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create image views!"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createGraphicsPipeline() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
auto vertShaderCode = readFile("../shaders/base.vert.spv"); |
|
|
|
auto fragShaderCode = readFile("../shaders/base.frag.spv"); |
|
|
|
|
|
|
|
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); |
|
|
|
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); |
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; |
|
|
|
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
|
|
|
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; |
|
|
|
vertShaderStageInfo.module = vertShaderModule; |
|
|
|
vertShaderStageInfo.pName = "main"; |
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; |
|
|
|
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
|
|
|
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
|
|
|
fragShaderStageInfo.module = fragShaderModule; |
|
|
|
fragShaderStageInfo.pName = "main"; |
|
|
|
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, |
|
|
|
fragShaderStageInfo }; |
|
|
|
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; |
|
|
|
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; |
|
|
|
vertexInputInfo.vertexBindingDescriptionCount = 0; |
|
|
|
vertexInputInfo.vertexAttributeDescriptionCount = 0; |
|
|
|
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; |
|
|
|
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; |
|
|
|
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
|
|
|
inputAssembly.primitiveRestartEnable = VK_FALSE; |
|
|
|
|
|
|
|
VkPipelineViewportStateCreateInfo viewportState{}; |
|
|
|
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; |
|
|
|
viewportState.viewportCount = 1; |
|
|
|
viewportState.scissorCount = 1; |
|
|
|
|
|
|
|
VkPipelineRasterizationStateCreateInfo rasterizer{}; |
|
|
|
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; |
|
|
|
rasterizer.depthClampEnable = VK_FALSE; |
|
|
|
rasterizer.rasterizerDiscardEnable = VK_FALSE; |
|
|
|
rasterizer.polygonMode = VK_POLYGON_MODE_FILL; |
|
|
|
rasterizer.lineWidth = 1.0f; |
|
|
|
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; |
|
|
|
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; |
|
|
|
rasterizer.depthBiasEnable = VK_FALSE; |
|
|
|
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisampling{}; |
|
|
|
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; |
|
|
|
multisampling.sampleShadingEnable = VK_FALSE; |
|
|
|
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
|
|
|
|
VkPipelineColorBlendAttachmentState colorBlendAttachment{}; |
|
|
|
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | |
|
|
|
VK_COLOR_COMPONENT_G_BIT | |
|
|
|
VK_COLOR_COMPONENT_B_BIT | |
|
|
|
VK_COLOR_COMPONENT_A_BIT; |
|
|
|
colorBlendAttachment.blendEnable = VK_FALSE; |
|
|
|
|
|
|
|
VkPipelineColorBlendStateCreateInfo colorBlending{}; |
|
|
|
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; |
|
|
|
colorBlending.logicOpEnable = VK_FALSE; |
|
|
|
colorBlending.logicOp = VK_LOGIC_OP_COPY; |
|
|
|
colorBlending.attachmentCount = 1; |
|
|
|
colorBlending.pAttachments = &colorBlendAttachment; |
|
|
|
colorBlending.blendConstants[0] = 0.0f; |
|
|
|
colorBlending.blendConstants[1] = 0.0f; |
|
|
|
colorBlending.blendConstants[2] = 0.0f; |
|
|
|
colorBlending.blendConstants[3] = 0.0f; |
|
|
|
|
|
|
|
std::vector<VkDynamicState> dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, |
|
|
|
VK_DYNAMIC_STATE_SCISSOR }; |
|
|
|
VkPipelineDynamicStateCreateInfo dynamicState{}; |
|
|
|
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
|
|
|
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()); |
|
|
|
dynamicState.pDynamicStates = dynamicStates.data(); |
|
|
|
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; |
|
|
|
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; |
|
|
|
pipelineLayoutInfo.setLayoutCount = 0; |
|
|
|
pipelineLayoutInfo.pushConstantRangeCount = 0; |
|
|
|
|
|
|
|
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != |
|
|
|
VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create pipeline layout!"); |
|
|
|
} |
|
|
|
|
|
|
|
VkGraphicsPipelineCreateInfo pipelineInfo{}; |
|
|
|
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; |
|
|
|
pipelineInfo.stageCount = 2; |
|
|
|
pipelineInfo.pStages = shaderStages; |
|
|
|
pipelineInfo.pVertexInputState = &vertexInputInfo; |
|
|
|
pipelineInfo.pInputAssemblyState = &inputAssembly; |
|
|
|
pipelineInfo.pViewportState = &viewportState; |
|
|
|
pipelineInfo.pRasterizationState = &rasterizer; |
|
|
|
pipelineInfo.pMultisampleState = &multisampling; |
|
|
|
pipelineInfo.pColorBlendState = &colorBlending; |
|
|
|
pipelineInfo.pDynamicState = &dynamicState; |
|
|
|
pipelineInfo.layout = pipelineLayout; |
|
|
|
pipelineInfo.renderPass = renderPass; |
|
|
|
pipelineInfo.subpass = 0; |
|
|
|
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
if (vkCreateGraphicsPipelines( |
|
|
|
device, |
|
|
|
VK_NULL_HANDLE, |
|
|
|
1, |
|
|
|
&pipelineInfo, |
|
|
|
nullptr, |
|
|
|
&graphicsPipeline) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create graphics pipeline!"); |
|
|
|
} |
|
|
|
|
|
|
|
vkDestroyShaderModule(device, fragShaderModule, nullptr); |
|
|
|
vkDestroyShaderModule(device, vertShaderModule, nullptr); |
|
|
|
} |
|
|
|
|
|
|
|
VkShaderModule createShaderModule(const std::vector<char>& code) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
VkShaderModuleCreateInfo createInfo{}; |
|
|
|
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; |
|
|
|
createInfo.codeSize = code.size(); |
|
|
|
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data()); |
|
|
|
|
|
|
|
VkShaderModule shaderModule; |
|
|
|
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create shader module!"); |
|
|
|
} |
|
|
|
|
|
|
|
return shaderModule; |
|
|
|
} |
|
|
|
|
|
|
|
void createRenderPass() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
VkAttachmentDescription colorAttachment{}; |
|
|
|
colorAttachment.format = swapChainImageFormat; |
|
|
|
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; |
|
|
|
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
|
|
|
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
|
|
|
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
|
|
|
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
|
|
|
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
|
|
|
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
|
|
|
|
|
|
|
VkAttachmentReference colorAttachmentRef{}; |
|
|
|
colorAttachmentRef.attachment = 0; |
|
|
|
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
|
|
|
|
|
|
|
VkSubpassDescription subpass{}; |
|
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
|
|
|
subpass.colorAttachmentCount = 1; |
|
|
|
subpass.pColorAttachments = &colorAttachmentRef; |
|
|
|
|
|
|
|
VkSubpassDependency dependency{}; |
|
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; |
|
|
|
dependency.dstSubpass = 0; |
|
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
|
|
|
dependency.srcAccessMask = 0; |
|
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
|
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
|
|
|
|
|
|
|
VkRenderPassCreateInfo renderPassInfo{}; |
|
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
|
|
|
renderPassInfo.attachmentCount = 1; |
|
|
|
renderPassInfo.pAttachments = &colorAttachment; |
|
|
|
renderPassInfo.subpassCount = 1; |
|
|
|
renderPassInfo.pSubpasses = &subpass; |
|
|
|
renderPassInfo.dependencyCount = 1; |
|
|
|
renderPassInfo.pDependencies = &dependency; |
|
|
|
|
|
|
|
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create render pass!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createFramebuffers() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
swapChainFramebuffers.resize(swapChainImageViews.size()); |
|
|
|
|
|
|
|
for (size_t i = 0; i < swapChainImageViews.size(); i++) { |
|
|
|
VkImageView attachments[] = { swapChainImageViews[i] }; |
|
|
|
|
|
|
|
VkFramebufferCreateInfo framebufferInfo{}; |
|
|
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; |
|
|
|
framebufferInfo.renderPass = renderPass; |
|
|
|
framebufferInfo.attachmentCount = 1; |
|
|
|
framebufferInfo.pAttachments = attachments; |
|
|
|
framebufferInfo.width = swapChainExtent.width; |
|
|
|
framebufferInfo.height = swapChainExtent.height; |
|
|
|
framebufferInfo.layers = 1; |
|
|
|
|
|
|
|
if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != |
|
|
|
VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create framebuffer!"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createCommandPool() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); |
|
|
|
|
|
|
|
VkCommandPoolCreateInfo poolInfo{}; |
|
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
|
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
|
|
|
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); |
|
|
|
|
|
|
|
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create command pool!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createCommandBuffer() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
VkCommandBufferAllocateInfo allocInfo{}; |
|
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
|
|
allocInfo.commandPool = commandPool; |
|
|
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
|
|
|
allocInfo.commandBufferCount = 1; |
|
|
|
|
|
|
|
if (vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to allocate command buffers!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) |
|
|
|
{ |
|
|
|
VkCommandBufferBeginInfo beginInfo{}; |
|
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
|
|
|
|
|
|
|
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to begin recording command buffer!"); |
|
|
|
} |
|
|
|
|
|
|
|
VkRenderPassBeginInfo renderPassInfo{}; |
|
|
|
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
|
|
|
renderPassInfo.renderPass = renderPass; |
|
|
|
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; |
|
|
|
renderPassInfo.renderArea.offset = { 0, 0 }; |
|
|
|
renderPassInfo.renderArea.extent = swapChainExtent; |
|
|
|
|
|
|
|
VkClearValue clearColor = { { { 0.0f, 0.0f, 0.0f, 1.0f } } }; |
|
|
|
renderPassInfo.clearValueCount = 1; |
|
|
|
renderPassInfo.pClearValues = &clearColor; |
|
|
|
|
|
|
|
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); |
|
|
|
|
|
|
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); |
|
|
|
|
|
|
|
VkViewport viewport{}; |
|
|
|
viewport.x = 0.0f; |
|
|
|
viewport.y = 0.0f; |
|
|
|
viewport.width = static_cast<float>(swapChainExtent.width); |
|
|
|
viewport.height = static_cast<float>(swapChainExtent.height); |
|
|
|
viewport.minDepth = 0.0f; |
|
|
|
viewport.maxDepth = 1.0f; |
|
|
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport); |
|
|
|
|
|
|
|
VkRect2D scissor{}; |
|
|
|
scissor.offset = { 0, 0 }; |
|
|
|
scissor.extent = swapChainExtent; |
|
|
|
vkCmdSetScissor(commandBuffer, 0, 1, &scissor); |
|
|
|
|
|
|
|
vkCmdDraw(commandBuffer, 3, 1, 0, 0); |
|
|
|
|
|
|
|
vkCmdEndRenderPass(commandBuffer); |
|
|
|
|
|
|
|
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to record command buffer!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void createSyncObjects() |
|
|
|
{ |
|
|
|
VkSemaphoreCreateInfo semaphoreInfo{}; |
|
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
|
|
|
|
|
|
|
VkFenceCreateInfo fenceInfo{}; |
|
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
|
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
|
|
|
|
|
|
|
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != |
|
|
|
VK_SUCCESS || |
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != |
|
|
|
VK_SUCCESS || |
|
|
|
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFence) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to create synchronization objects for a frame!"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void drawFrame() |
|
|
|
{ |
|
|
|
vkWaitForFences(device, 1, &inFlightFence, VK_TRUE, UINT64_MAX); |
|
|
|
vkResetFences(device, 1, &inFlightFence); |
|
|
|
|
|
|
|
uint32_t imageIndex; |
|
|
|
vkAcquireNextImageKHR( |
|
|
|
device, |
|
|
|
swapChain, |
|
|
|
UINT64_MAX, |
|
|
|
imageAvailableSemaphore, |
|
|
|
VK_NULL_HANDLE, |
|
|
|
&imageIndex); |
|
|
|
|
|
|
|
vkResetCommandBuffer(commandBuffer, /*VkCommandBufferResetFlagBits*/ 0); |
|
|
|
recordCommandBuffer(commandBuffer, imageIndex); |
|
|
|
|
|
|
|
VkSubmitInfo submitInfo{}; |
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
|
|
|
|
|
|
|
VkSemaphore waitSemaphores[] = { imageAvailableSemaphore }; |
|
|
|
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; |
|
|
|
submitInfo.waitSemaphoreCount = 1; |
|
|
|
submitInfo.pWaitSemaphores = waitSemaphores; |
|
|
|
submitInfo.pWaitDstStageMask = waitStages; |
|
|
|
|
|
|
|
submitInfo.commandBufferCount = 1; |
|
|
|
submitInfo.pCommandBuffers = &commandBuffer; |
|
|
|
|
|
|
|
VkSemaphore signalSemaphores[] = { renderFinishedSemaphore }; |
|
|
|
submitInfo.signalSemaphoreCount = 1; |
|
|
|
submitInfo.pSignalSemaphores = signalSemaphores; |
|
|
|
|
|
|
|
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFence) != VK_SUCCESS) { |
|
|
|
throw std::runtime_error("failed to submit draw command buffer!"); |
|
|
|
} |
|
|
|
|
|
|
|
VkPresentInfoKHR presentInfo{}; |
|
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
|
|
|
|
|
|
|
presentInfo.waitSemaphoreCount = 1; |
|
|
|
presentInfo.pWaitSemaphores = signalSemaphores; |
|
|
|
|
|
|
|
VkSwapchainKHR swapChains[] = { swapChain }; |
|
|
|
presentInfo.swapchainCount = 1; |
|
|
|
presentInfo.pSwapchains = swapChains; |
|
|
|
|
|
|
|
presentInfo.pImageIndices = &imageIndex; |
|
|
|
|
|
|
|
vkQueuePresentKHR(presentQueue, &presentInfo); |
|
|
|
} |
|
|
|
|
|
|
|
void cleanup() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); |
|
|
|
vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); |
|
|
|
vkDestroyFence(device, inFlightFence, nullptr); |
|
|
|
|
|
|
|
vkDestroyCommandPool(device, commandPool, nullptr); |
|
|
|
|
|
|
|
for (auto framebuffer : swapChainFramebuffers) { |
|
|
|
vkDestroyFramebuffer(device, framebuffer, nullptr); |
|
|
|
} |
|
|
|
|
|
|
|
vkDestroyPipeline(device, graphicsPipeline, nullptr); |
|
|
|
vkDestroyRenderPass(device, renderPass, nullptr); |
|
|
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); |
|
|
|
|
|
|
|
for (auto imageView : swapChainImageViews) { |
|
|
|
vkDestroyImageView(device, imageView, nullptr); |
|
|
|
} |
|
|
|
|
|
|
|
vkDestroySwapchainKHR(device, swapChain, nullptr); |
|
|
|
vkDestroyDevice(device, nullptr); |
|
|
|
vkDestroySurfaceKHR(instance, surface, nullptr); |
|
|
|
vkDestroyInstance(instance, nullptr); |
|
|
|
} |
|
|
|
|
|
|
|
static std::vector<char> readFile(const std::string& filename) |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
std::ifstream file(filename, std::ios::ate | std::ios::binary); |
|
|
|
if (!file.is_open()) { |
|
|
|
throw std::runtime_error("failed to open file!"); |
|
|
|
} |
|
|
|
size_t fileSize = (size_t)file.tellg(); |
|
|
|
std::vector<char> buffer(fileSize); |
|
|
|
file.seekg(0); |
|
|
|
file.read(buffer.data(), fileSize); |
|
|
|
file.close(); |
|
|
|
|
|
|
|
return buffer; |
|
|
|
} |
|
|
|
}; |
|
|
|
} // namespace Heck
|
|
|
|
|
|
|
|
int main() |
|
|
|
|
|
|
|
GLFWwindow* window = nullptr; |
|
|
|
Heck::VulkanBase *vb = nullptr; |
|
|
|
|
|
|
|
void init() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
glfwInit(); |
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); |
|
|
|
window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); |
|
|
|
} |
|
|
|
|
|
|
|
void mainLoop() |
|
|
|
{ |
|
|
|
HelloTriangleApplication app{}; |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
while (!glfwWindowShouldClose(window)) { |
|
|
|
glfwPollEvents(); |
|
|
|
vb->drawFrame(); |
|
|
|
|
|
|
|
try { |
|
|
|
app.run(); |
|
|
|
} catch (const std::exception& e) { |
|
|
|
std::cerr << e.what() << std::endl; |
|
|
|
return EXIT_FAILURE; |
|
|
|
} |
|
|
|
|
|
|
|
vkDeviceWaitIdle(vb->device); |
|
|
|
} |
|
|
|
|
|
|
|
void cleanup() |
|
|
|
{ |
|
|
|
HECK_LOG_INFO(""); |
|
|
|
glfwDestroyWindow(window); |
|
|
|
glfwTerminate(); |
|
|
|
} |
|
|
|
|
|
|
|
int main() |
|
|
|
{ |
|
|
|
Heck::Log::set_level(HECK_LOG_LEVEL_ALL); |
|
|
|
init(); |
|
|
|
|
|
|
|
Heck::VulkanBase _vb{ *window }; |
|
|
|
vb = &_vb; |
|
|
|
vb->init(); |
|
|
|
|
|
|
|
mainLoop(); |
|
|
|
|
|
|
|
vb->cleanup(); |
|
|
|
cleanup(); |
|
|
|
|
|
|
|
return EXIT_SUCCESS; |
|
|
|
} |
|
|
|