1
0
mirror of https://github.com/Yours3lf/rpi-vk-driver.git synced 2024-12-04 16:24:15 +01:00
rpi-vk-driver/test/triangle/triangle.cpp

1074 lines
35 KiB
C++
Raw Normal View History

#include <iostream>
#include <vector>
#include <algorithm>
2018-05-12 14:24:25 +02:00
#include <string.h>
2018-05-10 23:10:35 +02:00
#include "driver/CustomAssert.h"
#include <vulkan/vulkan.h>
2018-05-07 17:13:39 +02:00
#include "driver/vkExt.h"
//#define GLFW_INCLUDE_VULKAN
//#define VK_USE_PLATFORM_WIN32_KHR
//#include <GLFW/glfw3.h>
//#define GLFW_EXPOSE_NATIVE_WIN32
//#include <GLFW/glfw3native.h>
//GLFWwindow * window;
2018-08-26 19:52:21 +02:00
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
2018-08-26 19:52:21 +02:00
const char* fragShader =
2018-09-08 18:53:51 +02:00
"#version 100\n"
"precision mediump float;\n"
"void main() { gl_FragColor = vec4(0.8, 0.3, 0.5, 1.0); }\n";
2018-08-26 19:52:21 +02:00
const char* vertShader =
2018-09-08 18:53:51 +02:00
"#version 100\n"
"precision highp float;\n"
"attribute vec2 vertex;\n"
"void main(){ gl_Position = vec4(vertex, 0, 1); }\n";
// Note: support swap chain recreation (not only required for resized windows!)
// Note: window resize may not result in Vulkan telling that the swap chain should be recreated, should be handled explicitly!
void run();
void setupVulkan();
void mainLoop();
void cleanup();
void createInstance();
void createWindowSurface();
void findPhysicalDevice();
void checkSwapChainSupport();
void findQueueFamilies();
void createLogicalDevice();
void createSemaphores();
void createSwapChain();
void createCommandQueues();
void draw();
2018-08-26 19:52:21 +02:00
void CreateRenderPass();
void CreateFramebuffer();
void CreateShaders();
void CreatePipeline();
2018-09-08 18:53:51 +02:00
void CreateVertexBuffer();
2018-08-26 19:52:21 +02:00
void recordCommandBuffers();
VkSurfaceFormatKHR chooseSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& surfaceCapabilities);
VkPresentModeKHR choosePresentMode(const std::vector<VkPresentModeKHR> presentModes);
2018-08-26 19:52:21 +02:00
VkInstance instance; //
VkSurfaceKHR windowSurface; //
VkPhysicalDevice physicalDevice;
VkDevice device; //
VkSemaphore imageAvailableSemaphore; //
VkSemaphore renderingFinishedSemaphore; //
VkSwapchainKHR swapChain; //
VkCommandPool commandPool; //
std::vector<VkCommandBuffer> presentCommandBuffers; //
std::vector<VkImage> swapChainImages; //
VkRenderPass renderPass; //
std::vector<VkFramebuffer> fbs; //
VkShaderModule vsModule; //
VkShaderModule fsModule; //
VkPipeline pipeline; //
VkQueue graphicsQueue;
VkQueue presentQueue;
2018-09-08 18:53:51 +02:00
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkPhysicalDeviceMemoryProperties pdmp;
2018-08-26 19:52:21 +02:00
std::vector<VkImageView> views; //?
uint32_t graphicsQueueFamily;
uint32_t presentQueueFamily;
void cleanup() {
vkDeviceWaitIdle(device);
// Note: this is done implicitly when the command pool is freed, but nice to know about
vkFreeCommandBuffers(device, commandPool, presentCommandBuffers.size(), presentCommandBuffers.data());
vkDestroyCommandPool(device, commandPool, nullptr);
vkDestroySemaphore(device, imageAvailableSemaphore, nullptr);
vkDestroySemaphore(device, renderingFinishedSemaphore, nullptr);
for(int c = 0; c < views.size(); ++c)
vkDestroyImageView(device, views[c], 0);
for (int c = 0; c < fbs.size(); ++c)
vkDestroyFramebuffer(device, fbs[c], 0);
vkDestroyRenderPass(device, renderPass, 0);
vkDestroyShaderModule(device, vsModule, 0);
vkDestroyShaderModule(device, fsModule, 0);
vkDestroyPipeline(device, pipeline, 0);
// Note: implicitly destroys images (in fact, we're not allowed to do that explicitly)
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, windowSurface, nullptr);
vkDestroyInstance(instance, nullptr);
}
void run() {
// Note: dynamically loading loader may be a better idea to fail gracefully when Vulkan is not supported
// Create window for Vulkan
//glfwInit();
//glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
//glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
2018-08-26 19:52:21 +02:00
//window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "The 630 line cornflower blue window", nullptr, nullptr);
// Use Vulkan
setupVulkan();
mainLoop();
cleanup();
}
void setupVulkan() {
createInstance();
createWindowSurface();
findPhysicalDevice();
checkSwapChainSupport();
findQueueFamilies();
createLogicalDevice();
createSemaphores();
createSwapChain();
createCommandQueues();
2018-08-26 19:52:21 +02:00
CreateRenderPass();
CreateFramebuffer();
CreateShaders();
CreatePipeline();
2018-09-08 18:53:51 +02:00
CreateVertexBuffer();
2018-08-26 19:52:21 +02:00
recordCommandBuffers();
}
void mainLoop() {
//while (!glfwWindowShouldClose(window)) {
2018-08-26 19:52:21 +02:00
for(int c = 0; c < 10; ++c){
draw();
2018-08-26 19:52:21 +02:00
//glfwPollEvents();
}
}
void createInstance() {
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
2018-08-26 19:52:21 +02:00
appInfo.pApplicationName = "VulkanTriangle";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
2018-08-26 19:52:21 +02:00
appInfo.pEngineName = "TriangleEngine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
// Get instance extensions required by GLFW to draw to window
//unsigned int glfwExtensionCount;
//const char** glfwExtensions;
//glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
// Check for extensions
2018-05-12 14:24:25 +02:00
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
2018-05-12 14:24:25 +02:00
if (extensionCount == 0) {
std::cerr << "no extensions supported!" << std::endl;
assert(0);
}
2018-05-12 14:24:25 +02:00
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, availableExtensions.data());
2018-05-12 14:24:25 +02:00
std::cout << "supported extensions:" << std::endl;
2018-05-12 14:24:25 +02:00
for (const auto& extension : availableExtensions) {
std::cout << "\t" << extension.extensionName << std::endl;
}
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
//createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.enabledExtensionCount = 0;
//createInfo.ppEnabledExtensionNames = glfwExtensions;
createInfo.ppEnabledExtensionNames = 0;
createInfo.enabledLayerCount = 0;
createInfo.ppEnabledLayerNames = 0;
// Initialize Vulkan instance
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
std::cerr << "failed to create instance!" << std::endl;
assert(0);
}
else {
std::cout << "created vulkan instance" << std::endl;
}
}
void createWindowSurface() {
2018-05-07 17:13:39 +02:00
if (vkCreateRpiSurfaceKHR(instance, 0, 0, &windowSurface) != VK_SUCCESS) {
std::cerr << "failed to create window surface!" << std::endl;
assert(0);
}
2018-05-07 17:13:39 +02:00
std::cout << "created window surface" << std::endl;
}
void findPhysicalDevice() {
// Try to find 1 Vulkan supported device
// Note: perhaps refactor to loop through devices and find first one that supports all required features and extensions
uint32_t deviceCount = 1;
VkResult res = vkEnumeratePhysicalDevices(instance, &deviceCount, &physicalDevice);
if (res != VK_SUCCESS && res != VK_INCOMPLETE) {
std::cerr << "enumerating physical devices failed!" << std::endl;
assert(0);
}
if (deviceCount == 0) {
std::cerr << "no physical devices that support vulkan!" << std::endl;
assert(0);
}
std::cout << "physical device with vulkan support found" << std::endl;
2018-09-08 21:53:22 +02:00
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &pdmp);
// Check device features
// Note: will apiVersion >= appInfo.apiVersion? Probably yes, but spec is unclear.
2018-05-12 14:24:25 +02:00
VkPhysicalDeviceProperties deviceProperties;
VkPhysicalDeviceFeatures deviceFeatures;
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures);
uint32_t supportedVersion[] = {
VK_VERSION_MAJOR(deviceProperties.apiVersion),
VK_VERSION_MINOR(deviceProperties.apiVersion),
VK_VERSION_PATCH(deviceProperties.apiVersion)
};
std::cout << "physical device supports version " << supportedVersion[0] << "." << supportedVersion[1] << "." << supportedVersion[2] << std::endl;
}
void checkSwapChainSupport() {
2018-05-12 14:24:25 +02:00
uint32_t extensionCount = 0;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
if (extensionCount == 0) {
std::cerr << "physical device doesn't support any extensions" << std::endl;
assert(0);
}
std::vector<VkExtensionProperties> deviceExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, deviceExtensions.data());
for (const auto& extension : deviceExtensions) {
if (strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
std::cout << "physical device supports swap chains" << std::endl;
return;
}
}
std::cerr << "physical device doesn't support swap chains" << std::endl;
assert(0);
}
void findQueueFamilies() {
// Check queue families
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
if (queueFamilyCount == 0) {
std::cout << "physical device has no queue families!" << std::endl;
assert(0);
}
// Find queue family with graphics support
// Note: is a transfer queue necessary to copy vertices to the gpu or can a graphics queue handle that?
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
std::cout << "physical device has " << queueFamilyCount << " queue families" << std::endl;
bool foundGraphicsQueueFamily = false;
bool foundPresentQueueFamily = false;
for (uint32_t i = 0; i < queueFamilyCount; i++) {
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, windowSurface, &presentSupport);
if (queueFamilies[i].queueCount > 0 && queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphicsQueueFamily = i;
foundGraphicsQueueFamily = true;
if (presentSupport) {
presentQueueFamily = i;
foundPresentQueueFamily = true;
break;
}
}
if (!foundPresentQueueFamily && presentSupport) {
presentQueueFamily = i;
foundPresentQueueFamily = true;
}
}
if (foundGraphicsQueueFamily) {
std::cout << "queue family #" << graphicsQueueFamily << " supports graphics" << std::endl;
if (foundPresentQueueFamily) {
std::cout << "queue family #" << presentQueueFamily << " supports presentation" << std::endl;
}
else {
std::cerr << "could not find a valid queue family with present support" << std::endl;
assert(0);
}
}
else {
std::cerr << "could not find a valid queue family with graphics support" << std::endl;
assert(0);
}
}
void createLogicalDevice() {
// Greate one graphics queue and optionally a separate presentation queue
float queuePriority = 1.0f;
VkDeviceQueueCreateInfo queueCreateInfo[2] = {};
queueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo[0].queueFamilyIndex = graphicsQueueFamily;
queueCreateInfo[0].queueCount = 1;
queueCreateInfo[0].pQueuePriorities = &queuePriority;
queueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo[0].queueFamilyIndex = presentQueueFamily;
queueCreateInfo[0].queueCount = 1;
queueCreateInfo[0].pQueuePriorities = &queuePriority;
// Create logical device from physical device
// Note: there are separate instance and device extensions!
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pQueueCreateInfos = queueCreateInfo;
if (graphicsQueueFamily == presentQueueFamily) {
deviceCreateInfo.queueCreateInfoCount = 1;
}
else {
deviceCreateInfo.queueCreateInfoCount = 2;
}
const char* deviceExtensions = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
deviceCreateInfo.enabledExtensionCount = 1;
deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions;
if (vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device) != VK_SUCCESS) {
std::cerr << "failed to create logical device" << std::endl;
assert(0);
}
std::cout << "created logical device" << std::endl;
// Get graphics and presentation queues (which may be the same)
vkGetDeviceQueue(device, graphicsQueueFamily, 0, &graphicsQueue);
vkGetDeviceQueue(device, presentQueueFamily, 0, &presentQueue);
std::cout << "acquired graphics and presentation queues" << std::endl;
}
void createSemaphores() {
VkSemaphoreCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
if (vkCreateSemaphore(device, &createInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS ||
vkCreateSemaphore(device, &createInfo, nullptr, &renderingFinishedSemaphore) != VK_SUCCESS) {
std::cerr << "failed to create semaphores" << std::endl;
assert(0);
}
else {
std::cout << "created semaphores" << std::endl;
}
}
void createSwapChain() {
// Find surface capabilities
VkSurfaceCapabilitiesKHR surfaceCapabilities;
if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, windowSurface, &surfaceCapabilities) != VK_SUCCESS) {
std::cerr << "failed to acquire presentation surface capabilities" << std::endl;
assert(0);
}
// Find supported surface formats
uint32_t formatCount;
if (vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, windowSurface, &formatCount, nullptr) != VK_SUCCESS || formatCount == 0) {
std::cerr << "failed to get number of supported surface formats" << std::endl;
assert(0);
}
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
if (vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, windowSurface, &formatCount, surfaceFormats.data()) != VK_SUCCESS) {
std::cerr << "failed to get supported surface formats" << std::endl;
assert(0);
}
// Find supported present modes
uint32_t presentModeCount;
if (vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, windowSurface, &presentModeCount, nullptr) != VK_SUCCESS || presentModeCount == 0) {
std::cerr << "failed to get number of supported presentation modes" << std::endl;
assert(0);
}
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
if (vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, windowSurface, &presentModeCount, presentModes.data()) != VK_SUCCESS) {
std::cerr << "failed to get supported presentation modes" << std::endl;
assert(0);
}
// Determine number of images for swap chain
uint32_t imageCount = surfaceCapabilities.minImageCount + 1;
if (surfaceCapabilities.maxImageCount != 0 && imageCount > surfaceCapabilities.maxImageCount) {
imageCount = surfaceCapabilities.maxImageCount;
}
std::cout << "using " << imageCount << " images for swap chain" << std::endl;
// Select a surface format
VkSurfaceFormatKHR surfaceFormat = chooseSurfaceFormat(surfaceFormats);
// Select swap chain size
VkExtent2D swapChainExtent = chooseSwapExtent(surfaceCapabilities);
// Check if swap chain supports being the destination of an image transfer
// Note: AMD driver bug, though it would be nice to implement a workaround that doesn't use transfering
//if (!(surfaceCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
// std::cerr << "swap chain image does not support VK_IMAGE_TRANSFER_DST usage" << std::endl;
2018-08-26 19:52:21 +02:00
//assert(0);
//}
// Determine transformation to use (preferring no transform)
VkSurfaceTransformFlagBitsKHR surfaceTransform;
if (surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
surfaceTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
}
else {
surfaceTransform = surfaceCapabilities.currentTransform;
}
// Choose presentation mode (preferring MAILBOX ~= triple buffering)
VkPresentModeKHR presentMode = choosePresentMode(presentModes);
// Finally, create the swap chain
VkSwapchainCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = windowSurface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = swapChainExtent;
createInfo.imageArrayLayers = 1;
2018-08-26 19:52:21 +02:00
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
createInfo.preTransform = surfaceTransform;
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) {
std::cerr << "failed to create swap chain" << std::endl;
assert(0);
}
else {
std::cout << "created swap chain" << std::endl;
}
// Store the images used by the swap chain
// Note: these are the images that swap chain image indices refer to
// Note: actual number of images may differ from requested number, since it's a lower bound
uint32_t actualImageCount = 0;
if (vkGetSwapchainImagesKHR(device, swapChain, &actualImageCount, nullptr) != VK_SUCCESS || actualImageCount == 0) {
std::cerr << "failed to acquire number of swap chain images" << std::endl;
assert(0);
}
swapChainImages.resize(actualImageCount);
2018-08-26 19:52:21 +02:00
views.resize(actualImageCount);
if (vkGetSwapchainImagesKHR(device, swapChain, &actualImageCount, swapChainImages.data()) != VK_SUCCESS) {
std::cerr << "failed to acquire swap chain images" << std::endl;
assert(0);
}
std::cout << "acquired swap chain images" << std::endl;
}
VkSurfaceFormatKHR chooseSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
// We can either choose any format
if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {
return { VK_FORMAT_R8G8B8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR };
}
// Or go with the standard format - if available
for (const auto& availableSurfaceFormat : availableFormats) {
if (availableSurfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM) {
return availableSurfaceFormat;
}
}
// Or fall back to the first available one
return availableFormats[0];
}
VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& surfaceCapabilities) {
if (surfaceCapabilities.currentExtent.width == -1) {
VkExtent2D swapChainExtent = {};
#define min(a, b) (a < b ? a : b)
#define max(a, b) (a > b ? a : b)
2018-08-26 19:52:21 +02:00
swapChainExtent.width = min(max(WINDOW_WIDTH, surfaceCapabilities.minImageExtent.width), surfaceCapabilities.maxImageExtent.width);
swapChainExtent.height = min(max(WINDOW_HEIGHT, surfaceCapabilities.minImageExtent.height), surfaceCapabilities.maxImageExtent.height);
return swapChainExtent;
}
else {
return surfaceCapabilities.currentExtent;
}
}
VkPresentModeKHR choosePresentMode(const std::vector<VkPresentModeKHR> presentModes) {
for (const auto& presentMode : presentModes) {
if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return presentMode;
}
}
// If mailbox is unavailable, fall back to FIFO (guaranteed to be available)
return VK_PRESENT_MODE_FIFO_KHR;
}
void createCommandQueues() {
// Create presentation command pool
// Note: only command buffers for a single queue family can be created from this pool
VkCommandPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolCreateInfo.queueFamilyIndex = presentQueueFamily;
if (vkCreateCommandPool(device, &poolCreateInfo, nullptr, &commandPool) != VK_SUCCESS) {
std::cerr << "failed to create command queue for presentation queue family" << std::endl;
assert(0);
}
else {
std::cout << "created command pool for presentation queue family" << std::endl;
}
// Get number of swap chain images and create vector to hold command queue for each one
presentCommandBuffers.resize(swapChainImages.size());
// Allocate presentation command buffers
// Note: secondary command buffers are only for nesting in primary command buffers
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)swapChainImages.size();
if (vkAllocateCommandBuffers(device, &allocInfo, presentCommandBuffers.data()) != VK_SUCCESS) {
std::cerr << "failed to allocate presentation command buffers" << std::endl;
assert(0);
}
else {
std::cout << "allocated presentation command buffers" << std::endl;
}
2018-08-26 19:52:21 +02:00
}
2018-08-26 19:52:21 +02:00
void recordCommandBuffers()
{
// Prepare data for recording command buffers
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
// Note: contains value for each subresource range
VkClearColorValue clearColor = {
2018-08-26 19:52:21 +02:00
{ 0.4f, 0.6f, 0.9f, 1.0f } // R, G, B, A
};
2018-08-26 19:52:21 +02:00
VkClearValue clearValue = {};
clearValue.color = clearColor;
VkImageSubresourceRange subResourceRange = {};
subResourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subResourceRange.baseMipLevel = 0;
subResourceRange.levelCount = 1;
subResourceRange.baseArrayLayer = 0;
subResourceRange.layerCount = 1;
2018-08-26 19:52:21 +02:00
VkRenderPassBeginInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.renderArea.offset.x = 0;
renderPassInfo.renderArea.offset.y = 0;
renderPassInfo.renderArea.extent.width = WINDOW_WIDTH;
renderPassInfo.renderArea.extent.height = WINDOW_HEIGHT;
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearValue;
VkViewport viewport = { 0 };
viewport.height = (float)WINDOW_HEIGHT;
viewport.width = (float)WINDOW_WIDTH;
viewport.minDepth = (float)0.0f;
viewport.maxDepth = (float)1.0f;
VkRect2D scissor = { 0 };
scissor.extent.width = WINDOW_WIDTH;
scissor.extent.height = WINDOW_HEIGHT;
scissor.offset.x = 0;
scissor.offset.y = 0;
// Record the command buffer for every swap chain image
for (uint32_t i = 0; i < swapChainImages.size(); i++) {
// Record command buffer
vkBeginCommandBuffer(presentCommandBuffers[i], &beginInfo);
2018-08-26 19:52:21 +02:00
renderPassInfo.framebuffer = fbs[i];
vkCmdBeginRenderPass(presentCommandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
2018-08-26 19:52:21 +02:00
vkCmdBindPipeline(presentCommandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
2018-08-26 19:52:21 +02:00
vkCmdSetViewport(presentCommandBuffers[i], 0, 1, &viewport);
vkCmdSetScissor(presentCommandBuffers[i], 0, 1, &scissor);
2018-09-08 18:53:51 +02:00
VkDeviceSize offsets = 0;
vkCmdBindVertexBuffers(presentCommandBuffers[i], 0, 1, &vertexBuffer, &offsets );
2018-08-26 19:52:21 +02:00
vkCmdDraw(presentCommandBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(presentCommandBuffers[i]);
if (vkEndCommandBuffer(presentCommandBuffers[i]) != VK_SUCCESS) {
std::cerr << "failed to record command buffer" << std::endl;
assert(0);
}
else {
std::cout << "recorded command buffer for image " << i << std::endl;
}
}
}
void draw() {
// Acquire image
uint32_t imageIndex;
VkResult res = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) {
std::cerr << "failed to acquire image" << std::endl;
assert(0);
}
std::cout << "acquired image" << std::endl;
// Wait for image to be available and draw
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &imageAvailableSemaphore;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &renderingFinishedSemaphore;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &presentCommandBuffers[imageIndex];
if (vkQueueSubmit(presentQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
std::cerr << "failed to submit draw command buffer" << std::endl;
assert(0);
}
std::cout << "submitted draw command buffer" << std::endl;
// Present drawn image
// Note: semaphore here is not strictly necessary, because commands are processed in submission order within a single queue
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &renderingFinishedSemaphore;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapChain;
presentInfo.pImageIndices = &imageIndex;
res = vkQueuePresentKHR(presentQueue, &presentInfo);
if (res != VK_SUCCESS) {
std::cerr << "failed to submit present command buffer" << std::endl;
assert(0);
}
std::cout << "submitted presentation command buffer" << std::endl;
}
2018-08-26 19:52:21 +02:00
void CreateRenderPass()
{
VkAttachmentReference attachRef = {};
attachRef.attachment = 0;
attachRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpassDesc = {};
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &attachRef;
VkAttachmentDescription attachDesc = {};
attachDesc.format = VkFormat::VK_FORMAT_R8G8B8A8_UNORM; //Todo
attachDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachDesc.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
attachDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkRenderPassCreateInfo renderPassCreateInfo = {};
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCreateInfo.attachmentCount = 1;
renderPassCreateInfo.pAttachments = &attachDesc;
renderPassCreateInfo.subpassCount = 1;
renderPassCreateInfo.pSubpasses = &subpassDesc;
VkResult res = vkCreateRenderPass(device, &renderPassCreateInfo, NULL, &renderPass);
printf("Created a render pass\n");
}
void CreateFramebuffer()
{
fbs.resize(swapChainImages.size());
VkResult res;
for (uint32_t i = 0; i < swapChainImages.size(); i++) {
VkImageViewCreateInfo ViewCreateInfo = {};
ViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
ViewCreateInfo.image = swapChainImages[i];
ViewCreateInfo.format = VkFormat::VK_FORMAT_R8G8B8A8_UNORM; //Todo
ViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
ViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
ViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
ViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
ViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
ViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
ViewCreateInfo.subresourceRange.baseMipLevel = 0;
ViewCreateInfo.subresourceRange.levelCount = 1;
ViewCreateInfo.subresourceRange.baseArrayLayer = 0;
ViewCreateInfo.subresourceRange.layerCount = 1;
res = vkCreateImageView(device, &ViewCreateInfo, NULL, &views[i]);
VkFramebufferCreateInfo fbCreateInfo = {};
fbCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbCreateInfo.renderPass = renderPass;
fbCreateInfo.attachmentCount = 1;
fbCreateInfo.pAttachments = &views[i];
fbCreateInfo.width = WINDOW_WIDTH;
fbCreateInfo.height = WINDOW_HEIGHT;
fbCreateInfo.layers = 1;
res = vkCreateFramebuffer(device, &fbCreateInfo, NULL, &fbs[i]);
}
printf("Frame buffers created\n");
}
VkShaderModule VulkanCreateShaderModule(VkDevice& device, char* code)
{
int codeSize = strlen(code);
VkShaderModuleCreateInfo shaderCreateInfo = {};
shaderCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderCreateInfo.codeSize = codeSize;
shaderCreateInfo.pCode = (const uint32_t*)code;
VkShaderModule shaderModule;
VkResult res = vkCreateShaderModule(device, &shaderCreateInfo, NULL, &shaderModule);
printf("Created shader\n");
return shaderModule;
}
void CreateShaders()
{
char* vptr = (char*)malloc(strlen(vertShader) + 1);
memcpy(vptr, vertShader, strlen(vertShader) + 1);
char* fptr = (char*)malloc(strlen(fragShader) + 1);
memcpy(fptr, fragShader, strlen(fragShader) + 1);
vsModule = VulkanCreateShaderModule(device, vptr);
assert(vsModule);
fsModule = VulkanCreateShaderModule(device, fptr);
assert(fsModule);
free(vptr);
free(fptr);
}
#define VERTEX_BUFFER_BIND_ID 0
void CreatePipeline()
{
VkPipelineShaderStageCreateInfo shaderStageCreateInfo[2] = {};
shaderStageCreateInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageCreateInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shaderStageCreateInfo[0].module = vsModule;
shaderStageCreateInfo[0].pName = "main";
shaderStageCreateInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStageCreateInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shaderStageCreateInfo[1].module = fsModule;
shaderStageCreateInfo[1].pName = "main";
2018-09-08 21:53:22 +02:00
VkVertexInputBindingDescription vertexInputBindingDescription =
{
0,
sizeof(float) * 2,
VK_VERTEX_INPUT_RATE_VERTEX
};
VkVertexInputAttributeDescription vertexInputAttributeDescription =
{
0,
0,
VK_FORMAT_R32G32_SFLOAT,
0
};
2018-08-26 19:52:21 +02:00
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
2018-09-08 21:53:22 +02:00
vertexInputInfo.vertexAttributeDescriptionCount = 1;
vertexInputInfo.pVertexAttributeDescriptions = &vertexInputAttributeDescription;
vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = &vertexInputBindingDescription;
2018-08-26 19:52:21 +02:00
VkPipelineInputAssemblyStateCreateInfo pipelineIACreateInfo = {};
pipelineIACreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pipelineIACreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkViewport vp = {};
vp.x = 0.0f;
vp.y = 0.0f;
vp.width = (float)WINDOW_WIDTH;
vp.height = (float)WINDOW_HEIGHT;
vp.minDepth = 0.0f;
vp.maxDepth = 1.0f;
VkPipelineViewportStateCreateInfo vpCreateInfo = {};
vpCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vpCreateInfo.viewportCount = 1;
vpCreateInfo.pViewports = &vp;
VkPipelineRasterizationStateCreateInfo rastCreateInfo = {};
rastCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rastCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
2018-09-08 21:53:22 +02:00
rastCreateInfo.cullMode = VK_CULL_MODE_NONE;
2018-08-26 19:52:21 +02:00
rastCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rastCreateInfo.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo pipelineMSCreateInfo = {};
pipelineMSCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
VkPipelineColorBlendAttachmentState blendAttachState = {};
blendAttachState.colorWriteMask = 0xf;
2018-09-08 21:53:22 +02:00
blendAttachState.blendEnable = false;
2018-08-26 19:52:21 +02:00
VkPipelineColorBlendStateCreateInfo blendCreateInfo = {};
blendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blendCreateInfo.attachmentCount = 1;
blendCreateInfo.pAttachments = &blendAttachState;
2018-09-08 21:53:22 +02:00
VkPipelineDepthStencilStateCreateInfo depthStencilState = {};
depthStencilState.depthTestEnable = false;
depthStencilState.stencilTestEnable = false;
2018-08-26 19:52:21 +02:00
VkGraphicsPipelineCreateInfo pipelineInfo = {};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = &shaderStageCreateInfo[0];
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &pipelineIACreateInfo;
pipelineInfo.pViewportState = &vpCreateInfo;
pipelineInfo.pRasterizationState = &rastCreateInfo;
pipelineInfo.pMultisampleState = &pipelineMSCreateInfo;
pipelineInfo.pColorBlendState = &blendCreateInfo;
pipelineInfo.renderPass = renderPass;
pipelineInfo.basePipelineIndex = -1;
2018-09-08 21:53:22 +02:00
pipelineInfo.pDepthStencilState = &depthStencilState;
2018-08-26 19:52:21 +02:00
VkResult res = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, NULL, &pipeline);
printf("Graphics pipeline created\n");
}
2018-09-08 18:53:51 +02:00
uint32_t getMemoryTypeIndex(VkPhysicalDeviceMemoryProperties deviceMemoryProperties, uint32_t typeBits, VkMemoryPropertyFlags properties)
{
// Iterate over all memory types available for the device used in this example
for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
{
if ((typeBits & 1) == 1)
{
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
return i;
}
}
typeBits >>= 1;
}
assert(0);
}
void CreateVertexBuffer()
{
unsigned vboSize = sizeof(float) * 2 * 3; //3 x vec2
VkMemoryRequirements mr;
VkBuffer stagingVertexBuffer;
VkDeviceMemory stagingVertexBufferMemory;
{ //create staging buffer
VkBufferCreateInfo stagingci = {};
stagingci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
stagingci.size = vboSize;
stagingci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VkResult res = vkCreateBuffer(device, &stagingci, 0, &stagingVertexBuffer);
vkGetBufferMemoryRequirements(device, stagingVertexBuffer, &mr);
VkMemoryAllocateInfo stagingmai = {};
stagingmai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
stagingmai.allocationSize = mr.size;
stagingmai.memoryTypeIndex = getMemoryTypeIndex(pdmp, mr.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
res = vkAllocateMemory(device, &stagingmai, 0, &stagingVertexBufferMemory);
float vertices[] =
{
-1, -1,
1, -1,
0, 1
};
void* data;
res = vkMapMemory(device, stagingVertexBufferMemory, 0, mr.size, 0, &data);
memcpy(data, vertices, mr.size);
vkUnmapMemory(device, stagingVertexBufferMemory);
res = vkBindBufferMemory(device, stagingVertexBuffer, stagingVertexBufferMemory, 0);
}
{ //final vertex buffer
VkBufferCreateInfo ci = {};
ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
ci.size = vboSize;
ci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VkResult res = vkCreateBuffer(device, &ci, 0, &vertexBuffer);
VkMemoryAllocateInfo mai = {};
mai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mai.allocationSize = mr.size;
mai.memoryTypeIndex = getMemoryTypeIndex(pdmp, mr.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
res = vkAllocateMemory(device, &mai, 0, &vertexBufferMemory);
res = vkBindBufferMemory(device, vertexBuffer, vertexBufferMemory, 0);
}
{ //copy from staging to final
VkCommandBuffer cmdBuffer;
VkCommandBufferAllocateInfo cmdBufAllocateInfo = {};
cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufAllocateInfo.commandPool = commandPool;
cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmdBufAllocateInfo.commandBufferCount = 1;
VkResult res = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &cmdBuffer);
// If requested, also start the new command buffer
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
res = vkBeginCommandBuffer(cmdBuffer, &beginInfo);
VkBufferCopy copyRegion = {};
copyRegion.size = vboSize;
vkCmdCopyBuffer(cmdBuffer, stagingVertexBuffer, vertexBuffer, 1, &copyRegion);
res = vkEndCommandBuffer(cmdBuffer);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmdBuffer;
// Create fence to ensure that the command buffer has finished executing
VkFenceCreateInfo fenceCreateInfo = {};
fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceCreateInfo.flags = 0;
VkFence fence;
res = vkCreateFence(device, &fenceCreateInfo, nullptr, &fence);
// Submit to the queue
res = vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence);
// Wait for the fence to signal that command buffer has finished executing
res = vkWaitForFences(device, 1, &fence, VK_TRUE, -1);
vkDestroyFence(device, fence, nullptr);
vkFreeCommandBuffers(device, commandPool, 1, &cmdBuffer);
//clean up staging
vkDestroyBuffer(device, stagingVertexBuffer, 0);
vkFreeMemory(device, stagingVertexBufferMemory, 0);
}
printf("Vertex buffer created\n");
}
int main() {
// Note: dynamically loading loader may be a better idea to fail gracefully when Vulkan is not supported
// Create window for Vulkan
//glfwInit();
//glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
//glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
2018-08-26 19:52:21 +02:00
//window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "The 630 line cornflower blue window", nullptr, nullptr);
// Use Vulkan
setupVulkan();
mainLoop();
cleanup();
2018-08-26 19:52:21 +02:00
return 0;
}