1082 lines
30 KiB
C++
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);
|
|
}
|
|
|
|
|