procedural-3d-engine/base/vulkanexamplebase.cpp

1082 lines
30 KiB
C++

/*
* Vulkan Example base class
*
* Copyright (C) 2015 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
#include "vulkanexamplebase.h"
VkResult VulkanExampleBase::createInstance(bool enableValidation)
{
this->enableValidation = enableValidation;
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = name.c_str();
appInfo.pEngineName = name.c_str();
// Temporary workaround for drivers not supporting SDK 1.0.3 upon launch
// todo : Use VK_API_VERSION
appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 2);
std::vector<const char*> enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME };
#ifdef _WIN32
enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#else
// todo : linux/android
enabledExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#endif
// todo : check if all extensions are present
VkInstanceCreateInfo instanceCreateInfo = {};
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pNext = NULL;
instanceCreateInfo.pApplicationInfo = &appInfo;
if (enabledExtensions.size() > 0)
{
if (enableValidation)
{
enabledExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
instanceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size();
instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data();
}
if (enableValidation)
{
instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; // todo : change validation layer names!
instanceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames;
}
return vkCreateInstance(&instanceCreateInfo, nullptr, &instance);
}
VkResult VulkanExampleBase::createDevice(VkDeviceQueueCreateInfo requestedQueues, bool enableValidation)
{
std::vector<const char*> enabledExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pNext = NULL;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = &requestedQueues;
deviceCreateInfo.pEnabledFeatures = NULL;
if (enabledExtensions.size() > 0)
{
deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data();
}
if (enableValidation)
{
deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; // todo : validation layer names
deviceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames;
}
return vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device);
}
bool VulkanExampleBase::checkCommandBuffers()
{
for (auto& cmdBuffer : drawCmdBuffers)
{
if (cmdBuffer == VK_NULL_HANDLE)
{
return false;
}
}
return true;
}
void VulkanExampleBase::createCommandBuffers()
{
// Create one command buffer per frame buffer
// in the swap chain
// Command buffers store a reference to the
// frame buffer inside their render pass info
// so for static usage withouth having to rebuild
// them each frame, we use one per frame buffer
drawCmdBuffers.resize(swapChain.imageCount);
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
vkTools::initializers::commandBufferAllocateInfo(
cmdPool,
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
(uint32_t)drawCmdBuffers.size());
VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, drawCmdBuffers.data());
assert(!vkRes);
// Command buffers for submitting present barriers
cmdBufAllocateInfo.commandBufferCount = 1;
// Pre present
vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &prePresentCmdBuffer);
assert(!vkRes);
// Post present
vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &postPresentCmdBuffer);
assert(!vkRes);
}
void VulkanExampleBase::destroyCommandBuffers()
{
vkFreeCommandBuffers(device, cmdPool, (uint32_t)drawCmdBuffers.size(), drawCmdBuffers.data());
vkFreeCommandBuffers(device, cmdPool, 1, &prePresentCmdBuffer);
vkFreeCommandBuffers(device, cmdPool, 1, &postPresentCmdBuffer);
}
void VulkanExampleBase::createSetupCommandBuffer()
{
if (setupCmdBuffer != VK_NULL_HANDLE)
{
vkFreeCommandBuffers(device, cmdPool, 1, &setupCmdBuffer);
setupCmdBuffer = VK_NULL_HANDLE; // todo : check if still necessary
}
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
vkTools::initializers::commandBufferAllocateInfo(
cmdPool,
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1);
VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &setupCmdBuffer);
assert(!vkRes);
// todo : Command buffer is also started here, better put somewhere else
// todo : Check if necessaray at all...
VkCommandBufferBeginInfo cmdBufInfo = {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
// todo : check null handles, flags?
vkRes = vkBeginCommandBuffer(setupCmdBuffer, &cmdBufInfo);
assert(!vkRes);
}
void VulkanExampleBase::flushSetupCommandBuffer()
{
VkResult err;
if (setupCmdBuffer == VK_NULL_HANDLE)
return;
err = vkEndCommandBuffer(setupCmdBuffer);
assert(!err);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &setupCmdBuffer;
err = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
assert(!err);
err = vkQueueWaitIdle(queue);
assert(!err);
vkFreeCommandBuffers(device, cmdPool, 1, &setupCmdBuffer);
setupCmdBuffer = VK_NULL_HANDLE; // todo : check if still necessary
}
void VulkanExampleBase::createPipelineCache()
{
VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
VkResult err = vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache);
assert(!err);
}
void VulkanExampleBase::prepare()
{
if (enableValidation)
{
vkDebug::setupDebugging(instance, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, NULL);
}
createCommandPool();
createSetupCommandBuffer();
setupSwapChain();
createCommandBuffers();
setupDepthStencil();
setupRenderPass();
createPipelineCache();
setupFrameBuffer();
flushSetupCommandBuffer();
// Recreate setup command buffer for derived class
createSetupCommandBuffer();
// Create a simple texture loader class
textureLoader = new vkTools::VulkanTextureLoader(physicalDevice, device, queue, cmdPool);
}
VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShader(const char * fileName, VkShaderStageFlagBits stage)
{
VkPipelineShaderStageCreateInfo shaderStage = {};
shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStage.stage = stage;
shaderStage.module = vkTools::loadShader(fileName, device, stage);
shaderStage.pName = "main"; // todo : make param
assert(shaderStage.module != NULL);
shaderModules.push_back(shaderStage.module);
return shaderStage;
}
VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShaderGLSL(const char * fileName, VkShaderStageFlagBits stage)
{
VkPipelineShaderStageCreateInfo shaderStage = {};
shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shaderStage.stage = stage;
shaderStage.module = vkTools::loadShaderGLSL(fileName, device, stage);
shaderStage.pName = "main"; // todo : make param
assert(shaderStage.module != NULL);
shaderModules.push_back(shaderStage.module);
return shaderStage;
}
VkBool32 VulkanExampleBase::createBuffer(
VkBufferUsageFlags usage,
VkDeviceSize size,
void * data,
VkBuffer *buffer,
VkDeviceMemory *memory)
{
VkMemoryRequirements memReqs;
VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo();
VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usage, size);
VkResult err = vkCreateBuffer(device, &bufferCreateInfo, nullptr, buffer);
assert(!err);
vkGetBufferMemoryRequirements(device, *buffer, &memReqs);
memAlloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAlloc.memoryTypeIndex);
err = vkAllocateMemory(device, &memAlloc, nullptr, memory);
assert(!err);
if (data != nullptr)
{
void *mapped;
err = vkMapMemory(device, *memory, 0, size, 0, &mapped);
assert(!err);
memcpy(mapped, data, size);
vkUnmapMemory(device, *memory);
}
err = vkBindBufferMemory(device, *buffer, *memory, 0);
assert(!err);
return true;
}
VkBool32 VulkanExampleBase::createBuffer(VkBufferUsageFlags usage, VkDeviceSize size, void * data, VkBuffer * buffer, VkDeviceMemory * memory, VkDescriptorBufferInfo * descriptor)
{
VkBool32 res = createBuffer(usage, size, data, buffer, memory);
if (res)
{
descriptor->offset = 0;
descriptor->buffer = *buffer;
descriptor->range = size;
return true;
}
else
{
return false;
}
}
void VulkanExampleBase::loadMesh(
const char * filename,
vkMeshLoader::MeshBuffer * meshBuffer,
std::vector<vkMeshLoader::VertexLayout> vertexLayout,
float scale)
{
VulkanMeshLoader *mesh = new VulkanMeshLoader();
mesh->LoadMesh(filename);
assert(mesh->m_Entries.size() > 0);
mesh->createVulkanBuffers(
device,
deviceMemoryProperties,
meshBuffer,
vertexLayout,
scale);
delete(mesh);
}
void VulkanExampleBase::renderLoop()
{
#ifdef _WIN32
MSG msg;
while (TRUE)
{
auto tStart = std::chrono::high_resolution_clock::now();
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (msg.message == WM_QUIT)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
render();
auto tEnd = std::chrono::high_resolution_clock::now();
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
frameTimer = (float)tDiff / 1000.0f;
// Convert to clamped timer value
if (!paused)
{
timer += timerSpeed * frameTimer;
if (timer > 1.0)
{
timer -= 1.0f;
}
}
}
#else
xcb_flush(connection);
while (!quit)
{
auto tStart = std::chrono::high_resolution_clock::now();
xcb_generic_event_t *event;
event = xcb_poll_for_event(connection);
if (event)
{
handleEvent(event);
free(event);
}
render();
auto tEnd = std::chrono::high_resolution_clock::now();
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
frameTimer = tDiff / 1000.0f;
}
#endif
}
void VulkanExampleBase::submitPrePresentBarrier(VkImage image)
{
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
VkResult vkRes = vkBeginCommandBuffer(prePresentCmdBuffer, &cmdBufInfo);
assert(!vkRes);
VkImageMemoryBarrier prePresentBarrier = vkTools::initializers::imageMemoryBarrier();
prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
prePresentBarrier.dstAccessMask = 0;
prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
prePresentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
prePresentBarrier.image = image;
vkCmdPipelineBarrier(
prePresentCmdBuffer,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_FLAGS_NONE,
0, nullptr, // No memory barriers,
0, nullptr, // No buffer barriers,
1, &prePresentBarrier);
vkRes = vkEndCommandBuffer(prePresentCmdBuffer);
assert(!vkRes);
VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &prePresentCmdBuffer;
vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
assert(!vkRes);
}
void VulkanExampleBase::submitPostPresentBarrier(VkImage image)
{
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
VkResult vkRes = vkBeginCommandBuffer(postPresentCmdBuffer, &cmdBufInfo);
assert(!vkRes);
VkImageMemoryBarrier postPresentBarrier = vkTools::initializers::imageMemoryBarrier();
postPresentBarrier.srcAccessMask = 0;
postPresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
postPresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
postPresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
postPresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
postPresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
postPresentBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
postPresentBarrier.image = image;
vkCmdPipelineBarrier(
postPresentCmdBuffer,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
0,
0, nullptr, // No memory barriers,
0, nullptr, // No buffer barriers,
1, &postPresentBarrier);
vkRes = vkEndCommandBuffer(postPresentCmdBuffer);
assert(!vkRes);
VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &postPresentCmdBuffer;
vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
assert(!vkRes);
}
VulkanExampleBase::VulkanExampleBase(bool enableValidation)
{
// Check for validation command line flag
#ifdef _WIN32
for (int32_t i = 0; i < __argc; i++)
{
if (__argv[i] == std::string("-validation"))
{
enableValidation = true;
}
}
#endif
#ifndef _WIN32
initxcbConnection();
#endif
initVulkan(enableValidation);
// Enable console if validation is active
// Debug message callback will output to it
#ifdef _WIN32
if (enableValidation)
{
setupConsole("VulkanExample");
}
#endif
}
VulkanExampleBase::~VulkanExampleBase()
{
// Clean up Vulkan resources
swapChain.cleanup();
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
if (setupCmdBuffer != VK_NULL_HANDLE)
{
vkFreeCommandBuffers(device, cmdPool, 1, &setupCmdBuffer);
}
destroyCommandBuffers();
vkDestroyRenderPass(device, renderPass, nullptr);
for (uint32_t i = 0; i < frameBuffers.size(); i++)
{
vkDestroyFramebuffer(device, frameBuffers[i], nullptr);
}
for (auto& shaderModule : shaderModules)
{
vkDestroyShaderModule(device, shaderModule, nullptr);
}
vkDestroyImageView(device, depthStencil.view, nullptr);
vkDestroyImage(device, depthStencil.image, nullptr);
vkFreeMemory(device, depthStencil.mem, nullptr);
vkDestroyPipelineCache(device, pipelineCache, nullptr);
if (textureLoader)
{
delete textureLoader;
}
vkDestroyCommandPool(device, cmdPool, nullptr);
vkDestroyDevice(device, nullptr);
if (enableValidation)
{
vkDebug::freeDebugCallback(instance);
}
vkDestroyInstance(instance, nullptr);
#ifndef _WIN32
xcb_destroy_window(connection, window);
xcb_disconnect(connection);
#endif
}
void VulkanExampleBase::initVulkan(bool enableValidation)
{
VkResult err;
// Vulkan instance
err = createInstance(enableValidation);
if (err)
{
vkTools::exitFatal("Could not create Vulkan instance : \n" + vkTools::errorString(err), "Fatal error");
}
// Physical device
uint32_t gpuCount = 0;
// Get number of available physical devices
err = vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr);
assert(!err);
assert(gpuCount > 0);
// Enumerate devices
std::vector<VkPhysicalDevice> physicalDevices(gpuCount);
err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data());
if (err)
{
vkTools::exitFatal("Could not enumerate phyiscal devices : \n" + vkTools::errorString(err), "Fatal error");
}
// Note :
// This example will always use the first physical device reported,
// change the vector index if you have multiple Vulkan devices installed
// and want to use another one
physicalDevice = physicalDevices[0];
// Find a queue that supports graphics operations
uint32_t graphicsQueueIndex = 0;
uint32_t queueCount;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
assert(queueCount >= 1);
std::vector<VkQueueFamilyProperties> queueProps;
queueProps.resize(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
for (graphicsQueueIndex = 0; graphicsQueueIndex < queueCount; graphicsQueueIndex++)
{
if (queueProps[graphicsQueueIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT)
break;
}
assert(graphicsQueueIndex < queueCount);
// Vulkan device
std::array<float, 1> queuePriorities = { 0.0f };
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = graphicsQueueIndex;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = queuePriorities.data();
err = createDevice(queueCreateInfo, enableValidation);
assert(!err);
// Gather physical device memory properties
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
// Get the graphics queue
vkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
// Find a suitable depth format
VkBool32 validDepthFormat = vkTools::getSupportedDepthFormat(physicalDevice, &depthFormat);
assert(validDepthFormat);
swapChain.connect(instance, physicalDevice, device);
}
#ifdef _WIN32
// Win32 : Sets up a console window and redirects standard output to it
void VulkanExampleBase::setupConsole(std::string title)
{
AllocConsole();
AttachConsole(GetCurrentProcessId());
freopen("CON", "w", stdout);
SetConsoleTitle(TEXT(title.c_str()));
if (enableValidation)
{
std::cout << "Validation enabled:\n";
}
}
HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc)
{
this->windowInstance = hinstance;
bool fullscreen = false;
// Check command line arguments
for (int32_t i = 0; i < __argc; i++)
{
if (__argv[i] == std::string("-fullscreen"))
{
fullscreen = true;
}
}
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = wndproc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hinstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = name.c_str();
wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
if (!RegisterClassEx(&wndClass))
{
std::cout << "Could not register window class!\n";
fflush(stdout);
exit(1);
}
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
if (fullscreen)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = screenWidth;
dmScreenSettings.dmPelsHeight = screenHeight;
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if ((width != screenWidth) && (height != screenHeight))
{
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
if (MessageBox(NULL, "Fullscreen Mode not supported!\n Switch to window mode?", "Error", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
{
fullscreen = FALSE;
}
else
{
return FALSE;
}
}
}
}
DWORD dwExStyle;
DWORD dwStyle;
if (fullscreen)
{
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
}
RECT windowRect;
if (fullscreen)
{
windowRect.left = (long)0;
windowRect.right = (long)screenWidth;
windowRect.top = (long)0;
windowRect.bottom = (long)screenHeight;
}
else
{
windowRect.left = (long)screenWidth / 2 - width / 2;
windowRect.right = (long)width;
windowRect.top = (long)screenHeight / 2 - height / 2;
windowRect.bottom = (long)height;
}
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
window = CreateWindowEx(0,
name.c_str(),
title.c_str(),
// WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU,
dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
windowRect.left,
windowRect.top,
windowRect.right,
windowRect.bottom,
NULL,
NULL,
hinstance,
NULL);
if (!window)
{
printf("Could not create window!\n");
fflush(stdout);
return 0;
exit(1);
}
ShowWindow(window, SW_SHOW);
SetForegroundWindow(window);
SetFocus(window);
return window;
}
void VulkanExampleBase::handleMessages(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
prepared = false;
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
case WM_PAINT:
ValidateRect(window, NULL);
break;
case WM_KEYDOWN:
switch (wParam)
{
case 0x50:
paused = !paused;
break;
case VK_ESCAPE:
exit(0);
break;
}
break;
case WM_RBUTTONDOWN:
case WM_LBUTTONDOWN:
mousePos.x = (float)LOWORD(lParam);
mousePos.y = (float)HIWORD(lParam);
break;
case WM_MOUSEMOVE:
if (wParam & MK_RBUTTON)
{
int32_t posx = LOWORD(lParam);
int32_t posy = HIWORD(lParam);
zoom += (mousePos.y - (float)posy) * .005f * zoomSpeed;
mousePos = glm::vec2((float)posx, (float)posy);
viewChanged();
}
if (wParam & MK_LBUTTON)
{
int32_t posx = LOWORD(lParam);
int32_t posy = HIWORD(lParam);
rotation.x += (mousePos.y - (float)posy) * 1.25f * rotationSpeed;
rotation.y -= (mousePos.x - (float)posx) * 1.25f * rotationSpeed;
mousePos = glm::vec2((float)posx, (float)posy);
viewChanged();
}
break;
}
}
#else
// Linux : Setup window
// TODO : Not finished...
xcb_window_t VulkanExampleBase::setupWindow()
{
uint32_t value_mask, value_list[32];
window = xcb_generate_id(connection);
value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
value_list[0] = screen->black_pixel;
value_list[1] = XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE;
xcb_create_window(connection,
XCB_COPY_FROM_PARENT,
window, screen->root,
0, 0, width, height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
value_mask, value_list);
/* Magic code that will send notification when window is destroyed */
xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 1, 12, "WM_PROTOCOLS");
xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(connection, cookie, 0);
xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(connection, 0, 16, "WM_DELETE_WINDOW");
atom_wm_delete_window = xcb_intern_atom_reply(connection, cookie2, 0);
xcb_change_property(connection, XCB_PROP_MODE_REPLACE,
window, (*reply).atom, 4, 32, 1,
&(*atom_wm_delete_window).atom);
xcb_change_property(connection, XCB_PROP_MODE_REPLACE,
window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
title.size(), title.c_str());
free(reply);
xcb_map_window(connection, window);
return(window);
}
// Initialize XCB connection
void VulkanExampleBase::initxcbConnection()
{
const xcb_setup_t *setup;
xcb_screen_iterator_t iter;
int scr;
connection = xcb_connect(NULL, &scr);
if (connection == NULL) {
printf("Could not find a compatible Vulkan ICD!\n");
fflush(stdout);
exit(1);
}
setup = xcb_get_setup(connection);
iter = xcb_setup_roots_iterator(setup);
while (scr-- > 0)
xcb_screen_next(&iter);
screen = iter.data;
}
void VulkanExampleBase::handleEvent(const xcb_generic_event_t *event)
{
switch (event->response_type & 0x7f)
{
case XCB_CLIENT_MESSAGE:
if ((*(xcb_client_message_event_t*)event).data.data32[0] ==
(*atom_wm_delete_window).atom) {
quit = true;
}
break;
case XCB_MOTION_NOTIFY:
{
xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event;
if (mouseButtons.left)
{
rotation.x += (mousePos.y - (float)motion->event_y) * 1.25f;
rotation.y -= (mousePos.x - (float)motion->event_x) * 1.25f;
viewChanged();
}
if (mouseButtons.right)
{
zoom += (mousePos.y - (float)motion->event_y) * .005f;
viewChanged();
}
mousePos = glm::vec2((float)motion->event_x, (float)motion->event_y);
}
break;
case XCB_BUTTON_PRESS:
{
xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
mouseButtons.left = (press->detail & XCB_BUTTON_INDEX_1);
mouseButtons.right = (press->detail & XCB_BUTTON_INDEX_3);
}
break;
case XCB_BUTTON_RELEASE:
{
xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
if (press->detail & XCB_BUTTON_INDEX_1)
mouseButtons.left = false;
if (press->detail & XCB_BUTTON_INDEX_3)
mouseButtons.right = false;
}
break;
case XCB_KEY_RELEASE:
{
const xcb_key_release_event_t *key =
(const xcb_key_release_event_t *)event;
if (key->detail == 0x9)
quit = true;
}
break;
case XCB_DESTROY_NOTIFY:
quit = true;
break;
default:
break;
}
}
#endif
void VulkanExampleBase::viewChanged()
{
// For overriding on derived class
}
VkBool32 VulkanExampleBase::getMemoryType(uint32_t typeBits, VkFlags properties, uint32_t * typeIndex)
{
for (uint32_t i = 0; i < 32; i++)
{
if ((typeBits & 1) == 1)
{
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
*typeIndex = i;
return true;
}
}
typeBits >>= 1;
}
return false;
}
void VulkanExampleBase::createCommandPool()
{
VkCommandPoolCreateInfo cmdPoolInfo = {};
cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmdPoolInfo.queueFamilyIndex = swapChain.queueNodeIndex;
cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VkResult vkRes = vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool);
assert(!vkRes);
}
void VulkanExampleBase::setupDepthStencil()
{
VkImageCreateInfo image = {};
image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image.pNext = NULL;
image.imageType = VK_IMAGE_TYPE_2D;
image.format = depthFormat;
image.extent = { width, height, 1 };
image.mipLevels = 1;
image.arrayLayers = 1;
image.samples = VK_SAMPLE_COUNT_1_BIT;
image.tiling = VK_IMAGE_TILING_OPTIMAL;
image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
image.flags = 0;
VkMemoryAllocateInfo mem_alloc = {};
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
mem_alloc.pNext = NULL;
mem_alloc.allocationSize = 0;
mem_alloc.memoryTypeIndex = 0;
VkImageViewCreateInfo depthStencilView = {};
depthStencilView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
depthStencilView.pNext = NULL;
depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthStencilView.format = depthFormat;
depthStencilView.flags = 0;
depthStencilView.subresourceRange = {};
depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
depthStencilView.subresourceRange.baseMipLevel = 0;
depthStencilView.subresourceRange.levelCount = 1;
depthStencilView.subresourceRange.baseArrayLayer = 0;
depthStencilView.subresourceRange.layerCount = 1;
VkMemoryRequirements memReqs;
VkResult err;
err = vkCreateImage(device, &image, nullptr, &depthStencil.image);
assert(!err);
vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs);
mem_alloc.allocationSize = memReqs.size;
getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
err = vkAllocateMemory(device, &mem_alloc, nullptr, &depthStencil.mem);
assert(!err);
err = vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0);
assert(!err);
vkTools::setImageLayout(setupCmdBuffer, depthStencil.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
depthStencilView.image = depthStencil.image;
err = vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view);
assert(!err);
}
void VulkanExampleBase::setupFrameBuffer()
{
VkImageView attachments[2];
// Depth/Stencil attachment is the same for all frame buffers
attachments[1] = depthStencil.view;
VkFramebufferCreateInfo frameBufferCreateInfo = {};
frameBufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
frameBufferCreateInfo.pNext = NULL;
frameBufferCreateInfo.renderPass = renderPass;
frameBufferCreateInfo.attachmentCount = 2;
frameBufferCreateInfo.pAttachments = attachments;
frameBufferCreateInfo.width = width;
frameBufferCreateInfo.height = height;
frameBufferCreateInfo.layers = 1;
// Create frame buffers for every swap chain image
frameBuffers.resize(swapChain.imageCount);
for (uint32_t i = 0; i < frameBuffers.size(); i++)
{
attachments[0] = swapChain.buffers[i].view;
VkResult err = vkCreateFramebuffer(device, &frameBufferCreateInfo, nullptr, &frameBuffers[i]);
assert(!err);
}
}
void VulkanExampleBase::setupRenderPass()
{
VkAttachmentDescription attachments[2];
attachments[0].format = colorformat;
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_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
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_DONT_CARE;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
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 subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = NULL;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorReference;
subpass.pResolveAttachments = NULL;
subpass.pDepthStencilAttachment = &depthReference;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = NULL;
VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.pNext = NULL;
renderPassInfo.attachmentCount = 2;
renderPassInfo.pAttachments = attachments;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 0;
renderPassInfo.pDependencies = NULL;
VkResult err;
err = vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass);
assert(!err);
}
void VulkanExampleBase::initSwapchain()
{
#ifdef _WIN32
swapChain.initSurface(windowInstance, window);
#else
swapChain.initSurface(connection, window);
#endif
}
void VulkanExampleBase::setupSwapChain()
{
swapChain.create(setupCmdBuffer, &width, &height);
}