/* * 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 #endif #include 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("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(colorFormat)); m_size = m_swapChain.update(m_size.width, m_size.height, vsync); m_colorFormat = static_cast(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(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 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(m_framebuffers[i]), name.c_str()}); } #endif // _DEBUG } void nvvkhl::AppBase::createRenderPass() { if(m_renderPass) { m_device.destroy(m_renderPass); } std::array 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 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(attachments.size())); renderPassInfo.setPAttachments(attachments.data()); renderPassInfo.setSubpassCount(1); renderPassInfo.setPSubpasses(&subpassDescription); renderPassInfo.setDependencyCount(static_cast(subpassDependencies.size())); renderPassInfo.setPDependencies(subpassDependencies.data()); m_renderPass = m_device.createRenderPass(renderPassInfo); #ifdef _DEBUG m_device.setDebugUtilsObjectNameEXT( {vk::ObjectType::eRenderPass, reinterpret_cast(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 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(m_size.width), static_cast(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(w), static_cast(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(x), static_cast(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 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(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(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(glfwGetWindowUserPointer(window)); app->onFramebufferSize(w, h); } void nvvkhl::AppBase::mousebutton_cb(GLFWwindow* window, int button, int action, int mods) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->onMouseButton(button, action, mods); } void nvvkhl::AppBase::cursorpos_cb(GLFWwindow* window, double x, double y) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->onMouseMotion(static_cast(x), static_cast(y)); } void nvvkhl::AppBase::scroll_cb(GLFWwindow* window, double x, double y) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->onMouseWheel(static_cast(y)); } void nvvkhl::AppBase::key_cb(GLFWwindow* window, int key, int scancode, int action, int mods) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->onKeyboard(key, scancode, action, mods); } void nvvkhl::AppBase::char_cb(GLFWwindow* window, unsigned int key) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->onKeyboardChar(key); } void nvvkhl::AppBase::drop_cb(GLFWwindow* window, int count, const char** paths) { auto app = reinterpret_cast(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& nvvkhl::AppBase::getFramebuffers() { return m_framebuffers; } const std::vector& 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(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(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(); }