1131 lines
No EOL
36 KiB
C++
1131 lines
No EOL
36 KiB
C++
#define GLFW_INCLUDE_VULKAN
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <cassert>
|
|
#include <chrono>
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
using TimePoint = std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>;
|
|
using TimeMs = long;
|
|
|
|
TimeMs timeDeltaMs(const TimePoint& point) {
|
|
auto timeDelta = std::chrono::system_clock::now() - point;
|
|
auto timeDelayMs = std::chrono::duration_cast<std::chrono::milliseconds>(timeDelta).count();
|
|
return timeDelayMs;
|
|
}
|
|
|
|
struct SwapChainSupportDetails {
|
|
VkSurfaceCapabilitiesKHR capabilities{};
|
|
std::vector<VkSurfaceFormatKHR> formats;
|
|
std::vector<VkPresentModeKHR> presentModes;
|
|
};
|
|
|
|
struct Vertex {
|
|
glm::vec2 pos;
|
|
glm::vec3 color;
|
|
};
|
|
|
|
const std::vector<const char *> gDeviceExtensions = {
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
};
|
|
|
|
const std::vector<const char *> gValidationLayers = {
|
|
#ifdef NDEBUG
|
|
#else
|
|
"VK_LAYER_KHRONOS_validation"
|
|
#endif
|
|
};
|
|
|
|
const std::vector<Vertex> vertices = {
|
|
{{0.f, -0.5f}, {1.f, 0.f, 0.f}},
|
|
{{0.5f, 0.5f}, {0.f, 1.f, 0.f}},
|
|
{{-0.5f, 0.5f}, {0.f, 0.f, 1.f}},
|
|
};
|
|
|
|
class Application {
|
|
public:
|
|
void run() {
|
|
initWindow();
|
|
initVulkan();
|
|
mainLoop();
|
|
cleanup();
|
|
}
|
|
|
|
void scheduleWindowResize(int sizeX, int sizeY) {
|
|
mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
|
|
mWindowSizeDirtyFlag = true;
|
|
mWindowFramebufferSize = std::make_pair(sizeX, sizeY);
|
|
assert(!(sizeX <= 0 || sizeY <= 0));
|
|
}
|
|
|
|
private:
|
|
void initWindow() {
|
|
// glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
|
|
glfwInit();
|
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
// glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
|
mWindow = glfwCreateWindow(800, 600, "App", nullptr, nullptr);
|
|
scheduleWindowResize(800, 600);
|
|
|
|
glfwSetWindowUserPointer(mWindow, this);
|
|
glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow* window, int sizeX, int sizeY){
|
|
((Application*) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY);
|
|
});
|
|
}
|
|
|
|
void initVulkan() {
|
|
createInstance();
|
|
createWindowSurface();
|
|
pickPhysicalDevice();
|
|
findPhysicalDeviceQueueFamilies();
|
|
createLogicalDevice();
|
|
getQueues();
|
|
|
|
int width, height;
|
|
glfwGetFramebufferSize(mWindow, &width, &height);
|
|
|
|
createSwapChain(width, height);
|
|
createSwapChainImageViews();
|
|
|
|
createRenderPass();
|
|
createGraphicsPipeline();
|
|
|
|
createSwapChainFramebuffers();
|
|
|
|
createCommandPool();
|
|
createCommandBuffer();
|
|
|
|
createSynchronizationObjects();
|
|
|
|
createVertexBuffer();
|
|
}
|
|
|
|
void mainLoop() {
|
|
while (!glfwWindowShouldClose(mWindow)) {
|
|
glfwPollEvents();
|
|
|
|
if (mWindowSizeDirtyFlag) {
|
|
auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime);
|
|
if (timeDelayMs > mWindowSizeApplyMinDelay) {
|
|
recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second);
|
|
mWindowSizeDirtyFlag = false;
|
|
}
|
|
}
|
|
|
|
drawFrame();
|
|
}
|
|
|
|
vkDeviceWaitIdle(mDevice);
|
|
}
|
|
|
|
void createInstance() {
|
|
if (!checkValidationLayerSupport()) {
|
|
throw std::runtime_error("no required validation layers present");
|
|
}
|
|
|
|
uint32_t glfwExtensionCount = 0;
|
|
const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
|
|
|
VkApplicationInfo appInfo{
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
.pApplicationName = "App",
|
|
.applicationVersion = VK_MAKE_VERSION(0, 0, 0),
|
|
.pEngineName = "no",
|
|
.engineVersion = VK_MAKE_VERSION(0, 0, 0),
|
|
.apiVersion = VK_API_VERSION_1_0,
|
|
};
|
|
|
|
VkInstanceCreateInfo createInfo{
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pApplicationInfo = &appInfo,
|
|
.enabledLayerCount = (uint32_t) gValidationLayers.size(),
|
|
.ppEnabledLayerNames = gValidationLayers.data(),
|
|
.enabledExtensionCount = glfwExtensionCount,
|
|
.ppEnabledExtensionNames = glfwExtensions,
|
|
};
|
|
|
|
if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create instance!");
|
|
}
|
|
}
|
|
|
|
static bool checkValidationLayerSupport() {
|
|
uint32_t layerCount;
|
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
|
|
|
std::vector<VkLayerProperties> availableLayers(layerCount);
|
|
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
|
|
|
|
for (const auto &requestedLayer: gValidationLayers) {
|
|
bool presents = false;
|
|
for (auto &layer: availableLayers) {
|
|
if (strcmp(requestedLayer, layer.layerName) == 0) {
|
|
presents = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!presents) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void pickPhysicalDevice() {
|
|
uint32_t deviceCount = 0;
|
|
vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
|
|
|
|
if (deviceCount == 0) {
|
|
throw std::runtime_error("no gpu with vulkan support");
|
|
}
|
|
|
|
std::vector<VkPhysicalDevice> devices(deviceCount);
|
|
vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
|
|
|
|
for (const auto &device: devices) {
|
|
if (isDeviceSuitable(device)) {
|
|
mPhysicalDevice = device;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!mPhysicalDevice) throw std::runtime_error("no suitable gpu");
|
|
}
|
|
|
|
bool isDeviceSuitable(VkPhysicalDevice device) {
|
|
VkPhysicalDeviceProperties properties;
|
|
VkPhysicalDeviceFeatures features;
|
|
|
|
vkGetPhysicalDeviceProperties(device, &properties);
|
|
vkGetPhysicalDeviceFeatures(device, &features);
|
|
|
|
// if (properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) return false;
|
|
if (!features.geometryShader) return false;
|
|
if (!checkDeviceExtensions(device)) return false;
|
|
if (!checkDeviceSwapChain(device)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool checkDeviceSwapChain(VkPhysicalDevice device) {
|
|
SwapChainSupportDetails details = querySwapChainSupportDetails(device);
|
|
return !(details.presentModes.empty() || details.formats.empty());
|
|
}
|
|
|
|
static bool checkDeviceExtensions(VkPhysicalDevice device) {
|
|
uint32_t extensionsCount = 0;
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, nullptr);
|
|
|
|
std::vector<VkExtensionProperties> extensions(extensionsCount);
|
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionsCount, extensions.data());
|
|
|
|
for (const auto &requiredExtension: gDeviceExtensions) {
|
|
bool found = false;
|
|
for (const auto &extension: extensions) {
|
|
if (strcmp(requiredExtension, extension.extensionName) == 0) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void findPhysicalDeviceQueueFamilies() {
|
|
uint32_t queuesCount = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, nullptr);
|
|
|
|
std::vector<VkQueueFamilyProperties> queueFamilyProperties(queuesCount);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queuesCount, queueFamilyProperties.data());
|
|
|
|
int index = 0;
|
|
for (const auto &familyProperty: queueFamilyProperties) {
|
|
if (familyProperty.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
mGraphicsQueueFamilyIndex = index;
|
|
}
|
|
|
|
VkBool32 presentationQueueSupport = false;
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(mPhysicalDevice, index, mSurface, &presentationQueueSupport);
|
|
|
|
if (presentationQueueSupport) {
|
|
mPresentationQueueFamilyIndex = index;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
if (mPresentationQueueFamilyIndex == -1 || mGraphicsQueueFamilyIndex == -1) {
|
|
throw std::runtime_error("nu require queue families found");
|
|
}
|
|
}
|
|
|
|
SwapChainSupportDetails querySwapChainSupportDetails(VkPhysicalDevice device) {
|
|
SwapChainSupportDetails details;
|
|
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities);
|
|
|
|
uint32_t formatCount = 0;
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr);
|
|
if (formatCount != 0) {
|
|
details.formats.resize(formatCount);
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data());
|
|
}
|
|
|
|
uint32_t modeCount = 0;
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &modeCount, nullptr);
|
|
if (modeCount != 0) {
|
|
details.presentModes.resize(modeCount);
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &modeCount, details.presentModes.data());
|
|
}
|
|
|
|
return details;
|
|
}
|
|
|
|
void createLogicalDevice() {
|
|
float queuePriority = 1.f;
|
|
|
|
VkDeviceQueueCreateInfo graphicsQueueCreateInfos{
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = mGraphicsQueueFamilyIndex,
|
|
.queueCount = 1,
|
|
.pQueuePriorities = &queuePriority,
|
|
};
|
|
|
|
VkDeviceQueueCreateInfo presentationQueueCreateInfos{
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = mPresentationQueueFamilyIndex,
|
|
.queueCount = 1,
|
|
.pQueuePriorities = &queuePriority,
|
|
};
|
|
|
|
std::vector<VkDeviceQueueCreateInfo> queues = {graphicsQueueCreateInfos};
|
|
|
|
if (mPresentationQueueFamilyIndex != mGraphicsQueueFamilyIndex) {
|
|
queues.push_back(presentationQueueCreateInfos);
|
|
}
|
|
|
|
VkPhysicalDeviceFeatures features{};
|
|
|
|
VkDeviceCreateInfo deviceCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
.queueCreateInfoCount = (uint32_t) queues.size(),
|
|
.pQueueCreateInfos = queues.data(),
|
|
.enabledExtensionCount = (uint32_t) gDeviceExtensions.size(),
|
|
.ppEnabledExtensionNames = gDeviceExtensions.data(),
|
|
.pEnabledFeatures = &features,
|
|
};
|
|
|
|
vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice);
|
|
|
|
if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device");
|
|
}
|
|
|
|
void getQueues() {
|
|
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
|
|
vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue);
|
|
}
|
|
|
|
|
|
void createWindowSurface() {
|
|
if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create vulkan window surface");
|
|
}
|
|
}
|
|
|
|
void recreateSwapChain(int sizeX, int sizeY) {
|
|
vkDeviceWaitIdle(mDevice);
|
|
|
|
destroySwapChainFramebuffers();
|
|
destroySwapChainImageViews();
|
|
destroySwapChain();
|
|
|
|
createSwapChain(sizeX, sizeY);
|
|
createSwapChainImageViews();
|
|
createSwapChainFramebuffers();
|
|
}
|
|
|
|
void createSwapChain(int sizeX, int sizeY) {
|
|
SwapChainSupportDetails details = querySwapChainSupportDetails(mPhysicalDevice);
|
|
|
|
VkSurfaceFormatKHR surfaceFormat = pickSwapChainSurfaceFormat(details);
|
|
VkPresentModeKHR presentMode = pickSwapChainPresentMode(details);
|
|
VkExtent2D extent2D = pickSwapChainExtent(details.capabilities, sizeX, sizeY);
|
|
|
|
// +1 so will not have to wait for device to finish frame to query an image to render to
|
|
uint32_t imageCount = details.capabilities.minImageCount + 1;
|
|
|
|
if (details.capabilities.maxImageCount > 0 && imageCount > details.capabilities.maxImageCount) {
|
|
imageCount = details.capabilities.maxImageCount;
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR createInfo{
|
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
|
.surface = mSurface,
|
|
.minImageCount = imageCount,
|
|
.imageFormat = surfaceFormat.format,
|
|
.imageExtent = extent2D,
|
|
.imageArrayLayers = 1,
|
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
.preTransform = details.capabilities.currentTransform,
|
|
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
.presentMode = presentMode,
|
|
.clipped = VK_TRUE,
|
|
.oldSwapchain = VK_NULL_HANDLE,
|
|
};
|
|
|
|
uint32_t queueIndices[] = {mGraphicsQueueFamilyIndex, mPresentationQueueFamilyIndex};
|
|
|
|
if (mGraphicsQueueFamilyIndex != mPresentationQueueFamilyIndex) {
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
|
createInfo.queueFamilyIndexCount = 2;
|
|
createInfo.pQueueFamilyIndices = queueIndices;
|
|
} else {
|
|
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
}
|
|
|
|
if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create swap chain");
|
|
}
|
|
|
|
mSwapChainExtent = extent2D;
|
|
mSwapChainFormat = surfaceFormat.format;
|
|
|
|
uint32_t createdImageCount = 0;
|
|
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, nullptr);
|
|
mSwapChainImages.resize(createdImageCount);
|
|
vkGetSwapchainImagesKHR(mDevice, mSwapChain, &createdImageCount, mSwapChainImages.data());
|
|
}
|
|
|
|
static VkSurfaceFormatKHR pickSwapChainSurfaceFormat(const SwapChainSupportDetails &details) {
|
|
for (const auto &format: details.formats) {
|
|
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
|
|
return format;
|
|
}
|
|
}
|
|
|
|
return details.formats.front();
|
|
}
|
|
|
|
static VkPresentModeKHR pickSwapChainPresentMode(const SwapChainSupportDetails &details) {
|
|
for (const auto &mode: details.presentModes) {
|
|
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
|
return mode;
|
|
}
|
|
}
|
|
|
|
return VK_PRESENT_MODE_FIFO_KHR; // guaranteed exists
|
|
}
|
|
|
|
static VkExtent2D pickSwapChainExtent(const VkSurfaceCapabilitiesKHR &capabilities, int sizeX, int sizeY) {
|
|
// if set by vulkan just keep it
|
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
|
|
return capabilities.currentExtent;
|
|
}
|
|
|
|
VkExtent2D out = {(uint32_t) sizeX, (uint32_t) sizeY};
|
|
|
|
out.width = std::clamp(out.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
|
out.height = std::clamp(out.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
|
|
|
return out;
|
|
}
|
|
|
|
void createSwapChainImageViews() {
|
|
mSwapChainImageViews.resize(mSwapChainImages.size());
|
|
|
|
int i = 0;
|
|
for (const auto &image: mSwapChainImages) {
|
|
|
|
VkImageViewCreateInfo createInfo{
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
.image = image,
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
|
.format = mSwapChainFormat,
|
|
.components = {
|
|
.r = VK_COMPONENT_SWIZZLE_R,
|
|
.g = VK_COMPONENT_SWIZZLE_G,
|
|
.b = VK_COMPONENT_SWIZZLE_B,
|
|
.a = VK_COMPONENT_SWIZZLE_A,
|
|
},
|
|
.subresourceRange = {
|
|
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1,
|
|
}
|
|
};
|
|
|
|
if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) {
|
|
throw std::runtime_error("cannot create image view for the swap chain");
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void createSwapChainFramebuffers() {
|
|
mSwapChainFrameBuffers.resize(mSwapChainImageViews.size());
|
|
|
|
for (size_t i = 0; i < mSwapChainImageViews.size(); i++) {
|
|
|
|
VkImageView attachments[] = {
|
|
mSwapChainImageViews[i],
|
|
};
|
|
|
|
VkFramebufferCreateInfo framebufferCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
.renderPass = mGraphicsRenderPass,
|
|
.attachmentCount = 1,
|
|
.pAttachments = attachments,
|
|
.width = mSwapChainExtent.width,
|
|
.height = mSwapChainExtent.height,
|
|
.layers = 1,
|
|
};
|
|
|
|
if (vkCreateFramebuffer(mDevice, &framebufferCreateInfo, nullptr, &mSwapChainFrameBuffers[i]) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create swapchain framebuffers");
|
|
}
|
|
}
|
|
}
|
|
|
|
void destroySwapChainFramebuffers() {
|
|
for (const auto &buffer: mSwapChainFrameBuffers) {
|
|
vkDestroyFramebuffer(mDevice, buffer, nullptr);
|
|
}
|
|
}
|
|
|
|
void destroySwapChain() {
|
|
vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr);
|
|
}
|
|
|
|
void destroySwapChainImageViews() {
|
|
for (const auto &imageView: mSwapChainImageViews) {
|
|
vkDestroyImageView(mDevice, imageView, nullptr);
|
|
}
|
|
}
|
|
|
|
void createGraphicsPipeline() {
|
|
std::vector<char> vertShaderByteCode = readFile("bin/vert.spv");
|
|
mShaderModuleVert = createShaderModule(vertShaderByteCode);
|
|
|
|
VkPipelineShaderStageCreateInfo vertShaderStageCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
.module = mShaderModuleVert,
|
|
.pName = "main",
|
|
};
|
|
|
|
std::vector<char> fragShaderByteCode = readFile("bin/frag.spv");
|
|
mShaderModuleFrag = createShaderModule(fragShaderByteCode);
|
|
|
|
VkPipelineShaderStageCreateInfo fragShaderStageCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.module = mShaderModuleFrag,
|
|
.pName = "main",
|
|
};
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStageCreateInfos[] = {
|
|
vertShaderStageCreateInfo,
|
|
fragShaderStageCreateInfo
|
|
};
|
|
|
|
std::vector<VkDynamicState> dynamicStates = {
|
|
VK_DYNAMIC_STATE_VIEWPORT,
|
|
VK_DYNAMIC_STATE_SCISSOR,
|
|
};
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
.dynamicStateCount = (uint32_t) dynamicStates.size(),
|
|
.pDynamicStates = dynamicStates.data(),
|
|
};
|
|
|
|
auto attributes = getShaderAttributesDescriptors();
|
|
auto vertexInput = getShaderVertexInputDescription();
|
|
|
|
VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
.vertexBindingDescriptionCount = 1,
|
|
.pVertexBindingDescriptions = &vertexInput,
|
|
.vertexAttributeDescriptionCount = (uint32_t) attributes.size(),
|
|
.pVertexAttributeDescriptions = attributes.data(),
|
|
};
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
.primitiveRestartEnable = VK_FALSE,
|
|
};
|
|
|
|
VkViewport viewport{
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = (float) mSwapChainExtent.width,
|
|
.height = (float) mSwapChainExtent.height,
|
|
.minDepth = 0.f,
|
|
.maxDepth = 1.f,
|
|
};
|
|
|
|
VkRect2D scissor{
|
|
.offset = {0, 0},
|
|
.extent = mSwapChainExtent,
|
|
};
|
|
|
|
VkPipelineViewportStateCreateInfo viewportState{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
.viewportCount = 1,
|
|
.pViewports = &viewport,
|
|
.scissorCount = 1,
|
|
.pScissors = &scissor,
|
|
};
|
|
|
|
VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
.depthClampEnable = VK_FALSE,
|
|
.rasterizerDiscardEnable = VK_FALSE,
|
|
.polygonMode = VK_POLYGON_MODE_FILL,
|
|
.cullMode = VK_CULL_MODE_BACK_BIT,
|
|
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
|
|
|
.depthBiasEnable = VK_FALSE,
|
|
.depthBiasConstantFactor = 0.f,
|
|
.depthBiasClamp = 0.f,
|
|
.depthBiasSlopeFactor = 0.f,
|
|
|
|
.lineWidth = 1,
|
|
};
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
|
.sampleShadingEnable = VK_FALSE,
|
|
};
|
|
|
|
VkPipelineColorBlendAttachmentState colorBlendAttachmentState{
|
|
.blendEnable = VK_FALSE,
|
|
.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
|
|
VK_COLOR_COMPONENT_A_BIT),
|
|
};
|
|
|
|
VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
.logicOpEnable = VK_FALSE,
|
|
.attachmentCount = 1,
|
|
.pAttachments = &colorBlendAttachmentState,
|
|
};
|
|
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
};
|
|
|
|
if (vkCreatePipelineLayout(mDevice, &pipelineLayoutCreateInfo, nullptr, &mGraphicsPipelineLayout) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create pipeline layout");
|
|
}
|
|
|
|
VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
.stageCount = 2,
|
|
.pStages = shaderStageCreateInfos,
|
|
.pVertexInputState = &vertexInputStateCreateInfo,
|
|
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
|
.pViewportState = &viewportState,
|
|
.pRasterizationState = &rasterizationStateCreateInfo,
|
|
.pMultisampleState = &multisampleStateCreateInfo,
|
|
.pColorBlendState = &colorBlendStateCreateInfo,
|
|
.pDynamicState = &dynamicStateCreateInfo,
|
|
.layout = mGraphicsPipelineLayout,
|
|
.renderPass = mGraphicsRenderPass,
|
|
.subpass = 0,
|
|
};
|
|
|
|
if (vkCreateGraphicsPipelines(mDevice, nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &mGraphicsPipeline) !=
|
|
VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create graphics pipeline");
|
|
}
|
|
}
|
|
|
|
void createRenderPass() {
|
|
VkAttachmentDescription colorAttachment{
|
|
.format = mSwapChainFormat,
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
|
};
|
|
|
|
VkAttachmentReference colorAttachmentReference{
|
|
.attachment = 0,
|
|
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
};
|
|
|
|
VkSubpassDescription subpassDescription{
|
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
.colorAttachmentCount = 1,
|
|
.pColorAttachments = &colorAttachmentReference,
|
|
};
|
|
|
|
VkSubpassDependency dependency {
|
|
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
|
.dstSubpass = 0,
|
|
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
.srcAccessMask = 0,
|
|
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
};
|
|
|
|
VkRenderPassCreateInfo renderPassCreateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
.attachmentCount = 1,
|
|
.pAttachments = &colorAttachment,
|
|
.subpassCount = 1,
|
|
.pSubpasses = &subpassDescription,
|
|
.dependencyCount = 1,
|
|
.pDependencies = &dependency
|
|
};
|
|
|
|
if (vkCreateRenderPass(mDevice, &renderPassCreateInfo, nullptr, &mGraphicsRenderPass) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create render pass");
|
|
}
|
|
}
|
|
|
|
void destroyGraphicsPipeline() {
|
|
vkDestroyShaderModule(mDevice, mShaderModuleVert, nullptr);
|
|
vkDestroyShaderModule(mDevice, mShaderModuleFrag, nullptr);
|
|
vkDestroyPipelineLayout(mDevice, mGraphicsPipelineLayout, nullptr);
|
|
vkDestroyPipeline(mDevice, mGraphicsPipeline, nullptr);
|
|
}
|
|
|
|
VkShaderModule createShaderModule(const std::vector<char> &bytecode) {
|
|
VkShaderModuleCreateInfo createInfo{
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
.codeSize = (uint32_t) bytecode.size(),
|
|
.pCode = (uint32_t *) bytecode.data(),
|
|
};
|
|
|
|
VkShaderModule shaderModule;
|
|
|
|
if (vkCreateShaderModule(mDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
|
|
throw std::runtime_error("cannot create shader module");
|
|
}
|
|
|
|
return shaderModule;
|
|
}
|
|
|
|
static std::vector<char> readFile(const std::string &fileName) {
|
|
std::ifstream file(fileName, std::ios::ate | std::ios::binary);
|
|
|
|
if (!file.is_open()) {
|
|
throw std::runtime_error("cannot open file");
|
|
}
|
|
|
|
size_t fileSize = (size_t) file.tellg();
|
|
std::vector<char> buffer(fileSize);
|
|
|
|
file.seekg(0);
|
|
file.read(buffer.data(), (std::streamsize) fileSize);
|
|
|
|
file.close();
|
|
|
|
return buffer;
|
|
}
|
|
|
|
void createCommandPool() {
|
|
VkCommandPoolCreateInfo createInfo{
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
.queueFamilyIndex = mGraphicsQueueFamilyIndex,
|
|
};
|
|
|
|
if (vkCreateCommandPool(mDevice, &createInfo, nullptr, &mCommandPool) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create command pool");
|
|
}
|
|
}
|
|
|
|
void createCommandBuffer() {
|
|
VkCommandBufferAllocateInfo allocateInfo{
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
.commandPool = mCommandPool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
.commandBufferCount = 1,
|
|
};
|
|
|
|
if (vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create command buffer");
|
|
}
|
|
}
|
|
|
|
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) {
|
|
VkPhysicalDeviceMemoryProperties memoryProperties;
|
|
vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memoryProperties);
|
|
|
|
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
|
|
if (typeFilter & (1 << i) && (memoryProperties.memoryTypes[i].propertyFlags & flags) == flags) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("no suitable memory for vertex buffer found");
|
|
return {};
|
|
}
|
|
|
|
void createVertexBuffer() {
|
|
size_t size = vertices.size() * sizeof(vertices[0]);
|
|
|
|
VkBuffer stagingBuffer;
|
|
VkDeviceMemory stagingBufferMemory;
|
|
|
|
createBuffer(&stagingBuffer, &stagingBufferMemory,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
|
|
|
|
{
|
|
void* memory;
|
|
vkMapMemory(mDevice, stagingBufferMemory, 0, size, 0, &memory);
|
|
memcpy(memory, vertices.data(), size);
|
|
vkUnmapMemory(mDevice, stagingBufferMemory);
|
|
}
|
|
|
|
createBuffer(&mVertexBuffer, &mVertexBufferMemory,
|
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size,
|
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
|
|
|
|
copyBuffer(stagingBuffer, mVertexBuffer, size);
|
|
|
|
destroyBuffer(stagingBuffer, stagingBufferMemory);
|
|
}
|
|
|
|
void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) {
|
|
VkCommandBufferAllocateInfo allocateInfo {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
.commandPool = mCommandPool,
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
.commandBufferCount = 1
|
|
};
|
|
|
|
VkCommandBuffer commandBuffer;
|
|
vkAllocateCommandBuffers(mDevice, &allocateInfo, &commandBuffer);
|
|
|
|
VkCommandBufferBeginInfo beginInfo {
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
};
|
|
|
|
vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
|
|
|
VkBufferCopy copyRegion {
|
|
.srcOffset = 0,
|
|
.dstOffset = 0,
|
|
.size = size,
|
|
};
|
|
|
|
vkCmdCopyBuffer(commandBuffer, src, dst, 1, ©Region);
|
|
vkEndCommandBuffer(commandBuffer);
|
|
|
|
VkSubmitInfo submitInfo {
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = &commandBuffer,
|
|
};
|
|
|
|
vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(mGraphicsQueue);
|
|
|
|
vkFreeCommandBuffers(mDevice, mCommandPool, 1, &commandBuffer);
|
|
}
|
|
|
|
void createBuffer(VkBuffer *buffer, VkDeviceMemory *memory, VkMemoryPropertyFlags properties, VkDeviceSize size,
|
|
VkBufferUsageFlags usage) {
|
|
|
|
VkBufferCreateInfo createInfo {
|
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
.size = size,
|
|
.usage = usage,
|
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
};
|
|
|
|
if (vkCreateBuffer(mDevice, &createInfo, nullptr, buffer) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to create vertex buffer");
|
|
}
|
|
|
|
VkMemoryRequirements memoryRequirements;
|
|
vkGetBufferMemoryRequirements(mDevice, *buffer, &memoryRequirements);
|
|
|
|
VkMemoryAllocateInfo allocateInfo = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
.allocationSize = memoryRequirements.size,
|
|
.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, properties),
|
|
};
|
|
|
|
if (vkAllocateMemory(mDevice, &allocateInfo, nullptr, memory) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to allocate vertex buffer memory");
|
|
}
|
|
|
|
vkBindBufferMemory(mDevice, *buffer, *memory, 0);
|
|
}
|
|
|
|
void destroyBuffer(VkBuffer buffer, VkDeviceMemory memory) {
|
|
vkDestroyBuffer(mDevice, buffer, nullptr);
|
|
vkFreeMemory(mDevice, memory, nullptr);
|
|
}
|
|
|
|
void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, uint32_t swapChainImageIndex) {
|
|
VkCommandBufferBeginInfo commandBufferBeginInfo{
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
};
|
|
|
|
if (vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to begin command buffer =");
|
|
}
|
|
|
|
VkClearValue clearColor{.color = {.float32 = {0.f, 0.f, 0.f, 1.f}}};
|
|
|
|
VkRenderPassBeginInfo renderPassBeginInfo{
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
.renderPass = mGraphicsRenderPass,
|
|
.framebuffer = mSwapChainFrameBuffers[swapChainImageIndex],
|
|
.renderArea = {
|
|
.offset = {0, 0},
|
|
.extent = mSwapChainExtent,
|
|
},
|
|
.clearValueCount = 1,
|
|
.pClearValues = &clearColor,
|
|
};
|
|
|
|
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline);
|
|
|
|
VkViewport viewport{
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = (float) mSwapChainExtent.width,
|
|
.height = (float) mSwapChainExtent.height,
|
|
.minDepth = 0.f,
|
|
.maxDepth = 1.f,
|
|
};
|
|
|
|
VkRect2D scissor{
|
|
.offset = {0, 0},
|
|
.extent = mSwapChainExtent,
|
|
};
|
|
|
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
|
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
|
|
|
VkBuffer vertexBuffers[] = { mVertexBuffer };
|
|
VkDeviceSize offsets[] = { 0 };
|
|
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
|
|
|
|
vkCmdDraw(commandBuffer, vertices.size(), 1, 0, 0);
|
|
|
|
vkCmdEndRenderPass(commandBuffer);
|
|
|
|
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to end command buffer");
|
|
}
|
|
}
|
|
|
|
void destroyCommandPool() {
|
|
vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
|
|
}
|
|
|
|
void createSynchronizationObjects() {
|
|
VkSemaphoreCreateInfo semaphoreCreateInfo {
|
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
|
};
|
|
|
|
VkFenceCreateInfo fenceCreateInfo {
|
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
|
};
|
|
|
|
bool failure = false;
|
|
|
|
failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreImageAcquired) != VK_SUCCESS;
|
|
failure |= vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, &mSemaphoreFramebufferDrawn) != VK_SUCCESS;
|
|
failure |= vkCreateFence(mDevice, &fenceCreateInfo, nullptr, &mFenceCanStartNewFrame) != VK_SUCCESS;
|
|
|
|
if (failure) throw std::runtime_error("failed to create synchronization objects");
|
|
}
|
|
|
|
void drawFrame() {
|
|
vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX);
|
|
vkResetFences(mDevice, 1, &mFenceCanStartNewFrame);
|
|
|
|
uint32_t imageIndex = 0;
|
|
vkAcquireNextImageKHR(mDevice, mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, &imageIndex);
|
|
|
|
vkResetCommandBuffer(mCommandBuffer, 0);
|
|
populateGraphicsCommandBuffer(mCommandBuffer, imageIndex);
|
|
|
|
VkSemaphore waitSemaphores[] = { mSemaphoreImageAcquired };
|
|
VkSemaphore signalSemaphores[] = { mSemaphoreFramebufferDrawn };
|
|
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
|
|
|
VkSubmitInfo submitInfo {
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
.waitSemaphoreCount = 1,
|
|
.pWaitSemaphores = waitSemaphores,
|
|
.pWaitDstStageMask = waitStages,
|
|
.commandBufferCount = 1,
|
|
.pCommandBuffers = &mCommandBuffer,
|
|
.signalSemaphoreCount = 1,
|
|
.pSignalSemaphores = signalSemaphores,
|
|
};
|
|
|
|
if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) {
|
|
throw std::runtime_error("failed to submit to graphics queue");
|
|
}
|
|
|
|
VkSwapchainKHR swapchains[] = { mSwapChain };
|
|
|
|
VkPresentInfoKHR presentInfo {
|
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
|
.waitSemaphoreCount = 1,
|
|
.pWaitSemaphores = signalSemaphores,
|
|
.swapchainCount = 1,
|
|
.pSwapchains = swapchains,
|
|
.pImageIndices = &imageIndex,
|
|
};
|
|
|
|
vkQueuePresentKHR(mPresentQueue, &presentInfo);
|
|
/* Somehow res is always VK_SUCCESS
|
|
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
|
|
recreateSwapChain();
|
|
} else if (res != VK_SUCCESS) {
|
|
throw std::runtime_error("cannot acquire new khr image");
|
|
}
|
|
*/
|
|
}
|
|
|
|
void createBuffer() {
|
|
|
|
}
|
|
|
|
static VkVertexInputBindingDescription getShaderVertexInputDescription() {
|
|
return {
|
|
.binding = 0,
|
|
.stride = sizeof(Vertex),
|
|
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
|
};
|
|
}
|
|
|
|
static std::vector<VkVertexInputAttributeDescription> getShaderAttributesDescriptors() {
|
|
std::vector<VkVertexInputAttributeDescription> out(2);
|
|
|
|
out[0] = {
|
|
.location = 0,
|
|
.binding = 0,
|
|
.format = VK_FORMAT_R32G32_SFLOAT,
|
|
.offset = offsetof(Vertex, pos),
|
|
};
|
|
|
|
out[1] = {
|
|
.location = 1,
|
|
.binding = 0,
|
|
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
|
.offset = offsetof(Vertex, color),
|
|
};
|
|
|
|
return out;
|
|
}
|
|
|
|
void destroySynchronizationObjects() {
|
|
vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr);
|
|
vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr);
|
|
vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr);
|
|
}
|
|
|
|
void cleanup() {
|
|
destroyBuffer(mVertexBuffer, mVertexBufferMemory);
|
|
|
|
destroySynchronizationObjects();
|
|
|
|
destroyCommandPool();
|
|
|
|
destroySwapChainFramebuffers();
|
|
|
|
vkDestroyRenderPass(mDevice, mGraphicsRenderPass, nullptr);
|
|
|
|
destroyGraphicsPipeline();
|
|
destroySwapChainImageViews();
|
|
destroySwapChain();
|
|
|
|
vkDestroyDevice(mDevice, nullptr);
|
|
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
|
|
vkDestroyInstance(mInstance, nullptr);
|
|
glfwDestroyWindow(mWindow);
|
|
glfwTerminate();
|
|
}
|
|
|
|
private:
|
|
GLFWwindow *mWindow = nullptr;
|
|
|
|
bool mWindowSizeDirtyFlag = true;
|
|
TimePoint mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
|
|
TimeMs mWindowSizeApplyMinDelay = 200;
|
|
std::pair<int, int> mWindowFramebufferSize = { 0, 0 };
|
|
|
|
uint32_t mGraphicsQueueFamilyIndex = -1;
|
|
uint32_t mPresentationQueueFamilyIndex = -1;
|
|
|
|
VkInstance mInstance = VK_NULL_HANDLE;
|
|
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
|
|
VkDevice mDevice = VK_NULL_HANDLE;
|
|
|
|
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
|
|
VkQueue mPresentQueue = VK_NULL_HANDLE;
|
|
|
|
VkSurfaceKHR mSurface = VK_NULL_HANDLE;
|
|
VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
|
|
VkFormat mSwapChainFormat{};
|
|
VkExtent2D mSwapChainExtent{};
|
|
std::vector<VkImage> mSwapChainImages;
|
|
std::vector<VkImageView> mSwapChainImageViews;
|
|
std::vector<VkFramebuffer> mSwapChainFrameBuffers;
|
|
|
|
VkPipeline mGraphicsPipeline = VK_NULL_HANDLE;
|
|
VkShaderModule mShaderModuleVert = VK_NULL_HANDLE;
|
|
VkShaderModule mShaderModuleFrag = VK_NULL_HANDLE;
|
|
VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE;
|
|
VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader
|
|
|
|
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
|
VkCommandBuffer mCommandBuffer = VK_NULL_HANDLE;
|
|
|
|
VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE;
|
|
VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE;
|
|
VkFence mFenceCanStartNewFrame = VK_NULL_HANDLE;
|
|
|
|
VkBuffer mVertexBuffer = VK_NULL_HANDLE;
|
|
VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE;
|
|
};
|
|
|
|
int main() {
|
|
Application app;
|
|
|
|
try {
|
|
app.run();
|
|
} catch (const std::exception &e) {
|
|
std::cerr << e.what() << std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
} |