You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
785 lines
25 KiB
785 lines
25 KiB
2 years ago
|
/*
|
||
|
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
|
||
|
#include "nvvkhl/appbase_vkpp.hpp"
|
||
|
#include "nvp/perproject_globals.hpp"
|
||
|
|
||
|
#ifdef LINUX
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
|
||
|
#include <cmath>
|
||
|
|
||
|
void nvvkhl::AppBase::onResize(int, int) {}
|
||
|
|
||
|
void nvvkhl::AppBase::setup(const vk::Instance& instance, const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t graphicsQueueIndex)
|
||
|
{
|
||
|
// Initialize function pointers
|
||
|
vk::DynamicLoader dl;
|
||
|
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
|
||
|
dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance, device);
|
||
|
|
||
|
m_instance = instance;
|
||
|
m_device = device;
|
||
|
m_physicalDevice = physicalDevice;
|
||
|
m_graphicsQueueIndex = graphicsQueueIndex;
|
||
|
m_queue = m_device.getQueue(m_graphicsQueueIndex, 0);
|
||
|
m_cmdPool = m_device.createCommandPool({vk::CommandPoolCreateFlagBits::eResetCommandBuffer, graphicsQueueIndex});
|
||
|
m_pipelineCache = device.createPipelineCache(vk::PipelineCacheCreateInfo());
|
||
|
|
||
|
ImGuiH::SetCameraJsonFile(getProjectName());
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::destroy()
|
||
|
{
|
||
|
m_device.waitIdle();
|
||
|
|
||
|
if(ImGui::GetCurrentContext() != nullptr)
|
||
|
{
|
||
|
//ImGui::ShutdownVK();
|
||
|
ImGui_ImplVulkan_Shutdown();
|
||
|
ImGui::DestroyContext();
|
||
|
}
|
||
|
|
||
|
m_device.destroy(m_renderPass);
|
||
|
m_device.destroy(m_depthView);
|
||
|
m_device.destroy(m_depthImage);
|
||
|
m_device.freeMemory(m_depthMemory);
|
||
|
m_device.destroy(m_pipelineCache);
|
||
|
|
||
|
for(uint32_t i = 0; i < m_swapChain.getImageCount(); i++)
|
||
|
{
|
||
|
m_device.destroy(m_waitFences[i]);
|
||
|
m_device.destroy(m_framebuffers[i]);
|
||
|
m_device.freeCommandBuffers(m_cmdPool, m_commandBuffers[i]);
|
||
|
}
|
||
|
m_swapChain.deinit();
|
||
|
m_device.destroy(m_imguiDescPool);
|
||
|
|
||
|
m_device.destroy(m_cmdPool);
|
||
|
if(m_surface)
|
||
|
m_instance.destroySurfaceKHR(m_surface);
|
||
|
}
|
||
|
|
||
|
VkSurfaceKHR nvvkhl::AppBase::getVkSurface(const vk::Instance& instance, GLFWwindow* window)
|
||
|
{
|
||
|
assert(instance);
|
||
|
m_window = window;
|
||
|
|
||
|
VkSurfaceKHR surface{};
|
||
|
VkResult err = glfwCreateWindowSurface(instance, window, nullptr, &surface);
|
||
|
if(err != VK_SUCCESS)
|
||
|
{
|
||
|
assert(!"Failed to create a Window surface");
|
||
|
}
|
||
|
m_surface = surface;
|
||
|
|
||
|
return surface;
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::createSwapchain(const vk::SurfaceKHR& surface, uint32_t width, uint32_t height, vk::Format colorFormat, vk::Format depthFormat, bool vsync)
|
||
|
{
|
||
|
m_size = vk::Extent2D(width, height);
|
||
|
m_colorFormat = colorFormat;
|
||
|
m_depthFormat = depthFormat;
|
||
|
m_vsync = vsync;
|
||
|
|
||
|
// Find the most suitable depth format
|
||
|
if(m_depthFormat == vk::Format::eUndefined)
|
||
|
{
|
||
|
auto feature = vk::FormatFeatureFlagBits::eDepthStencilAttachment;
|
||
|
for(const auto& f : {vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint})
|
||
|
{
|
||
|
if((m_physicalDevice.getFormatProperties(f).optimalTilingFeatures & feature) == feature)
|
||
|
{
|
||
|
m_depthFormat = f;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_swapChain.init(m_device, m_physicalDevice, m_queue, m_graphicsQueueIndex, surface, static_cast<VkFormat>(colorFormat));
|
||
|
m_size = m_swapChain.update(m_size.width, m_size.height, vsync);
|
||
|
m_colorFormat = static_cast<vk::Format>(m_swapChain.getFormat());
|
||
|
|
||
|
// Create Synchronization Primitives
|
||
|
m_waitFences.resize(m_swapChain.getImageCount());
|
||
|
for(auto& fence : m_waitFences)
|
||
|
{
|
||
|
fence = m_device.createFence({vk::FenceCreateFlagBits::eSignaled});
|
||
|
}
|
||
|
|
||
|
// Command buffers store a reference to the frame buffer inside their render pass info
|
||
|
// so for static usage without having to rebuild them each frame, we use one per frame buffer
|
||
|
m_commandBuffers =
|
||
|
m_device.allocateCommandBuffers({m_cmdPool, vk::CommandBufferLevel::ePrimary, m_swapChain.getImageCount()});
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
for(size_t i = 0; i < m_commandBuffers.size(); i++)
|
||
|
{
|
||
|
std::string name = std::string("AppBase") + std::to_string(i);
|
||
|
m_device.setDebugUtilsObjectNameEXT(
|
||
|
{vk::ObjectType::eCommandBuffer, reinterpret_cast<const uint64_t&>(m_commandBuffers[i]), name.c_str()});
|
||
|
}
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
// Setup camera
|
||
|
CameraManip.setWindowSize(m_size.width, m_size.height);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::createFrameBuffers()
|
||
|
{
|
||
|
// Recreate the frame buffers
|
||
|
for(auto framebuffer : m_framebuffers)
|
||
|
{
|
||
|
m_device.destroy(framebuffer);
|
||
|
}
|
||
|
|
||
|
// Array of attachment (color, depth)
|
||
|
std::array<vk::ImageView, 2> attachments;
|
||
|
|
||
|
// Create frame buffers for every swap chain image
|
||
|
vk::FramebufferCreateInfo framebufferCreateInfo;
|
||
|
framebufferCreateInfo.renderPass = m_renderPass;
|
||
|
framebufferCreateInfo.attachmentCount = 2;
|
||
|
framebufferCreateInfo.width = m_size.width;
|
||
|
framebufferCreateInfo.height = m_size.height;
|
||
|
framebufferCreateInfo.layers = 1;
|
||
|
framebufferCreateInfo.pAttachments = attachments.data();
|
||
|
|
||
|
// Create frame buffers for every swap chain image
|
||
|
m_framebuffers.resize(m_swapChain.getImageCount());
|
||
|
for(uint32_t i = 0; i < m_swapChain.getImageCount(); i++)
|
||
|
{
|
||
|
attachments[0] = m_swapChain.getImageView(i);
|
||
|
attachments[1] = m_depthView;
|
||
|
m_framebuffers[i] = m_device.createFramebuffer(framebufferCreateInfo);
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
for(size_t i = 0; i < m_framebuffers.size(); i++)
|
||
|
{
|
||
|
std::string name = std::string("AppBase") + std::to_string(i);
|
||
|
m_device.setDebugUtilsObjectNameEXT(
|
||
|
{vk::ObjectType::eFramebuffer, reinterpret_cast<const uint64_t&>(m_framebuffers[i]), name.c_str()});
|
||
|
}
|
||
|
#endif // _DEBUG
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::createRenderPass()
|
||
|
{
|
||
|
if(m_renderPass)
|
||
|
{
|
||
|
m_device.destroy(m_renderPass);
|
||
|
}
|
||
|
|
||
|
std::array<vk::AttachmentDescription, 2> attachments;
|
||
|
// Color attachment
|
||
|
attachments[0].setFormat(m_colorFormat);
|
||
|
attachments[0].setLoadOp(vk::AttachmentLoadOp::eClear);
|
||
|
attachments[0].setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
|
||
|
|
||
|
// Depth attachment
|
||
|
attachments[1].setFormat(m_depthFormat);
|
||
|
attachments[1].setLoadOp(vk::AttachmentLoadOp::eClear);
|
||
|
attachments[1].setStencilLoadOp(vk::AttachmentLoadOp::eClear);
|
||
|
attachments[1].setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||
|
|
||
|
// One color, one depth
|
||
|
const vk::AttachmentReference colorReference{0, vk::ImageLayout::eColorAttachmentOptimal};
|
||
|
const vk::AttachmentReference depthReference{1, vk::ImageLayout::eDepthStencilAttachmentOptimal};
|
||
|
|
||
|
std::array<vk::SubpassDependency, 1> subpassDependencies;
|
||
|
// Transition from final to initial (VK_SUBPASS_EXTERNAL refers to all commands executed outside of the actual renderpass)
|
||
|
subpassDependencies[0].setSrcSubpass(VK_SUBPASS_EXTERNAL);
|
||
|
subpassDependencies[0].setDstSubpass(0);
|
||
|
subpassDependencies[0].setSrcStageMask(vk::PipelineStageFlagBits::eBottomOfPipe);
|
||
|
subpassDependencies[0].setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput);
|
||
|
subpassDependencies[0].setSrcAccessMask(vk::AccessFlagBits::eMemoryRead);
|
||
|
subpassDependencies[0].setDstAccessMask(vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite);
|
||
|
subpassDependencies[0].setDependencyFlags(vk::DependencyFlagBits::eByRegion);
|
||
|
|
||
|
vk::SubpassDescription subpassDescription;
|
||
|
subpassDescription.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
|
||
|
subpassDescription.setColorAttachmentCount(1);
|
||
|
subpassDescription.setPColorAttachments(&colorReference);
|
||
|
subpassDescription.setPDepthStencilAttachment(&depthReference);
|
||
|
|
||
|
vk::RenderPassCreateInfo renderPassInfo;
|
||
|
renderPassInfo.setAttachmentCount(static_cast<uint32_t>(attachments.size()));
|
||
|
renderPassInfo.setPAttachments(attachments.data());
|
||
|
renderPassInfo.setSubpassCount(1);
|
||
|
renderPassInfo.setPSubpasses(&subpassDescription);
|
||
|
renderPassInfo.setDependencyCount(static_cast<uint32_t>(subpassDependencies.size()));
|
||
|
renderPassInfo.setPDependencies(subpassDependencies.data());
|
||
|
|
||
|
m_renderPass = m_device.createRenderPass(renderPassInfo);
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
m_device.setDebugUtilsObjectNameEXT(
|
||
|
{vk::ObjectType::eRenderPass, reinterpret_cast<const uint64_t&>(m_renderPass), "AppBase"});
|
||
|
#endif // _DEBUG
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::createDepthBuffer()
|
||
|
{
|
||
|
if(m_depthView)
|
||
|
m_device.destroy(m_depthView);
|
||
|
if(m_depthImage)
|
||
|
m_device.destroy(m_depthImage);
|
||
|
if(m_depthMemory)
|
||
|
m_device.freeMemory(m_depthMemory);
|
||
|
|
||
|
// Depth information
|
||
|
const vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||
|
vk::ImageCreateInfo depthStencilCreateInfo;
|
||
|
depthStencilCreateInfo.setImageType(vk::ImageType::e2D);
|
||
|
depthStencilCreateInfo.setExtent(vk::Extent3D{m_size.width, m_size.height, 1});
|
||
|
depthStencilCreateInfo.setFormat(m_depthFormat);
|
||
|
depthStencilCreateInfo.setMipLevels(1);
|
||
|
depthStencilCreateInfo.setArrayLayers(1);
|
||
|
depthStencilCreateInfo.setUsage(vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eTransferSrc);
|
||
|
// Create the depth image
|
||
|
m_depthImage = m_device.createImage(depthStencilCreateInfo);
|
||
|
|
||
|
// Allocate the memory
|
||
|
const vk::MemoryRequirements memReqs = m_device.getImageMemoryRequirements(m_depthImage);
|
||
|
vk::MemoryAllocateInfo memAllocInfo;
|
||
|
memAllocInfo.allocationSize = memReqs.size;
|
||
|
memAllocInfo.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||
|
m_depthMemory = m_device.allocateMemory(memAllocInfo);
|
||
|
|
||
|
// Bind image and memory
|
||
|
m_device.bindImageMemory(m_depthImage, m_depthMemory, 0);
|
||
|
|
||
|
// Create an image barrier to change the layout from undefined to DepthStencilAttachmentOptimal
|
||
|
vk::CommandBuffer cmdBuffer = m_device.allocateCommandBuffers({m_cmdPool, vk::CommandBufferLevel::ePrimary, 1})[0];
|
||
|
cmdBuffer.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||
|
|
||
|
// Put barrier on top, Put barrier inside setup command buffer
|
||
|
vk::ImageSubresourceRange subresourceRange;
|
||
|
subresourceRange.aspectMask = aspect;
|
||
|
subresourceRange.levelCount = 1;
|
||
|
subresourceRange.layerCount = 1;
|
||
|
vk::ImageMemoryBarrier imageMemoryBarrier;
|
||
|
imageMemoryBarrier.oldLayout = vk::ImageLayout::eUndefined;
|
||
|
imageMemoryBarrier.newLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
|
||
|
imageMemoryBarrier.image = m_depthImage;
|
||
|
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||
|
imageMemoryBarrier.srcAccessMask = vk::AccessFlags();
|
||
|
imageMemoryBarrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite;
|
||
|
const vk::PipelineStageFlags srcStageMask = vk::PipelineStageFlagBits::eTopOfPipe;
|
||
|
const vk::PipelineStageFlags destStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests;
|
||
|
|
||
|
cmdBuffer.pipelineBarrier(srcStageMask, destStageMask, vk::DependencyFlags(), nullptr, nullptr, imageMemoryBarrier);
|
||
|
cmdBuffer.end();
|
||
|
m_queue.submit(vk::SubmitInfo{0, nullptr, nullptr, 1, &cmdBuffer}, vk::Fence());
|
||
|
m_queue.waitIdle();
|
||
|
m_device.freeCommandBuffers(m_cmdPool, cmdBuffer);
|
||
|
|
||
|
// Setting up the view
|
||
|
vk::ImageViewCreateInfo depthStencilView;
|
||
|
depthStencilView.setViewType(vk::ImageViewType::e2D);
|
||
|
depthStencilView.setFormat(m_depthFormat);
|
||
|
depthStencilView.setSubresourceRange(subresourceRange);
|
||
|
depthStencilView.setImage(m_depthImage);
|
||
|
m_depthView = m_device.createImageView(depthStencilView);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::prepareFrame()
|
||
|
{
|
||
|
// Resize protection - should be cached by the glFW callback
|
||
|
int w, h;
|
||
|
glfwGetFramebufferSize(m_window, &w, &h);
|
||
|
if(w != (int)m_size.width || h != (int)m_size.height)
|
||
|
{
|
||
|
onFramebufferSize(w, h);
|
||
|
}
|
||
|
|
||
|
// Acquire the next image from the swap chain
|
||
|
if(!m_swapChain.acquire())
|
||
|
{
|
||
|
assert(!"This shouldn't happen");
|
||
|
}
|
||
|
|
||
|
// Use a fence to wait until the command buffer has finished execution before using it again
|
||
|
uint32_t imageIndex = m_swapChain.getActiveImageIndex();
|
||
|
while(m_device.waitForFences(m_waitFences[imageIndex], VK_TRUE, 10000) == vk::Result::eTimeout)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// start new frame with updated camera
|
||
|
updateCamera();
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::submitFrame()
|
||
|
{
|
||
|
uint32_t imageIndex = m_swapChain.getActiveImageIndex();
|
||
|
m_device.resetFences(m_waitFences[imageIndex]);
|
||
|
|
||
|
// In case of using NVLINK
|
||
|
const uint32_t deviceMask = m_useNvlink ? 0b0000'0011 : 0b0000'0001;
|
||
|
const std::array<uint32_t, 2> deviceIndex = {0, 1};
|
||
|
|
||
|
vk::DeviceGroupSubmitInfo deviceGroupSubmitInfo;
|
||
|
deviceGroupSubmitInfo.setWaitSemaphoreCount(1);
|
||
|
deviceGroupSubmitInfo.setCommandBufferCount(1);
|
||
|
deviceGroupSubmitInfo.setPCommandBufferDeviceMasks(&deviceMask);
|
||
|
deviceGroupSubmitInfo.setSignalSemaphoreCount(m_useNvlink ? 2 : 1);
|
||
|
deviceGroupSubmitInfo.setPSignalSemaphoreDeviceIndices(deviceIndex.data());
|
||
|
deviceGroupSubmitInfo.setPWaitSemaphoreDeviceIndices(deviceIndex.data());
|
||
|
|
||
|
vk::Semaphore semaphoreRead = m_swapChain.getActiveReadSemaphore();
|
||
|
vk::Semaphore semaphoreWrite = m_swapChain.getActiveWrittenSemaphore();
|
||
|
|
||
|
// Pipeline stage at which the queue submission will wait (via pWaitSemaphores)
|
||
|
const vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||
|
// The submit info structure specifies a command buffer queue submission batch
|
||
|
vk::SubmitInfo submitInfo;
|
||
|
submitInfo.setPWaitDstStageMask(&waitStageMask); // Pointer to the list of pipeline stages that the semaphore waits will occur at
|
||
|
submitInfo.setPWaitSemaphores(&semaphoreRead); // Semaphore(s) to wait upon before the submitted command buffer starts executing
|
||
|
submitInfo.setWaitSemaphoreCount(1); // One wait semaphore
|
||
|
submitInfo.setPSignalSemaphores(&semaphoreWrite); // Semaphore(s) to be signaled when command buffers have completed
|
||
|
submitInfo.setSignalSemaphoreCount(1); // One signal semaphore
|
||
|
submitInfo.setPCommandBuffers(&m_commandBuffers[imageIndex]); // Command buffers(s) to execute in this batch (submission)
|
||
|
submitInfo.setCommandBufferCount(1); // One command buffer
|
||
|
submitInfo.setPNext(&deviceGroupSubmitInfo);
|
||
|
|
||
|
// Submit to the graphics queue passing a wait fence
|
||
|
m_queue.submit(submitInfo, m_waitFences[imageIndex]);
|
||
|
|
||
|
// Presenting frame
|
||
|
m_swapChain.present(m_queue);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::setViewport(const vk::CommandBuffer& cmdBuf)
|
||
|
{
|
||
|
cmdBuf.setViewport(0, {vk::Viewport(0.0f, 0.0f, static_cast<float>(m_size.width), static_cast<float>(m_size.height), 0.0f, 1.0f)});
|
||
|
cmdBuf.setScissor(0, {{{0, 0}, {m_size.width, m_size.height}}});
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onFramebufferSize(int w, int h)
|
||
|
{
|
||
|
if(w == 0 || h == 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Update imgui
|
||
|
if(ImGui::GetCurrentContext() != nullptr)
|
||
|
{
|
||
|
auto& imgui_io = ImGui::GetIO();
|
||
|
imgui_io.DisplaySize = ImVec2(static_cast<float>(w), static_cast<float>(h));
|
||
|
}
|
||
|
// Wait to finish what is currently drawing
|
||
|
m_device.waitIdle();
|
||
|
m_queue.waitIdle();
|
||
|
|
||
|
// Request new swapschain image size
|
||
|
m_size = m_swapChain.update(m_size.width, m_size.height, m_vsync);
|
||
|
|
||
|
if(m_size.width != w || m_size.height != h)
|
||
|
{
|
||
|
LOGW("Requested size (%d, %d) is different from created size (%u, %u) ", w, h, m_size.width, m_size.height);
|
||
|
}
|
||
|
|
||
|
CameraManip.setWindowSize(m_size.width, m_size.height);
|
||
|
// Invoking Sample callback
|
||
|
onResize(m_size.width, m_size.height);
|
||
|
// Recreating other resources
|
||
|
createDepthBuffer();
|
||
|
createFrameBuffers();
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onMouseMotion(int x, int y)
|
||
|
{
|
||
|
if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(m_inputs.lmb || m_inputs.rmb || m_inputs.mmb)
|
||
|
{
|
||
|
CameraManip.mouseMove(x, y, m_inputs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onKeyboard(int key, int, int action, int mods)
|
||
|
{
|
||
|
const bool pressed = action != GLFW_RELEASE;
|
||
|
|
||
|
if(pressed && key == GLFW_KEY_F10)
|
||
|
{
|
||
|
m_show_gui = !m_show_gui;
|
||
|
}
|
||
|
else if(pressed && key == GLFW_KEY_ESCAPE)
|
||
|
{
|
||
|
glfwSetWindowShouldClose(m_window, 1);
|
||
|
}
|
||
|
|
||
|
// Remember all keys that are simultaneously pressed for animating the camera
|
||
|
if(pressed)
|
||
|
{
|
||
|
m_keys.insert(key);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_keys.erase(key);
|
||
|
}
|
||
|
|
||
|
// Keeping track of the modifiers
|
||
|
m_inputs.ctrl = mods & GLFW_MOD_CONTROL;
|
||
|
m_inputs.shift = mods & GLFW_MOD_SHIFT;
|
||
|
m_inputs.alt = mods & GLFW_MOD_ALT;
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onKeyboardChar(unsigned char key)
|
||
|
{
|
||
|
if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureKeyboard)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Toggling vsync
|
||
|
if(key == 'v')
|
||
|
{
|
||
|
m_vsync = !m_vsync;
|
||
|
m_device.waitIdle();
|
||
|
m_queue.waitIdle();
|
||
|
m_swapChain.update(m_size.width, m_size.height, m_vsync);
|
||
|
createFrameBuffers();
|
||
|
}
|
||
|
|
||
|
if(key == 'h' || key == '?')
|
||
|
m_showHelp = !m_showHelp;
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onMouseButton(int button, int action, int mods)
|
||
|
{
|
||
|
(void)mods;
|
||
|
|
||
|
double x, y;
|
||
|
glfwGetCursorPos(m_window, &x, &y);
|
||
|
CameraManip.setMousePosition(static_cast<int>(x), static_cast<int>(y));
|
||
|
|
||
|
m_inputs.lmb = (button == GLFW_MOUSE_BUTTON_LEFT) && (action == GLFW_PRESS);
|
||
|
m_inputs.mmb = (button == GLFW_MOUSE_BUTTON_MIDDLE) && (action == GLFW_PRESS);
|
||
|
m_inputs.rmb = (button == GLFW_MOUSE_BUTTON_RIGHT) && (action == GLFW_PRESS);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onMouseWheel(int delta)
|
||
|
{
|
||
|
if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureMouse)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CameraManip.wheel(delta > 0 ? 1 : -1, m_inputs);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::onFileDrop(const char* filename) {}
|
||
|
|
||
|
void nvvkhl::AppBase::initGUI(uint32_t subpassID)
|
||
|
{
|
||
|
assert(m_renderPass && "Render Pass must be set");
|
||
|
|
||
|
// UI
|
||
|
ImGui::CreateContext();
|
||
|
ImGuiIO& io = ImGui::GetIO();
|
||
|
io.IniFilename = nullptr; // Avoiding the INI file
|
||
|
io.LogFilename = nullptr;
|
||
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||
|
//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
|
||
|
|
||
|
ImGuiH::setStyle();
|
||
|
ImGuiH::setFonts();
|
||
|
|
||
|
std::vector<vk::DescriptorPoolSize> poolSize{{vk::DescriptorType::eSampler, 1}, {vk::DescriptorType::eCombinedImageSampler, 1}};
|
||
|
vk::DescriptorPoolCreateInfo poolInfo{{}, 2, poolSize};
|
||
|
m_imguiDescPool = m_device.createDescriptorPool(poolInfo);
|
||
|
|
||
|
// Setup Platform/Renderer backends
|
||
|
ImGui_ImplVulkan_InitInfo init_info = {};
|
||
|
init_info.Instance = m_instance;
|
||
|
init_info.PhysicalDevice = m_physicalDevice;
|
||
|
init_info.Device = m_device;
|
||
|
init_info.QueueFamily = m_graphicsQueueIndex;
|
||
|
init_info.Queue = m_queue;
|
||
|
init_info.PipelineCache = VK_NULL_HANDLE;
|
||
|
init_info.DescriptorPool = m_imguiDescPool;
|
||
|
init_info.Subpass = subpassID;
|
||
|
init_info.MinImageCount = 2;
|
||
|
init_info.ImageCount = static_cast<int>(m_framebuffers.size());
|
||
|
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; // <--- need argument?
|
||
|
init_info.CheckVkResultFn = nullptr;
|
||
|
init_info.Allocator = nullptr;
|
||
|
ImGui_ImplVulkan_Init(&init_info, m_renderPass);
|
||
|
|
||
|
|
||
|
// Upload Fonts
|
||
|
auto cmdbuf = m_device.allocateCommandBuffers({m_cmdPool, vk::CommandBufferLevel::ePrimary, 1})[0];
|
||
|
cmdbuf.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||
|
ImGui_ImplVulkan_CreateFontsTexture(cmdbuf);
|
||
|
cmdbuf.end();
|
||
|
m_queue.submit(vk::SubmitInfo{0, nullptr, nullptr, 1, &cmdbuf}, vk::Fence());
|
||
|
m_device.waitIdle();
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::fitCamera(const nvmath::vec3f& boxMin, const nvmath::vec3f& boxMax, bool instantFit)
|
||
|
{
|
||
|
CameraManip.fit(boxMin, boxMax, instantFit, false, m_size.width / static_cast<float>(m_size.height));
|
||
|
}
|
||
|
|
||
|
bool nvvkhl::AppBase::isMinimized(bool doSleeping)
|
||
|
{
|
||
|
int w, h;
|
||
|
glfwGetWindowSize(m_window, &w, &h);
|
||
|
bool minimized(w == 0 || h == 0);
|
||
|
if(minimized && doSleeping)
|
||
|
{
|
||
|
#ifdef _WIN32
|
||
|
Sleep(50);
|
||
|
#else
|
||
|
usleep(50);
|
||
|
#endif
|
||
|
}
|
||
|
return minimized;
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::setTitle(const std::string& title)
|
||
|
{
|
||
|
glfwSetWindowTitle(m_window, title.c_str());
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::setupGlfwCallbacks(GLFWwindow* window)
|
||
|
{
|
||
|
m_window = window;
|
||
|
glfwSetWindowUserPointer(window, this);
|
||
|
glfwSetKeyCallback(window, &key_cb);
|
||
|
glfwSetCharCallback(window, &char_cb);
|
||
|
glfwSetCursorPosCallback(window, &cursorpos_cb);
|
||
|
glfwSetMouseButtonCallback(window, &mousebutton_cb);
|
||
|
glfwSetScrollCallback(window, &scroll_cb);
|
||
|
glfwSetFramebufferSizeCallback(window, &framebuffersize_cb);
|
||
|
glfwSetDropCallback(window, &drop_cb);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::framebuffersize_cb(GLFWwindow* window, int w, int h)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
app->onFramebufferSize(w, h);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::mousebutton_cb(GLFWwindow* window, int button, int action, int mods)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
app->onMouseButton(button, action, mods);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::cursorpos_cb(GLFWwindow* window, double x, double y)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
app->onMouseMotion(static_cast<int>(x), static_cast<int>(y));
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::scroll_cb(GLFWwindow* window, double x, double y)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
app->onMouseWheel(static_cast<int>(y));
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::key_cb(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
app->onKeyboard(key, scancode, action, mods);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::char_cb(GLFWwindow* window, unsigned int key)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
app->onKeyboardChar(key);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::drop_cb(GLFWwindow* window, int count, const char** paths)
|
||
|
{
|
||
|
auto app = reinterpret_cast<AppBase*>(glfwGetWindowUserPointer(window));
|
||
|
int i;
|
||
|
for(i = 0; i < count; i++)
|
||
|
app->onFileDrop(paths[i]);
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::useNvlink(bool useNvlink)
|
||
|
{
|
||
|
m_useNvlink = useNvlink;
|
||
|
}
|
||
|
|
||
|
vk::Instance nvvkhl::AppBase::getInstance()
|
||
|
{
|
||
|
return m_instance;
|
||
|
}
|
||
|
|
||
|
vk::Device nvvkhl::AppBase::getDevice()
|
||
|
{
|
||
|
return m_device;
|
||
|
}
|
||
|
|
||
|
vk::PhysicalDevice nvvkhl::AppBase::getPhysicalDevice()
|
||
|
{
|
||
|
return m_physicalDevice;
|
||
|
}
|
||
|
|
||
|
vk::Queue nvvkhl::AppBase::getQueue()
|
||
|
{
|
||
|
return m_queue;
|
||
|
}
|
||
|
|
||
|
uint32_t nvvkhl::AppBase::getQueueFamily()
|
||
|
{
|
||
|
return m_graphicsQueueIndex;
|
||
|
}
|
||
|
|
||
|
vk::CommandPool nvvkhl::AppBase::getCommandPool()
|
||
|
{
|
||
|
return m_cmdPool;
|
||
|
}
|
||
|
|
||
|
vk::RenderPass nvvkhl::AppBase::getRenderPass()
|
||
|
{
|
||
|
return m_renderPass;
|
||
|
}
|
||
|
|
||
|
vk::Extent2D nvvkhl::AppBase::getSize()
|
||
|
{
|
||
|
return m_size;
|
||
|
}
|
||
|
|
||
|
vk::PipelineCache nvvkhl::AppBase::getPipelineCache()
|
||
|
{
|
||
|
return m_pipelineCache;
|
||
|
}
|
||
|
|
||
|
vk::SurfaceKHR nvvkhl::AppBase::getSurface()
|
||
|
{
|
||
|
return m_surface;
|
||
|
}
|
||
|
|
||
|
const std::vector<vk::Framebuffer>& nvvkhl::AppBase::getFramebuffers()
|
||
|
{
|
||
|
return m_framebuffers;
|
||
|
}
|
||
|
|
||
|
const std::vector<vk::CommandBuffer>& nvvkhl::AppBase::getCommandBuffers()
|
||
|
{
|
||
|
return m_commandBuffers;
|
||
|
}
|
||
|
|
||
|
uint32_t nvvkhl::AppBase::getCurFrame() const
|
||
|
{
|
||
|
return m_swapChain.getActiveImageIndex();
|
||
|
}
|
||
|
|
||
|
vk::Format nvvkhl::AppBase::getColorFormat() const
|
||
|
{
|
||
|
return m_colorFormat;
|
||
|
}
|
||
|
|
||
|
vk::Format nvvkhl::AppBase::getDepthFormat() const
|
||
|
{
|
||
|
return m_depthFormat;
|
||
|
}
|
||
|
|
||
|
bool nvvkhl::AppBase::showGui()
|
||
|
{
|
||
|
return m_show_gui;
|
||
|
}
|
||
|
|
||
|
uint32_t nvvkhl::AppBase::getMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties) const
|
||
|
{
|
||
|
auto deviceMemoryProperties = m_physicalDevice.getMemoryProperties();
|
||
|
for(uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
|
||
|
{
|
||
|
if(((typeBits & (1 << i)) > 0) && (deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
LOGE("Unable to find memory type %u\n", static_cast<unsigned int>(properties));
|
||
|
assert(0);
|
||
|
return ~0u;
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::uiDisplayHelp()
|
||
|
{
|
||
|
if(m_showHelp)
|
||
|
{
|
||
|
ImGui::BeginChild("Help", ImVec2(370, 120), true);
|
||
|
ImGui::Text("%s", CameraManip.getHelp().c_str());
|
||
|
ImGui::EndChild();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void nvvkhl::AppBase::updateCamera()
|
||
|
{
|
||
|
// measure one frame at a time
|
||
|
float factor = static_cast<float>(m_timer.elapsed());
|
||
|
m_timer.reset();
|
||
|
|
||
|
// Allow camera movement only when not editing
|
||
|
if(ImGui::GetCurrentContext() != nullptr && ImGui::GetIO().WantCaptureKeyboard)
|
||
|
return;
|
||
|
|
||
|
// For all pressed keys - apply the action
|
||
|
CameraManip.keyMotion(0, 0, nvh::CameraManipulator::NoAction);
|
||
|
for(auto key : m_keys)
|
||
|
{
|
||
|
switch(key)
|
||
|
{
|
||
|
case GLFW_KEY_W:
|
||
|
CameraManip.keyMotion(factor, 0, nvh::CameraManipulator::Dolly);
|
||
|
break;
|
||
|
case GLFW_KEY_S:
|
||
|
CameraManip.keyMotion(-factor, 0, nvh::CameraManipulator::Dolly);
|
||
|
break;
|
||
|
case GLFW_KEY_A:
|
||
|
case GLFW_KEY_LEFT:
|
||
|
CameraManip.keyMotion(-factor, 0, nvh::CameraManipulator::Pan);
|
||
|
break;
|
||
|
case GLFW_KEY_UP:
|
||
|
CameraManip.keyMotion(0, factor, nvh::CameraManipulator::Pan);
|
||
|
break;
|
||
|
case GLFW_KEY_D:
|
||
|
case GLFW_KEY_RIGHT:
|
||
|
CameraManip.keyMotion(factor, 0, nvh::CameraManipulator::Pan);
|
||
|
break;
|
||
|
case GLFW_KEY_DOWN:
|
||
|
CameraManip.keyMotion(0, -factor, nvh::CameraManipulator::Pan);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This makes the camera to transition smoothly to the new position
|
||
|
CameraManip.updateAnim();
|
||
|
}
|