Created
September 8, 2021 14:56
-
-
Save paroj/4afe6e771d9fc4af83f537d675c0287d to your computer and use it in GitHub Desktop.
minimal vulkan.hpp with SDL2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SDL2/SDL.h> | |
#include <SDL2/SDL_vulkan.h> | |
#include <vulkan/vulkan.hpp> | |
#include <algorithm> | |
VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, | |
VkDebugUtilsMessageTypeFlagsEXT messageTypes, | |
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) | |
{ | |
auto types = vk::to_string((vk::DebugUtilsMessageTypeFlagsEXT)messageTypes); | |
auto severity = vk::to_string((vk::DebugUtilsMessageSeverityFlagBitsEXT)messageSeverity); | |
printf("%s%s: %s\n", severity.c_str(), types.c_str(), pCallbackData->pMessage); | |
return VK_FALSE; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
SDL_Init(SDL_INIT_VIDEO); | |
auto wpos = SDL_WINDOWPOS_CENTERED; | |
auto win = SDL_CreateWindow("vulkantest", wpos, wpos, 800, 600, SDL_WINDOW_VULKAN); | |
vk::InstanceCreateInfo createInfo; | |
SDL_Vulkan_GetInstanceExtensions(win, &createInfo.enabledExtensionCount, nullptr); | |
std::vector<const char*> extensions(createInfo.enabledExtensionCount); | |
SDL_Vulkan_GetInstanceExtensions(win, &createInfo.enabledExtensionCount, extensions.data()); | |
extensions.push_back("VK_EXT_debug_utils"); | |
extensions.push_back("VK_EXT_debug_report"); | |
createInfo.enabledExtensionCount = extensions.size(); | |
createInfo.ppEnabledExtensionNames = extensions.data(); | |
createInfo.enabledLayerCount = 1; | |
const char* validationLayers[] = {"VK_LAYER_KHRONOS_validation"}; | |
createInfo.ppEnabledLayerNames = validationLayers; | |
#if 0 | |
printf("requested extensions\n"); | |
for(auto e : extensions) | |
printf("\t%s\n", e); | |
printf("available layers\n"); | |
auto instanceLayers = vk::enumerateInstanceLayerProperties(); | |
for(auto l : instanceLayers) | |
printf("\t%s\n", l.layerName); | |
printf("available extensions\n"); | |
auto exts = vk::enumerateInstanceExtensionProperties(); | |
for(auto e : exts) | |
printf("\t%s\n", e.extensionName); | |
#endif | |
vk::DebugUtilsMessengerCreateInfoEXT messengerInfo( | |
{}, | |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | | |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, | |
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | | |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | | |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation, | |
debug_callback); | |
createInfo.pNext = &messengerInfo; | |
VkSurfaceKHR surface; | |
auto instance = vk::createInstanceUnique(createInfo); | |
{ // dummy scope for auto memory management | |
vk::DispatchLoaderDynamic loader(*instance, vk::Device()); | |
auto debugUtilsMessenger = | |
instance->createDebugUtilsMessengerEXTUnique(messengerInfo, nullptr, loader); | |
auto pdevice = instance->enumeratePhysicalDevices().front(); | |
auto properties = pdevice.getProperties(); | |
auto qproperties = pdevice.getQueueFamilyProperties(); | |
printf("device: %s | %s\n", properties.deviceName, vk::to_string(properties.deviceType).c_str()); | |
// find device graphics queue | |
uint32_t graphicsQidx = std::distance( | |
qproperties.begin(), | |
std::find_if(qproperties.begin(), qproperties.end(), [](const vk::QueueFamilyProperties& fp) { | |
return fp.queueFlags & vk::QueueFlagBits::eGraphics; | |
})); | |
// create logical device | |
float queuePriority = 1.0f; | |
vk::DeviceQueueCreateInfo deviceQInfo({}, graphicsQidx, 1, &queuePriority); | |
vk::DeviceCreateInfo deviceInfo({}, 1, &deviceQInfo); | |
deviceInfo.enabledExtensionCount = 1; | |
const char* dextensions[] = {"VK_KHR_swapchain"}; | |
deviceInfo.ppEnabledExtensionNames = dextensions; | |
auto device = pdevice.createDeviceUnique(deviceInfo); | |
auto graphicsQueue = device->getQueue(graphicsQidx, 0); | |
SDL_Vulkan_CreateSurface(win, VkInstance(*instance), &surface); | |
uint32_t presentQidx = -1; | |
if (pdevice.getSurfaceSupportKHR(graphicsQidx, surface)) | |
presentQidx = graphicsQidx; | |
if(presentQidx != graphicsQidx) | |
{ | |
printf("different queues are not handled!\n"); | |
return -1; | |
} | |
#if 0 | |
auto formats = pdevice.getSurfaceFormatsKHR(surface); | |
for(auto f : formats) | |
printf("\t%s - %s\n", vk::to_string(f.format).c_str(), vk::to_string(f.colorSpace).c_str()); | |
#endif | |
// create swapchain | |
auto surfaceCaps = pdevice.getSurfaceCapabilitiesKHR(surface); | |
vk::Extent2D swapchainExtent; | |
SDL_Vulkan_GetDrawableSize(win, (int*)&swapchainExtent.width, (int*)&swapchainExtent.height); | |
printf("%d x %d\n", swapchainExtent.width, swapchainExtent.height); | |
vk::SwapchainCreateInfoKHR swapChainInfo({}, surface, surfaceCaps.minImageCount, | |
vk::Format::eB8G8R8A8Srgb); | |
swapChainInfo.imageExtent = swapchainExtent; | |
swapChainInfo.presentMode = vk::PresentModeKHR::eFifo; | |
swapChainInfo.imageArrayLayers = 1; | |
swapChainInfo.imageUsage = | |
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst; | |
swapChainInfo.imageSharingMode = vk::SharingMode::eExclusive; | |
swapChainInfo.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity; | |
auto swapChain = device->createSwapchainKHRUnique(swapChainInfo); | |
auto images = device->getSwapchainImagesKHR(*swapChain); | |
// create command buffer | |
auto commandPool = device->createCommandPoolUnique({{}, graphicsQidx}); | |
auto commandBuffers = device->allocateCommandBuffersUnique( | |
{commandPool.get(), vk::CommandBufferLevel::ePrimary, uint32_t(images.size())}); | |
vk::CommandBufferBeginInfo cmdInfo(vk::CommandBufferUsageFlagBits::eSimultaneousUse); | |
vk::ClearColorValue clearCol(std::array<float, 4>{1, 0, 0, 0}); | |
vk::ImageSubresourceRange imgRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1); | |
vk::ImageMemoryBarrier imgBarrier; | |
imgBarrier.subresourceRange = imgRange; | |
for (size_t i = 0; i < commandBuffers.size(); i++) | |
{ | |
auto& cb = commandBuffers[i]; | |
cb->begin(cmdInfo); | |
// transition to General layout | |
imgBarrier.image = images[i]; | |
imgBarrier.oldLayout = vk::ImageLayout::eUndefined; // i.e. discard contents | |
imgBarrier.newLayout = vk::ImageLayout::eGeneral; | |
cb->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, | |
vk::DependencyFlags(), 0, nullptr, 0, nullptr, 1, &imgBarrier); | |
cb->clearColorImage(images[i], vk::ImageLayout::eGeneral, &clearCol, 1, &imgRange); | |
// transition to present layout | |
imgBarrier.oldLayout = vk::ImageLayout::eGeneral; | |
imgBarrier.newLayout = vk::ImageLayout::ePresentSrcKHR; | |
cb->pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, | |
vk::PipelineStageFlagBits::eBottomOfPipe, vk::DependencyFlags(), 0, nullptr, | |
0, nullptr, 1, &imgBarrier); | |
cb->end(); | |
} | |
auto imgSemaphore = device->createSemaphoreUnique({}); | |
vk::PipelineStageFlags waitStages[] = {vk::PipelineStageFlagBits::eColorAttachmentOutput}; | |
// render loop | |
for (int i = 0; i < 30; i++) | |
{ | |
auto imgIdx = device->acquireNextImageKHR(*swapChain, -1, *imgSemaphore, vk::Fence()); | |
vk::SubmitInfo submit; | |
submit.waitSemaphoreCount = 1; | |
submit.pWaitSemaphores = &imgSemaphore.get(); | |
submit.commandBufferCount = 1; | |
submit.pCommandBuffers = &commandBuffers[imgIdx.value].get(); | |
submit.pWaitDstStageMask = waitStages; | |
graphicsQueue.submit(1, &submit, vk::Fence()); | |
vk::PresentInfoKHR present; | |
present.swapchainCount = 1; | |
present.pSwapchains = &swapChain.get(); | |
present.pImageIndices = &imgIdx.value; | |
graphicsQueue.presentKHR(present); | |
} | |
device->waitIdle(); | |
} | |
instance->destroySurfaceKHR(surface); | |
SDL_DestroyWindow(win); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment