diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ac0a9a..9a0935a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ include_directories(external/tinygltf) include_directories(external/ktx/include) include_directories(external/ktx/other_include) include_directories(external/glfw-3.4/include) +include_directories(external/glfw) include_directories(base) OPTION(USE_D2D_WSI "Build the project using Direct to Display swapchain" OFF) diff --git a/base/camera.hpp b/base/camera.hpp index 160c26e..920b8c6 100644 --- a/base/camera.hpp +++ b/base/camera.hpp @@ -23,6 +23,7 @@ private: glm::mat4 rotM = glm::mat4(1.0f); glm::mat4 transM; + //根据传入角度,绕x,y,z轴旋转 rotM = glm::rotate(rotM, glm::radians(rotation.x * (flipY ? -1.0f : 1.0f)), glm::vec3(1.0f, 0.0f, 0.0f)); rotM = glm::rotate(rotM, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); rotM = glm::rotate(rotM, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); @@ -86,7 +87,7 @@ public: float getFarClip() { return zfar; } - + // 投影计算 void setPerspective(float fov, float aspect, float znear, float zfar) { this->fov = fov; @@ -153,6 +154,7 @@ public: { if (moving()) { + // 相机朝向 glm::vec3 camFront; camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y)); camFront.y = sin(glm::radians(rotation.x)); @@ -161,6 +163,7 @@ public: float moveSpeed = deltaTime * movementSpeed; + // 位置的平移计算 if (keys.up) position += camFront * moveSpeed; if (keys.down) diff --git a/base/vulkanexamplebase.cpp b/base/vulkanexamplebase.cpp index 98b827d..598497e 100644 --- a/base/vulkanexamplebase.cpp +++ b/base/vulkanexamplebase.cpp @@ -9,2173 +9,28 @@ #include "VulkanExampleBase.h" -std::vector VulkanExampleBase::args; - -VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char * pLayerPrefix, const char * pMsg, void * pUserData) -{ - std::string prefix(""); - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - prefix += "ERROR:"; - }; - if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - prefix += "WARNING:"; - }; - if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { - prefix += "DEBUG:"; - } - std::stringstream debugMessage; - debugMessage << prefix << " [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg; -#if defined(__ANDROID__) - LOGD("%s", debugMessage.str().c_str()); -#else - std::cout << debugMessage.str() << "\n"; -#endif - fflush(stdout); - return VK_FALSE; -} - -VkResult VulkanExampleBase::createInstance(bool enableValidation) -{ - -} -void VulkanExampleBase::prepare() -{ - /* - Swapchain - */ - initSwapchain(); - setupSwapChain(); - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - width = swapChain.extent.width; - height = swapChain.extent.height; -#endif - - /* - Command pool - */ - 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; - VK_CHECK_RESULT(vkCreateCommandPool(device, &cmdPoolInfo, nullptr, &cmdPool)); - - /* - Render pass - */ - - if (settings.multiSampling) { - std::array attachments = {}; - - // Multisampled attachment that we render to - attachments[0].format = swapChain.colorFormat; - attachments[0].samples = settings.sampleCount; - 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_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - // This is the frame buffer attachment to where the multisampled image - // will be resolved to and which will be presented to the swapchain - attachments[1].format = swapChain.colorFormat; - attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - 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_UNDEFINED; - attachments[1].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - // Multisampled depth attachment we render to - attachments[2].format = depthFormat; - attachments[2].samples = settings.sampleCount; - attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // Depth resolve attachment - attachments[3].format = depthFormat; - attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[3].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 = 2; - depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - // Resolve attachment reference for the color attachment - VkAttachmentReference resolveReference = {}; - resolveReference.attachment = 1; - resolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorReference; - // Pass our resolve attachments to the sub pass - subpass.pResolveAttachments = &resolveReference; - subpass.pDepthStencilAttachment = &depthReference; - - std::array dependencies; - - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - VkRenderPassCreateInfo renderPassCI = {}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpass; - renderPassCI.dependencyCount = 2; - renderPassCI.pDependencies = dependencies.data(); - VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); - } - else { - std::array attachments = {}; - // Color attachment - attachments[0].format = swapChain.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_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - // Depth attachment - 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_CLEAR; - attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - 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 subpassDescription = {}; - subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDescription.colorAttachmentCount = 1; - subpassDescription.pColorAttachments = &colorReference; - subpassDescription.pDepthStencilAttachment = &depthReference; - subpassDescription.inputAttachmentCount = 0; - subpassDescription.pInputAttachments = nullptr; - subpassDescription.preserveAttachmentCount = 0; - subpassDescription.pPreserveAttachments = nullptr; - subpassDescription.pResolveAttachments = nullptr; - - // Subpass dependencies for layout transitions - std::array dependencies; - - dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; - dependencies[0].dstSubpass = 0; - dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - dependencies[1].srcSubpass = 0; - dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; - dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; - - VkRenderPassCreateInfo renderPassCI{}; - renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCI.attachmentCount = static_cast(attachments.size()); - renderPassCI.pAttachments = attachments.data(); - renderPassCI.subpassCount = 1; - renderPassCI.pSubpasses = &subpassDescription; - renderPassCI.dependencyCount = static_cast(dependencies.size()); - renderPassCI.pDependencies = dependencies.data(); - VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); - } - - /* - Pipeline cache - */ - VkPipelineCacheCreateInfo pipelineCacheCreateInfo{}; - pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; - VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); - - /* - Frame buffer - */ - setupFrameBuffer(); -} - -void VulkanExampleBase::fileDropped(std::string filename) { } - -void VulkanExampleBase::renderFrame() -{ - auto tStart = std::chrono::high_resolution_clock::now(); - - render(); - - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = (float)tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) { - lastFPS = static_cast((float)frameCounter * (1000.0f / fpsTimer)); - fpsTimer = 0.0f; - frameCounter = 0; - } -} - -void VulkanExampleBase::renderLoop() -{ - destWidth = width; - destHeight = height; -#if defined(_WIN32) - MSG msg; - bool quitMessageReceived = false; - while (!quitMessageReceived) { - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - if (msg.message == WM_QUIT) { - quitMessageReceived = true; - break; - } - } - if (!IsIconic(window)) { - renderFrame(); - } - } -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - while (1) - { - int ident; - int events; - struct android_poll_source* source; - bool destroy = false; - - focused = true; - - while ((ident = ALooper_pollAll(focused ? 0 : -1, NULL, &events, (void**)&source)) >= 0) - { - if (source != NULL) - { - source->process(androidApp, source); - } - if (androidApp->destroyRequested != 0) - { - LOGD("Android app destroy requested"); - destroy = true; - break; - } - } - - // App destruction requested - // Exit loop, example will be destroyed in application main - if (destroy) - { - break; - } - - // Render frame - if (prepared) - { - auto tStart = std::chrono::high_resolution_clock::now(); - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - - // Check gamepad state - const float deadZone = 0.0015f; - // todo : check if gamepad is present - // todo : time based and relative axis positions - if (camera.type != Camera::CameraType::firstperson) - { - // Rotate - if (std::abs(gamePadState.axisLeft.x) > deadZone) { - camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f)); - } - if (std::abs(gamePadState.axisLeft.y) > deadZone) { - camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f)); - } - } else { - camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer); - } - } - } -#elif defined(_DIRECT2DISPLAY) - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - } -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - - while (wl_display_prepare_read(display) != 0) - wl_display_dispatch_pending(display); - wl_display_flush(display); - wl_display_read_events(display); - wl_display_dispatch_pending(display); - - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - wl_shell_surface_set_title(shell_surface, title.c_str()); - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - } -#elif defined(VK_USE_PLATFORM_XCB_KHR) - xcb_flush(connection); - while (!quit) - { - auto tStart = std::chrono::high_resolution_clock::now(); - xcb_generic_event_t *event; - while ((event = xcb_poll_for_event(connection))) - { - handleEvent(event); - free(event); - } - render(); - frameCounter++; - auto tEnd = std::chrono::high_resolution_clock::now(); - auto tDiff = std::chrono::duration(tEnd - tStart).count(); - frameTimer = tDiff / 1000.0f; - camera.update(frameTimer); - fpsTimer += (float)tDiff; - if (fpsTimer > 1000.0f) - { - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - title.size(), title.c_str()); - lastFPS = (float)frameCounter * (1000.0f / fpsTimer); - fpsTimer = 0.0f; - frameCounter = 0; - } - } -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - [NSApp run]; -#endif - // Flush device to make sure all resources can be freed - vkDeviceWaitIdle(device); -} - -VulkanExampleBase::VulkanExampleBase() -{ - char* numConvPtr; - // Parse command line arguments - for (size_t i = 0; i < args.size(); i++) - { - if (args[i] == std::string("-validation")) { - settings.validation = true; - } - if (args[i] == std::string("-vsync")) { - settings.vsync = true; - } - if ((args[i] == std::string("-f")) || (args[i] == std::string("--fullscreen"))) { - settings.fullscreen = true; - } - if ((args[i] == std::string("-w")) || (args[i] == std::string("--width"))) { - uint32_t w = strtol(args[i + 1], &numConvPtr, 10); - if (numConvPtr != args[i + 1]) { width = w; }; - } - if ((args[i] == std::string("-h")) || (args[i] == std::string("--height"))) { - uint32_t h = strtol(args[i + 1], &numConvPtr, 10); - if (numConvPtr != args[i + 1]) { height = h; }; - } - } - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - // Vulkan library is loaded dynamically on Android - bool libLoaded = vks::android::loadVulkanLibrary(); - assert(libLoaded); -#elif defined(_DIRECT2DISPLAY) - -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - initWaylandConnection(); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - initxcbConnection(); -#endif - -#if defined(_WIN32) - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - FILE *stream; - freopen_s(&stream, "CONOUT$", "w+", stdout); - freopen_s(&stream, "CONOUT$", "w+", stderr); - SetConsoleTitle(TEXT("Vulkan validation output")); -#endif -} - VulkanExampleBase::~VulkanExampleBase() { - // Clean up Vulkan resources - swapChain.cleanup(); - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { - vkDestroyFramebuffer(device, frameBuffers[i], nullptr); - } - vkDestroyImageView(device, depthStencil.view, nullptr); - vkDestroyImage(device, depthStencil.image, nullptr); - vkFreeMemory(device, depthStencil.mem, nullptr); - vkDestroyPipelineCache(device, pipelineCache, nullptr); - vkDestroyCommandPool(device, cmdPool, nullptr); - if (settings.multiSampling) { - vkDestroyImage(device, multisampleTarget.color.image, nullptr); - vkDestroyImageView(device, multisampleTarget.color.view, nullptr); - vkFreeMemory(device, multisampleTarget.color.memory, nullptr); - vkDestroyImage(device, multisampleTarget.depth.image, nullptr); - vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); - vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); - } - delete vulkanDevice; - if (settings.validation) { - vkDestroyDebugReportCallback(instance, debugReportCallback, nullptr); - } - vkDestroyInstance(instance, nullptr); -#if defined(_DIRECT2DISPLAY) -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - wl_shell_surface_destroy(shell_surface); - wl_surface_destroy(surface); - if (keyboard) - wl_keyboard_destroy(keyboard); - if (pointer) - wl_pointer_destroy(pointer); - wl_seat_destroy(seat); - wl_shell_destroy(shell); - wl_compositor_destroy(compositor); - wl_registry_destroy(registry); - wl_display_disconnect(display); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - // todo : android cleanup (if required) -#elif defined(VK_USE_PLATFORM_XCB_KHR) - xcb_destroy_window(connection, window); - xcb_disconnect(connection); -#endif -} - -void VulkanExampleBase::initVulkan() -{ - VkResult err; - - /* - Instance creation - */ - err = createInstance(settings.validation); - if (err) { - std::cerr << "Could not create Vulkan instance!" << std::endl; - exit(err); - } - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - vks::android::loadVulkanFunctions(instance); -#endif - - /* - Validation layers - */ - if (settings.validation) { - vkCreateDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); - vkDestroyDebugReportCallback = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); - VkDebugReportCallbackCreateInfoEXT debugCreateInfo{}; - debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; - debugCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)debugMessageCallback; - debugCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - VK_CHECK_RESULT(vkCreateDebugReportCallback(instance, &debugCreateInfo, nullptr, &debugReportCallback)); - } - - /* - GPU selection - */ - uint32_t gpuCount = 0; - VK_CHECK_RESULT(vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr)); - assert(gpuCount > 0); - std::vector physicalDevices(gpuCount); - err = vkEnumeratePhysicalDevices(instance, &gpuCount, physicalDevices.data()); - if (err) { - std::cerr << "Could not enumerate physical devices!" << std::endl; - exit(err); - } - uint32_t selectedDevice = 0; -#if !defined(VK_USE_PLATFORM_ANDROID_KHR) - for (size_t i = 0; i < args.size(); i++) { - if ((args[i] == std::string("-g")) || (args[i] == std::string("--gpu"))) { - char* endptr; - selectedPhysicalDeviceIndex = strtol(args[i + 1], &endptr, 10); - if (endptr != args[i + 1]) { - if (selectedPhysicalDeviceIndex > gpuCount - 1) { - std::cerr << "Selected device index " << selectedPhysicalDeviceIndex << " is out of range, reverting to device 0 (use -listgpus to show available Vulkan devices)" << std::endl; - } else { - std::cout << "Selected Vulkan device " << selectedPhysicalDeviceIndex << std::endl; - selectedDevice = selectedPhysicalDeviceIndex; - } - }; - break; - } - } -#endif - - physicalDevice = physicalDevices[selectedDevice]; - - vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); - vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); - - /* - Device creation - */ - vulkanDevice = new vks::VulkanDevice(physicalDevice); - VkPhysicalDeviceFeatures enabledFeatures{}; - if (deviceFeatures.samplerAnisotropy) { - enabledFeatures.samplerAnisotropy = VK_TRUE; - } - std::vector enabledExtensions{}; - VkResult res = vulkanDevice->createLogicalDevice(enabledFeatures, enabledExtensions); - if (res != VK_SUCCESS) { - std::cerr << "Could not create Vulkan device!" << std::endl; - exit(res); - } - device = vulkanDevice->logicalDevice; - - /* - Graphics queue - */ - vkGetDeviceQueue(device, vulkanDevice->queueFamilyIndices.graphics, 0, &queue); - - /* - Suitable depth format - */ - std::vector depthFormats = { VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM }; - VkBool32 validDepthFormat = false; - for (auto& format : depthFormats) { - VkFormatProperties formatProps; - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps); - if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) { - depthFormat = format; - validDepthFormat = true; - break; - } - } - assert(validDepthFormat); - - swapChain.connect(instance, physicalDevice, device); - -#if defined(VK_USE_PLATFORM_ANDROID_KHR) - // Get Android device name and manufacturer (to display along GPU name) - androidProduct = ""; - char prop[PROP_VALUE_MAX+1]; - int len = __system_property_get("ro.product.manufacturer", prop); - if (len > 0) { - androidProduct += std::string(prop) + " "; - }; - len = __system_property_get("ro.product.model", prop); - if (len > 0) { - androidProduct += std::string(prop); - }; - LOGD("androidProduct = %s", androidProduct.c_str()); -#endif -} - -#if defined(_WIN32) - -HWND VulkanExampleBase::setupWindow(HINSTANCE hinstance, WNDPROC wndproc) -{ - this->windowInstance = hinstance; - - WNDCLASSEX wndClass; - - wndClass.cbSize = sizeof(WNDCLASSEX); - wndClass.style = CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = wndproc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = hinstance; - wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wndClass.lpszMenuName = NULL; - wndClass.lpszClassName = name.c_str(); - wndClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); - - if (!RegisterClassEx(&wndClass)) { - std::cout << "Could not register window class!\n"; - fflush(stdout); - exit(1); - } - - int screenWidth = GetSystemMetrics(SM_CXSCREEN); - int screenHeight = GetSystemMetrics(SM_CYSCREEN); - - if (settings.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 != (uint32_t)screenWidth) && (height != (uint32_t)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) { - settings.fullscreen = false; - } else { - return nullptr; - } - } - } - } - - DWORD dwExStyle; - DWORD dwStyle; - - if (settings.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; - windowRect.left = 0L; - windowRect.top = 0L; - windowRect.right = settings.fullscreen ? (long)screenWidth : (long)width; - windowRect.bottom = settings.fullscreen ? (long)screenHeight : (long)height; - - AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); - - window = CreateWindowEx(WS_EX_ACCEPTFILES, - name.c_str(), - title.c_str(), - dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - 0, - 0, - windowRect.right - windowRect.left, - windowRect.bottom - windowRect.top, - NULL, - NULL, - hinstance, - NULL); - - if (!settings.fullscreen) { - uint32_t x = (GetSystemMetrics(SM_CXSCREEN) - windowRect.right) / 2; - uint32_t y = (GetSystemMetrics(SM_CYSCREEN) - windowRect.bottom) / 2; - SetWindowPos(window, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - - if (!window) { - printf("Could not create window!\n"); - fflush(stdout); - return nullptr; - 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 KEY_P: - paused = !paused; - break; - case KEY_ESCAPE: - PostQuitMessage(0); - break; - } - - if (camera.firstperson) - { - switch (wParam) - { - case KEY_W: - camera.keys.up = true; - break; - case KEY_S: - camera.keys.down = true; - break; - case KEY_A: - camera.keys.left = true; - break; - case KEY_D: - camera.keys.right = true; - break; - } - } - - break; - case WM_KEYUP: - if (camera.firstperson) - { - switch (wParam) - { - case KEY_W: - camera.keys.up = false; - break; - case KEY_S: - camera.keys.down = false; - break; - case KEY_A: - camera.keys.left = false; - break; - case KEY_D: - camera.keys.right = false; - break; - } - } - break; - case WM_LBUTTONDOWN: - mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); - mouseButtons.left = true; - break; - case WM_RBUTTONDOWN: - mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); - mouseButtons.right = true; - break; - case WM_MBUTTONDOWN: - mousePos = glm::vec2((float)LOWORD(lParam), (float)HIWORD(lParam)); - mouseButtons.middle = true; - break; - case WM_LBUTTONUP: - mouseButtons.left = false; - break; - case WM_RBUTTONUP: - mouseButtons.right = false; - break; - case WM_MBUTTONUP: - mouseButtons.middle = false; - break; - case WM_MOUSEWHEEL: - { - short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); - camera.translate(glm::vec3(0.0f, 0.0f, -(float)wheelDelta * 0.005f * camera.movementSpeed)); - break; - } - case WM_MOUSEMOVE: - { - handleMouseMove(LOWORD(lParam), HIWORD(lParam)); - break; - } - case WM_SIZE: - if ((prepared) && (wParam != SIZE_MINIMIZED)) { - if ((resizing) || ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))) { - destWidth = LOWORD(lParam); - destHeight = HIWORD(lParam); - windowResize(); - } - } - break; - case WM_ENTERSIZEMOVE: - resizing = true; - break; - case WM_EXITSIZEMOVE: - resizing = false; - break; - case WM_DROPFILES: - { - std::string fname; - HDROP hDrop = reinterpret_cast(wParam); - // extract files here - char filename[MAX_PATH]; - uint32_t count = DragQueryFileA(hDrop, -1, nullptr, 0); - for (uint32_t i = 0; i < count; ++i) { - if (DragQueryFileA(hDrop, i, filename, MAX_PATH)) { - fname = filename; - } - break; - } - DragFinish(hDrop); - fileDropped(fname); - break; - } - } -} -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) -int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent* event) -{ - ImGuiIO& io = ImGui::GetIO(); - bool uiMouseCapture = io.WantCaptureMouse; - - VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) - { - int32_t eventSource = AInputEvent_getSource(event); - switch (eventSource) { - case AINPUT_SOURCE_JOYSTICK: { - // Left thumbstick - vulkanExample->gamePadState.axisLeft.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0); - vulkanExample->gamePadState.axisLeft.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0); - // Right thumbstick - vulkanExample->gamePadState.axisRight.x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Z, 0); - vulkanExample->gamePadState.axisRight.y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RZ, 0); - break; - } - - // FIXME: Reusing code for TOUCHSCREEN seemingly works well for MOUSE event source, - // but it would be better to provide a dedicated event handling logic for MOUSE event source. - case AINPUT_SOURCE_MOUSE: - case AINPUT_SOURCE_TOUCHSCREEN: { - int32_t action = AMotionEvent_getAction(event); - int32_t pointerCount = AMotionEvent_getPointerCount(event); - int32_t flags = action & AMOTION_EVENT_ACTION_MASK; - - switch (flags) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - for (uint32_t i = 0; i < pointerCount; i++) { - vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); - vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); - }; - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - if (pointerIndex < 2) { - vulkanExample->touchPoints[pointerIndex].down = true; - } - if (pointerCount < 2) { - // Detect single tap - int64_t eventTime = AMotionEvent_getEventTime(event); - int64_t downTime = AMotionEvent_getDownTime(event); - if (eventTime - downTime <= vks::android::TAP_TIMEOUT) { - float deadZone = (160.f / vks::android::screenDensity) * vks::android::TAP_SLOP * vks::android::TAP_SLOP; - float x = AMotionEvent_getX(event, 0) - vulkanExample->touchPoints[0].x; - float y = AMotionEvent_getY(event, 0) - vulkanExample->touchPoints[0].y; - if ((x * x + y * y) < deadZone) { - vulkanExample->mousePos.x = vulkanExample->touchPoints[0].x; - vulkanExample->mousePos.y = vulkanExample->touchPoints[0].y; - vulkanExample->mouseButtons.left = true; - } - }; - } - break; - } - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - if (pointerIndex < 2) { - vulkanExample->touchPoints[pointerIndex].down = false; - } - if (pointerCount < 2) { - vulkanExample->touchPoints[1].down = false; - } - break; - } - case AMOTION_EVENT_ACTION_MOVE: { - // Pinch and zoom - if (!uiMouseCapture && vulkanExample->touchPoints[0].down && vulkanExample->touchPoints[1].down) { - for (uint32_t i = 0; i < pointerCount; i++) { - if (vulkanExample->touchPoints[i].down) { - vulkanExample->touchPoints[i].x = AMotionEvent_getX(event, i); - vulkanExample->touchPoints[i].y = AMotionEvent_getY(event, i); - } - }; - float dx = vulkanExample->touchPoints[1].x - vulkanExample->touchPoints[0].x; - float dy = vulkanExample->touchPoints[1].y - vulkanExample->touchPoints[0].y; - float d = sqrt(dx * dx + dy * dy); - if (d < vulkanExample->pinchDist) { - vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, 0.03f)); - }; - if (d > vulkanExample->pinchDist) { - vulkanExample->camera.translate(glm::vec3(0.0f, 0.0f, -0.03f)); - }; - vulkanExample->pinchDist = d; - } else { - // Rotate - if (!uiMouseCapture && vulkanExample->touchPoints[0].down) { - int32_t eventX = AMotionEvent_getX(event, 0); - int32_t eventY = AMotionEvent_getY(event, 0); - - float deltaX = (vulkanExample->touchPoints[0].y - eventY) * vulkanExample->camera.rotationSpeed * 0.5f; - float deltaY = (vulkanExample->touchPoints[0].x - eventX) * vulkanExample->camera.rotationSpeed * 0.5f; - - vulkanExample->camera.rotate(glm::vec3(deltaX, 0.0f, 0.0f)); - vulkanExample->camera.rotate(glm::vec3(0.0f, -deltaY, 0.0f)); - - vulkanExample->touchPoints[0].x = eventX; - vulkanExample->touchPoints[0].y = eventY; - } - } - break; - } - default: - return 1; - } - } - - return 1; - } - } - - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) - { - int32_t keyCode = AKeyEvent_getKeyCode((const AInputEvent*)event); - int32_t action = AKeyEvent_getAction((const AInputEvent*)event); - int32_t button = 0; - - if (action == AKEY_EVENT_ACTION_UP) - return 0; - - switch (keyCode) - { - case AKEYCODE_BUTTON_START: - vulkanExample->paused = !vulkanExample->paused; - break; - }; - - LOGD("Button %d pressed", keyCode); - } - - return 0; -} - -void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd) -{ - assert(app->userData != NULL); - VulkanExampleBase* vulkanExample = reinterpret_cast(app->userData); - switch (cmd) - { - case APP_CMD_SAVE_STATE: - LOGD("APP_CMD_SAVE_STATE"); - /* - 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 (androidApp->window != NULL) - { - vulkanExample->initVulkan(); - vulkanExample->prepare(); - assert(vulkanExample->prepared); - } - else - { - LOGE("No window assigned!"); - } - break; - case APP_CMD_LOST_FOCUS: - LOGD("APP_CMD_LOST_FOCUS"); - vulkanExample->focused = false; - break; - case APP_CMD_GAINED_FOCUS: - LOGD("APP_CMD_GAINED_FOCUS"); - vulkanExample->focused = true; - break; - case APP_CMD_TERM_WINDOW: - // Window is hidden or closed, clean up resources - LOGD("APP_CMD_TERM_WINDOW"); - vulkanExample->swapChain.cleanup(); - break; - } -} -#elif defined(_DIRECT2DISPLAY) -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) -/*static*/void VulkanExampleBase::registryGlobalCb(void *data, - wl_registry *registry, uint32_t name, const char *interface, - uint32_t version) -{ - VulkanExampleBase *self = reinterpret_cast(data); - self->registryGlobal(registry, name, interface, version); -} - -/*static*/void VulkanExampleBase::seatCapabilitiesCb(void *data, wl_seat *seat, - uint32_t caps) -{ - VulkanExampleBase *self = reinterpret_cast(data); - self->seatCapabilities(seat, caps); -} - -/*static*/void VulkanExampleBase::pointerEnterCb(void *data, - wl_pointer *pointer, uint32_t serial, wl_surface *surface, - wl_fixed_t sx, wl_fixed_t sy) -{ -} - -/*static*/void VulkanExampleBase::pointerLeaveCb(void *data, - wl_pointer *pointer, uint32_t serial, wl_surface *surface) -{ -} - -/*static*/void VulkanExampleBase::pointerMotionCb(void *data, - wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) -{ - VulkanExampleBase *self = reinterpret_cast(data); - self->pointerMotion(pointer, time, sx, sy); -} -void VulkanExampleBase::pointerMotion(wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) -{ - handleMouseMove(wl_fixed_to_int(sx), wl_fixed_to_int(sy)); -} - -/*static*/void VulkanExampleBase::pointerButtonCb(void *data, - wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, - uint32_t state) -{ - VulkanExampleBase *self = reinterpret_cast(data); - self->pointerButton(pointer, serial, time, button, state); -} - -void VulkanExampleBase::pointerButton(struct wl_pointer *pointer, - uint32_t serial, uint32_t time, uint32_t button, uint32_t state) -{ - switch (button) - { - case BTN_LEFT: - mouseButtons.left = !!state; - break; - case BTN_MIDDLE: - mouseButtons.middle = !!state; - break; - case BTN_RIGHT: - mouseButtons.right = !!state; - break; - default: - break; - } -} - -/*static*/void VulkanExampleBase::pointerAxisCb(void *data, - wl_pointer *pointer, uint32_t time, uint32_t axis, - wl_fixed_t value) -{ - VulkanExampleBase *self = reinterpret_cast(data); - self->pointerAxis(pointer, time, axis, value); -} - -void VulkanExampleBase::pointerAxis(wl_pointer *pointer, uint32_t time, - uint32_t axis, wl_fixed_t value) -{ - double d = wl_fixed_to_double(value); - switch (axis) - { - case REL_X: - camera.translate(glm::vec3(0.0f, 0.0f, -d * 0.005f * camera.movementSpeed)); - break; - default: - break; - } -} - -/*static*/void VulkanExampleBase::keyboardKeymapCb(void *data, - struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) -{ -} - -/*static*/void VulkanExampleBase::keyboardEnterCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, - struct wl_surface *surface, struct wl_array *keys) -{ -} - -/*static*/void VulkanExampleBase::keyboardLeaveCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, - struct wl_surface *surface) -{ -} - -/*static*/void VulkanExampleBase::keyboardKeyCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, - uint32_t key, uint32_t state) -{ - VulkanExampleBase *self = reinterpret_cast(data); - self->keyboardKey(keyboard, serial, time, key, state); -} - -void VulkanExampleBase::keyboardKey(struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, uint32_t state) -{ - switch (key) - { - case KEY_W: - camera.keys.up = !!state; - break; - case KEY_S: - camera.keys.down = !!state; - break; - case KEY_A: - camera.keys.left = !!state; - break; - case KEY_D: - camera.keys.right = !!state; - break; - case KEY_P: - if (state) - paused = !paused; - break; - case KEY_ESC: - quit = true; - break; - } -} - -/*static*/void VulkanExampleBase::keyboardModifiersCb(void *data, - struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group) -{ -} - -void VulkanExampleBase::seatCapabilities(wl_seat *seat, uint32_t caps) -{ - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !pointer) - { - pointer = wl_seat_get_pointer(seat); - static const struct wl_pointer_listener pointer_listener = - { pointerEnterCb, pointerLeaveCb, pointerMotionCb, pointerButtonCb, - pointerAxisCb, }; - wl_pointer_add_listener(pointer, &pointer_listener, this); - } - else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && pointer) - { - wl_pointer_destroy(pointer); - pointer = nullptr; - } - - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !keyboard) - { - keyboard = wl_seat_get_keyboard(seat); - static const struct wl_keyboard_listener keyboard_listener = - { keyboardKeymapCb, keyboardEnterCb, keyboardLeaveCb, keyboardKeyCb, - keyboardModifiersCb, }; - wl_keyboard_add_listener(keyboard, &keyboard_listener, this); - } - else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && keyboard) - { - wl_keyboard_destroy(keyboard); - keyboard = nullptr; - } -} - -void VulkanExampleBase::registryGlobal(wl_registry *registry, uint32_t name, - const char *interface, uint32_t version) -{ - if (strcmp(interface, "wl_compositor") == 0) - { - compositor = (wl_compositor *) wl_registry_bind(registry, name, - &wl_compositor_interface, 3); - } - else if (strcmp(interface, "wl_shell") == 0) - { - shell = (wl_shell *) wl_registry_bind(registry, name, - &wl_shell_interface, 1); - } - else if (strcmp(interface, "wl_seat") == 0) - { - seat = (wl_seat *) wl_registry_bind(registry, name, &wl_seat_interface, - 1); - - static const struct wl_seat_listener seat_listener = - { seatCapabilitiesCb, }; - wl_seat_add_listener(seat, &seat_listener, this); - } -} - -/*static*/void VulkanExampleBase::registryGlobalRemoveCb(void *data, - struct wl_registry *registry, uint32_t name) -{ -} - -void VulkanExampleBase::initWaylandConnection() -{ - display = wl_display_connect(NULL); - if (!display) - { - std::cout << "Could not connect to Wayland display!\n"; - fflush(stdout); - exit(1); - } - - registry = wl_display_get_registry(display); - if (!registry) - { - std::cout << "Could not get Wayland registry!\n"; - fflush(stdout); - exit(1); - } - - static const struct wl_registry_listener registry_listener = - { registryGlobalCb, registryGlobalRemoveCb }; - wl_registry_add_listener(registry, ®istry_listener, this); - wl_display_dispatch(display); - wl_display_roundtrip(display); - if (!compositor || !shell || !seat) - { - std::cout << "Could not bind Wayland protocols!\n"; - fflush(stdout); - exit(1); - } -} - -static void PingCb(void *data, struct wl_shell_surface *shell_surface, - uint32_t serial) -{ - wl_shell_surface_pong(shell_surface, serial); -} - -static void ConfigureCb(void *data, struct wl_shell_surface *shell_surface, - uint32_t edges, int32_t width, int32_t height) -{ -} - -static void PopupDoneCb(void *data, struct wl_shell_surface *shell_surface) -{ -} - -wl_shell_surface *VulkanExampleBase::setupWindow() -{ - surface = wl_compositor_create_surface(compositor); - shell_surface = wl_shell_get_shell_surface(shell, surface); - - static const struct wl_shell_surface_listener shell_surface_listener = - { PingCb, ConfigureCb, PopupDoneCb }; - - wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, this); - wl_shell_surface_set_toplevel(shell_surface); - wl_shell_surface_set_title(shell_surface, title.c_str()); - return shell_surface; -} - -#elif defined(VK_USE_PLATFORM_XCB_KHR) - -static inline xcb_intern_atom_reply_t* intern_atom_helper(xcb_connection_t *conn, bool only_if_exists, const char *str) -{ - xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, only_if_exists, strlen(str), str); - return xcb_intern_atom_reply(conn, cookie, NULL); -} - -// Set up a window using XCB and request event types -xcb_window_t VulkanExampleBase::setupWindow() -{ - uint32_t value_mask, value_list[32]; - - window = xcb_generate_id(connection); - - value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; - value_list[0] = screen->black_pixel; - value_list[1] = - XCB_EVENT_MASK_KEY_RELEASE | - XCB_EVENT_MASK_KEY_PRESS | - XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_POINTER_MOTION | - XCB_EVENT_MASK_BUTTON_PRESS | - XCB_EVENT_MASK_BUTTON_RELEASE; - - if (settings.fullscreen) - { - width = destWidth = screen->width_in_pixels; - height = destHeight = screen->height_in_pixels; - } - - 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_reply_t* reply = intern_atom_helper(connection, true, "WM_PROTOCOLS"); - atom_wm_delete_window = intern_atom_helper(connection, false, "WM_DELETE_WINDOW"); - - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, (*reply).atom, 4, 32, 1, - &(*atom_wm_delete_window).atom); - - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, - window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, - title.size(), title.c_str()); - - free(reply); - - if (settings.fullscreen) - { - xcb_intern_atom_reply_t *atom_wm_state = intern_atom_helper(connection, false, "_NET_WM_STATE"); - xcb_intern_atom_reply_t *atom_wm_fullscreen = intern_atom_helper(connection, false, "_NET_WM_STATE_FULLSCREEN"); - xcb_change_property(connection, - XCB_PROP_MODE_REPLACE, - window, atom_wm_state->atom, - XCB_ATOM_ATOM, 32, 1, - &(atom_wm_fullscreen->atom)); - free(atom_wm_fullscreen); - free(atom_wm_state); - } - - 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; - handleMouseMove((int32_t)motion->event_x, (int32_t)motion->event_y); - break; - } - break; - case XCB_BUTTON_PRESS: - { - xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; - if (press->detail == XCB_BUTTON_INDEX_1) - mouseButtons.left = true; - if (press->detail == XCB_BUTTON_INDEX_2) - mouseButtons.middle = true; - if (press->detail == XCB_BUTTON_INDEX_3) - mouseButtons.right = true; - } - break; - case XCB_BUTTON_RELEASE: - { - xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; - if (press->detail == XCB_BUTTON_INDEX_1) - mouseButtons.left = false; - if (press->detail == XCB_BUTTON_INDEX_2) - mouseButtons.middle = false; - if (press->detail == XCB_BUTTON_INDEX_3) - mouseButtons.right = false; - } - break; - case XCB_KEY_PRESS: - { - const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; - switch (keyEvent->detail) - { - case KEY_W: - camera.keys.up = true; - break; - case KEY_S: - camera.keys.down = true; - break; - case KEY_A: - camera.keys.left = true; - break; - case KEY_D: - camera.keys.right = true; - break; - case KEY_P: - paused = !paused; - break; - } - } - break; - case XCB_KEY_RELEASE: - { - const xcb_key_release_event_t *keyEvent = (const xcb_key_release_event_t *)event; - switch (keyEvent->detail) - { - case KEY_W: - camera.keys.up = false; - break; - case KEY_S: - camera.keys.down = false; - break; - case KEY_A: - camera.keys.left = false; - break; - case KEY_D: - camera.keys.right = false; - break; - case KEY_ESCAPE: - quit = true; - break; - } - } - break; - case XCB_DESTROY_NOTIFY: - quit = true; - break; - 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; - default: - break; - } -} -#elif defined(VK_USE_PLATFORM_MACOS_MVK) -@interface AppDelegate : NSObject -{ -} - -@end - -@implementation AppDelegate -{ -} - -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender -{ - return YES; -} - -@end - -CVReturn OnDisplayLinkOutput(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, - CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) -{ - @autoreleasepool - { - auto vulkanExampleBase = static_cast(displayLinkContext); - vulkanExampleBase->renderFrame(); - } - return kCVReturnSuccess; -} - -@interface View : NSView -{ -@public - VulkanExampleBase* vulkanExampleBase; -} - -@end - -@implementation View -{ - CVDisplayLinkRef displayLink; -} - -- (instancetype)initWithFrame:(NSRect)frameRect -{ - self = [super initWithFrame:(frameRect)]; - if (self) - { - self.wantsLayer = YES; - self.layer = [CAMetalLayer layer]; - } - return self; -} - -- (void)viewDidMoveToWindow -{ - CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); - CVDisplayLinkSetOutputCallback(displayLink, &OnDisplayLinkOutput, vulkanExampleBase); - CVDisplayLinkStart(displayLink); -} - -- (BOOL)acceptsFirstResponder -{ - return YES; -} - -- (void)keyDown:(NSEvent*)event -{ - switch (event.keyCode) - { - case kVK_ANSI_P: - vulkanExampleBase->paused = !vulkanExampleBase->paused; - break; - case kVK_Escape: - [NSApp terminate:nil]; - break; - default: - break; - } -} - -- (void)keyUp:(NSEvent*)event -{ - if (vulkanExampleBase->camera.firstperson) - { - switch (event.keyCode) - { - case kVK_ANSI_W: - vulkanExampleBase->camera.keys.up = false; - break; - case kVK_ANSI_S: - vulkanExampleBase->camera.keys.down = false; - break; - case kVK_ANSI_A: - vulkanExampleBase->camera.keys.left = false; - break; - case kVK_ANSI_D: - vulkanExampleBase->camera.keys.right = false; - break; - default: - break; - } - } -} - -- (void)mouseDown:(NSEvent *)event -{ - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); - vulkanExampleBase->mouseButtons.left = true; -} - -- (void)mouseUp:(NSEvent *)event -{ - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mousePos = glm::vec2(point.x, point.y); - vulkanExampleBase->mouseButtons.left = false; -} - -- (void)otherMouseDown:(NSEvent *)event -{ - vulkanExampleBase->mouseButtons.right = true; -} - -- (void)otherMouseUp:(NSEvent *)event -{ - vulkanExampleBase->mouseButtons.right = false; -} - -- (void)mouseDragged:(NSEvent *)event -{ - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mouseDragged(point.x, point.y); -} - -- (void)mouseMoved:(NSEvent *)event -{ - NSPoint location = [event locationInWindow]; - NSPoint point = [self convertPoint:location fromView:nil]; - vulkanExampleBase->mouseDragged(point.x, point.y); -} - -- (void)scrollWheel:(NSEvent *)event -{ - short wheelDelta = [event deltaY]; - vulkanExampleBase->camera.translate(glm::vec3(0.0f, 0.0f, - -(float)wheelDelta * 0.05f * vulkanExampleBase->camera.movementSpeed)); -} - -- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize -{ - CVDisplayLinkStop(displayLink); - vulkanExampleBase->windowWillResize(frameSize.width, frameSize.height); - return frameSize; -} - -- (void)windowDidResize:(NSNotification *)notification -{ - vulkanExampleBase->windowDidResize(); - CVDisplayLinkStart(displayLink); -} - -- (BOOL)windowShouldClose:(NSWindow *)sender -{ - return TRUE; -} - -- (void)windowWillClose:(NSNotification *)notification -{ - CVDisplayLinkStop(displayLink); -} - -@end - -NSWindow* VulkanExampleBase::setupWindow() -{ - NSApp = [NSApplication sharedApplication]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - [NSApp setDelegate:[AppDelegate new]]; - - const auto kContentRect = NSMakeRect(0.0f, 0.0f, width, height); - - window = [[NSWindow alloc] initWithContentRect:kContentRect - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable - backing:NSBackingStoreBuffered - defer:NO]; - [window setTitle:@(title.c_str())]; - [window setAcceptsMouseMovedEvents:YES]; - [window center]; - [window makeKeyAndOrderFront:nil]; - - auto view = [[View alloc] initWithFrame:kContentRect]; - view->vulkanExampleBase = this; - - [window setDelegate:view]; - [window setContentView:view]; - - return window; -} - -void VulkanExampleBase::mouseDragged(float x, float y) -{ - handleMouseMove(static_cast(x), static_cast(y)); -} - -void VulkanExampleBase::windowWillResize(float x, float y) -{ - resizing = true; - if (prepared) - { - destWidth = x; - destHeight = y; - windowResize(); - } - std::cout << "resize" << std::endl; -} - -void VulkanExampleBase::windowDidResize() -{ - std::cout << "done" << std::endl; - resizing = false; -} -#endif - -void VulkanExampleBase::windowResized() {} - -void VulkanExampleBase::setupFrameBuffer() -{ - /* - MSAA - */ - VkFormat colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM; - VkFormat depthFormat = findDepthFormat(); - if (settings.headless) - { - if (settings.multiSampling) - { - - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = colorAttachmentFormat; - imageCI.extent.width = settings.width; - imageCI.extent.height = settings.height; - imageCI.extent.depth = 1; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples = settings.sampleCount; - imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.color.image)); - - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device, multisampleTarget.color.image, &memReqs); - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - VkBool32 lazyMemTypePresent; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); - if (!lazyMemTypePresent) { - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.color.memory)); - vkBindImageMemory(device, multisampleTarget.color.image, multisampleTarget.color.memory, 0); - - // Create image view for the MSAA color image target - VkImageViewCreateInfo imageViewCI{}; - imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCI.image = multisampleTarget.color.image; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.format = colorAttachmentFormat; - imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; - imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; - imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; - imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; - imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.layerCount = 1; - VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.color.view)); - - // Depth target - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = depthFormat; - imageCI.extent.width = settings.width; - imageCI.extent.height = settings.height; - imageCI.extent.depth = 1; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples = settings.sampleCount; - imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depth.image)); - - vkGetImageMemoryRequirements(device, multisampleTarget.depth.image, &memReqs); - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); - if (!lazyMemTypePresent) { - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.depth.memory)); - vkBindImageMemory(device, multisampleTarget.depth.image, multisampleTarget.depth.memory, 0); - - // Create image view for the MSAA target - imageViewCI.image = multisampleTarget.depth.image; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.format = depthFormat; - imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; - imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; - imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; - imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; - imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.layerCount = 1; - VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.depth.view)); - } - // creat color image - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.format = colorAttachmentFormat; - imageCI.extent.width = settings.width; - imageCI.extent.height = settings.height; - imageCI.extent.depth = 1; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCI.samples = VK_SAMPLE_COUNT_1_BIT; - imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &colorAttachment.image)); - - VkMemoryRequirements memReqs; - vkGetImageMemoryRequirements(device, colorAttachment.image, &memReqs); - - VkMemoryAllocateInfo memAllocInfo{}; - memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAllocInfo.allocationSize = memReqs.size; - VkBool32 lazyMemTypePresent; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); - if (!lazyMemTypePresent) { - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - } - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &colorAttachment.memory)); - - vkBindImageMemory(device, colorAttachment.image, colorAttachment.memory, 0); - - // Create image view for the color image - VkImageViewCreateInfo imageViewCI{}; - imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCI.image = colorAttachment.image; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.format = colorAttachmentFormat; - imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; - imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; - imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; - imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; - imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.layerCount = 1; - VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &colorAttachment.view)); - - // create image for the depth image - 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 = settings.width; - image.extent.height = settings.height; - image.extent.depth = 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; - - VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthStencil.image)); - - VkMemoryAllocateInfo memAlloc = {}; - memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memAlloc.pNext = NULL; - memAlloc.allocationSize = 0; - memAlloc.memoryTypeIndex = 0; - VkMemoryRequirements depthAttachmentMemReqs; - vkGetImageMemoryRequirements(device, depthStencil.image, &memReqs); - memAlloc.allocationSize = depthAttachmentMemReqs.size; - memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &depthStencil.mem)); - VK_CHECK_RESULT(vkBindImageMemory(device, depthStencil.image, depthStencil.mem, 0)); - - - // create image view for depth image - 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.image = depthStencil.image; - depthStencilView.flags = 0; - depthStencilView.subresourceRange = {}; - depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - if (depthFormat >= VK_FORMAT_D16_UNORM_S8_UINT) - { - depthStencilView.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - depthStencilView.subresourceRange.baseMipLevel = 0; - depthStencilView.subresourceRange.levelCount = 1; - depthStencilView.subresourceRange.baseArrayLayer = 0; - depthStencilView.subresourceRange.layerCount = 1; - VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthStencil.view)); - - } - else - { - - swapChainImageViews.resize(swapChain.imageCount); - for (size_t i = 0; i < swapChain.imageCount; i++) - { - VkImageViewCreateInfo creatInfo{}; - creatInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - creatInfo.image = swapChain.images[i]; - creatInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - creatInfo.format = swapChain.colorFormat; - - creatInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - creatInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - creatInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - creatInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - creatInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - creatInfo.subresourceRange.baseMipLevel = 0; - creatInfo.subresourceRange.levelCount = 1; - creatInfo.subresourceRange.baseArrayLayer = 0; - creatInfo.subresourceRange.layerCount = 1; - - if (vkCreateImageView(device, &creatInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) - { - throw std::runtime_error("failed to creat image view"); - } - } - } - - if (settings.headless) - { - auto frameRange = settings.endFrameIndex - settings.startFrameCount; - if (settings.multiSampling) - { - - for (int i = 0; i < frameRange; i++) - { - VkImageView attachments[4]; - attachments[0] = multisampleTarget.color.view; - attachments[1] = multisampleTarget.depth.view; - attachments[2] = depthStencil.view; - attachments[3] = colorAttachment.view; - - VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo(); - framebufferCreateInfo.renderPass = renderPass; - framebufferCreateInfo.attachmentCount = 4; - framebufferCreateInfo.pAttachments = attachments; - framebufferCreateInfo.width = settings.width; - framebufferCreateInfo.height = settings.height; - framebufferCreateInfo.layers = 1; - - VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &frameBuffers[i])); - } - - } - else - { - for (int i = 0; i < frameRange; i++) - { - VkImageView attachments[2]; - attachments[0] = colorAttachment.view; - attachments[1] = depthStencil.view; - - VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo(); - framebufferCreateInfo.renderPass = renderPass; - framebufferCreateInfo.attachmentCount = 2; - framebufferCreateInfo.pAttachments = attachments; - framebufferCreateInfo.width = settings.width; - framebufferCreateInfo.height = settings.height; - framebufferCreateInfo.layers = 1; - VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &frameBuffers[i])); - - } - } - - - } - else - { - createSwapChainFramebuffer(); - } -} - -void VulkanExampleBase::createSwapChainFramebuffer() -{ - uint32_t attachmentCount; - VkImageView attachments[attachmentCount]; - - if (settings.multiSampling) { - attachmentCount = 4; - attachments[0] = multisampleTarget.color.view; - attachments[1] = multisampleTarget.depth.view; - attachments[2] = depthStencil.view; - - } - else { - attachmentCount = 2; - attachments[1] = depthStencil.view; - } - - VkFramebufferCreateInfo frameBufferCI{}; - frameBufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - frameBufferCI.pNext = NULL; - frameBufferCI.renderPass = renderPass; - frameBufferCI.attachmentCount = attachmentCount; - frameBufferCI.pAttachments = attachments; - frameBufferCI.width = settings.width; - frameBufferCI.height = settings.height; - frameBufferCI.layers = 1; - - - - // Create frame buffers for every swap chain image - frameBuffers.resize(swapChainImageViews.size()); - for (uint32_t i = 0; i < swapChainImageViews.size(); i++) { - if (settings.multiSampling) { - attachments[3] = swapChainImageViews[i]; - } - else { - attachments[0] = swapChainImageViews[i]; - } - VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &frameBuffers[i])); - } -} - -void VulkanExampleBase::windowResize() -{ - if (!prepared) { - return; - } - prepared = false; - - vkDeviceWaitIdle(device); - width = destWidth; - height = destHeight; - setupSwapChain(); - if (settings.multiSampling) { - vkDestroyImageView(device, multisampleTarget.color.view, nullptr); - vkDestroyImage(device, multisampleTarget.color.image, nullptr); - vkFreeMemory(device, multisampleTarget.color.memory, nullptr); - vkDestroyImageView(device, multisampleTarget.depth.view, nullptr); - vkDestroyImage(device, multisampleTarget.depth.image, nullptr); - vkFreeMemory(device, multisampleTarget.depth.memory, nullptr); - } - vkDestroyImageView(device, depthStencil.view, nullptr); - vkDestroyImage(device, depthStencil.image, nullptr); - vkFreeMemory(device, depthStencil.mem, nullptr); - for (uint32_t i = 0; i < frameBuffers.size(); i++) { - vkDestroyFramebuffer(device, frameBuffers[i], nullptr); - } - setupFrameBuffer(); - vkDeviceWaitIdle(device); - - camera.updateAspectRatio((float)width / (float)height); - windowResized(); - - prepared = true; -} - -void VulkanExampleBase::handleMouseMove(int32_t x, int32_t y) -{ - int32_t dx = (int32_t)mousePos.x - x; - int32_t dy = (int32_t)mousePos.y - y; - ImGuiIO& io = ImGui::GetIO(); - bool handled = io.WantCaptureMouse; - - if (handled) { - mousePos = glm::vec2((float)x, (float)y); - return; - } - - if (handled) { - mousePos = glm::vec2((float)x, (float)y); - return; - } - - if (mouseButtons.left) { - camera.rotate(glm::vec3(dy * camera.rotationSpeed, -dx * camera.rotationSpeed, 0.0f)); - } - if (mouseButtons.right) { - camera.translate(glm::vec3(-0.0f, 0.0f, dy * .005f * camera.movementSpeed)); - } - if (mouseButtons.middle) { - camera.translate(glm::vec3(-dx * 0.01f, dy * 0.01f, 0.0f)); - } - mousePos = glm::vec2((float)x, (float)y); } -void VulkanExampleBase::initSwapchain() -{ -#if defined(_WIN32) - swapChain.initSurface(windowInstance, window); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - swapChain.initSurface(androidApp->window); -#elif defined(_DIRECT2DISPLAY) - swapChain.initSurface(width, height); -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - swapChain.initSurface(display, surface); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - swapChain.initSurface(connection, window); -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - swapChain.initSurface((__bridge void*)[window contentView]); -#endif -} -void VulkanExampleBase::setupSwapChain() -{ - swapChain.create(&width, &height, settings.vsync); -} -VkFormat VulkanExampleBase::findDepthFormat() -{ - return findSupportedFormat( - { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, - VK_IMAGE_TILING_OPTIMAL, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT - ); -} -VkFormat VulkanExampleBase::findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) -{ - for (VkFormat format : candidates) - { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); - if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) - { - return format; - } - else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) - { - return format; - } - } - throw std::runtime_error("failed to find supported format"); -} + + + + + + + + + + + + + + + + diff --git a/base/vulkanexamplebase.h b/base/vulkanexamplebase.h index 3ff3db2..c1f33d3 100644 --- a/base/vulkanexamplebase.h +++ b/base/vulkanexamplebase.h @@ -8,43 +8,17 @@ #pragma once -#ifdef _WIN32 -#pragma comment(linker, "/subsystem:windows") -#include -#include -#include -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) -#include -#include -#include -#include -#include "VulkanAndroid.h" -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) -#include -#elif defined(_DIRECT2DISPLAY) -// -#elif defined(VK_USE_PLATFORM_XCB_KHR) -#include -#elif defined(VK_USE_PLATFORM_MACOS_MVK) -#include -#include -#include -#include -#endif - #include #include #include -#define GLM_FORCE_RADIANS -#define GLM_FORCE_DEPTH_ZERO_TO_ONE -#define GLM_ENABLE_EXPERIMENTAL -#include + #include #include #include #include + #include "vulkan/vulkan.h" #include "VulkanTools.h" @@ -54,51 +28,37 @@ #include "VulkanDevice.hpp" #include "VulkanSwapChain.hpp" -#include "imgui/imgui.h" +//#include "imgui/imgui.h" class VulkanExampleBase { private: - float fpsTimer = 0.0f; + uint32_t frameCounter = 0; - uint32_t destWidth; - uint32_t destHeight; + bool resizing = false; - void handleMouseMove(int32_t x, int32_t y); + //void handleMouseMove(int32_t x, int32_t y); PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallback; PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallback; VkDebugReportCallbackEXT debugReportCallback; - struct MultisampleTarget { - struct { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - } color; - struct { - VkImage image; - VkImageView view; - VkDeviceMemory memory; - } depth; - } multisampleTarget; + protected: VkInstance instance; - VkPhysicalDevice physicalDevice; + VkPhysicalDeviceProperties deviceProperties; - VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; - VkDevice device; - vks::VulkanDevice *vulkanDevice; + + VkQueue queue; VkFormat depthFormat; VkCommandPool cmdPool; - VkRenderPass renderPass; + std::vectorframeBuffers; uint32_t currentBuffer = 0; - VkDescriptorPool descriptorPool; - VkPipelineCache pipelineCache; - VulkanSwapChain swapChain; - std::string title = "Vulkan Example"; - std::string name = "vulkanExample"; + + + //VulkanSwapChain swapChain; void windowResize(); public: static std::vector args; @@ -106,67 +66,31 @@ public: bool prepared = false; uint32_t width = 1280; uint32_t height = 720; - float frameTimer = 1.0f; - Camera camera; - glm::vec2 mousePos; - bool paused = false; - uint32_t lastFPS = 0; - - struct Settings { - uint32_t width = 1280; - uint32_t height = 720; - bool validation = true; // 鏍¢獙灞傚紑鍏 - bool fullscreen = false; // 鍏ㄥ睆寮鍏 - bool vsync = false; // 鍨傜洿鍚屾寮鍏 - bool multiSampling = true; // 澶氶噸閲囨牱 - bool rotateModel = true; // 妯″瀷鑷棆杞(鏆傛椂澶辨晥) - bool headless = false; // 鏃犲ご寮鍏 - bool outputPNGimage = false; - uint32_t endFrameIndex = 75; - bool enableSaveToImageSequeue = true; // 鍥剧墖搴忓垪寮鍏筹紙鏆傛椂寮冪敤锛 - uint32_t outputFrameCount = 100; // 鍥剧墖搴忓垪缁撴潫甯 - bool takeScreenShot = false; // 鎴睆锛堟殏鏃跺純鐢級 - uint32_t startFrameCount = 1; // 鍥剧墖搴忓垪寮濮嬪抚 - - uint32_t videoFrameRate = 25; - - VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; // 澶氶噸閲囨牱鍊嶇巼 - } settings; - struct DepthStencil { - VkImage image; - VkDeviceMemory mem; - VkImageView view; - } depthStencil; + + + bool paused = false; + - struct ColorAttachment { - VkImage image; - VkDeviceMemory memory; - VkImageView view; - } colorAttachment; + + + - std::vector swapChainImageViews; + struct GamePadState { glm::vec2 axisLeft = glm::vec2(0.0f); glm::vec2 axisRight = glm::vec2(0.0f); } gamePadState; - struct MouseButtons { - bool left = false; - bool right = false; - bool middle = false; - } mouseButtons; + VulkanExampleBase(); virtual ~VulkanExampleBase(); - void initVulkan(); - - virtual VkResult createInstance(bool enableValidation); - virtual void render() = 0; + //virtual void render() = 0; virtual void windowResized(); virtual void setupFrameBuffer(); void createSwapChainFramebuffer(); @@ -178,6 +102,4 @@ public: VkFormat findDepthFormat(); VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); - void renderLoop(); - void renderFrame(); }; diff --git a/data/environments/metro_noord_4k_hdr16f_cube.ktx b/data/environments/metro_noord_4k_hdr16f_cube.ktx index f62890e..5f8728b 100644 Binary files a/data/environments/metro_noord_4k_hdr16f_cube.ktx and b/data/environments/metro_noord_4k_hdr16f_cube.ktx differ diff --git a/data/output/video/device0/result.mp4 b/data/output/video/device0/result.mp4 index 6927580..d52aebf 100644 Binary files a/data/output/video/device0/result.mp4 and b/data/output/video/device0/result.mp4 differ diff --git a/src/render/render.cpp b/src/render/render.cpp index f275471..edadcdb 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -26,6 +26,15 @@ PlumageRender::PlumageRender() } +void PlumageRender::initVulkan() +{ + createInstance(); + + createSurface(); + + pickupPhysicalDevice(); +} + VkResult PlumageRender::createInstance() { // check validation layers @@ -49,7 +58,7 @@ VkResult PlumageRender::createInstance() createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; - auto requiredExtensions = getRequiredExtensions(setter); + auto requiredExtensions = getRequiredExtensions(); createInfo.enabledExtensionCount = static_cast(requiredExtensions.size()); createInfo.ppEnabledExtensionNames = requiredExtensions.data(); @@ -77,6 +86,1185 @@ VkResult PlumageRender::createInstance() } } +bool PlumageRender::checkValidationLayerSupport() +{ + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + // check if validationLayers can be found in availableLayers + for (const char* layerName : validationLayers) + { + bool layerFound = false; + for (const auto& layerProperties : availableLayers) { + if (strcmp(layerName, layerProperties.layerName) == 0) + { + layerFound = true; + break; + } + } + if (!layerFound) + { + return false; + } + } + return true; +} + +std::vector PlumageRender::getRequiredExtensions() +{ + std::vector extensions; + if (!settings.headless) + { + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + } + + if (settings.validation) + { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + } + + return extensions; +} + +void PlumageRender::setupDebugMessager() +{ + if (!settings.validation) + { + return; + } + + VkDebugUtilsMessengerCreateInfoEXT createInfo{}; + populateDebugMessengerCreateInfo(createInfo); + + + if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) + { + throw std::runtime_error("failed to set up debug messenger in setupDebugMessenger"); + } +} + +void PlumageRender::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& debugCreateInfo) +{ + debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + + debugCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + debugCreateInfo.pfnUserCallback = &debugCallback; + debugCreateInfo.pUserData = nullptr; +} + +VKAPI_ATTR VkBool32 VKAPI_CALL PlumageRender::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) +{ + { + std::cerr << "validation layer: " << pCallbackData->pMessage << "/n" << std::endl; + + return VK_FALSE; + } +} + +VkResult PlumageRender::CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) +{ + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + + if (func != nullptr) + { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } + else + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void PlumageRender::DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) +{ + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + + if (func != nullptr) + { + func(instance, debugMessenger, pAllocator); + } +} + +void PlumageRender::createSurface() +{ + if (settings.headless) + { + return; + } + + if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) + { + throw std::runtime_error("failed to create window surface in createSurface()"); + } +} + +void PlumageRender::pickupPhysicalDevice() +{ + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + if (deviceCount == 0) + { + throw std::runtime_error("failed to find GPUs with Vulkan support"); + + } + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + if (settings.selectedPhysicalDeviceIndex <= deviceCount - 1) + { + physicalDevice = devices[settings.selectedPhysicalDeviceIndex]; + } + + for (const auto& device : devices) + { + if (isDeviceSuitable(device)) + { + physicalDevice = device; + break; + } + + } + if (physicalDevice == VK_NULL_HANDLE) + { + throw std::runtime_error("failed to find a suitable GPU"); + } +} + +bool PlumageRender::isDeviceSuitable(VkPhysicalDevice device) +{ + if (settings.headless) + { + bool extensionsSupported = checkDeviceExtensionSupport(device); + return extensionsSupported; + } + else + { // 闈炴棤澶翠笅鍦ㄦ鏌ユ墿灞曟敮鎸佺殑鍚屾椂瑕佹鏌wapchain + bool extensionsSupported = checkDeviceExtensionSupport(device); + bool swapChainAdequate = false; + QueueFamilyIndices indices = findQueueFamilies(device); + if (extensionsSupported) + { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); + } + } +} + +bool PlumageRender::checkDeviceExtensionSupport(VkPhysicalDevice device) +{ + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(swapchainExtensions.begin(), swapchainExtensions.end()); + + for (const auto& extension : availableExtensions) + { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); +} + +PlumageRender::QueueFamilyIndices PlumageRender::findQueueFamilies(VkPhysicalDevice device) +{ + QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + VkBool32 presentSupport = false; + + // 妫鏌ユ樉绀洪槦鍒楁敮鎸 + int i = 0; + if (!settings.headless) + { + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + } + + for (const auto& queueFamily : queueFamilies) + { + if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + // 鏃犲ご涓嬩笉闇瑕佹鏌resent queue + if (settings.headless) + { + if (indices.isGraphicsFamilyComplete()) + { + break; + } + } + else + { + if (indices.isGraphicsFamilyComplete() && indices.isPresentFamilyComplete()) + { + break; + } + } + if (presentSupport) + { + indices.presentFamily = i; + } + i++; + } + + return indices; +} + +PlumageRender::SwapChainSupportDetails PlumageRender::querySwapChainSupport(VkPhysicalDevice device) +{ + // 鑾峰緱surface缁嗚妭 + SwapChainSupportDetails details; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + // 妫鏌ユ牸寮忔敮鎸侊紙image锛 + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + if (formatCount != 0) + { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + // 妫鏌ユ樉绀烘ā寮忔敮鎸 + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + + if (presentModeCount != 0) + { + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; +} + +void PlumageRender::createLogicalDevice() +{ + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + std::vector queueCreateInfos; + + if (settings.headless) + { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); + queueCreateInfo.queueCount = 1; + float queuePriority = 1.0f; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + else + { + + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(),indices.presentFamily.value() }; + + float queuePriority = 1.0f; + for (uint32_t queueFamily : uniqueQueueFamilies) + { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + } + + VkPhysicalDeviceFeatures enableFeatures{}; + if (deviceFeatures.samplerAnisotropy) { + enableFeatures.samplerAnisotropy = VK_TRUE; + } + + std::vector enabledExtensions{}; + if (!settings.headless) + { + for (auto swapchainExtension : swapchainExtensions) + { + enabledExtensions.push_back(swapchainExtension); + } + } + + VkDeviceCreateInfo deviceCreateInfo{}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); + deviceCreateInfo.pEnabledFeatures = &enableFeatures; + deviceCreateInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); + deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); + + //鏂扮増鏈瑅ulkan宸蹭笉瀵瑰疄渚嬪拰璁惧鐗瑰畾楠岃瘉灞傚仛鍖哄垎锛屾澶勪繚璇佸吋瀹规 + if (settings.validation) + { + deviceCreateInfo.enabledLayerCount = static_cast(validationLayers.size()); + deviceCreateInfo.ppEnabledLayerNames = validationLayers.data(); + } + else + { + deviceCreateInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device) != VK_SUCCESS) + { + throw std::runtime_error("failed to create logical device"); + } + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicQueue); + if (settings.headless) + { + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + } +} + +void PlumageRender::createSwapChain() +{ + if (settings.headless) + { + return; + } + + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) + { + imageCount = swapChainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(),indices.presentFamily.value() }; + if (indices.graphicsFamily != indices.presentFamily) + { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + + } + else + { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChainKHR) != VK_SUCCESS) + { + throw std::runtime_error("failed to create swap chain !"); + } + + vkGetSwapchainImagesKHR(device, swapChainKHR, &imageCount, nullptr); + swapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChainKHR, &imageCount, swapChainImages.data()); + // store swap format and swap extent to member variable + swapChainImageFormat = surfaceFormat.format; + swapChainExtent = extent; +} + +VkSurfaceFormatKHR PlumageRender::chooseSwapSurfaceFormat(const std::vector& availableFormats) +{ + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8_SRGB && availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) + { + return availableFormat; + } + } + + return availableFormats[0]; +} + +VkPresentModeKHR PlumageRender::chooseSwapPresentMode(const std::vector& availablePresentModes) +{ + // Get available present modes + uint32_t presentModeCount; + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL)); + assert(presentModeCount > 0); + + std::vector presentModes(presentModeCount); + VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data())); + // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec + // This mode waits for the vertical blank ("v-sync") + // 鍨傜洿鍚屾妯″紡 + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + // If v-sync is not requested, try to find a mailbox mode + // It's the lowest latency non-tearing present mode available + if (!settings.vsync) + { + for (size_t i = 0; i < presentModeCount; i++) + { + if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) + { + swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; + break; + } + if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) + { + swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + } + } + } +} + +VkExtent2D PlumageRender::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) +{ + if (capabilities.currentExtent.width != std::numeric_limits::max()) + { + return capabilities.currentExtent; + } + else + { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(height) + }; + + actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } +} + +void PlumageRender::cleanupSwapChain() +{ + for (auto framebuffer : framebuffers) + { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + + for (auto imageView : swapChainImageViews) + { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device, swapChainKHR, nullptr); +} + +void PlumageRender::createCommandPool() +{ + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) + { + throw std::runtime_error("failed to create command pool in createCommandpool"); + } +} + +void PlumageRender::createRenderPass() +{ + VkFormat colorAttachmentFormat; + VkFormat depthAttachmentFormat = findDepthFormat(); + VkImageLayout colorAttachmentFinallayout; + if (settings.headless) + { + colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM; + colorAttachmentFinallayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + } + else + { + colorAttachmentFormat = swapChainImageFormat; + colorAttachmentFinallayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + + if (settings.multiSampling) { + std::array attachments = {}; + + // Multisampled attachment that we render to + attachments[0].format = colorAttachmentFormat; + attachments[0].samples = settings.sampleCount; + 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_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + // This is the frame buffer attachment to where the multisampled image + // will be resolved to and which will be presented to the swapchain + attachments[1].format = colorAttachmentFormat; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + 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_UNDEFINED; + attachments[1].finalLayout = colorAttachmentFinallayout; + + // Multisampled depth attachment we render to + attachments[2].format = depthAttachmentFormat; + attachments[2].samples = settings.sampleCount; + attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[2].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Depth resolve attachment + attachments[3].format = depthAttachmentFormat; + attachments[3].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[3].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[3].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[3].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[3].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[3].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 = 2; + depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + // Resolve attachment reference for the color attachment + VkAttachmentReference resolveReference = {}; + resolveReference.attachment = 1; + resolveReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorReference; + // Pass our resolve attachments to the sub pass + subpass.pResolveAttachments = &resolveReference; + subpass.pDepthStencilAttachment = &depthReference; + + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCI = {}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpass; + renderPassCI.dependencyCount = 2; + renderPassCI.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); + } + else { + std::array attachments = {}; + // Color attachment + attachments[0].format = colorAttachmentFormat; + 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_UNDEFINED; + attachments[0].finalLayout = colorAttachmentFinallayout; + // Depth attachment + attachments[1].format = depthAttachmentFormat; + 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_CLEAR; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + 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 subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + subpassDescription.pDepthStencilAttachment = &depthReference; + subpassDescription.inputAttachmentCount = 0; + subpassDescription.pInputAttachments = nullptr; + subpassDescription.preserveAttachmentCount = 0; + subpassDescription.pPreserveAttachments = nullptr; + subpassDescription.pResolveAttachments = nullptr; + + // Subpass dependencies for layout transitions + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo renderPassCI{}; + renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCI.attachmentCount = static_cast(attachments.size()); + renderPassCI.pAttachments = attachments.data(); + renderPassCI.subpassCount = 1; + renderPassCI.pSubpasses = &subpassDescription; + renderPassCI.dependencyCount = static_cast(dependencies.size()); + renderPassCI.pDependencies = dependencies.data(); + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassCI, nullptr, &renderPass)); + } +} + +VkFormat PlumageRender::findDepthFormat() +{ + return findSupportedFormat( + { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT + ); +} + +VkFormat PlumageRender::findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) +{ + for (VkFormat format : candidates) + { + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); + + if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) + { + return format; + } + else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) + { + return format; + } + } + throw std::runtime_error("failed to find supported format"); +} + +bool PlumageRender::hasStencilComponent(VkFormat format) +{ + return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; +} + +void PlumageRender::createPipelineCache() +{ + VkPipelineCacheCreateInfo pipelineCacheCreateInfo{}; + pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache)); +} + +void PlumageRender::createFramebuffer() +{ + if (settings.headless) + { + auto frameRange = settings.endFrameIndex - settings.startFrameCount; + if (settings.multiSampling) + { + + for (int i = 0; i < frameRange; i++) + { + VkImageView attachments[4]; + attachments[0] = multisampleTarget.colorAttachment.view; + attachments[1] = multisampleTarget.depthAttachment.view; + attachments[2] = depthAttachment.view; + attachments[3] = colorAttachment.view; + + VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo(); + framebufferCreateInfo.renderPass = renderPass; + framebufferCreateInfo.attachmentCount = 4; + framebufferCreateInfo.pAttachments = attachments; + framebufferCreateInfo.width = settings.width; + framebufferCreateInfo.height = settings.height; + framebufferCreateInfo.layers = 1; + + VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i])); + } + + } + else + { + for (int i = 0; i < frameRange; i++) + { + VkImageView attachments[2]; + attachments[0] = colorAttachment.view; + attachments[1] = depthAttachment.view; + + VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo(); + framebufferCreateInfo.renderPass = renderPass; + framebufferCreateInfo.attachmentCount = 2; + framebufferCreateInfo.pAttachments = attachments; + framebufferCreateInfo.width = settings.width; + framebufferCreateInfo.height = settings.height; + framebufferCreateInfo.layers = 1; + VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i])); + + } + } + + createImageView(); + } + else + { + createSwapChainFramebuffer(); + createImageView(); + } +} + +void PlumageRender::createSwapChainFramebuffer() +{ + uint32_t attachmentCount; + VkImageView attachments[attachmentCount]; + + if (settings.multiSampling) { + attachmentCount = 4; + attachments[0] = multisampleTarget.colorAttachment.view; + attachments[1] = multisampleTarget.depthAttachment.view; + attachments[2] = depthAttachment.view; + + } + else { + attachmentCount = 2; + attachments[1] = depthAttachment.view; + } + + VkFramebufferCreateInfo frameBufferCI{}; + frameBufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + frameBufferCI.pNext = NULL; + frameBufferCI.renderPass = renderPass; + frameBufferCI.attachmentCount = attachmentCount; + frameBufferCI.pAttachments = attachments; + frameBufferCI.width = settings.width; + frameBufferCI.height = settings.height; + frameBufferCI.layers = 1; + + + + // Create frame buffers for every swap chain image + framebuffers.resize(swapChainImageViews.size()); + for (uint32_t i = 0; i < swapChainImageViews.size(); i++) { + if (settings.multiSampling) { + attachments[3] = swapChainImageViews[i]; + } + else { + attachments[0] = swapChainImageViews[i]; + } + VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &framebuffers[i])); + } +} + +void PlumageRender::createImageView() +{ + VkFormat colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM; + VkFormat depthFormat = findDepthFormat(); + if (settings.headless) + { + if (settings.multiSampling) + { + + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = colorAttachmentFormat; + imageCI.extent.width = settings.width; + imageCI.extent.height = settings.height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = settings.sampleCount; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.colorAttachment.image)); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, multisampleTarget.colorAttachment.image, &memReqs); + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + VkBool32 lazyMemTypePresent; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.colorAttachment.memory)); + vkBindImageMemory(device, multisampleTarget.colorAttachment.image, multisampleTarget.colorAttachment.memory, 0); + + // Create image view for the MSAA color image target + VkImageViewCreateInfo imageViewCI{}; + imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCI.image = multisampleTarget.colorAttachment.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = colorAttachmentFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.colorAttachment.view)); + + // Depth target + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = depthFormat; + imageCI.extent.width = settings.width; + imageCI.extent.height = settings.height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = settings.sampleCount; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &multisampleTarget.depthAttachment.image)); + + vkGetImageMemoryRequirements(device, multisampleTarget.depthAttachment.image, &memReqs); + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &multisampleTarget.depthAttachment.memory)); + vkBindImageMemory(device, multisampleTarget.depthAttachment.image, multisampleTarget.depthAttachment.memory, 0); + + // Create image view for the MSAA target + imageViewCI.image = multisampleTarget.depthAttachment.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = depthFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &multisampleTarget.depthAttachment.view)); + } + // creat color image + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.format = colorAttachmentFormat; + imageCI.extent.width = settings.width; + imageCI.extent.height = settings.height; + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCI.samples = VK_SAMPLE_COUNT_1_BIT; + imageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VK_CHECK_RESULT(vkCreateImage(device, &imageCI, nullptr, &colorAttachment.image)); + + VkMemoryRequirements memReqs; + vkGetImageMemoryRequirements(device, colorAttachment.image, &memReqs); + + VkMemoryAllocateInfo memAllocInfo{}; + memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAllocInfo.allocationSize = memReqs.size; + VkBool32 lazyMemTypePresent; + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, &lazyMemTypePresent); + if (!lazyMemTypePresent) { + memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &colorAttachment.memory)); + + vkBindImageMemory(device, multisampleTarget.colorAttachment.image, colorAttachment.memory, 0); + + // Create image view for the color image + VkImageViewCreateInfo imageViewCI{}; + imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCI.image = colorAttachment.image; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = colorAttachmentFormat; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_R; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_G; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_B; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_A; + imageViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &imageViewCI, nullptr, &colorAttachment.view)); + + // create image for the depth image + 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 = settings.width; + image.extent.height = settings.height; + image.extent.depth = 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; + + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &depthAttachment.image)); + + VkMemoryAllocateInfo memAlloc = {}; + memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memAlloc.pNext = NULL; + memAlloc.allocationSize = 0; + memAlloc.memoryTypeIndex = 0; + VkMemoryRequirements depthAttachmentMemReqs; + vkGetImageMemoryRequirements(device, depthAttachment.image, &memReqs); + memAlloc.allocationSize = depthAttachmentMemReqs.size; + memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &depthAttachment.memory)); + VK_CHECK_RESULT(vkBindImageMemory(device, depthAttachment.image, depthAttachment.memory, 0)); + + + // create image view for depth image + 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.image = depthAttachment.image; + depthStencilView.flags = 0; + depthStencilView.subresourceRange = {}; + depthStencilView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + if (depthFormat >= VK_FORMAT_D16_UNORM_S8_UINT) + { + depthStencilView.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + depthStencilView.subresourceRange.baseMipLevel = 0; + depthStencilView.subresourceRange.levelCount = 1; + depthStencilView.subresourceRange.baseArrayLayer = 0; + depthStencilView.subresourceRange.layerCount = 1; + VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &depthAttachment.view)); + + } + else + { + swapChainImageViews.resize(swapChainImages.size()); + + for (size_t i = 0; i < swapChainImages.size(); i++) + { + VkImageViewCreateInfo creatInfo{}; + creatInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + creatInfo.image = swapChainImages[i]; + creatInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + creatInfo.format = swapChainImageFormat; + + creatInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + creatInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + creatInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + creatInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + creatInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + creatInfo.subresourceRange.baseMipLevel = 0; + creatInfo.subresourceRange.levelCount = 1; + creatInfo.subresourceRange.baseArrayLayer = 0; + creatInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &creatInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) + { + throw std::runtime_error("failed to creat image view"); + } + } + } +} + +void PlumageRender::renderLoop() +{ + if (settings.headless) + { + uint32_t frameRange = settings.endFrameIndex - settings.startFrameCount; + for (size_t i = 0; i < frameRange; i++) + { + drawFrame(); + } + } + else + { + while (!glfwWindowShouldClose(window)) + { + glfwPollEvents(); + drawFrame(); + } + } + vkDeviceWaitIdle(device); +} + +void PlumageRender::drawFrame() +{ + auto tStart = std::chrono::high_resolution_clock::now(); + + render(); + + frameCounter++; + auto tEnd = std::chrono::high_resolution_clock::now(); + auto tDiff = std::chrono::duration(tEnd - tStart).count(); + frameTimer = (float)tDiff / 1000.0f; + camera.update(frameTimer); + fpsTimer += (float)tDiff; + if (fpsTimer > 1000.0f) { + lastFPS = static_cast((float)frameCounter * (1000.0f / fpsTimer)); + fpsTimer = 0.0f; + frameCounter = 0; + } +} + + + +bool PlumageRender::swapChainAcquireNextImage(bool framebuffeerResized, uint32_t imageIndex, uint32_t frameIndex) +{ + VkResult result = vkAcquireNextImageKHR(device, swapChainKHR, UINT64_MAX, renderCompleteSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex); + + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebuffeerResized) + { + framebuffeerResized = false; + recreateSwapChain(); + return framebuffeerResized; + } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) + { + throw std::runtime_error("failed to acquire swap chain image in drawFrame"); + } +} + +void PlumageRender::recreateSwapChain() +{ + int width = 0, height = 0; + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) + { + if (glfwWindowShouldClose(window)) + { + return; + } + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(device); + + cleanupSwapChain(); + + createSwapChain(); + createImageView(); + createFramebuffer(); +} + +void PlumageRender::submitToGraphicQueue(uint32_t frameIndex, uint32_t currentBuffer) +{ + const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pWaitDstStageMask = &waitDstStageMask; + submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[currentBuffer]; + submitInfo.commandBufferCount = 1; + VK_CHECK_RESULT(vkQueueSubmit(graphicQueue, 1, &submitInfo, waitFences[frameIndex])); +} + +void PlumageRender::imageToQueuePresent(uint32_t frameIndex, uint32_t imageIndex, bool framebufferResized) +{ + //鏄剧ず闃熷垪 + VkSemaphore signalSemaphores[] = { renderCompleteSemaphores[frameIndex] }; + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = { swapChainKHR }; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + { + framebufferResized = false; + recreateSwapChain(); + } + else if (result != VK_SUCCESS) + { + throw std::runtime_error("failed to present swap chain image in drawFrame"); + } +} void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode) { if (node->mesh) { @@ -156,7 +1344,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } } - void PlumageRender::buildCommandBuffers() +void PlumageRender::buildCommandBuffers() { VkCommandBufferBeginInfo cmdBufferBeginInfo{}; cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -177,15 +1365,15 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; - renderPassBeginInfo.renderArea.extent.width = width; - renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.renderArea.extent.width = settings.width; + renderPassBeginInfo.renderArea.extent.height = settings.height; renderPassBeginInfo.clearValueCount = settings.multiSampling ? 3 : 2; renderPassBeginInfo.pClearValues = clearValues; for (uint32_t i = 0; i < commandBuffers.size(); ++i) { - renderPassBeginInfo.framebuffer = frameBuffers[i]; + renderPassBeginInfo.framebuffer = framebuffers[i]; VkCommandBuffer currentCB = commandBuffers[i]; @@ -193,14 +1381,14 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode vkCmdBeginRenderPass(currentCB, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport{}; - viewport.width = (float)width; - viewport.height = (float)height; + viewport.width = (float)settings.width; + viewport.height = (float)settings.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(currentCB, 0, 1, &viewport); VkRect2D scissor{}; - scissor.extent = { width, height }; + scissor.extent = { settings.width, settings.height }; vkCmdSetScissor(currentCB, 0, 1, &scissor); VkDeviceSize offsets[1] = { 0 }; @@ -244,7 +1432,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - void PlumageRender::loadScene(std::string filename) +void PlumageRender::loadScene(std::string filename) { std::cout << "Loading scene from " << filename << std::endl; models.scene.destroy(device); @@ -252,14 +1440,14 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode animationIndex = 0; animationTimer = 0.0f; auto tStart = std::chrono::high_resolution_clock::now(); - models.scene.loadFromFile(filename, vulkanDevice, queue); + models.scene.loadFromFile(filename, vulkanDevice, graphicQueue); auto tFileLoad = std::chrono::duration(std::chrono::high_resolution_clock::now() - tStart).count(); std::cout << "Loading took " << tFileLoad << " ms" << std::endl; camera.setPosition({ 0.0f, 0.0f, -6.0f }); camera.setRotation({ 0.0f, 0.0f, 0.0f }); } - void PlumageRender::loadEnvironment(std::string filename) +void PlumageRender::loadEnvironment(std::string filename) { std::cout << "Loading environment from " << filename << std::endl; if (textures.environmentCube.image) { @@ -267,11 +1455,11 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode textures.irradianceCube.destroy(); textures.prefilteredCube.destroy(); } - textures.environmentCube.loadFromFile(filename, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); + textures.environmentCube.loadFromFile(filename, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, graphicQueue); generateCubemaps(); } - void PlumageRender::loadAssets() +void PlumageRender::loadAssets() { const std::string assetpath = getAssetPath(); @@ -288,7 +1476,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode readDirectory(assetpath + "environments", "*.ktx", environments, false); - textures.empty.loadFromFile(PlumageRender::filePath.emptyEnvmapFilePath, VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + textures.empty.loadFromFile(PlumageRender::filePath.emptyEnvmapFilePath, VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, graphicQueue); std::string sceneFile = filePath.glTFModelFilePath; std::string envMapFile = filePath.envMapFilePath; @@ -314,12 +1502,12 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } loadScene(sceneFile.c_str()); - models.skybox.loadFromFile(PlumageRender::filePath.skyboxModleFilePath, vulkanDevice, queue); + models.skybox.loadFromFile(PlumageRender::filePath.skyboxModleFilePath, vulkanDevice, graphicQueue); loadEnvironment(envMapFile.c_str()); } - void PlumageRender::setupNodeDescriptorSet(glTFModel::Node* node) +void PlumageRender::setupNodeDescriptorSet(glTFModel::Node* node) { /* This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures) @@ -347,7 +1535,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } } - void PlumageRender::setupDescriptors() +void PlumageRender::setupDescriptors() { /* Descriptor Pool @@ -372,15 +1560,16 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } } + auto swapChainImageCount = static_cast(swapChainImages.size()); std::vector poolSizes = { - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (4 + meshCount) * swapChain.imageCount }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageSamplerCount * swapChain.imageCount } + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (4 + meshCount) * swapChainImageCount}, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageSamplerCount * swapChainImageCount } }; VkDescriptorPoolCreateInfo descriptorPoolCI{}; descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCI.poolSizeCount = 2; descriptorPoolCI.pPoolSizes = poolSizes.data(); - descriptorPoolCI.maxSets = (2 + materialCount + meshCount) * swapChain.imageCount; + descriptorPoolCI.maxSets = (2 + materialCount + meshCount) * swapChainImages.size(); VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); /* @@ -570,7 +1759,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } } - void PlumageRender::preparePipelines() +void PlumageRender::preparePipelines() { VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -718,10 +1907,10 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - // generate two cube maps +// generate two cube maps // irradiance cube map // prefileter environment cube map - void PlumageRender::generateCubemaps() +void PlumageRender::generateCubemaps() { enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 }; @@ -912,7 +2101,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; vkCmdPipelineBarrier(layoutCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - vulkanDevice->flushCommandBuffer(layoutCmd, queue, true); + vulkanDevice->flushCommandBuffer(layoutCmd, graphicQueue, true); } // Descriptors @@ -1113,7 +2302,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.subresourceRange = subresourceRange; vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); + vulkanDevice->flushCommandBuffer(cmdBuf, graphicQueue, false); } for (uint32_t m = 0; m < numMips; m++) { @@ -1209,7 +2398,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); } - vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); + vulkanDevice->flushCommandBuffer(cmdBuf, graphicQueue, false); } } @@ -1224,7 +2413,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode imageMemoryBarrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.subresourceRange = subresourceRange; vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); - vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); + vulkanDevice->flushCommandBuffer(cmdBuf, graphicQueue, false); } @@ -1258,8 +2447,8 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; } } - // generate BRDF integration map for roughness/NdotV - void PlumageRender::generateBRDFLUT() +// generate BRDF integration map for roughness/NdotV +void PlumageRender::generateBRDFLUT() { auto tStart = std::chrono::high_resolution_clock::now(); @@ -1496,9 +2685,9 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdDraw(cmdBuf, 3, 1, 0, 0); vkCmdEndRenderPass(cmdBuf); - vulkanDevice->flushCommandBuffer(cmdBuf, queue); + vulkanDevice->flushCommandBuffer(cmdBuf, graphicQueue); - vkQueueWaitIdle(queue); + vkQueueWaitIdle(graphicQueue); vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelinelayout, nullptr); @@ -1516,8 +2705,8 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl; } - // Prepare and initialize uniform buffer containing shader uniforms - void PlumageRender::prepareUniformBuffers() +// Prepare and initialize uniform buffer containing shader uniforms +void PlumageRender::prepareUniformBuffers() { for (auto& uniformBuffer : uniformBuffers) { uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene)); @@ -1527,7 +2716,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode updateUniformBuffers(); } - void PlumageRender::updateUniformBuffers() +void PlumageRender::updateUniformBuffers() { // Scene shaderDataScene.projection = camera.matrices.perspective; @@ -1568,7 +2757,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view)); } - void PlumageRender::updateShaderData() +void PlumageRender::updateShaderData() { shaderData.lightDir = glm::vec4( sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), @@ -1578,7 +2767,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - void PlumageRender::initWindow(int Width, int Height) +void PlumageRender::initWindow(int Width, int Height) { glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -1588,13 +2777,13 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); } - void PlumageRender::framebufferResizeCallback(GLFWwindow* window, int width, int height) +void PlumageRender::framebufferResizeCallback(GLFWwindow* window, int width, int height) { auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); app->framebufferResized = true; } - void PlumageRender::windowResized() +void PlumageRender::windowResized() { buildCommandBuffers(); vkDeviceWaitIdle(device); @@ -1603,13 +2792,17 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode updateUIOverlay(); } - void PlumageRender::prepare() +void PlumageRender::prepare() { - VulkanExampleBase::prepare(); + createSwapChain(); + + createCommandPool(); + + createRenderPass(); camera.type = Camera::CameraType::lookat; - camera.setPerspective(45.0f, (float)width / (float)height, 0.1f, 256.0f); + camera.setPerspective(45.0f, (float)settings.width / (float)settings.height, 0.1f, 256.0f); camera.rotationSpeed = 0.25f; camera.movementSpeed = 0.1f; @@ -1617,9 +2810,9 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode waitFences.resize(renderAhead); presentCompleteSemaphores.resize(renderAhead); renderCompleteSemaphores.resize(renderAhead); - commandBuffers.resize(swapChain.imageCount); - uniformBuffers.resize(swapChain.imageCount); - descriptorSets.resize(swapChain.imageCount); + commandBuffers.resize(swapChainImages.size()); + uniformBuffers.resize(swapChainImages.size()); + descriptorSets.resize(swapChainImages.size()); // Command buffer execution fences for (auto& waitFence : waitFences) { VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT }; @@ -1638,7 +2831,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode { VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - cmdBufAllocateInfo.commandPool = cmdPool; + cmdBufAllocateInfo.commandPool = commandPool; cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cmdBufAllocateInfo.commandBufferCount = static_cast(commandBuffers.size()); VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandBuffers.data())); @@ -1651,7 +2844,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode setupDescriptors(); preparePipelines(); - gui = new UI(vulkanDevice, renderPass, queue, pipelineCache, settings.sampleCount); + gui = new UI(vulkanDevice, renderPass, graphicQueue, pipelineCache, settings.sampleCount); updateUIOverlay(); buildCommandBuffers(); @@ -1659,7 +2852,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode prepared = true; } - void PlumageRender::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue) +void PlumageRender::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue) { VkSubmitInfo submitInfo = vks::initializers::submitInfo(); submitInfo.commandBufferCount = 1; @@ -1672,8 +2865,8 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode vkDestroyFence(device, fence, nullptr); } - // todo :鏍规嵁physicalDeviceIndex纭畾瀛愭枃浠跺す璺緞锛宖rameIndex纭畾fileName - void PlumageRender::writeImageToFile(std::string filePath) +// todo :鏍规嵁physicalDeviceIndex纭畾瀛愭枃浠跺す璺緞锛宖rameIndex纭畾fileName +void PlumageRender::writeImageToFile(std::string filePath) { bool screenshotSaved = false; @@ -1683,7 +2876,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode VkFormatProperties formatProps; // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) - vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps); + vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChainImageFormat, &formatProps); if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl; supportsBlit = false; @@ -1697,15 +2890,19 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } // Source for the copy is the last rendered swapchain image - VkImage srcImage = swapChain.images[currentBuffer]; + if (settings.headless) + { + VkImage srcImage = colorAttachment.image; + } + VkImage srcImage = swapChainImages[currentBuffer]; // Create the linear tiled destination image to copy to and to read the memory from VkImageCreateInfo imageCreateCI(vks::initializers::imageCreateInfo()); imageCreateCI.imageType = VK_IMAGE_TYPE_2D; // Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ imageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM; - imageCreateCI.extent.width = width; - imageCreateCI.extent.height = height; + imageCreateCI.extent.width = settings.width; + imageCreateCI.extent.height = settings.height; imageCreateCI.extent.depth = 1; imageCreateCI.arrayLayers = 1; imageCreateCI.mipLevels = 1; @@ -1759,8 +2956,8 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode { // Define the region to blit (we will blit the whole swapchain image) VkOffset3D blitSize; - blitSize.x = width; - blitSize.y = height; + blitSize.x = settings.width; + blitSize.y = settings.height; blitSize.z = 1; VkImageBlit imageBlitRegion{}; imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -1787,8 +2984,8 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode imageCopyRegion.srcSubresource.layerCount = 1; imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.dstSubresource.layerCount = 1; - imageCopyRegion.extent.width = width; - imageCopyRegion.extent.height = height; + imageCopyRegion.extent.width = settings.width; + imageCopyRegion.extent.height = settings.height; imageCopyRegion.extent.depth = 1; // Issue the copy command @@ -1824,7 +3021,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }); - vulkanDevice->flushCommandBuffer(copyCmd, queue); + vulkanDevice->flushCommandBuffer(copyCmd, graphicQueue); // Get layout of the image (including row pitch) VkImageSubresource subResource{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; @@ -1839,14 +3036,14 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode if (settings.outputPNGimage) { - stbi_write_png(filePath.c_str(), width, height, 4, data, static_cast(subResourceLayout.rowPitch)); + stbi_write_png(filePath.c_str(), settings.width, settings.height, 4, data, static_cast(subResourceLayout.rowPitch)); } else { std::ofstream file(filePath, std::ios::out | std::ios::binary); // ppm header - file << "P6\n" << width << "\n" << height << "\n" << 255 << "\n"; + file << "P6\n" << settings.width << "\n" << settings.height << "\n" << 255 << "\n"; // If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components bool colorSwizzle = false; @@ -1855,13 +3052,13 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode if (!supportsBlit) { std::vector formatsBGR = { VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM }; - colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end()); + colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChainImageFormat) != formatsBGR.end()); } // ppm binary pixel data - for (uint32_t y = 0; y < height; y++) + for (uint32_t y = 0; y < settings.height; y++) { unsigned int* row = (unsigned int*)data; - for (uint32_t x = 0; x < width; x++) + for (uint32_t x = 0; x < settings.width; x++) { if (colorSwizzle) { @@ -1891,7 +3088,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - void PlumageRender::outputImageSequence() +void PlumageRender::outputImageSequence() { if (savedFrameCounter == settings.startFrameCount) @@ -1900,7 +3097,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode removeImageSequence(); } - filePath.deviceSpecFilePath = filePath.imageOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex); + filePath.deviceSpecFilePath = filePath.imageOutputPath + "/device" + std::to_string(settings.selectedPhysicalDeviceIndex); if (savedFrameCounter > settings.outputFrameCount) { @@ -1944,7 +3141,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode savedFrameCounter++; } - void PlumageRender::imageSequenceToVideo() +void PlumageRender::imageSequenceToVideo() { if (!signal.imageSequenceOutputComplete) { @@ -1954,7 +3151,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode { return; } - std::string deviceFilePath = filePath.videoOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex); + std::string deviceFilePath = filePath.videoOutputPath + "/device" + std::to_string(settings.selectedPhysicalDeviceIndex); if (_access(deviceFilePath.c_str(), 0) == -1) { std::filesystem::create_directories(deviceFilePath.c_str()); @@ -1979,7 +3176,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode removeImageSequence(); } - void PlumageRender::removeImageSequence() +void PlumageRender::removeImageSequence() { if (savedFrameCounter != settings.startFrameCount) { @@ -2009,7 +3206,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - uint32_t PlumageRender::getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) +uint32_t PlumageRender::getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties deviceMemoryProperties; vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties); @@ -2027,35 +3224,34 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode return 0; } - void PlumageRender::render() +void PlumageRender::render() { if (!prepared) { return; } - updateUIOverlay(); + uint32_t imageIndex; + + if (settings.headless) + { + updateUIOverlay(); + } + //鍔犲叆鍐欏埌鏂囦欢鐨勫嚱鏁 //swapChainImage = swapChain.images[frameIndex]; //outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath); - outputImageSequence(); + VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX)); - imageSequenceToVideo(); + framebufferResized = swapChainAcquireNextImage(framebufferResized, imageIndex, frameIndex); + VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex])); - - VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], ¤tBuffer); - if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) { - windowResize(); - } - else { - VK_CHECK_RESULT(acquire); - - - } - + outputImageSequence(); + imageSequenceToVideo(); + // Update UBOs updateUniformBuffers(); UniformBufferSet currentUB = uniformBuffers[currentBuffer]; @@ -2063,30 +3259,11 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData)); memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox)); - const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pWaitDstStageMask = &waitDstStageMask; - submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[currentBuffer]; - submitInfo.commandBufferCount = 1; - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex])); + submitToGraphicQueue(frameIndex,currentBuffer); - //鏄剧ず闃熷垪 - VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]); - if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) { - if (present == VK_ERROR_OUT_OF_DATE_KHR) { - windowResize(); - return; - } - else { - VK_CHECK_RESULT(present); - } - } + imageToQueuePresent(frameIndex, imageIndex, framebufferResized); + frameIndex += 1; frameIndex %= renderAhead; @@ -2115,7 +3292,7 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - void PlumageRender::fileDropped(std::string filename) +void PlumageRender::fileDropped(std::string filename) { vkDeviceWaitIdle(device); loadScene(filename); @@ -2124,12 +3301,80 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode } - void PlumageRender::updateUIOverlay() +void PlumageRender::destroyVulkan() + { + + // Clean up used Vulkan resources + // Note : Inherited destructor cleans up resources stored in base class + vkDestroyPipeline(device, pipelines.skybox, nullptr); + vkDestroyPipeline(device, pipelines.pbr, nullptr); + vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr); + + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr); + + models.scene.destroy(device); + models.skybox.destroy(device); + + for (auto buffer : uniformBuffers) { + buffer.params.destroy(); + buffer.scene.destroy(); + buffer.skybox.destroy(); + } + for (auto fence : waitFences) { + vkDestroyFence(device, fence, nullptr); + } + for (auto semaphore : renderCompleteSemaphores) { + vkDestroySemaphore(device, semaphore, nullptr); + } + for (auto semaphore : presentCompleteSemaphores) { + vkDestroySemaphore(device, semaphore, nullptr); + } + + textures.environmentCube.destroy(); + textures.irradianceCube.destroy(); + textures.prefilteredCube.destroy(); + textures.lutBrdf.destroy(); + textures.empty.destroy(); + delete gui; + // Clean up Vulkan resources + cleanupSwapChain(); + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + vkDestroyRenderPass(device, renderPass, nullptr); + for (uint32_t i = 0; i < framebuffers.size(); i++) { + vkDestroyFramebuffer(device, framebuffers[i], nullptr); + } + vkDestroyImageView(device, depthAttachment.view, nullptr); + vkDestroyImage(device, depthAttachment.image, nullptr); + vkFreeMemory(device, depthAttachment.memory, nullptr); + vkDestroyImageView(device, colorAttachment.view, nullptr); + vkDestroyImage(device, colorAttachment.image, nullptr); + vkFreeMemory(device, colorAttachment.memory, nullptr); + vkDestroyPipelineCache(device, pipelineCache, nullptr); + vkDestroyCommandPool(device, commandPool, nullptr); + if (settings.multiSampling) { + vkDestroyImage(device, multisampleTarget.colorAttachment.image, nullptr); + vkDestroyImageView(device, multisampleTarget.colorAttachment.view, nullptr); + vkFreeMemory(device, multisampleTarget.colorAttachment.memory, nullptr); + vkDestroyImage(device, multisampleTarget.depthAttachment.image, nullptr); + vkDestroyImageView(device, multisampleTarget.depthAttachment.view, nullptr); + vkFreeMemory(device, multisampleTarget.depthAttachment.memory, nullptr); + } + delete vulkanDevice; + if (settings.validation) { + DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); + } + vkDestroyInstance(instance, nullptr); + } + +void PlumageRender::updateUIOverlay() { ImGuiIO& io = ImGui::GetIO(); ImVec2 lastDisplaySize = io.DisplaySize; - io.DisplaySize = ImVec2((float)width, (float)height); + io.DisplaySize = ImVec2((float)settings.width, (float)settings.height); io.DeltaTime = frameTimer; io.MousePos = ImVec2(mousePos.x, mousePos.y); @@ -2340,19 +3585,18 @@ void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFMode - int main(HINSTANCE hInstance, HINSTANCE, LPSTR, int) +int main() +{ + PlumageRender* plumageRender; + plumageRender = new PlumageRender(); + plumageRender->initVulkan(); + if (plumageRender->settings.headless) { - PlumageRender* plumageRender; - for (int32_t i = 0; i < __argc; i++) { PlumageRender::args.push_back(__argv[i]); }; - plumageRender = new PlumageRender(); - plumageRender->initVulkan(); - if (plumageRender->settings.headless) - { - plumageRender->initWindow(); - } - plumageRender->prepare(); - plumageRender->renderLoop(); - delete(plumageRender); - return 0; + plumageRender->initWindow(plumageRender->settings.width,plumageRender->settings.height); } -#endif + plumageRender->prepare(); + plumageRender->renderLoop(); + delete(plumageRender); + return 0; +} + diff --git a/src/render/render.h b/src/render/render.h index d90d1ed..cdd613f 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -3,8 +3,13 @@ #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb/stb_image_write.h" +#define GLFW_INCLUDE_VULKAN +#include - +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#define GLM_ENABLE_EXPERIMENTAL +#include #include #include @@ -14,27 +19,34 @@ #include #include #include +#include +#include #include #include #include #include #include -#include +#include #include #include -#include "VulkanExampleBase.h" +//#include "VulkanExampleBase.h" #include "glTFModel.h" #include #include "VulkanDevice.hpp" #include "ui.hpp" #include +#include + + + + #define ENABLE_VALIDATION false -class PlumageRender : public VulkanExampleBase +class PlumageRender { public: bool wireframe = false; @@ -42,8 +54,41 @@ public: bool ToneMapping = true; bool pbrEnabled = true; + std::vector args; + GLFWwindow* window; + const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" + + }; + + const std::vector swapchainExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + //"VK_EXT_headless_surface" + }; + + std::string title; + std::string name; + Camera camera; + + VkPhysicalDevice physicalDevice; + VkPhysicalDeviceFeatures deviceFeatures; + VkDevice device; + vks::VulkanDevice* vulkanDevice; + + std::vector swapChainImageViews; + + VkRenderPass renderPass; + + VkPipelineCache pipelineCache; + + float fpsTimer = 0.0f; + + VkDescriptorPool descriptorPool; + + uint32_t currentBuffer; + struct stat { @@ -56,6 +101,7 @@ public: }signal; + bool prepared = false; struct Models { @@ -106,6 +152,9 @@ public: }chineseUI; + + VkInstance instance; + struct UniformBufferSet { Buffer scene; Buffer skybox; @@ -190,6 +239,27 @@ public: } filePath; + struct Settings { + uint32_t width = 1280; + uint32_t height = 720; + bool validation = true; // 鏍¢獙灞傚紑鍏 + bool fullscreen = false; // 鍏ㄥ睆寮鍏 + bool vsync = false; // 鍨傜洿鍚屾寮鍏 + bool multiSampling = true; // 澶氶噸閲囨牱 + bool rotateModel = true; // 妯″瀷鑷棆杞(鏆傛椂澶辨晥) + bool headless = false; // 鏃犲ご寮鍏 + bool outputPNGimage = false; + uint32_t endFrameIndex = 75; + bool enableSaveToImageSequeue = true; // 鍥剧墖搴忓垪寮鍏筹紙鏆傛椂寮冪敤锛 + uint32_t outputFrameCount = 100; // 鍥剧墖搴忓垪缁撴潫甯 + bool takeScreenShot = false; // 鎴睆锛堟殏鏃跺純鐢級 + uint32_t startFrameCount = 1; // 鍥剧墖搴忓垪寮濮嬪抚 + uint32_t selectedPhysicalDeviceIndex = 0; // 閫夊畾鐨勬樉鍗$储寮曪紝鏁扮粍褰㈠紡浠0寮濮 + uint32_t videoFrameRate = 25; + + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; // 澶氶噸閲囨牱鍊嶇巼 + } settings; + float modelrot = 0.0f; glm::vec3 modelPos = glm::vec3(0.0f); @@ -303,48 +373,157 @@ public: bool framebufferResized = false; + VkDebugUtilsMessengerEXT debugMessenger; + + VkSurfaceKHR surface; + + VkQueue graphicQueue; + VkQueue presentQueue; + + VkSwapchainKHR swapChainKHR; + VkFormat swapChainImageFormat; + std::vector swapChainImages; + VkExtent2D swapChainExtent; + + uint32_t imageIndex; + + VkCommandPool commandPool; + + struct QueueFamilyIndices + { + std::optional graphicsFamily; + std::optional presentFamily; + + bool isGraphicsFamilyComplete() + { + return graphicsFamily.has_value(); + } + bool isPresentFamilyComplete() + { + return presentFamily.has_value(); + } + }; + + struct SwapChainSupportDetails + { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector presentModes; + }; + + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; + + struct MultisampleTarget { + struct { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + } colorAttachment; + struct { + VkImage image; + VkImageView view; + VkDeviceMemory memory; + } depthAttachment; + } multisampleTarget; + + struct DepthAttachment { + VkImage image; + VkDeviceMemory memory; + VkImageView view; + } depthAttachment; + + struct ColorAttachment { + VkImage image; + VkDeviceMemory memory; + VkImageView view; + } colorAttachment; + + std::vector framebuffers; + + uint32_t lastFPS = 0; + float frameTimer = 1.0f; + uint32_t frameCounter =0; + + glm::vec2 mousePos; + + struct MouseButtons { + bool left = false; + bool right = false; + bool middle = false; + } mouseButtons; + PlumageRender(); - ~PlumageRender() - { - // Clean up used Vulkan resources - // Note : Inherited destructor cleans up resources stored in base class - vkDestroyPipeline(device, pipelines.skybox, nullptr); - vkDestroyPipeline(device, pipelines.pbr, nullptr); - vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr); + ~PlumageRender(); + - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); - vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr); - - models.scene.destroy(device); - models.skybox.destroy(device); - - for (auto buffer : uniformBuffers) { - buffer.params.destroy(); - buffer.scene.destroy(); - buffer.skybox.destroy(); - } - for (auto fence : waitFences) { - vkDestroyFence(device, fence, nullptr); - } - for (auto semaphore : renderCompleteSemaphores) { - vkDestroySemaphore(device, semaphore, nullptr); - } - for (auto semaphore : presentCompleteSemaphores) { - vkDestroySemaphore(device, semaphore, nullptr); - } - - textures.environmentCube.destroy(); - textures.irradianceCube.destroy(); - textures.prefilteredCube.destroy(); - textures.lutBrdf.destroy(); - textures.empty.destroy(); - delete gui; - } + void initVulkan(); VkResult createInstance(); + + bool checkValidationLayerSupport(); + std::vector getRequiredExtensions(); + void setupDebugMessager(); + void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& debugCreateInfo); + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger); + + void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator); + + void createSurface(); + + void pickupPhysicalDevice(); + bool isDeviceSuitable(VkPhysicalDevice device); + bool checkDeviceExtensionSupport(VkPhysicalDevice device); + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device); + + // 鍒涘缓绋嬪簭浣跨敤鐨勯昏緫璁惧 + void createLogicalDevice(); + + void prepare(); + + // 鍒涘缓浜ゆ崲閾 + void createSwapChain(); + VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); + VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + void cleanupSwapChain(); + + void createCommandPool(); + + void createRenderPass(); + VkFormat findDepthFormat(); + VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); + bool hasStencilComponent(VkFormat format); + + void createPipelineCache(); + + void createFramebuffer(); + void createSwapChainFramebuffer(); + void createImageView(); + + void renderLoop(); + + void drawFrame(); + + + bool swapChainAcquireNextImage(bool framebuffeerResized, uint32_t imageIndex, uint32_t frameIndex); + + void recreateSwapChain(); + + void submitToGraphicQueue(uint32_t frameIndex, uint32_t currentBuffer); + + void imageToQueuePresent(uint32_t frameIndex, uint32_t imageIndex, bool framebufferResized); + void renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode); void loadScene(std::string filename); void loadEnvironment(std::string filename); @@ -360,9 +539,9 @@ public: void updateUniformBuffers(); void updateShaderData(); void initWindow(int Width,int Height); - void framebufferResizeCallback(GLFWwindow* window, int width, int height); + static void framebufferResizeCallback(GLFWwindow* window, int width, int height); + void windowResized(); - void prepare(); void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue); void writeImageToFile(std::string filePath); @@ -374,4 +553,5 @@ public: virtual void render(); virtual void updateUIOverlay(); virtual void fileDropped(std::string filename); + void destroyVulkan(); }; \ No newline at end of file