2016-02-16 15:07:25 +01:00
|
|
|
/*
|
|
|
|
|
* Vulkan Example base class
|
|
|
|
|
*
|
2016-03-13 12:15:58 +01:00
|
|
|
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
|
2016-02-16 15:07:25 +01:00
|
|
|
*
|
|
|
|
|
* 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();
|
2016-04-09 14:20:57 +02:00
|
|
|
appInfo.apiVersion = VK_API_VERSION_1_0;
|
2016-02-16 15:07:25 +01:00
|
|
|
|
|
|
|
|
std::vector<const char*> enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME };
|
|
|
|
|
|
2016-03-20 14:55:46 +01:00
|
|
|
// Enable surface extensions depending on os
|
|
|
|
|
#if defined(_WIN32)
|
2016-02-16 15:07:25 +01:00
|
|
|
enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__ANDROID__)
|
|
|
|
|
enabledExtensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
|
|
|
|
#elif defined(__linux__)
|
2016-02-16 15:07:25 +01:00
|
|
|
enabledExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2016-03-13 17:15:44 +01:00
|
|
|
instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount;
|
2016-02-16 15:07:25 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2016-03-13 12:15:58 +01:00
|
|
|
deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount;
|
2016-02-16 15:07:25 +01:00
|
|
|
deviceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 16:51:00 +01:00
|
|
|
std::string VulkanExampleBase::getWindowTitle()
|
|
|
|
|
{
|
|
|
|
|
std::string device(deviceProperties.deviceName);
|
|
|
|
|
std::string windowTitle;
|
|
|
|
|
windowTitle = title + " - " + device + " - " + std::to_string(frameCounter) + " fps";
|
|
|
|
|
return windowTitle;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-21 20:10:09 +01:00
|
|
|
const std::string VulkanExampleBase::getAssetPath()
|
|
|
|
|
{
|
|
|
|
|
#if defined(__ANDROID__)
|
|
|
|
|
return "";
|
|
|
|
|
#else
|
|
|
|
|
return "./../data/";
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
bool VulkanExampleBase::checkCommandBuffers()
|
|
|
|
|
{
|
|
|
|
|
for (auto& cmdBuffer : drawCmdBuffers)
|
|
|
|
|
{
|
|
|
|
|
if (cmdBuffer == VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::createCommandBuffers()
|
|
|
|
|
{
|
2016-03-13 17:15:44 +01:00
|
|
|
// Create one command buffer per frame buffer
|
2016-02-16 15:07:25 +01:00
|
|
|
// in the swap chain
|
2016-03-13 17:15:44 +01:00
|
|
|
// Command buffers store a reference to the
|
2016-02-16 15:07:25 +01:00
|
|
|
// frame buffer inside their render pass info
|
2016-03-13 17:15:44 +01:00
|
|
|
// so for static usage withouth having to rebuild
|
2016-02-16 15:07:25 +01:00
|
|
|
// them each frame, we use one per frame buffer
|
|
|
|
|
|
|
|
|
|
drawCmdBuffers.resize(swapChain.imageCount);
|
|
|
|
|
|
2016-03-13 17:15:44 +01:00
|
|
|
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
|
2016-02-16 15:07:25 +01:00
|
|
|
vkTools::initializers::commandBufferAllocateInfo(
|
|
|
|
|
cmdPool,
|
|
|
|
|
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
|
|
|
(uint32_t)drawCmdBuffers.size());
|
|
|
|
|
|
|
|
|
|
VkResult vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, drawCmdBuffers.data());
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
|
2016-03-01 18:54:36 +01:00
|
|
|
// Command buffers for submitting present barriers
|
2016-02-16 15:07:25 +01:00
|
|
|
cmdBufAllocateInfo.commandBufferCount = 1;
|
2016-03-01 18:54:36 +01:00
|
|
|
// Pre present
|
|
|
|
|
vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &prePresentCmdBuffer);
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
// Post present
|
2016-02-16 15:07:25 +01:00
|
|
|
vkRes = vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &postPresentCmdBuffer);
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::destroyCommandBuffers()
|
|
|
|
|
{
|
|
|
|
|
vkFreeCommandBuffers(device, cmdPool, (uint32_t)drawCmdBuffers.size(), drawCmdBuffers.data());
|
2016-03-01 18:54:36 +01:00
|
|
|
vkFreeCommandBuffers(device, cmdPool, 1, &prePresentCmdBuffer);
|
2016-02-16 15:07:25 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
VkCommandBufferBeginInfo cmdBufInfo = {};
|
|
|
|
|
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
|
|
|
|
|
|
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);
|
2016-03-19 20:02:08 +01:00
|
|
|
setupCmdBuffer = VK_NULL_HANDLE;
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-16 14:42:41 +02:00
|
|
|
VkCommandBuffer VulkanExampleBase::createCommandBuffer(VkCommandBufferLevel level, bool begin)
|
|
|
|
|
{
|
|
|
|
|
VkCommandBuffer cmdBuffer;
|
|
|
|
|
|
|
|
|
|
VkCommandBufferAllocateInfo cmdBufAllocateInfo =
|
|
|
|
|
vkTools::initializers::commandBufferAllocateInfo(
|
|
|
|
|
cmdPool,
|
|
|
|
|
level,
|
|
|
|
|
1);
|
|
|
|
|
|
|
|
|
|
vkTools::checkResult(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &cmdBuffer));
|
|
|
|
|
|
|
|
|
|
// If requested, also start the new command buffer
|
|
|
|
|
if (begin)
|
|
|
|
|
{
|
|
|
|
|
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
|
|
|
|
|
vkTools::checkResult(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cmdBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free)
|
|
|
|
|
{
|
|
|
|
|
if (commandBuffer == VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vkTools::checkResult(vkEndCommandBuffer(commandBuffer));
|
|
|
|
|
|
|
|
|
|
VkSubmitInfo submitInfo = {};
|
|
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
|
submitInfo.commandBufferCount = 1;
|
|
|
|
|
submitInfo.pCommandBuffers = &commandBuffer;
|
|
|
|
|
|
|
|
|
|
vkTools::checkResult(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
|
|
|
|
|
vkTools::checkResult(vkQueueWaitIdle(queue));
|
|
|
|
|
|
|
|
|
|
if (free)
|
|
|
|
|
{
|
|
|
|
|
vkFreeCommandBuffers(device, cmdPool, 1, &commandBuffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
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();
|
2016-03-13 17:15:44 +01:00
|
|
|
// Create a simple texture loader class
|
2016-02-16 15:07:25 +01:00
|
|
|
textureLoader = new vkTools::VulkanTextureLoader(physicalDevice, device, queue, cmdPool);
|
2016-03-23 19:18:54 +01:00
|
|
|
#if defined(__ANDROID__)
|
|
|
|
|
textureLoader->assetManager = androidApp->activity->assetManager;
|
|
|
|
|
#endif
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-21 20:10:09 +01:00
|
|
|
VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShader(std::string fileName, VkShaderStageFlagBits stage)
|
2016-02-16 15:07:25 +01:00
|
|
|
{
|
|
|
|
|
VkPipelineShaderStageCreateInfo shaderStage = {};
|
|
|
|
|
shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
|
|
|
shaderStage.stage = stage;
|
2016-03-20 15:45:40 +01:00
|
|
|
#if defined(__ANDROID__)
|
2016-03-21 20:10:09 +01:00
|
|
|
shaderStage.module = vkTools::loadShader(androidApp->activity->assetManager, fileName.c_str(), device, stage);
|
2016-03-20 15:45:40 +01:00
|
|
|
#else
|
2016-03-21 20:10:09 +01:00
|
|
|
shaderStage.module = vkTools::loadShader(fileName.c_str(), device, stage);
|
2016-03-20 15:45:40 +01:00
|
|
|
#endif
|
2016-02-16 15:07:25 +01:00
|
|
|
shaderStage.pName = "main"; // todo : make param
|
|
|
|
|
assert(shaderStage.module != NULL);
|
|
|
|
|
shaderModules.push_back(shaderStage.module);
|
|
|
|
|
return shaderStage;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-16 14:42:41 +02:00
|
|
|
VkBool32 VulkanExampleBase::createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, void * data, VkBuffer * buffer, VkDeviceMemory * memory)
|
2016-02-16 15:07:25 +01:00
|
|
|
{
|
|
|
|
|
VkMemoryRequirements memReqs;
|
|
|
|
|
VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo();
|
2016-04-16 14:42:41 +02:00
|
|
|
VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usageFlags, size);
|
|
|
|
|
|
|
|
|
|
vkTools::checkResult(vkCreateBuffer(device, &bufferCreateInfo, nullptr, buffer));
|
2016-02-16 15:07:25 +01:00
|
|
|
|
|
|
|
|
vkGetBufferMemoryRequirements(device, *buffer, &memReqs);
|
|
|
|
|
memAlloc.allocationSize = memReqs.size;
|
2016-04-16 14:42:41 +02:00
|
|
|
getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags, &memAlloc.memoryTypeIndex);
|
|
|
|
|
vkTools::checkResult(vkAllocateMemory(device, &memAlloc, nullptr, memory));
|
2016-02-16 15:07:25 +01:00
|
|
|
if (data != nullptr)
|
|
|
|
|
{
|
|
|
|
|
void *mapped;
|
2016-04-16 14:42:41 +02:00
|
|
|
vkTools::checkResult(vkMapMemory(device, *memory, 0, size, 0, &mapped));
|
2016-02-16 15:07:25 +01:00
|
|
|
memcpy(mapped, data, size);
|
|
|
|
|
vkUnmapMemory(device, *memory);
|
|
|
|
|
}
|
2016-04-16 14:42:41 +02:00
|
|
|
vkTools::checkResult(vkBindBufferMemory(device, *buffer, *memory, 0));
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-16 14:42:41 +02:00
|
|
|
VkBool32 VulkanExampleBase::createBuffer(VkBufferUsageFlags usage, VkDeviceSize size, void * data, VkBuffer *buffer, VkDeviceMemory *memory)
|
|
|
|
|
{
|
|
|
|
|
return createBuffer(usage, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, size, data, buffer, memory);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
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(
|
2016-03-23 23:02:10 +01:00
|
|
|
std::string filename,
|
2016-03-13 17:15:44 +01:00
|
|
|
vkMeshLoader::MeshBuffer * meshBuffer,
|
|
|
|
|
std::vector<vkMeshLoader::VertexLayout> vertexLayout,
|
2016-02-16 15:07:25 +01:00
|
|
|
float scale)
|
|
|
|
|
{
|
|
|
|
|
VulkanMeshLoader *mesh = new VulkanMeshLoader();
|
2016-03-23 19:18:54 +01:00
|
|
|
|
|
|
|
|
#if defined(__ANDROID__)
|
|
|
|
|
mesh->assetManager = androidApp->activity->assetManager;
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
mesh->LoadMesh(filename);
|
|
|
|
|
assert(mesh->m_Entries.size() > 0);
|
|
|
|
|
|
|
|
|
|
mesh->createVulkanBuffers(
|
|
|
|
|
device,
|
|
|
|
|
deviceMemoryProperties,
|
|
|
|
|
meshBuffer,
|
|
|
|
|
vertexLayout,
|
|
|
|
|
scale);
|
|
|
|
|
|
2016-04-03 13:51:47 +02:00
|
|
|
meshBuffer->dim = mesh->dim.size;
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
delete(mesh);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::renderLoop()
|
|
|
|
|
{
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(_WIN32)
|
2016-02-16 15:07:25 +01:00
|
|
|
MSG msg;
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
2016-03-17 20:20:43 +01:00
|
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
2016-02-16 15:07:25 +01:00
|
|
|
{
|
2016-03-17 20:20:43 +01:00
|
|
|
if (msg.message == WM_QUIT)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
|
}
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
render();
|
2016-03-13 16:51:00 +01:00
|
|
|
frameCounter++;
|
2016-02-16 15:07:25 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-13 16:51:00 +01:00
|
|
|
fpsTimer += (float)tDiff;
|
|
|
|
|
if (fpsTimer > 1000.0f)
|
|
|
|
|
{
|
|
|
|
|
std::string windowTitle = getWindowTitle();
|
|
|
|
|
SetWindowText(window, windowTitle.c_str());
|
|
|
|
|
fpsTimer = 0.0f;
|
|
|
|
|
frameCounter = 0.0f;
|
|
|
|
|
}
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__ANDROID__)
|
2016-03-20 20:04:27 +01:00
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
int ident;
|
|
|
|
|
int events;
|
|
|
|
|
struct android_poll_source* source;
|
2016-03-26 12:58:35 +01:00
|
|
|
bool destroy = false;
|
2016-03-20 20:04:27 +01:00
|
|
|
|
2016-03-26 12:58:35 +01:00
|
|
|
focused = true;
|
|
|
|
|
|
|
|
|
|
while ((ident = ALooper_pollAll(focused ? 0 : -1, NULL, &events, (void**)&source)) >= 0)
|
2016-03-20 20:04:27 +01:00
|
|
|
{
|
|
|
|
|
if (source != NULL)
|
|
|
|
|
{
|
|
|
|
|
source->process(androidApp, source);
|
|
|
|
|
}
|
|
|
|
|
if (androidApp->destroyRequested != 0)
|
|
|
|
|
{
|
2016-03-26 12:58:35 +01:00
|
|
|
LOGD("Android app destroy requested");
|
|
|
|
|
destroy = true;
|
|
|
|
|
break;
|
2016-03-20 20:04:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-26 12:58:35 +01:00
|
|
|
// App destruction requested
|
|
|
|
|
// Exit loop, example will be destroyed in application main
|
|
|
|
|
if (destroy)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 20:04:27 +01:00
|
|
|
// Render frame
|
|
|
|
|
if (prepared)
|
|
|
|
|
{
|
2016-03-23 22:16:05 +01:00
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
2016-03-20 20:04:27 +01:00
|
|
|
render();
|
2016-03-23 22:16:05 +01:00
|
|
|
frameCounter++;
|
|
|
|
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
|
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
|
|
|
|
|
frameTimer = tDiff / 1000.0f;
|
2016-03-24 22:43:33 +01:00
|
|
|
// Convert to clamped timer value
|
|
|
|
|
if (!paused)
|
|
|
|
|
{
|
|
|
|
|
timer += timerSpeed * frameTimer;
|
|
|
|
|
if (timer > 1.0)
|
|
|
|
|
{
|
|
|
|
|
timer -= 1.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-15 21:24:50 +02:00
|
|
|
fpsTimer += (float)tDiff;
|
|
|
|
|
if (fpsTimer > 1000.0f)
|
|
|
|
|
{
|
|
|
|
|
LOGD("%d fps", frameCounter);
|
|
|
|
|
fpsTimer = 0.0f;
|
|
|
|
|
frameCounter = 0.0f;
|
|
|
|
|
}
|
2016-03-20 21:46:49 +01:00
|
|
|
// Check gamepad state
|
2016-03-26 00:07:12 +01:00
|
|
|
const float deadZone = 0.0015f;
|
2016-03-20 21:46:49 +01:00
|
|
|
// todo : check if gamepad is present
|
|
|
|
|
// todo : time based and relative axis positions
|
|
|
|
|
bool updateView = false;
|
|
|
|
|
// Rotate
|
2016-03-26 00:07:12 +01:00
|
|
|
if (std::abs(gamePadState.axes.x - deadZone) > 0.0f)
|
2016-03-20 21:46:49 +01:00
|
|
|
{
|
2016-03-26 00:07:12 +01:00
|
|
|
rotation.y += gamePadState.axes.x * 0.5f * rotationSpeed;
|
2016-03-20 21:46:49 +01:00
|
|
|
updateView = true;
|
|
|
|
|
}
|
2016-03-26 00:07:12 +01:00
|
|
|
if (std::abs(gamePadState.axes.y - deadZone) > 0.0f)
|
2016-03-20 21:46:49 +01:00
|
|
|
{
|
2016-03-26 00:07:12 +01:00
|
|
|
rotation.x -= gamePadState.axes.y * 0.5f * rotationSpeed;
|
2016-03-20 21:46:49 +01:00
|
|
|
updateView = true;
|
|
|
|
|
}
|
|
|
|
|
// Zoom
|
2016-03-26 00:07:12 +01:00
|
|
|
if (std::abs(gamePadState.axes.rz - deadZone) > 0.0f)
|
2016-03-20 21:46:49 +01:00
|
|
|
{
|
2016-03-26 00:07:12 +01:00
|
|
|
zoom -= gamePadState.axes.rz * 0.01f * zoomSpeed;
|
2016-03-20 21:46:49 +01:00
|
|
|
updateView = true;
|
|
|
|
|
}
|
|
|
|
|
if (updateView)
|
|
|
|
|
{
|
|
|
|
|
viewChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-03-20 20:04:27 +01:00
|
|
|
}
|
|
|
|
|
}
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__linux__)
|
2016-02-16 15:07:25 +01:00
|
|
|
xcb_flush(connection);
|
|
|
|
|
while (!quit)
|
|
|
|
|
{
|
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
|
xcb_generic_event_t *event;
|
|
|
|
|
event = xcb_poll_for_event(connection);
|
2016-03-13 17:15:44 +01:00
|
|
|
if (event)
|
2016-02-16 15:07:25 +01:00
|
|
|
{
|
|
|
|
|
handleEvent(event);
|
|
|
|
|
free(event);
|
|
|
|
|
}
|
|
|
|
|
render();
|
2016-03-13 17:15:44 +01:00
|
|
|
frameCounter++;
|
2016-02-16 15:07:25 +01:00
|
|
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
|
auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
|
|
|
|
|
frameTimer = tDiff / 1000.0f;
|
2016-03-13 17:15:44 +01:00
|
|
|
// Convert to clamped timer value
|
|
|
|
|
if (!paused)
|
|
|
|
|
{
|
|
|
|
|
timer += timerSpeed * frameTimer;
|
|
|
|
|
if (timer > 1.0)
|
|
|
|
|
{
|
|
|
|
|
timer -= 1.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fpsTimer += (float)tDiff;
|
|
|
|
|
if (fpsTimer > 1000.0f)
|
|
|
|
|
{
|
|
|
|
|
std::string windowTitle = getWindowTitle();
|
|
|
|
|
xcb_change_property(connection, XCB_PROP_MODE_REPLACE,
|
|
|
|
|
window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
|
|
|
|
|
windowTitle.size(), windowTitle.c_str());
|
|
|
|
|
fpsTimer = 0.0f;
|
|
|
|
|
frameCounter = 0.0f;
|
|
|
|
|
}
|
2016-03-20 14:55:46 +01:00
|
|
|
}
|
2016-02-16 15:07:25 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 18:54:36 +01:00
|
|
|
void VulkanExampleBase::submitPrePresentBarrier(VkImage image)
|
|
|
|
|
{
|
|
|
|
|
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
|
|
|
|
|
|
|
|
|
|
VkResult vkRes = vkBeginCommandBuffer(prePresentCmdBuffer, &cmdBufInfo);
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
|
2016-03-01 19:09:01 +01:00
|
|
|
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;
|
|
|
|
|
|
2016-03-01 18:54:36 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
void VulkanExampleBase::submitPostPresentBarrier(VkImage image)
|
|
|
|
|
{
|
|
|
|
|
VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
|
|
|
|
|
|
|
|
|
|
VkResult vkRes = vkBeginCommandBuffer(postPresentCmdBuffer, &cmdBufInfo);
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
|
2016-03-01 19:09:01 +01:00
|
|
|
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;
|
2016-02-16 15:07:25 +01:00
|
|
|
|
|
|
|
|
vkCmdPipelineBarrier(
|
|
|
|
|
postPresentCmdBuffer,
|
|
|
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
|
|
|
0,
|
2016-03-01 18:54:36 +01:00
|
|
|
0, nullptr, // No memory barriers,
|
|
|
|
|
0, nullptr, // No buffer barriers,
|
2016-02-16 15:07:25 +01:00
|
|
|
1, &postPresentBarrier);
|
|
|
|
|
|
|
|
|
|
vkRes = vkEndCommandBuffer(postPresentCmdBuffer);
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
|
2016-03-01 18:54:36 +01:00
|
|
|
VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
|
2016-02-16 15:07:25 +01:00
|
|
|
submitInfo.commandBufferCount = 1;
|
|
|
|
|
submitInfo.pCommandBuffers = &postPresentCmdBuffer;
|
|
|
|
|
|
|
|
|
|
vkRes = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
|
|
|
|
assert(!vkRes);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-06 19:22:41 +01:00
|
|
|
VkSubmitInfo VulkanExampleBase::prepareSubmitInfo(
|
2016-03-13 17:15:44 +01:00
|
|
|
std::vector<VkCommandBuffer> commandBuffers,
|
2016-03-06 19:22:41 +01:00
|
|
|
VkPipelineStageFlags *pipelineStages)
|
|
|
|
|
{
|
|
|
|
|
VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
|
|
|
|
|
submitInfo.pWaitDstStageMask = pipelineStages;
|
|
|
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
|
|
|
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
|
|
|
|
|
submitInfo.commandBufferCount = commandBuffers.size();
|
|
|
|
|
submitInfo.pCommandBuffers = commandBuffers.data();
|
|
|
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
|
|
|
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
|
|
|
|
|
return submitInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
VulkanExampleBase::VulkanExampleBase(bool enableValidation)
|
|
|
|
|
{
|
|
|
|
|
// Check for validation command line flag
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(_WIN32)
|
2016-02-16 15:07:25 +01:00
|
|
|
for (int32_t i = 0; i < __argc; i++)
|
|
|
|
|
{
|
|
|
|
|
if (__argv[i] == std::string("-validation"))
|
|
|
|
|
{
|
|
|
|
|
enableValidation = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__ANDROID__)
|
|
|
|
|
// Vulkan library is loaded dynamically on Android
|
|
|
|
|
bool libLoaded = loadVulkanLibrary();
|
|
|
|
|
assert(libLoaded);
|
|
|
|
|
#elif defined(__linux__)
|
2016-02-16 15:07:25 +01:00
|
|
|
initxcbConnection();
|
|
|
|
|
#endif
|
2016-03-20 14:55:46 +01:00
|
|
|
|
|
|
|
|
#if !defined(__ANDROID__)
|
|
|
|
|
// Android Vulkan initialization is handled in APP_CMD_INIT_WINDOW event
|
2016-02-16 15:07:25 +01:00
|
|
|
initVulkan(enableValidation);
|
2016-03-20 14:55:46 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
2016-02-16 15:07:25 +01:00
|
|
|
// Enable console if validation is active
|
|
|
|
|
// Debug message callback will output to it
|
|
|
|
|
if (enableValidation)
|
|
|
|
|
{
|
|
|
|
|
setupConsole("VulkanExample");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VulkanExampleBase::~VulkanExampleBase()
|
|
|
|
|
{
|
|
|
|
|
// Clean up Vulkan resources
|
|
|
|
|
swapChain.cleanup();
|
2016-04-02 12:47:08 +02:00
|
|
|
if (descriptorPool != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
|
|
|
|
|
}
|
2016-03-13 17:15:44 +01:00
|
|
|
if (setupCmdBuffer != VK_NULL_HANDLE)
|
2016-02-16 15:07:25 +01:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
2016-03-06 19:22:41 +01:00
|
|
|
vkDestroySemaphore(device, semaphores.presentComplete, nullptr);
|
|
|
|
|
vkDestroySemaphore(device, semaphores.renderComplete, nullptr);
|
2016-03-06 12:57:23 +01:00
|
|
|
|
2016-03-13 17:15:44 +01:00
|
|
|
vkDestroyDevice(device, nullptr);
|
2016-02-16 15:07:25 +01:00
|
|
|
|
|
|
|
|
if (enableValidation)
|
|
|
|
|
{
|
|
|
|
|
vkDebug::freeDebugCallback(instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vkDestroyInstance(instance, nullptr);
|
|
|
|
|
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(__linux)
|
|
|
|
|
#if defined(__ANDROID__)
|
|
|
|
|
// todo : android cleanup (if required)
|
|
|
|
|
#else
|
2016-02-16 15:07:25 +01:00
|
|
|
xcb_destroy_window(connection, window);
|
|
|
|
|
xcb_disconnect(connection);
|
2016-03-13 17:15:44 +01:00
|
|
|
#endif
|
2016-03-20 14:55:46 +01:00
|
|
|
#endif
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(__ANDROID__)
|
|
|
|
|
loadVulkanFunctions(instance);
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
// Physical device
|
2016-02-17 18:37:36 +01:00
|
|
|
uint32_t gpuCount = 0;
|
|
|
|
|
// Get number of available physical devices
|
|
|
|
|
err = vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr);
|
2016-03-13 17:15:44 +01:00
|
|
|
assert(!err);
|
2016-02-17 18:37:36 +01:00
|
|
|
assert(gpuCount > 0);
|
|
|
|
|
// Enumerate devices
|
|
|
|
|
std::vector<VkPhysicalDevice> physicalDevices(gpuCount);
|
|
|
|
|
err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data());
|
2016-02-16 15:07:25 +01:00
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
vkTools::exitFatal("Could not enumerate phyiscal devices : \n" + vkTools::errorString(err), "Fatal error");
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 17:15:44 +01:00
|
|
|
// Note :
|
|
|
|
|
// This example will always use the first physical device reported,
|
|
|
|
|
// change the vector index if you have multiple Vulkan devices installed
|
2016-02-17 18:37:36 +01:00
|
|
|
// and want to use another one
|
|
|
|
|
physicalDevice = physicalDevices[0];
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
// 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);
|
|
|
|
|
|
2016-03-13 16:51:00 +01:00
|
|
|
vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
|
|
|
|
|
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(__ANDROID__)
|
|
|
|
|
LOGD(deviceProperties.deviceName);
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
// Gather physical device memory properties
|
|
|
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
|
|
|
|
|
|
|
|
|
|
// Get the graphics queue
|
|
|
|
|
vkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
|
|
|
|
|
|
2016-02-18 22:09:47 +01:00
|
|
|
// Find a suitable depth format
|
2016-02-18 21:44:26 +01:00
|
|
|
VkBool32 validDepthFormat = vkTools::getSupportedDepthFormat(physicalDevice, &depthFormat);
|
|
|
|
|
assert(validDepthFormat);
|
2016-02-16 15:07:25 +01:00
|
|
|
|
2016-02-23 22:00:09 +01:00
|
|
|
swapChain.connect(instance, physicalDevice, device);
|
2016-03-06 12:57:23 +01:00
|
|
|
|
2016-03-06 19:22:41 +01:00
|
|
|
// Create synchronization objects
|
|
|
|
|
VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo();
|
2016-03-06 12:57:23 +01:00
|
|
|
// Create a semaphore used to synchronize image presentation
|
2016-03-06 19:22:41 +01:00
|
|
|
// Ensures that the image is displayed before we start submitting new commands to the queu
|
|
|
|
|
err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete);
|
|
|
|
|
assert(!err);
|
|
|
|
|
// Create a semaphore used to synchronize command submission
|
|
|
|
|
// Ensures that the image is not presented until all commands have been sumbitted and executed
|
|
|
|
|
err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete);
|
2016-03-06 12:57:23 +01:00
|
|
|
assert(!err);
|
2016-03-06 19:22:41 +01:00
|
|
|
|
|
|
|
|
// Set up submit info structure
|
|
|
|
|
// Semaphores will stay the same during application lifetime
|
|
|
|
|
// Command buffer submission info is set by each example
|
|
|
|
|
submitInfo = vkTools::initializers::submitInfo();
|
|
|
|
|
submitInfo.pWaitDstStageMask = &submitPipelineStages;
|
|
|
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
|
|
|
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
|
|
|
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
|
|
|
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(_WIN32)
|
2016-02-16 15:07:25 +01:00
|
|
|
// 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);
|
2016-02-27 22:27:09 +01:00
|
|
|
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
2016-02-16 15:07:25 +01:00
|
|
|
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);
|
|
|
|
|
|
2016-03-13 16:51:00 +01:00
|
|
|
std::string windowTitle = getWindowTitle();
|
2016-02-16 15:07:25 +01:00
|
|
|
window = CreateWindowEx(0,
|
|
|
|
|
name.c_str(),
|
2016-03-13 16:51:00 +01:00
|
|
|
windowTitle.c_str(),
|
2016-02-16 15:07:25 +01:00
|
|
|
dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
|
|
|
|
windowRect.left,
|
|
|
|
|
windowRect.top,
|
|
|
|
|
windowRect.right,
|
|
|
|
|
windowRect.bottom,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
hinstance,
|
|
|
|
|
NULL);
|
|
|
|
|
|
2016-03-13 17:15:44 +01:00
|
|
|
if (!window)
|
2016-02-16 15:07:25 +01:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2016-03-03 16:41:57 +01:00
|
|
|
keyPressed((uint32_t)wParam);
|
2016-02-16 15:07:25 +01:00
|
|
|
break;
|
|
|
|
|
case WM_RBUTTONDOWN:
|
|
|
|
|
case WM_LBUTTONDOWN:
|
2016-03-30 22:48:58 +02:00
|
|
|
case WM_MBUTTONDOWN:
|
2016-02-16 15:07:25 +01:00
|
|
|
mousePos.x = (float)LOWORD(lParam);
|
|
|
|
|
mousePos.y = (float)HIWORD(lParam);
|
|
|
|
|
break;
|
2016-03-17 20:20:43 +01:00
|
|
|
case WM_MOUSEWHEEL:
|
|
|
|
|
{
|
|
|
|
|
short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
|
|
|
|
|
zoom += (float)wheelDelta * 0.005f * zoomSpeed;
|
|
|
|
|
viewChanged();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-02-16 15:07:25 +01:00
|
|
|
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();
|
|
|
|
|
}
|
2016-03-30 22:48:58 +02:00
|
|
|
if (wParam & MK_MBUTTON)
|
|
|
|
|
{
|
|
|
|
|
int32_t posx = LOWORD(lParam);
|
|
|
|
|
int32_t posy = HIWORD(lParam);
|
|
|
|
|
cameraPos.x -= (mousePos.x - (float)posx) * 0.01f;
|
|
|
|
|
cameraPos.y -= (mousePos.y - (float)posy) * 0.01f;
|
|
|
|
|
viewChanged();
|
|
|
|
|
mousePos.x = (float)posx;
|
|
|
|
|
mousePos.y = (float)posy;
|
|
|
|
|
}
|
2016-02-16 15:07:25 +01:00
|
|
|
break;
|
2016-04-10 11:12:04 +02:00
|
|
|
case WM_SIZE:
|
|
|
|
|
if ((prepared) && (wParam != SIZE_MINIMIZED))
|
|
|
|
|
{
|
|
|
|
|
destWidth = LOWORD(lParam);
|
|
|
|
|
destHeight = HIWORD(lParam);
|
|
|
|
|
if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))
|
|
|
|
|
{
|
|
|
|
|
windowResize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
|
|
|
if ((prepared) && ((destWidth != width) || (destHeight != height)))
|
|
|
|
|
{
|
|
|
|
|
windowResize();
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__ANDROID__)
|
2016-03-20 21:46:49 +01:00
|
|
|
int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* event)
|
|
|
|
|
{
|
|
|
|
|
VulkanExampleBase* vulkanExample = (VulkanExampleBase*)app->userData;
|
|
|
|
|
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
|
|
|
|
|
{
|
|
|
|
|
if (AInputEvent_getSource(event) == AINPUT_SOURCE_JOYSTICK)
|
|
|
|
|
{
|
|
|
|
|
vulkanExample->gamePadState.axes.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0);
|
|
|
|
|
vulkanExample->gamePadState.axes.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0);
|
|
|
|
|
vulkanExample->gamePadState.axes.z = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Z, 0);
|
|
|
|
|
vulkanExample->gamePadState.axes.rz = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RZ, 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// todo : touch input
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 17:35:54 +01:00
|
|
|
void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd)
|
|
|
|
|
{
|
|
|
|
|
assert(app->userData != NULL);
|
|
|
|
|
VulkanExampleBase* vulkanExample = (VulkanExampleBase*)app->userData;
|
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
|
|
|
|
case APP_CMD_SAVE_STATE:
|
2016-03-26 12:58:35 +01:00
|
|
|
LOGD("APP_CMD_SAVE_STATE");
|
2016-03-20 17:35:54 +01:00
|
|
|
/*
|
|
|
|
|
vulkanExample->app->savedState = malloc(sizeof(struct saved_state));
|
|
|
|
|
*((struct saved_state*)vulkanExample->app->savedState) = vulkanExample->state;
|
|
|
|
|
vulkanExample->app->savedStateSize = sizeof(struct saved_state);
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
case APP_CMD_INIT_WINDOW:
|
|
|
|
|
LOGD("APP_CMD_INIT_WINDOW");
|
|
|
|
|
if (vulkanExample->androidApp->window != NULL)
|
|
|
|
|
{
|
|
|
|
|
vulkanExample->initVulkan(false);
|
|
|
|
|
vulkanExample->initSwapchain();
|
|
|
|
|
vulkanExample->prepare();
|
|
|
|
|
assert(vulkanExample->prepared);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOGE("No window assigned!");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case APP_CMD_LOST_FOCUS:
|
2016-03-26 12:58:35 +01:00
|
|
|
LOGD("APP_CMD_LOST_FOCUS");
|
|
|
|
|
vulkanExample->focused = false;
|
|
|
|
|
break;
|
|
|
|
|
case APP_CMD_GAINED_FOCUS:
|
|
|
|
|
LOGD("APP_CMD_GAINED_FOCUS");
|
|
|
|
|
vulkanExample->focused = true;
|
2016-03-20 17:35:54 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__linux__)
|
2016-03-03 16:41:57 +01:00
|
|
|
// Set up a window using XCB and request event types
|
2016-02-16 15:07:25 +01:00
|
|
|
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;
|
2016-03-13 17:15:44 +01:00
|
|
|
value_list[1] =
|
2016-03-03 16:41:57 +01:00
|
|
|
XCB_EVENT_MASK_KEY_RELEASE |
|
2016-02-16 15:07:25 +01:00
|
|
|
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);
|
|
|
|
|
|
2016-03-13 17:15:44 +01:00
|
|
|
std::string windowTitle = getWindowTitle();
|
2016-02-16 15:07:25 +01:00
|
|
|
xcb_change_property(connection, XCB_PROP_MODE_REPLACE,
|
|
|
|
|
window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
|
2016-03-13 17:15:44 +01:00
|
|
|
title.size(), windowTitle.c_str());
|
2016-02-16 15:07:25 +01:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
2016-03-30 22:48:58 +02:00
|
|
|
if (mouseButtons.middle)
|
|
|
|
|
{
|
|
|
|
|
cameraPos.x -= (mousePos.x - (float)motion->event_x) * 0.01f;
|
|
|
|
|
cameraPos.y -= (mousePos.y - (float)motion->event_y) * 0.01f;
|
|
|
|
|
viewChanged();
|
|
|
|
|
mousePos.x = (float)motion->event_x;
|
|
|
|
|
mousePos.y = (float)motion->event_y;
|
|
|
|
|
}
|
2016-02-16 15:07:25 +01:00
|
|
|
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;
|
2016-03-30 22:48:58 +02:00
|
|
|
if (press->detail == XCB_BUTTON_INDEX_1)
|
|
|
|
|
mouseButtons.left = true;
|
2016-03-30 23:00:53 +02:00
|
|
|
if (press->detail == XCB_BUTTON_INDEX_2)
|
2016-03-30 22:48:58 +02:00
|
|
|
mouseButtons.middle = true;
|
|
|
|
|
if (press->detail == XCB_BUTTON_INDEX_3)
|
|
|
|
|
mouseButtons.right = true;
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case XCB_BUTTON_RELEASE:
|
|
|
|
|
{
|
|
|
|
|
xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
|
2016-03-30 22:48:58 +02:00
|
|
|
if (press->detail == XCB_BUTTON_INDEX_1)
|
2016-02-16 15:07:25 +01:00
|
|
|
mouseButtons.left = false;
|
2016-03-30 23:00:53 +02:00
|
|
|
if (press->detail == XCB_BUTTON_INDEX_2)
|
2016-03-30 22:48:58 +02:00
|
|
|
mouseButtons.middle = false;
|
|
|
|
|
if (press->detail == XCB_BUTTON_INDEX_3)
|
2016-02-16 15:07:25 +01:00
|
|
|
mouseButtons.right = false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case XCB_KEY_RELEASE:
|
|
|
|
|
{
|
2016-03-03 16:41:57 +01:00
|
|
|
const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event;
|
|
|
|
|
if (keyEvent->detail == 0x9)
|
2016-02-16 15:07:25 +01:00
|
|
|
quit = true;
|
2016-03-03 16:41:57 +01:00
|
|
|
keyPressed(keyEvent->detail);
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case XCB_DESTROY_NOTIFY:
|
|
|
|
|
quit = true;
|
|
|
|
|
break;
|
2016-04-11 19:44:03 +02:00
|
|
|
case XCB_CONFIGURE_NOTIFY:
|
|
|
|
|
{
|
|
|
|
|
const xcb_configure_notify_event_t *cfgEvent = (const xcb_configure_notify_event_t *)event;
|
|
|
|
|
if ((prepared) && ((cfgEvent->width != width) || (cfgEvent->height != height)))
|
|
|
|
|
{
|
|
|
|
|
destWidth = cfgEvent->width;
|
|
|
|
|
destHeight = cfgEvent->height;
|
|
|
|
|
if ((destWidth > 0) && (destHeight > 0))
|
|
|
|
|
{
|
|
|
|
|
windowResize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-02-16 15:07:25 +01:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::viewChanged()
|
|
|
|
|
{
|
2016-03-03 16:41:57 +01:00
|
|
|
// Can be overrdiden in derived class
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::keyPressed(uint32_t keyCode)
|
|
|
|
|
{
|
|
|
|
|
// Can be overriden in derived class
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-10 11:12:04 +02:00
|
|
|
void VulkanExampleBase::buildCommandBuffers()
|
|
|
|
|
{
|
|
|
|
|
// Can be overriden in derived class
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
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);
|
2016-03-13 12:15:58 +01:00
|
|
|
vkTools::setImageLayout(
|
2016-03-13 17:15:44 +01:00
|
|
|
setupCmdBuffer,
|
|
|
|
|
depthStencil.image,
|
|
|
|
|
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
|
|
|
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
2016-03-13 12:15:58 +01:00
|
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
2016-02-16 15:07:25 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-10 11:12:04 +02:00
|
|
|
void VulkanExampleBase::windowResize()
|
|
|
|
|
{
|
|
|
|
|
if (!prepared)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
prepared = false;
|
|
|
|
|
|
|
|
|
|
// Recreate swap chain
|
|
|
|
|
width = destWidth;
|
|
|
|
|
height = destHeight;
|
|
|
|
|
createSetupCommandBuffer();
|
|
|
|
|
setupSwapChain();
|
|
|
|
|
|
|
|
|
|
// Recreate the frame buffers
|
|
|
|
|
|
|
|
|
|
vkDestroyImageView(device, depthStencil.view, nullptr);
|
|
|
|
|
vkDestroyImage(device, depthStencil.image, nullptr);
|
|
|
|
|
vkFreeMemory(device, depthStencil.mem, nullptr);
|
|
|
|
|
setupDepthStencil();
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < frameBuffers.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
vkDestroyFramebuffer(device, frameBuffers[i], nullptr);
|
|
|
|
|
}
|
|
|
|
|
setupFrameBuffer();
|
|
|
|
|
|
|
|
|
|
flushSetupCommandBuffer();
|
|
|
|
|
|
|
|
|
|
// Command buffers need to be recreated as they may store
|
|
|
|
|
// references to the recreated frame buffer
|
|
|
|
|
destroyCommandBuffers();
|
|
|
|
|
createCommandBuffers();
|
|
|
|
|
buildCommandBuffers();
|
|
|
|
|
|
|
|
|
|
vkQueueWaitIdle(queue);
|
|
|
|
|
vkDeviceWaitIdle(device);
|
|
|
|
|
|
|
|
|
|
// Notify derived class
|
|
|
|
|
windowResized();
|
|
|
|
|
viewChanged();
|
|
|
|
|
|
|
|
|
|
prepared = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::windowResized()
|
|
|
|
|
{
|
|
|
|
|
// Can be overrdiden in derived class
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-16 15:07:25 +01:00
|
|
|
void VulkanExampleBase::initSwapchain()
|
|
|
|
|
{
|
2016-03-20 14:55:46 +01:00
|
|
|
#if defined(_WIN32)
|
2016-02-23 21:55:37 +01:00
|
|
|
swapChain.initSurface(windowInstance, window);
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__ANDROID__)
|
2016-03-20 15:45:40 +01:00
|
|
|
swapChain.initSurface(androidApp->window);
|
2016-03-20 14:55:46 +01:00
|
|
|
#elif defined(__linux__)
|
2016-02-23 21:55:37 +01:00
|
|
|
swapChain.initSurface(connection, window);
|
2016-02-16 15:07:25 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VulkanExampleBase::setupSwapChain()
|
|
|
|
|
{
|
2016-02-23 21:55:37 +01:00
|
|
|
swapChain.create(setupCmdBuffer, &width, &height);
|
2016-02-16 15:07:25 +01:00
|
|
|
}
|
2016-03-20 17:35:54 +01:00
|
|
|
|