Skip to content

Instantly share code, notes, and snippets.

@YukiSnowy
Last active September 5, 2024 09:35
Show Gist options
  • Save YukiSnowy/dc31f47448ac61dd6aedee18b5d53858 to your computer and use it in GitHub Desktop.
Save YukiSnowy/dc31f47448ac61dd6aedee18b5d53858 to your computer and use it in GitHub Desktop.
example SDL2 Vulkan application
// Windows
// g++ *.cpp -o vulkan -lSDL2main -lSDL2 -lvulkan-1
// Linux
// g++ *.cpp -o vulkan -lSDL2main -lSDL2 -lvulkan
// https://vulkan-tutorial.com/
#include <iostream>
using namespace std;
#include <SDL2/SDL.h>
SDL_Window *window;
char* window_name = "example SDL2 Vulkan application";
#include "vulkan_extern.h"
#include "vulkan_function.h"
Vulkan *vulkan;
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow(window_name,SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,800,600,SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN);
vulkan = new Vulkan();
init_vulkan_extern(vulkan);
SDL_Event event;
bool running = true;
while(running)
{
while(SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT)
{
running = false;
}
}
AcquireNextImage();
ResetCommandBuffer();
BeginCommandBuffer();
{
VkClearColorValue clear_color = {1.0f, 0.0f, 0.0f, 1.0f};
VkClearDepthStencilValue clear_depth_stencil = {1.0f, 0};
BeginRenderPass(clear_color,clear_depth_stencil);
{
}
EndRenderPass();
}
EndCommandBuffer();
QueueSubmit();
QueuePresent();
}
delete vulkan;
vulkan = nullptr;
SDL_DestroyWindow(window);
window = nullptr;
SDL_Quit();
return 0;
}
#include "vulkan_extern.h"
void init_vulkan_extern(Vulkan *vulkan)
{
////////////////////////////////////////////////////
/////// [Core]
//////////////////////////////////////////
vulkan->Create_Instance();
vulkan->Create_Debug();
vulkan->Create_Surface();
vulkan->Select_PhysicalDevice();
vulkan->Select_QueueFamily();
vulkan->Create_Device();
////////////////////////////////////////////////////
/////// [Screen]
//////////////////////////////////////////
bool test = vulkan->Create_Swapchain(false);
vulkan->Create_ImageViews();
vulkan->Setup_DepthStencil();
vulkan->Create_RenderPass();
vulkan->Create_Framebuffers();
///////////////////////////////////////////////////////////
vulkan->createCommandPool();
vulkan->createCommandBuffers();
vulkan->create_semaphores();
vulkan->createFences();
}
Vulkan::Vulkan()
{
}
Vulkan::~Vulkan()
{
}
////////////////////////////////////////////////////
/////// [Core]
//////////////////////////////////////////
#include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
extern SDL_Window *window;
extern char* window_name;
const vector<const char*> validationLayers = {
///has bug
//"VK_LAYER_LUNARG_standard_validation"
};
void Vulkan::Create_Instance()
{
unsigned int extensionCount = 0;
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr);
vector<const char *> extensionNames(extensionCount);
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensionNames.data());
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = window_name;
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo instanceCreateInfo = {};
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pApplicationInfo = &appInfo;
instanceCreateInfo.enabledLayerCount = validationLayers.size();
instanceCreateInfo.ppEnabledLayerNames = validationLayers.data();
instanceCreateInfo.enabledExtensionCount = extensionNames.size();
instanceCreateInfo.ppEnabledExtensionNames = extensionNames.data();
vkCreateInstance(&instanceCreateInfo, nullptr, &instance);
}
static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanReportFunc(
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objType,
uint64_t obj,
size_t location,
int32_t code,
const char* layerPrefix,
const char* msg,
void* userData)
{
printf("VULKAN VALIDATION: %s\n", msg);
return VK_FALSE;
}
PFN_vkCreateDebugReportCallbackEXT SDL2_vkCreateDebugReportCallbackEXT = nullptr;
void Vulkan::Create_Debug()
{
SDL2_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)SDL_Vulkan_GetVkGetInstanceProcAddr();
VkDebugReportCallbackCreateInfoEXT debugCallbackCreateInfo = {};
debugCallbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
debugCallbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
debugCallbackCreateInfo.pfnCallback = VulkanReportFunc;
SDL2_vkCreateDebugReportCallbackEXT(instance, &debugCallbackCreateInfo, 0, &debugCallback);
}
void Vulkan::Create_Surface()
{
SDL_Vulkan_CreateSurface(window, instance, &surface);
}
void Vulkan::Select_PhysicalDevice()
{
vector<VkPhysicalDevice> physicalDevices;
uint32_t physicalDeviceCount = 0;
vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr);
physicalDevices.resize(physicalDeviceCount);
vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data());
physical_devices = physicalDevices[0];
}
void Vulkan::Select_QueueFamily()
{
vector<VkQueueFamilyProperties> queueFamilyProperties;
uint32_t queueFamilyCount;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices, &queueFamilyCount, nullptr);
queueFamilyProperties.resize(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices, &queueFamilyCount, queueFamilyProperties.data());
int graphicIndex = -1;
int presentIndex = -1;
int i = 0;
for(const auto& queueFamily : queueFamilyProperties)
{
if(queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
graphicIndex = i;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices, i, surface, &presentSupport);
if(queueFamily.queueCount > 0 && presentSupport)
{
presentIndex = i;
}
if(graphicIndex != -1 && presentIndex != -1)
{
break;
}
i++;
}
graphics_QueueFamilyIndex = graphicIndex;
present_QueueFamilyIndex = presentIndex;
}
#include <set>
void Vulkan::Create_Device()
{
const std::vector<const char*> deviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
const float queue_priority[] = { 1.0f };
vector<VkDeviceQueueCreateInfo> queueCreateInfos;
set<uint32_t> uniqueQueueFamilies = { graphics_QueueFamilyIndex, present_QueueFamilyIndex };
float queuePriority = queue_priority[0];
for(int 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);
}
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = graphics_QueueFamilyIndex;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
//https://en.wikipedia.org/wiki/Anisotropic_filtering
VkPhysicalDeviceFeatures deviceFeatures = {};
deviceFeatures.samplerAnisotropy = VK_TRUE;
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = queueCreateInfos.size();
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledExtensionCount = deviceExtensions.size();
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
createInfo.enabledLayerCount = validationLayers.size();
createInfo.ppEnabledLayerNames = validationLayers.data();
vkCreateDevice(physical_devices, &createInfo, nullptr, &device);
vkGetDeviceQueue(device, graphics_QueueFamilyIndex, 0, &graphicsQueue);
vkGetDeviceQueue(device, present_QueueFamilyIndex, 0, &presentQueue);
}
////////////////////////////////////////////////////
/////// [Screen]
//////////////////////////////////////////
VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats)
{
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)
{
for (const auto& availablePresentMode : availablePresentModes)
{
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
{
return availablePresentMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
#define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x))
bool Vulkan::Create_Swapchain(bool resize)
{
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_devices, surface, &surfaceCapabilities);
vector<VkSurfaceFormatKHR> surfaceFormats;
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices, surface, &formatCount, nullptr);
if (formatCount != 0)
{
surfaceFormats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices, surface, &formatCount, surfaceFormats.data());
}
vector<VkPresentModeKHR> presentModes;
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_devices, surface, &presentModeCount, nullptr);
if (presentModeCount != 0)
{
presentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(physical_devices, surface, &presentModeCount, presentModes.data());
}
surfaceFormat = chooseSwapSurfaceFormat(surfaceFormats);
VkPresentModeKHR presentMode = chooseSwapPresentMode(presentModes);
int width,height = 0;
SDL_Vulkan_GetDrawableSize(window, &width, &height);
width = CLAMP(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
height = CLAMP(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
swapchainSize.width = width;
swapchainSize.height = height;
uint32_t imageCount = surfaceCapabilities.minImageCount + 1;
if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) {
imageCount = surfaceCapabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = surface;
createInfo.minImageCount = surfaceCapabilities.minImageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageExtent = swapchainSize;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
uint32_t queueFamilyIndices[] = {graphics_QueueFamilyIndex, present_QueueFamilyIndex};
if (graphics_QueueFamilyIndex != present_QueueFamilyIndex)
{
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
}
else
{
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
createInfo.preTransform = surfaceCapabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain);
vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, nullptr);
swapchainImages.resize(swapchainImageCount);
vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages.data());
return true;
}
//global createImageView
VkImageView Vulkan::createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags)
{
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = aspectFlags;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView imageView;
if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS)
{
throw std::runtime_error("failed to create texture image view!");
}
return imageView;
}
void Vulkan::Create_ImageViews()
{
swapchainImageViews.resize(swapchainImages.size());
for (uint32_t i = 0; i < swapchainImages.size(); i++)
{
swapchainImageViews[i] = createImageView(swapchainImages[i], surfaceFormat.format, VK_IMAGE_ASPECT_COLOR_BIT);
}
}
VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat)
{
std::vector<VkFormat> depthFormats = {
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM
};
for (auto& format : depthFormats)
{
VkFormatProperties formatProps;
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
*depthFormat = format;
return true;
}
}
return false;
}
//global findMemoryType
uint32_t Vulkan::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties)
{
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(physical_devices, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
{
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
return i;
}
}
throw std::runtime_error("failed to find suitable memory type!");
}
//global createImage
void Vulkan::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image,
VkDeviceMemory& imageMemory)
{
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS)
{
throw std::runtime_error("failed to create image!");
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(device, image, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate image memory!");
}
vkBindImageMemory(device, image, imageMemory, 0);
}
void Vulkan::Setup_DepthStencil()
{
VkBool32 validDepthFormat = getSupportedDepthFormat(physical_devices, &depthFormat);
createImage(swapchainSize.width, swapchainSize.height,
VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
depthImage, depthImageMemory);
depthImageView = createImageView(depthImage, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT);
}
void Vulkan::Create_RenderPass()
{
vector<VkAttachmentDescription> attachments(2);
attachments[0].format = surfaceFormat.format;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
attachments[1].format = depthFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference colorReference = {};
colorReference.attachment = 0;
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depthReference = {};
depthReference.attachment = 1;
depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpassDescription = {};
subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDescription.colorAttachmentCount = 1;
subpassDescription.pColorAttachments = &colorReference;
subpassDescription.pDepthStencilAttachment = &depthReference;
subpassDescription.inputAttachmentCount = 0;
subpassDescription.pInputAttachments = nullptr;
subpassDescription.preserveAttachmentCount = 0;
subpassDescription.pPreserveAttachments = nullptr;
subpassDescription.pResolveAttachments = nullptr;
vector<VkSubpassDependency> dependencies(1);
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpassDescription;
renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
renderPassInfo.pDependencies = dependencies.data();
vkCreateRenderPass(device, &renderPassInfo, nullptr, &render_pass);
}
void Vulkan::Create_Framebuffers()
{
swapchainFramebuffers.resize(swapchainImageViews.size());
for (size_t i = 0; i < swapchainImageViews.size(); i++)
{
std::vector<VkImageView> attachments(2);
attachments[0] = swapchainImageViews[i];
attachments[1] = depthImageView;
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = render_pass;
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = swapchainSize.width;
framebufferInfo.height = swapchainSize.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchainFramebuffers[i]) != VK_SUCCESS)
{
throw std::runtime_error("failed to create framebuffer!");
}
}
}
void Vulkan::createCommandPool()
{
VkResult result;
VkCommandPoolCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
createInfo.queueFamilyIndex = graphics_QueueFamilyIndex;
vkCreateCommandPool(device, &createInfo, nullptr, &commandPool);
}
void Vulkan::createCommandBuffers()
{
VkResult result;
VkCommandBufferAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.commandPool = commandPool;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocateInfo.commandBufferCount = swapchainImageCount;
commandBuffers.resize(swapchainImageCount);
vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data());
}
void Vulkan::createSemaphore(VkSemaphore *semaphore)
{
VkResult result;
VkSemaphoreCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
vkCreateSemaphore(device, &createInfo, nullptr, semaphore);
}
void Vulkan::create_semaphores()
{
createSemaphore(&imageAvailableSemaphore);
createSemaphore(&renderingFinishedSemaphore);
}
void Vulkan::createFences()
{
uint32_t i;
fences.resize(swapchainImageCount);
for(i = 0; i < swapchainImageCount; i++)
{
VkResult result;
VkFenceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
createInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
vkCreateFence(device, &createInfo, nullptr, &fences[i]);
}
}
#ifndef Vulkan_Extern_H
#define Vulkan_Extern_H
#include <vulkan/vulkan.h>
#include <iostream>
#include <vector>
using namespace std;
class Vulkan
{
private:
void createSemaphore(VkSemaphore *semaphore);
public:
Vulkan();
~Vulkan();
//global createImageView
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags);
//global createImage
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory);
//global findMemoryType
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
////////////////////////////////////////////////////
/////// [Core]
//////////////////////////////////////////
VkInstance instance;
vector<VkExtensionProperties> instance_extension;
void Create_Instance();
VkDebugReportCallbackEXT debugCallback;
void Create_Debug();
VkSurfaceKHR surface;
void Create_Surface();
VkPhysicalDevice physical_devices;
void Select_PhysicalDevice();
uint32_t graphics_QueueFamilyIndex;
uint32_t present_QueueFamilyIndex;
void Select_QueueFamily();
VkDevice device;
VkQueue graphicsQueue;
VkQueue presentQueue;
void Create_Device();
////////////////////////////////////////////////////
/////// [Screen]
//////////////////////////////////////////
VkSwapchainKHR swapchain;//
VkSurfaceCapabilitiesKHR surfaceCapabilities;//
VkSurfaceFormatKHR surfaceFormat;//
VkExtent2D swapchainSize;//
vector<VkImage> swapchainImages;//
uint32_t swapchainImageCount;//
bool Create_Swapchain(bool resize);
vector<VkImageView> swapchainImageViews;
void Create_ImageViews();
VkFormat depthFormat;//
VkImage depthImage;//
VkDeviceMemory depthImageMemory;//
VkImageView depthImageView;//
void Setup_DepthStencil();//
VkRenderPass render_pass;//
void Create_RenderPass();//
vector<VkFramebuffer> swapchainFramebuffers;
void Create_Framebuffers();
///////////////////////////////////////////////////////////
VkCommandPool commandPool;
void createCommandPool();
vector<VkCommandBuffer> commandBuffers;
void createCommandBuffers();
VkSemaphore imageAvailableSemaphore;
VkSemaphore renderingFinishedSemaphore;
void create_semaphores();
vector<VkFence> fences;
void createFences();
};
void init_vulkan_extern(Vulkan *vulkan);
#endif
#include "vulkan_function.h"
#include "vulkan_extern.h"
extern Vulkan *vulkan;
uint32_t frameIndex;
VkCommandBuffer commandBuffer;
VkImage image;
void AcquireNextImage()
{
vkAcquireNextImageKHR( vulkan->device,
vulkan->swapchain,
UINT64_MAX,
vulkan->imageAvailableSemaphore,
VK_NULL_HANDLE,
&frameIndex);
vkWaitForFences(vulkan->device, 1, &vulkan->fences[frameIndex], VK_FALSE, UINT64_MAX);
vkResetFences(vulkan->device, 1, &vulkan->fences[frameIndex]);
commandBuffer = vulkan->commandBuffers[frameIndex];
image = vulkan->swapchainImages[frameIndex];
}
void ResetCommandBuffer()
{
vkResetCommandBuffer(commandBuffer, 0);
}
void BeginCommandBuffer()
{
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
}
void EndCommandBuffer()
{
vkEndCommandBuffer(commandBuffer);
}
void FreeCommandBuffers()
{
vkFreeCommandBuffers(vulkan->device, vulkan->commandPool, 1, &commandBuffer);
}
void BeginRenderPass(VkClearColorValue clear_color,VkClearDepthStencilValue clear_depth_stencil)
{
VkRenderPassBeginInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = vulkan->render_pass;render_pass_info.framebuffer = vulkan->swapchainFramebuffers[frameIndex];
render_pass_info.renderArea.offset = {0, 0};
render_pass_info.renderArea.extent = vulkan->swapchainSize;
render_pass_info.clearValueCount = 1;
vector<VkClearValue> clearValues(2);
clearValues[0].color = clear_color;
clearValues[1].depthStencil = clear_depth_stencil;
render_pass_info.clearValueCount = static_cast<uint32_t>(clearValues.size());
render_pass_info.pClearValues = clearValues.data();
vkCmdBeginRenderPass(commandBuffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
}
void EndRenderPass()
{
vkCmdEndRenderPass(commandBuffer);
}
VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
void QueueSubmit()
{
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &vulkan->imageAvailableSemaphore;
submitInfo.pWaitDstStageMask = &waitDestStageMask;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &vulkan->renderingFinishedSemaphore;
vkQueueSubmit(vulkan->graphicsQueue, 1, &submitInfo, vulkan->fences[frameIndex]);
}
void QueuePresent()
{
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &vulkan->renderingFinishedSemaphore;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &vulkan->swapchain;
presentInfo.pImageIndices = &frameIndex;
vkQueuePresentKHR(vulkan->presentQueue, &presentInfo);
vkQueueWaitIdle(vulkan->presentQueue);
}
void SetViewport(int width,int height)
{
VkViewport viewport;
viewport.width = (float)width / 2;
viewport.height = (float)height;
viewport.minDepth = (float)0.0f;
viewport.maxDepth = (float)1.0f;
viewport.x = 0;
viewport.y = 0;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
}
void SetScissor(int width,int height)
{
VkRect2D scissor;
scissor.extent.width = width / 2;
scissor.extent.height = height;
scissor.offset.x = 0;
scissor.offset.y = 0;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
}
#ifndef Vulkan_Function_H
#define Vulkan_Function_H
#include <vulkan/vulkan.h>
void AcquireNextImage();
void ResetCommandBuffer();
void BeginCommandBuffer();
void EndCommandBuffer();
void FreeCommandBuffers();
void BeginRenderPass(VkClearColorValue clear_color,VkClearDepthStencilValue clear_depth_stencil);
void EndRenderPass();
void QueueSubmit();
void QueuePresent();
void SetViewport(int width,int height);
void SetScissor(int width,int height);
#endif
@ctreffs
Copy link

ctreffs commented Jun 10, 2021

Thanks for your example - it's great!
I had however to use the following statement on macOS to make it run:

g++ -std=c++11 *.cpp -o vulkan -lSDL2main -lSDL2 -lvulkan

@dilavni
Copy link

dilavni commented Aug 28, 2021

#include <SDL2/SDL_Vulkan.h> is not portable. the filename is "SDL_vulkan.h"; v is not capitalized.

@cdgiessen
Copy link

Line 99 in vulkan_extern.cpp is straight up wrong. It is casting vkGetInstanceProcAddr to vkCreateDebugReportCallbackEXT.

PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = reinterpret-cast<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(vkInstance, "vkCreateDebugReportCallbackEXT"));

is correct.
Except that this doesn't enable the Debug Report extension either, which means that the above code should return NULL.

@raiguard
Copy link

Attempting to run this on Fedora, Sway 1.8, and it gives the following error:

rai@tantal ~/dev/experiments/sdl2-vulkan
$ g++ *.cpp -o vulkan -lSDL2main -lSDL2 -lvulkan
main.cpp:9:21: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
    9 | char* window_name = "example SDL2 Vulkan application";
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rai@tantal ~/dev/experiments/sdl2-vulkan
$ ./vulkan
terminate called after throwing an instance of 'std::runtime_error'
  what():  surfaceFormats[0].format != VK_FORMAT_B8G8R8A8_UNORM
The process was killed by SIGABRT: Aborted

I tried both X11 and Wayland, no difference.

@YukiSnowy
Copy link
Author

YukiSnowy commented Sep 5, 2024

Attempting to run this on Fedora, Sway 1.8, and it gives the following error:

rai@tantal ~/dev/experiments/sdl2-vulkan
$ g++ *.cpp -o vulkan -lSDL2main -lSDL2 -lvulkan
main.cpp:9:21: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
    9 | char* window_name = "example SDL2 Vulkan application";
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rai@tantal ~/dev/experiments/sdl2-vulkan
$ ./vulkan
terminate called after throwing an instance of 'std::runtime_error'
  what():  surfaceFormats[0].format != VK_FORMAT_B8G8R8A8_UNORM
The process was killed by SIGABRT: Aborted

I tried both X11 and Wayland, no difference.

i update VK_FORMAT_B8G8R8A8_UNORM to VK_FORMAT_B8G8R8A8_SRGB because old code and old sdk version (vulkan sdk is update)
you can compile and try again (i tried then it's work on debian 12)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment