Skip to content

Instantly share code, notes, and snippets.

@beholdnec
Created August 4, 2016 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beholdnec/67bd2d035d57ebee242f7be574df66df to your computer and use it in GitHub Desktop.
Save beholdnec/67bd2d035d57ebee242f7be574df66df to your computer and use it in GitHub Desktop.
Vulkan API test; exhibits glitches
// Vulkan Test
// N.E.C.
#include <cstdio>
#include <condition_variable>
#include <thread>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <windowsx.h>
#define VK_USE_PLATFORM_WIN32_KHR
#include <vulkan/vulkan.h>
static const LPCTSTR WINDOW_CLASS_NAME = TEXT("VulkanTestWindowClass");
static HWND s_hWnd = NULL;
static VkInstance s_vkInstance = VK_NULL_HANDLE;
static VkPhysicalDevice s_vkPhysDevice = VK_NULL_HANDLE;
static VkDevice s_vkDevice = VK_NULL_HANDLE;
static VkSurfaceKHR s_vkSurface = VK_NULL_HANDLE;
static VkSwapchainKHR s_vkSwapchain = VK_NULL_HANDLE;
static std::vector<VkImage> s_vkSwapchainImages;
static VkQueue s_vkQueue = VK_NULL_HANDLE;
static VkPhysicalDeviceMemoryProperties s_vkMemProperties = {};
static VkCommandPool s_vkCmdPool = VK_NULL_HANDLE;
static VkSemaphore s_imageAcquiredSemaphore = VK_NULL_HANDLE;
static VkSemaphore s_drawDoneSemaphore = VK_NULL_HANDLE;
static const int TEX_WIDTH = 512;
static const int TEX_HEIGHT = 512;
static VkImage s_tex = VK_NULL_HANDLE;
static VkDeviceMemory s_texMem = VK_NULL_HANDLE;
// Render thread signals.
// Do not modify these variables without locking the mutex s_renderThreadSignalMutex.
// After modifying these variables, notify the condition variable s_renderThreadSignal.
static bool s_quit = false;
static bool s_recreateSwapchain = false;
static bool s_redraw = false;
static int s_x = 0;
static int s_y = 0;
static std::mutex s_renderThreadSignalMutex;
static std::condition_variable s_renderThreadSignal;
static LRESULT CALLBACK VulkanGXWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOUSEMOVE:
{
std::lock_guard<std::mutex> lk(s_renderThreadSignalMutex);
s_x = GET_X_LPARAM(lParam);
s_y = GET_Y_LPARAM(lParam);
s_redraw = true;
}
s_renderThreadSignal.notify_all();
return 0;
case WM_SIZE:
{
std::lock_guard<std::mutex> lk(s_renderThreadSignalMutex);
s_recreateSwapchain = true;
s_redraw = true;
}
s_renderThreadSignal.notify_all();
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
static void RecreateSwapchain()
{
VkResult result;
if (s_vkSwapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(s_vkDevice, s_vkSwapchain, NULL);
s_vkSwapchain = VK_NULL_HANDLE;
}
s_vkSwapchainImages.clear();
VkSurfaceCapabilitiesKHR surfCaps = {};
result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s_vkPhysDevice, s_vkSurface, &surfCaps);
uint32_t surfFormatCount = 0;
result = vkGetPhysicalDeviceSurfaceFormatsKHR(s_vkPhysDevice, s_vkSurface, &surfFormatCount, NULL);
// Just use the first surface format
std::vector<VkSurfaceFormatKHR> surfFormats(surfFormatCount);
result = vkGetPhysicalDeviceSurfaceFormatsKHR(s_vkPhysDevice, s_vkSurface, &surfFormatCount, &surfFormats[0]);
uint32_t presentModeCount = 0;
vkGetPhysicalDeviceSurfacePresentModesKHR(s_vkPhysDevice, s_vkSurface, &presentModeCount, NULL);
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(s_vkPhysDevice, s_vkSurface, &presentModeCount, &presentModes[0]);
printf("creating swapchain with size %u, %u\n", (unsigned int)surfCaps.currentExtent.width, (unsigned int)surfCaps.currentExtent.height);
VkSwapchainCreateInfoKHR swapchainCreateInfo = {};
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchainCreateInfo.surface = s_vkSurface;
swapchainCreateInfo.minImageCount = surfCaps.minImageCount;
swapchainCreateInfo.imageFormat = surfFormats[0].format;
swapchainCreateInfo.imageColorSpace = surfFormats[0].colorSpace;
swapchainCreateInfo.imageExtent = surfCaps.currentExtent;
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
swapchainCreateInfo.preTransform = surfCaps.currentTransform;
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapchainCreateInfo.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
swapchainCreateInfo.clipped = VK_TRUE;
result = vkCreateSwapchainKHR(s_vkDevice, &swapchainCreateInfo, NULL, &s_vkSwapchain);
uint32_t numSwapImages = 0;
result = vkGetSwapchainImagesKHR(s_vkDevice, s_vkSwapchain, &numSwapImages, NULL);
s_vkSwapchainImages.resize(numSwapImages);
result = vkGetSwapchainImagesKHR(s_vkDevice, s_vkSwapchain, &numSwapImages, &s_vkSwapchainImages[0]);
}
static uint32_t SelectMemoryType(uint32_t memoryTypeBits, VkFlags requirements)
{
for (uint32_t i = 0; i < s_vkMemProperties.memoryTypeCount; ++i) {
if ((memoryTypeBits & (1 << i)) &&
((s_vkMemProperties.memoryTypes[i].propertyFlags & requirements) == requirements)) {
return i;
}
}
return ~0UL;
}
static uint16_t makeRgb565(unsigned int r, unsigned int g, unsigned int b) {
return (r << 11) | (g << 5) | b;
}
static void Render()
{
//printf("rendering...\n");
VkResult result;
uint32_t imageIndex = 0;
result = vkAcquireNextImageKHR(s_vkDevice, s_vkSwapchain, 1000000000, s_imageAcquiredSemaphore, VK_NULL_HANDLE, &imageIndex);
if (result != VK_SUCCESS) {
printf("vkAcquireNextImageKHR unsuccessful\n");
return;
}
VkImage destImage = s_vkSwapchainImages[imageIndex];
VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
VkCommandBufferAllocateInfo cmdBufAllocInfo = {};
cmdBufAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufAllocInfo.commandBufferCount = 1;
cmdBufAllocInfo.commandPool = s_vkCmdPool;
result = vkAllocateCommandBuffers(s_vkDevice, &cmdBufAllocInfo, &cmdBuf);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
result = vkBeginCommandBuffer(cmdBuf, &beginInfo);
VkImageMemoryBarrier beginBarrier = {};
beginBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
beginBarrier.srcAccessMask = 0;
beginBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
beginBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
beginBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
beginBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
beginBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
beginBarrier.image = destImage;
beginBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, NULL,
0, NULL, 1, &beginBarrier);
VkImageSubresourceRange clearRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
VkClearColorValue clearColor;
clearColor.float32[0] = 0.2f;
clearColor.float32[1] = 0.1f;
clearColor.float32[2] = 0.6f;
clearColor.float32[3] = 1.0f;
vkCmdClearColorImage(cmdBuf, destImage, VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &clearRange);
VkImageBlit blitRegion = {};
blitRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
blitRegion.srcOffsets[0] = { 0, 0, 0 };
blitRegion.srcOffsets[1] = { TEX_WIDTH, TEX_HEIGHT, 1 };
blitRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
blitRegion.dstOffsets[0] = { s_x, s_y, 0 };
blitRegion.dstOffsets[1] = { s_x + TEX_WIDTH, s_y + TEX_HEIGHT, 1 };
vkCmdBlitImage(cmdBuf, s_tex, VK_IMAGE_LAYOUT_GENERAL, destImage, VK_IMAGE_LAYOUT_GENERAL, 1, &blitRegion, VK_FILTER_LINEAR);
VkImageMemoryBarrier presentBarrier = {};
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
presentBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
presentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
presentBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
presentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
presentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
presentBarrier.image = destImage;
presentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
0, 0, NULL, 0, NULL, 1, &presentBarrier);
result = vkEndCommandBuffer(cmdBuf);
VkPipelineStageFlags semaStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &s_imageAcquiredSemaphore;
submitInfo.pWaitDstStageMask = &semaStage;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmdBuf;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &s_drawDoneSemaphore;
result = vkQueueSubmit(s_vkQueue, 1, &submitInfo, VK_NULL_HANDLE);
VkResult presentResult = VK_SUCCESS;
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &s_drawDoneSemaphore;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &s_vkSwapchain;
presentInfo.pImageIndices = &imageIndex;
presentInfo.pResults = &presentResult;
result = vkQueuePresentKHR(s_vkQueue, &presentInfo);
vkQueueWaitIdle(s_vkQueue);
vkFreeCommandBuffers(s_vkDevice, s_vkCmdPool, 1, &cmdBuf);
}
static void RenderThread()
{
VkResult result;
const char* const LAYERS[] = {
"VK_LAYER_LUNARG_standard_validation",
};
const char* const EXTENSIONS[] = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
};
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.enabledLayerCount = sizeof(LAYERS) / sizeof(const char*);
createInfo.ppEnabledLayerNames = LAYERS;
createInfo.enabledExtensionCount = sizeof(EXTENSIONS) / sizeof(const char*);
createInfo.ppEnabledExtensionNames = EXTENSIONS;
result = vkCreateInstance(&createInfo, NULL, &s_vkInstance);
printf("vkCreateInstance result: %d\n", result);
uint32_t numPhysDevices = 1;
result = vkEnumeratePhysicalDevices(s_vkInstance, &numPhysDevices, &s_vkPhysDevice);
printf("vkEnumeratePhysicalDevices result: %d\n", result);
VkWin32SurfaceCreateInfoKHR win32SurfaceCreateInfo = {};
win32SurfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
win32SurfaceCreateInfo.hinstance = GetModuleHandle(NULL);
win32SurfaceCreateInfo.hwnd = s_hWnd;
result = vkCreateWin32SurfaceKHR(s_vkInstance, &win32SurfaceCreateInfo, NULL, &s_vkSurface);
printf("vkCreateWin32SurfaceKHR result: %d\n", result);
uint32_t queueCount;
vkGetPhysicalDeviceQueueFamilyProperties(s_vkPhysDevice, &queueCount, NULL);
printf("queue count: %u\n", (unsigned int)queueCount);
std::vector<VkQueueFamilyProperties> queueProps(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(s_vkPhysDevice, &queueCount, &queueProps[0]);
// Find a queue that supports both Graphics and Presenting
uint32_t queueNum = UINT32_MAX;
for (size_t i = 0; i < queueProps.size(); ++i) {
if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
VkBool32 supportsPresent = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(s_vkPhysDevice, i, s_vkSurface, &supportsPresent);
if (supportsPresent) {
queueNum = i;
break;
}
}
}
if (queueNum == UINT32_MAX) {
printf("No supported queue found\n");
return;
}
printf("Chosen queue: %u\n", (unsigned int)queueNum);
const float QUEUE_PRIORITIES[1] = { 0.0f };
VkDeviceQueueCreateInfo deviceQueueCreateInfo = {};
deviceQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo.queueFamilyIndex = queueNum;
deviceQueueCreateInfo.queueCount = 1;
deviceQueueCreateInfo.pQueuePriorities = QUEUE_PRIORITIES;
const char* const DEVICE_EXTENSIONS[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = &deviceQueueCreateInfo;
deviceCreateInfo.enabledExtensionCount = sizeof(DEVICE_EXTENSIONS) / sizeof(const char*);
deviceCreateInfo.ppEnabledExtensionNames = DEVICE_EXTENSIONS;
result = vkCreateDevice(s_vkPhysDevice, &deviceCreateInfo, NULL, &s_vkDevice);
printf("vkCreateDevice result: %d\n", result);
vkGetDeviceQueue(s_vkDevice, queueNum, 0, &s_vkQueue);
printf("vkGetDeviceQueue called\n");
VkCommandPoolCreateInfo cmdPoolCreateInfo = {};
cmdPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmdPoolCreateInfo.queueFamilyIndex = queueNum;
result = vkCreateCommandPool(s_vkDevice, &cmdPoolCreateInfo, NULL, &s_vkCmdPool);
printf("vkCreateCommandPool result: %d\n", result);
VkSemaphoreCreateInfo semaphoreCreateInfo = {};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
result = vkCreateSemaphore(s_vkDevice, &semaphoreCreateInfo, NULL, &s_imageAcquiredSemaphore);
printf("vkCreateSemaphore result: %d\n", result);
result = vkCreateSemaphore(s_vkDevice, &semaphoreCreateInfo, NULL, &s_drawDoneSemaphore);
printf("vkCreateSemaphore result: %d\n", result);
vkGetPhysicalDeviceMemoryProperties(s_vkPhysDevice, &s_vkMemProperties);
VkImageCreateInfo imageCreateInfo = {};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = VK_FORMAT_R5G6B5_UNORM_PACK16;
imageCreateInfo.extent = { TEX_WIDTH, TEX_HEIGHT, 1 };
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkCreateImage(s_vkDevice, &imageCreateInfo, NULL, &s_tex);
VkMemoryRequirements memReqs = {};
vkGetImageMemoryRequirements(s_vkDevice, s_tex, &memReqs);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memReqs.size;
allocInfo.memoryTypeIndex = SelectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vkAllocateMemory(s_vkDevice, &allocInfo, NULL, &s_texMem);
VkSubresourceLayout imageSubresourceLayout = {};
VkImageSubresource subresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };
vkGetImageSubresourceLayout(s_vkDevice, s_tex, &subresource, &imageSubresourceLayout);
uint8_t* pImageBytes = NULL;
result = vkMapMemory(s_vkDevice, s_texMem, 0, VK_WHOLE_SIZE, 0, (void**)&pImageBytes);
if (result == VK_SUCCESS) {
for (size_t y = 0; y < TEX_HEIGHT; ++y) {
uint16_t* row = (uint16_t*)&pImageBytes[y * imageSubresourceLayout.rowPitch];
for (size_t x = 0; x < TEX_WIDTH; ++x) {
row[x] = makeRgb565(15, 63 * x / TEX_WIDTH, 15 * y / TEX_HEIGHT);
}
}
} else {
printf("Failed to map memory: %d\n", result);
}
vkUnmapMemory(s_vkDevice, s_texMem);
vkBindImageMemory(s_vkDevice, s_tex, s_texMem, 0);
RecreateSwapchain();
Render();
bool done = false;
while (!done)
{
std::unique_lock<std::mutex> lk(s_renderThreadSignalMutex);
s_renderThreadSignal.wait(lk);
if (s_quit) {
done = true;
continue;
}
if (s_recreateSwapchain) {
RecreateSwapchain();
s_recreateSwapchain = false;
}
if (s_redraw) {
Render();
s_redraw = false;
}
}
}
int main()
{
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASS wc = {};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = VulkanGXWindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = WINDOW_CLASS_NAME;
RegisterClass(&wc);
s_hWnd = CreateWindow(WINDOW_CLASS_NAME, TEXT("Vulkan Test"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
std::thread renderThread(RenderThread);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
{
std::lock_guard<std::mutex> lk(s_renderThreadSignalMutex);
s_quit = true;
}
s_renderThreadSignal.notify_all();
printf("Joining render thread...\n");
renderThread.join();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment