// SPDX-FileCopyrightText: 2023 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later #include "rendermanager.h" #include #include #include #include #include #include #include #include #include "gamerenderer.h" #include "imgui.h" #include "imguipass.h" #include "simplerenderer.h" #include "swapchain.h" VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDebugUtilsMessengerEXT *pCallback) { // Note: It seems that static_cast<...> doesn't work. Use the C-style forced // cast. auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); if (func != nullptr) { return func(instance, pCreateInfo, pAllocator, pCallback); } else { return VK_ERROR_EXTENSION_NOT_PRESENT; } } VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { Q_UNUSED(messageSeverity) Q_UNUSED(messageType) Q_UNUSED(pUserData) qInfo() << pCallbackData->pMessage; return VK_FALSE; } RenderManager::RenderManager(GameData *data) : m_data(data) { Q_INIT_RESOURCE(shaders); m_device = new Device(); ctx = ImGui::CreateContext(); ImGui::SetCurrentContext(ctx); ImGui::GetIO().IniFilename = ""; ImGui::StyleColorsDark(); std::vector instanceExtensions = {"VK_EXT_debug_utils"}; uint32_t extensionCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); std::vector extensions(extensionCount); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); for (auto &extension : extensions) { if (strstr(extension.extensionName, "surface") != nullptr) { instanceExtensions.push_back(extension.extensionName); } if (strstr(extension.extensionName, "VK_KHR_get_physical_device_properties2") != nullptr) { instanceExtensions.push_back(extension.extensionName); } if (strstr(extension.extensionName, "VK_EXT_debug_utils") != nullptr) { instanceExtensions.push_back(extension.extensionName); } } VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = {}; debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; debugCreateInfo.pfnUserCallback = DebugCallback; VkApplicationInfo applicationInfo = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; applicationInfo.apiVersion = VK_API_VERSION_1_3; const char *layers[] = {"VK_LAYER_KHRONOS_validation"}; VkInstanceCreateInfo createInfo = {}; createInfo.pNext = &debugCreateInfo; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.ppEnabledExtensionNames = instanceExtensions.data(); createInfo.enabledExtensionCount = instanceExtensions.size(); createInfo.pApplicationInfo = &applicationInfo; // createInfo.ppEnabledLayerNames = layers; // createInfo.enabledLayerCount = 1; vkCreateInstance(&createInfo, nullptr, &m_device->instance); VkDebugUtilsMessengerEXT callback; CreateDebugUtilsMessengerEXT(m_device->instance, &debugCreateInfo, nullptr, &callback); // pick physical device uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(m_device->instance, &deviceCount, nullptr); std::vector devices(deviceCount); vkEnumeratePhysicalDevices(m_device->instance, &deviceCount, devices.data()); int preferredDevice = 0; int deviceIndex = 0; for (auto device : devices) { VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(device, &deviceProperties); if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { preferredDevice = deviceIndex; } deviceIndex++; } m_device->physicalDevice = devices[preferredDevice]; extensionCount = 0; vkEnumerateDeviceExtensionProperties(m_device->physicalDevice, nullptr, &extensionCount, nullptr); std::vector extensionProperties(extensionCount); vkEnumerateDeviceExtensionProperties(m_device->physicalDevice, nullptr, &extensionCount, extensionProperties.data()); // we want to choose the portability subset on platforms that // support it, this is a requirement of the portability spec std::vector deviceExtensions = {"VK_KHR_swapchain"}; for (auto extension : extensionProperties) { if (!strcmp(extension.extensionName, "VK_KHR_portability_subset")) deviceExtensions.push_back("VK_KHR_portability_subset"); } uint32_t graphicsFamilyIndex = 0, presentFamilyIndex = 0; // create logical device uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(m_device->physicalDevice, &queueFamilyCount, nullptr); std::vector queueFamilies(queueFamilyCount); vkGetPhysicalDeviceQueueFamilyProperties(m_device->physicalDevice, &queueFamilyCount, queueFamilies.data()); int i = 0; for (const auto &queueFamily : queueFamilies) { if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { graphicsFamilyIndex = i; } i++; } std::vector queueCreateInfos; if (graphicsFamilyIndex == presentFamilyIndex) { VkDeviceQueueCreateInfo queueCreateInfo = {}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = graphicsFamilyIndex; queueCreateInfo.queueCount = 1; float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; queueCreateInfos.push_back(queueCreateInfo); } else { // graphics { VkDeviceQueueCreateInfo queueCreateInfo = {}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = graphicsFamilyIndex; queueCreateInfo.queueCount = 1; float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; queueCreateInfos.push_back(queueCreateInfo); } // present { VkDeviceQueueCreateInfo queueCreateInfo = {}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = presentFamilyIndex; queueCreateInfo.queueCount = 1; float queuePriority = 1.0f; queueCreateInfo.pQueuePriorities = &queuePriority; queueCreateInfos.push_back(queueCreateInfo); } } VkPhysicalDeviceFeatures enabledFeatures{}; enabledFeatures.shaderClipDistance = VK_TRUE; enabledFeatures.shaderCullDistance = VK_TRUE; enabledFeatures.fillModeNonSolid = VK_TRUE; VkPhysicalDeviceDynamicRenderingLocalReadFeaturesKHR localReadFeaturesKhr{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES_KHR}; localReadFeaturesKhr.dynamicRenderingLocalRead = VK_TRUE; VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT unusedAttachmentsFeaturesExt{ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT}; unusedAttachmentsFeaturesExt.dynamicRenderingUnusedAttachments = VK_TRUE; unusedAttachmentsFeaturesExt.pNext = &localReadFeaturesKhr; VkPhysicalDeviceVulkan11Features enabled11Features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES}; enabled11Features.shaderDrawParameters = VK_TRUE; enabled11Features.pNext = &unusedAttachmentsFeaturesExt; VkPhysicalDeviceVulkan12Features enabled12Features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES}; enabled12Features.vulkanMemoryModel = VK_TRUE; enabled12Features.pNext = &enabled11Features; VkPhysicalDeviceVulkan13Features enabled13Features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES}; enabled13Features.shaderDemoteToHelperInvocation = VK_TRUE; enabled13Features.dynamicRendering = VK_TRUE; enabled13Features.synchronization2 = VK_TRUE; enabled13Features.pNext = &enabled12Features; VkDeviceCreateInfo deviceCeateInfo = {}; deviceCeateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCeateInfo.pQueueCreateInfos = queueCreateInfos.data(); deviceCeateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); deviceCeateInfo.ppEnabledExtensionNames = deviceExtensions.data(); deviceCeateInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); deviceCeateInfo.pEnabledFeatures = &enabledFeatures; deviceCeateInfo.pNext = &enabled13Features; vkCreateDevice(m_device->physicalDevice, &deviceCeateInfo, nullptr, &m_device->device); // get queues vkGetDeviceQueue(m_device->device, graphicsFamilyIndex, 0, &m_device->graphicsQueue); vkGetDeviceQueue(m_device->device, presentFamilyIndex, 0, &m_device->presentQueue); // command pool VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.queueFamilyIndex = graphicsFamilyIndex; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; vkCreateCommandPool(m_device->device, &poolInfo, nullptr, &m_device->commandPool); VkDescriptorPoolSize poolSize = {}; poolSize.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; poolSize.descriptorCount = 150; VkDescriptorPoolSize poolSize2 = {}; poolSize2.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; poolSize2.descriptorCount = 150; const std::array poolSizes = {poolSize, poolSize2}; VkDescriptorPoolCreateInfo poolCreateInfo = {}; poolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolCreateInfo.poolSizeCount = poolSizes.size(); poolCreateInfo.pPoolSizes = poolSizes.data(); poolCreateInfo.maxSets = 150; vkCreateDescriptorPool(m_device->device, &poolCreateInfo, nullptr, &m_device->descriptorPool); qInfo() << "Initialized renderer!"; } bool RenderManager::initSwapchain(VkSurfaceKHR surface, int width, int height) { if (m_device->swapChain == nullptr) { m_device->swapChain = new SwapChain(*m_device, surface, width, height); } else { m_device->swapChain->resize(surface, width, height); } // allocate command buffers for (int i = 0; i < 3; i++) { VkCommandBufferAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.commandPool = m_device->commandPool; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = 1; vkAllocateCommandBuffers(m_device->device, &allocInfo, &m_commandBuffers[i]); } VkAttachmentDescription colorAttachment = {}; colorAttachment.format = m_device->swapChain->surfaceFormat; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference colorAttachmentRef = {}; colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDependency dependency = {}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.srcAccessMask = 0; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dependencyFlags = 0; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; std::array attachments = {colorAttachment}; VkRenderPassCreateInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.attachmentCount = attachments.size(); renderPassInfo.pAttachments = attachments.data(); renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; vkCreateRenderPass(m_device->device, &renderPassInfo, nullptr, &m_renderPass); ImGui::SetCurrentContext(ctx); m_imGuiPass = new ImGuiPass(*this); if (qgetenv("NOVUS_USE_NEW_RENDERER") == QByteArrayLiteral("1")) { m_renderer = new GameRenderer(*m_device, m_data); } else { m_renderer = new SimpleRenderer(*m_device); } m_renderer->resize(); initBlitPipeline(); // this creates a desc set for the renderer's offscreen texture. need to make sure we regen it m_framebuffers.resize(m_device->swapChain->swapchainImages.size()); for (int i = 0; i < m_device->swapChain->swapchainImages.size(); i++) { VkFramebufferCreateInfo framebufferInfo = {}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = m_renderPass; framebufferInfo.attachmentCount = 1; framebufferInfo.pAttachments = &m_device->swapChain->swapchainViews[i]; framebufferInfo.width = m_device->swapChain->extent.width; framebufferInfo.height = m_device->swapChain->extent.height; framebufferInfo.layers = 1; vkCreateFramebuffer(m_device->device, &framebufferInfo, nullptr, &m_framebuffers[i]); } return true; } void RenderManager::resize(VkSurfaceKHR surface, int width, int height) { initSwapchain(surface, width, height); } void RenderManager::destroySwapchain() { if (m_device->swapChain->swapchain != VK_NULL_HANDLE) { vkDestroySwapchainKHR(m_device->device, m_device->swapChain->swapchain, nullptr); m_device->swapChain->swapchain = VK_NULL_HANDLE; } } void RenderManager::render(const std::vector &models) { vkWaitForFences(m_device->device, 1, &m_device->swapChain->inFlightFences[m_device->swapChain->currentFrame], VK_TRUE, std::numeric_limits::max()); uint32_t imageIndex = 0; VkResult result = vkAcquireNextImageKHR(m_device->device, m_device->swapChain->swapchain, std::numeric_limits::max(), m_device->swapChain->imageAvailableSemaphores[m_device->swapChain->currentFrame], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { return; } VkCommandBuffer commandBuffer = m_commandBuffers[m_device->swapChain->currentFrame]; VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; vkBeginCommandBuffer(commandBuffer, &beginInfo); updateCamera(camera); m_renderer->render(commandBuffer, m_device->swapChain->currentFrame, camera, models); VkRenderPassBeginInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = m_renderPass; renderPassInfo.framebuffer = m_framebuffers[imageIndex]; std::array clearValues = {}; clearValues[0].color.float32[0] = 0.24; clearValues[0].color.float32[1] = 0.24; clearValues[0].color.float32[2] = 0.24; clearValues[0].color.float32[3] = 1.0; clearValues[1].depthStencil = {1.0f, 0}; renderPassInfo.clearValueCount = clearValues.size(); renderPassInfo.pClearValues = clearValues.data(); renderPassInfo.renderArea.extent = m_device->swapChain->extent; vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descriptorSet, 0, nullptr); vkCmdDraw(commandBuffer, 4, 1, 0, 0); // Render offscreen texture, and overlay imgui if (m_imGuiPass != nullptr) { ImGui::SetCurrentContext(ctx); m_imGuiPass->render(commandBuffer); } vkCmdEndRenderPass(commandBuffer); vkEndCommandBuffer(commandBuffer); VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore waitSemaphores[] = {m_device->swapChain->imageAvailableSemaphores[m_device->swapChain->currentFrame]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; VkSemaphore signalSemaphores[] = {m_device->swapChain->renderFinishedSemaphores[m_device->swapChain->currentFrame]}; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; vkResetFences(m_device->device, 1, &m_device->swapChain->inFlightFences[m_device->swapChain->currentFrame]); if (vkQueueSubmit(m_device->graphicsQueue, 1, &submitInfo, m_device->swapChain->inFlightFences[m_device->swapChain->currentFrame]) != VK_SUCCESS) return; // present VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; VkSwapchainKHR swapChains[] = {m_device->swapChain->swapchain}; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &imageIndex; vkQueuePresentKHR(m_device->presentQueue, &presentInfo); m_device->swapChain->currentFrame = (m_device->swapChain->currentFrame + 1) % 3; } VkRenderPass RenderManager::presentationRenderPass() const { return m_renderPass; } DrawObject RenderManager::addDrawObject(const physis_MDL &model, int lod) { DrawObject DrawObject; DrawObject.model = model; reloadDrawObject(DrawObject, lod); return DrawObject; } void RenderManager::reloadDrawObject(DrawObject &DrawObject, uint32_t lod) { if (lod > DrawObject.model.num_lod) return; DrawObject.parts.clear(); for (uint32_t i = 0; i < DrawObject.model.lods[lod].num_parts; i++) { RenderPart renderPart; const physis_Part part = DrawObject.model.lods[lod].parts[i]; renderPart.materialIndex = part.material_index; size_t vertexSize = part.num_vertices * sizeof(Vertex); renderPart.vertexBuffer = m_device->createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); m_device->copyToBuffer(renderPart.vertexBuffer, (void *)part.vertices, vertexSize); size_t indexSize = part.num_indices * sizeof(uint16_t); renderPart.indexBuffer = m_device->createBuffer(indexSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); m_device->copyToBuffer(renderPart.indexBuffer, (void *)part.indices, indexSize); renderPart.numIndices = part.num_indices; DrawObject.parts.push_back(renderPart); } const size_t bufferSize = sizeof(glm::mat4) * 128; DrawObject.boneInfoBuffer = m_device->createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); } RenderTexture RenderManager::addTexture(const uint32_t width, const uint32_t height, const uint8_t *data, const uint32_t data_size) { RenderTexture newTexture = {}; VkImageCreateInfo imageInfo = {}; imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageInfo.imageType = VK_IMAGE_TYPE_2D; imageInfo.extent.width = width; imageInfo.extent.height = height; imageInfo.extent.depth = 1; imageInfo.mipLevels = 1; imageInfo.arrayLayers = 1; imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; vkCreateImage(m_device->device, &imageInfo, nullptr, &newTexture.handle); VkMemoryRequirements memRequirements; vkGetImageMemoryRequirements(m_device->device, newTexture.handle, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = m_device->findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(m_device->device, &allocInfo, nullptr, &newTexture.memory); vkBindImageMemory(m_device->device, newTexture.handle, newTexture.memory, 0); // copy image data VkBuffer stagingBuffer; VkDeviceMemory stagingBufferMemory; VkBufferCreateInfo bufferInfo = {}; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.size = data_size; bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkCreateBuffer(m_device->device, &bufferInfo, nullptr, &stagingBuffer); // allocate staging memory vkGetBufferMemoryRequirements(m_device->device, stagingBuffer, &memRequirements); allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = m_device->findMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); vkAllocateMemory(m_device->device, &allocInfo, nullptr, &stagingBufferMemory); vkBindBufferMemory(m_device->device, stagingBuffer, stagingBufferMemory, 0); // copy to staging buffer void *mapped_data; vkMapMemory(m_device->device, stagingBufferMemory, 0, data_size, 0, &mapped_data); memcpy(mapped_data, data, data_size); vkUnmapMemory(m_device->device, stagingBufferMemory); // copy staging buffer to image VkCommandBuffer commandBuffer = m_device->beginSingleTimeCommands(); VkImageSubresourceRange range = {}; range.baseMipLevel = 0; range.levelCount = 1; range.baseArrayLayer = 0; range.layerCount = 1; m_device->inlineTransitionImageLayout(commandBuffer, newTexture.handle, imageInfo.format, VK_IMAGE_ASPECT_COLOR_BIT, range, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageExtent = {(uint32_t)width, (uint32_t)height, 1}; vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, newTexture.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); m_device->inlineTransitionImageLayout(commandBuffer, newTexture.handle, imageInfo.format, VK_IMAGE_ASPECT_COLOR_BIT, range, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); m_device->endSingleTimeCommands(commandBuffer); range = {}; range.levelCount = 1; range.layerCount = 1; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = newTexture.handle; viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; viewInfo.format = imageInfo.format; viewInfo.subresourceRange = range; vkCreateImageView(m_device->device, &viewInfo, nullptr, &newTexture.view); VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.maxLod = 1.0f; vkCreateSampler(m_device->device, &samplerInfo, nullptr, &newTexture.sampler); return newTexture; } Device &RenderManager::device() { return *m_device; } void RenderManager::updateCamera(Camera &camera) { camera.aspectRatio = static_cast(m_device->swapChain->extent.width) / static_cast(m_device->swapChain->extent.height); camera.perspective = glm::perspective(glm::radians(camera.fieldOfView), camera.aspectRatio, camera.nearPlane, camera.farPlane); } void RenderManager::initBlitPipeline() { VkDescriptorSetLayoutBinding binding = {}; binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binding.descriptorCount = 1; binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; binding.binding = 0; VkDescriptorSetLayoutCreateInfo layoutInfo = {}; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.bindingCount = 1; layoutInfo.pBindings = &binding; vkCreateDescriptorSetLayout(m_device->device, &layoutInfo, nullptr, &m_setLayout); VkPipelineShaderStageCreateInfo vertexShaderStageInfo = {}; vertexShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; vertexShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; vertexShaderStageInfo.module = m_device->loadShaderFromDisk(":/shaders/blit.vert.spv"); vertexShaderStageInfo.pName = "main"; VkPipelineShaderStageCreateInfo fragmentShaderStageInfo = {}; fragmentShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; fragmentShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; fragmentShaderStageInfo.module = m_device->loadShaderFromDisk(":/shaders/blit.frag.spv"); fragmentShaderStageInfo.pName = "main"; std::array shaderStages = {vertexShaderStageInfo, fragmentShaderStageInfo}; VkPipelineVertexInputStateCreateInfo vertexInputState = {}; vertexInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkViewport viewport = {}; viewport.width = m_device->swapChain->extent.width; viewport.height = m_device->swapChain->extent.height; viewport.maxDepth = 1.0f; VkRect2D scissor = {}; scissor.extent = m_device->swapChain->extent; VkPipelineViewportStateCreateInfo viewportState = {}; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount = 1; viewportState.pViewports = &viewport; viewportState.scissorCount = 1; viewportState.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer = {}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.lineWidth = 1.0f; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; VkPipelineMultisampleStateCreateInfo multisampling = {}; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo colorBlending = {}; colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; VkPipelineDynamicStateCreateInfo dynamicState = {}; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; pipelineLayoutInfo.pSetLayouts = &m_setLayout; vkCreatePipelineLayout(m_device->device, &pipelineLayoutInfo, nullptr, &m_pipelineLayout); VkPipelineDepthStencilStateCreateInfo depthStencil = {}; depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; depthStencil.depthTestEnable = VK_TRUE; depthStencil.depthWriteEnable = VK_TRUE; depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; depthStencil.maxDepthBounds = 1.0f; VkGraphicsPipelineCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; createInfo.stageCount = shaderStages.size(); createInfo.pStages = shaderStages.data(); createInfo.pVertexInputState = &vertexInputState; createInfo.pInputAssemblyState = &inputAssembly; createInfo.pViewportState = &viewportState; createInfo.pRasterizationState = &rasterizer; createInfo.pMultisampleState = &multisampling; createInfo.pColorBlendState = &colorBlending; createInfo.pDynamicState = &dynamicState; createInfo.pDepthStencilState = &depthStencil; createInfo.layout = m_pipelineLayout; createInfo.renderPass = m_renderPass; vkCreateGraphicsPipelines(m_device->device, VK_NULL_HANDLE, 1, &createInfo, nullptr, &m_pipeline); VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.maxLod = 1.0f; vkCreateSampler(m_device->device, &samplerInfo, nullptr, &m_sampler); VkDescriptorSetAllocateInfo allocateInfo = {}; allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocateInfo.descriptorPool = m_device->descriptorPool; allocateInfo.descriptorSetCount = 1; allocateInfo.pSetLayouts = &m_setLayout; vkAllocateDescriptorSets(m_device->device, &allocateInfo, &m_descriptorSet); VkDescriptorImageInfo multiImageInfo = {}; multiImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; multiImageInfo.imageView = m_renderer->getCompositeTexture().imageView; multiImageInfo.sampler = m_sampler; VkWriteDescriptorSet multiDescriptorWrite2 = {}; multiDescriptorWrite2.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; multiDescriptorWrite2.dstSet = m_descriptorSet; multiDescriptorWrite2.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; multiDescriptorWrite2.descriptorCount = 1; multiDescriptorWrite2.pImageInfo = &multiImageInfo; multiDescriptorWrite2.dstBinding = 0; vkUpdateDescriptorSets(m_device->device, 1, &multiDescriptorWrite2, 0, nullptr); }