Skip to content

Instantly share code, notes, and snippets.

@YukiSnowy
Last active March 20, 2024 19:10
Show Gist options
  • Star 51 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • 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
// g++ *.cpp -o vulkan -lSDL2main -lSDL2 -lvulkan-1
// 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 = {0.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]
//////////////////////////////////////////
#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 surfaceFormatsCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices, surface,
&surfaceFormatsCount,
nullptr);
surfaceFormats.resize(surfaceFormatsCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_devices, surface,
&surfaceFormatsCount,
surfaceFormats.data());
if(surfaceFormats[0].format != VK_FORMAT_B8G8R8A8_UNORM)
{
throw std::runtime_error("surfaceFormats[0].format != VK_FORMAT_B8G8R8A8_UNORM");
}
surfaceFormat = surfaceFormats[0];
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 = VK_PRESENT_MODE_FIFO_KHR;
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.

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