#include "vkn/vulkan_api.h" #include "vkn/vulkan_window.h" #include "vkn/wrapper/buffer.h" #include "vkn/wrapper/device.h" #include "vkn/thread/buffer_worker.h" #include "vkn/thread/command_worker.h" #include "render/asset/mesh.h" #include "meta/enum.h" namespace vkn { inline bool operator==(const RenderPassKey& k1, const RenderPassKey& k2) { if (k1.initialColorLayoutMask != k2.initialColorLayoutMask) return false; for (int i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (k1.colorFormat[i] != k2.colorFormat[i]) return false; } if (k1.depthFormat != k2.depthFormat) return false; if (k1.clear != k2.clear) return false; if (k1.discardStart != k2.discardStart) return false; if (k1.discardEnd != k2.discardEnd) return false; if (k1.samples != k2.samples) return false; if (k1.needsResolveMask != k2.needsResolveMask) return false; if (k1.subpassMask != k2.subpassMask) return false; return true; } VulkanAPI::VulkanAPI() : RenderAPI(new VulkanContext()) , window(*VulkanWindow::Ptr()) , backend(VulkanEngineName) { } void VulkanAPI::Init() { } void VulkanAPI::Shutdown() { } void VulkanAPI::SetStaticMesh(Mesh& mesh) { auto& Indices = mesh.GetIndices(); auto& Vertices = mesh.GetVertices(); MeshVAO& VAO = MeshTable[mesh.GetGuid()]; VAO.indexCount = Indices.size(); VAO.vertexCount = Vertices.size(); Buffer indexBuffer{}; indexBuffer.ppBuffer = &VAO.indexBuffer; indexBuffer.pCpuData = Indices.data(); indexBuffer.size = sizeof(decltype(Indices[0])) * Indices.size(); indexBuffer.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; Backend::TransferWorker->Invoke(indexBuffer); Buffer vertexBuffer{}; vertexBuffer.ppBuffer = &VAO.vertexBuffer; vertexBuffer.pCpuData = Vertices.data(); vertexBuffer.size = Vertices.data_size(); vertexBuffer.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; Backend::TransferWorker->Invoke(vertexBuffer); } void VulkanAPI::DrawStaticMesh(Mesh& mesh) { } void VulkanAPI::LoadShader(Shader& shader) { } void VulkanAPI::BeginFrame() { window.Aquire(*(VulkanContext*)&context); } void VulkanAPI::EndFrame() { window.Present(*(VulkanContext*)&context); } VkRenderPass VulkanAPI::GetRenderPass(RenderPassKey config) { auto it = RenderPassCache.find(config); if (it != RenderPassCache.end()) { return it->second; } // Set up some const aliases for terseness. const VkAttachmentLoadOp kClear = VK_ATTACHMENT_LOAD_OP_CLEAR; const VkAttachmentLoadOp kDontCare = VK_ATTACHMENT_LOAD_OP_DONT_CARE; const VkAttachmentLoadOp kKeep = VK_ATTACHMENT_LOAD_OP_LOAD; const VkAttachmentStoreOp kDisableStore = VK_ATTACHMENT_STORE_OP_DONT_CARE; const VkAttachmentStoreOp kEnableStore = VK_ATTACHMENT_STORE_OP_STORE; VkAttachmentReference inputAttachmentRef[MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference colorAttachmentRefs[2][MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference resolveAttachmentRef[MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; VkAttachmentReference depthAttachmentRef = {}; const bool hasSubpasses = config.subpassMask != 0; const bool hasDepth = config.depthFormat != VK_FORMAT_UNDEFINED; VkSubpassDescription subpasses[2] = { { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pInputAttachments = nullptr, .pColorAttachments = colorAttachmentRefs[0], .pResolveAttachments = resolveAttachmentRef, .pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr }, { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .pInputAttachments = inputAttachmentRef, .pColorAttachments = colorAttachmentRefs[1], .pResolveAttachments = resolveAttachmentRef, .pDepthStencilAttachment = hasDepth ? &depthAttachmentRef : nullptr } }; // The attachment list contains: Color Attachments, Resolve Attachments, and Depth Attachment. // For simplicity, create an array that can hold the maximum possible number of attachments. // Note that this needs to have the same ordering as the corollary array in getFramebuffer. VkAttachmentDescription attachments[MAX_SUPPORTED_RENDER_TARGET_COUNT + MAX_SUPPORTED_RENDER_TARGET_COUNT + 1] = {}; // We support 2 subpasses, which means we need to supply 1 dependency struct. VkSubpassDependency dependencies[1] = { { .srcSubpass = 0, .dstSubpass = 1, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, } }; // Finally, create the VkRenderPass. VkRenderPassCreateInfo renderPassInfo{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 0u, .pAttachments = attachments, .subpassCount = hasSubpasses ? 2u : 1u, .pSubpasses = subpasses, .dependencyCount = hasSubpasses ? 1u : 0u, .pDependencies = dependencies }; int attachmentIndex = 0; // Populate the Color Attachments. for (int i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) { continue; } const VkImageLayout subpassLayout = VK_IMAGE_LAYOUT_GENERAL; uint32_t index; if (!hasSubpasses) { index = subpasses[0].colorAttachmentCount++; colorAttachmentRefs[0][index].layout = subpassLayout; colorAttachmentRefs[0][index].attachment = attachmentIndex; } else { // The Driver API consolidates all color attachments from the first and second subpasses // into a single list, and uses a bitmask to mark attachments that belong only to the // second subpass and should be available as inputs. All color attachments in the first // subpass are automatically made available to the second subpass. // If there are subpasses, we require the input attachment to be the first attachment. // Breaking this assumption would likely require enhancements to the Driver API in order // to supply Vulkan with all the information needed. if (config.subpassMask & (1 << i)) { index = subpasses[0].colorAttachmentCount++; colorAttachmentRefs[0][index].layout = subpassLayout; colorAttachmentRefs[0][index].attachment = attachmentIndex; index = subpasses[1].inputAttachmentCount++; inputAttachmentRef[index].layout = subpassLayout; inputAttachmentRef[index].attachment = attachmentIndex; } index = subpasses[1].colorAttachmentCount++; colorAttachmentRefs[1][index].layout = subpassLayout; colorAttachmentRefs[1][index].attachment = attachmentIndex; } const TargetBufferFlags flag = TargetBufferFlags(int(TargetBufferFlags::COLOR0) << i); const bool clear = any(config.clear & flag); const bool discard = any(config.discardStart & flag); attachments[attachmentIndex++] = { .format = config.colorFormat[i], .samples = config.samples, .loadOp = clear ? kClear : (discard ? kDontCare : kKeep), .storeOp = kEnableStore, .stencilLoadOp = kDontCare, .stencilStoreOp = kDisableStore, .initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; } // Nulling out the zero-sized lists is necessary to avoid VK_ERROR_OUT_OF_HOST_MEMORY on Adreno. if (subpasses[0].colorAttachmentCount == 0) { subpasses[0].pColorAttachments = nullptr; subpasses[0].pResolveAttachments = nullptr; subpasses[1].pColorAttachments = nullptr; subpasses[1].pResolveAttachments = nullptr; } // Populate the Resolve Attachments. VkAttachmentReference* pResolveAttachment = resolveAttachmentRef; for (int i = 0; i < MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (config.colorFormat[i] == VK_FORMAT_UNDEFINED) { continue; } if (!(config.needsResolveMask & (1 << i))) { pResolveAttachment->attachment = VK_ATTACHMENT_UNUSED; ++pResolveAttachment; continue; } pResolveAttachment->attachment = attachmentIndex; pResolveAttachment->layout = VK_IMAGE_LAYOUT_GENERAL; ++pResolveAttachment; attachments[attachmentIndex++] = { .format = config.colorFormat[i], .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = kDontCare, .storeOp = kEnableStore, .stencilLoadOp = kDontCare, .stencilStoreOp = kDisableStore, .initialLayout = VK_IMAGE_LAYOUT_GENERAL, .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; } // Populate the Depth Attachment. if (hasDepth) { const bool clear = any(config.clear & TargetBufferFlags::DEPTH); const bool discardStart = any(config.discardStart & TargetBufferFlags::DEPTH); const bool discardEnd = any(config.discardEnd & TargetBufferFlags::DEPTH); depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; depthAttachmentRef.attachment = attachmentIndex; attachments[attachmentIndex++] = { .format = config.depthFormat, .samples = (VkSampleCountFlagBits)config.samples, .loadOp = clear ? kClear : (discardStart ? kDontCare : kKeep), .storeOp = discardEnd ? kDisableStore : kEnableStore, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, }; } renderPassInfo.attachmentCount = attachmentIndex; VkRenderPass renderPass; VkResult error = vkCreateRenderPass(backend.GetDevice().Ptr(), &renderPassInfo, nullptr, &renderPass); RenderPassCache.emplace(config, renderPass); return renderPass; } }