#include #include #include #include #include #include #include #include #include #include "volk.h" #include "spdlog/spdlog.h" MeshPipeline::MeshPipeline() = default; MeshPipeline::~MeshPipeline() = default; void MeshPipeline::init( GfxDevice& gfxDevice, RenderResources& resources, VkFormat drawImageFormat, VkFormat depthImageFormat, VkFormat selectionImageFormat) { try { const auto vertexShader = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/mesh.vert"); const auto fragShader = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/mesh.frag"); const auto selectionShader = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/selection.frag"); const auto bufferRange = VkPushConstantRange{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .offset = 0, .size = sizeof(PushConstants), }; const auto pushConstantRanges = std::array{bufferRange}; const auto layouts = std::array{ resources.getBindlessDescSetLayout() }; VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = static_cast(layouts.size()); pipelineLayoutInfo.pSetLayouts = layouts.data(); pipelineLayoutInfo.pushConstantRangeCount = static_cast(pushConstantRanges.size()); pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data(); if (vkCreatePipelineLayout( gfxDevice.getDevice(), &pipelineLayoutInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("Could not create mesh pipeline layout"); } { PipelineConfigInfo pipelineConfig{}; Pipeline::DefaultPipelineConfigInfo(pipelineConfig); pipelineConfig.name = "Mesh Depth Prepass Pipeline"; pipelineConfig.pipelineLayout = m_pipelineLayout; pipelineConfig.vertexAttributeDescriptions = {}; pipelineConfig.vertexBindingDescriptions = {}; pipelineConfig.colorAttachments = {}; pipelineConfig.colorBlendInfo.attachmentCount = 0; pipelineConfig.depthAttachment = depthImageFormat; m_depthPrepassPipeline = std::make_unique( gfxDevice, vertexShader.string(), "", pipelineConfig ); } { PipelineConfigInfo pipelineConfig{}; Pipeline::DefaultPipelineConfigInfo(pipelineConfig); pipelineConfig.name = "Mesh Selection Prepass Pipeline"; pipelineConfig.pipelineLayout = m_pipelineLayout; pipelineConfig.vertexAttributeDescriptions = {}; pipelineConfig.vertexBindingDescriptions = {}; pipelineConfig.colorAttachments = {selectionImageFormat}; pipelineConfig.depthAttachment = depthImageFormat; m_selectionPrepassPipeline = std::make_unique( gfxDevice, vertexShader.string(), selectionShader.string(), pipelineConfig ); } { PipelineConfigInfo pipelineConfig{}; Pipeline::DefaultPipelineConfigInfo(pipelineConfig); pipelineConfig.name = "Mesh Pipeline"; pipelineConfig.pipelineLayout = m_pipelineLayout; pipelineConfig.vertexAttributeDescriptions = {}; pipelineConfig.vertexBindingDescriptions = {}; pipelineConfig.colorAttachments = {drawImageFormat}; pipelineConfig.depthAttachment = depthImageFormat; // depth was already written by the pre-pass, only shade the visible surface pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE; pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; m_pipeline = std::make_unique( gfxDevice, vertexShader.string(), fragShader.string(), pipelineConfig ); } } catch (...) { cleanup(gfxDevice.getDevice()); throw; } } void MeshPipeline::drawDepthPrepass( VkCommandBuffer cmd, VkExtent2D renderExtent, const RenderResources& resources, const GPUBuffer& sceneDataBuffer, const IndirectDrawList& drawList, const IndirectDrawList::View& view, bool writeObjectIds) { const auto& pipeline = writeObjectIds ? m_selectionPrepassPipeline : m_depthPrepassPipeline; if (!pipeline) { return; } drawMeshes( cmd, *pipeline, renderExtent, resources, sceneDataBuffer, drawList, view); } void MeshPipeline::draw( VkCommandBuffer cmd, VkExtent2D renderExtent, const RenderResources& resources, const GPUBuffer& sceneDataBuffer, const IndirectDrawList& drawList, const IndirectDrawList::View& view) { if (!m_pipeline) { return; } drawMeshes( cmd, *m_pipeline, renderExtent, resources, sceneDataBuffer, drawList, view); } void MeshPipeline::drawMeshes( VkCommandBuffer cmd, const Pipeline& pipeline, VkExtent2D renderExtent, const RenderResources& resources, const GPUBuffer& sceneDataBuffer, const IndirectDrawList& drawList, const IndirectDrawList::View& view) { pipeline.bind(cmd); resources.bindBindlessDescSet(cmd, m_pipelineLayout); const auto viewport = VkViewport{ .x = 0.f, .y = 0.f, .width = static_cast(renderExtent.width), .height = static_cast(renderExtent.height), .minDepth = 0.f, .maxDepth = 1.f, }; vkCmdSetViewport(cmd, 0, 1, &viewport); const auto scissor = VkRect2D{ .offset = {}, .extent = renderExtent, }; vkCmdSetScissor(cmd, 0, 1, &scissor); vkCmdSetPolygonModeEXT( cmd, m_renderWireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL ); // The same for every draw, per-draw data (transform, material, vertex buffer) // lives in the instance buffer and is looked up with gl_InstanceIndex const auto pushConstants = PushConstants{ .sceneDataBuffer = sceneDataBuffer.address, .instanceBuffer = drawList.getInstanceBufferAddress(), }; vkCmdPushConstants( cmd, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstants), &pushConstants ); vkutil::cmdBeginLabelFormatted( cmd, vkutil::labelColors::draw, "%u indirect draws, %u instances", view.commandCount, view.instanceCount); drawList.draw(cmd, resources.meshes().getIndexBuffer(), view); vkutil::cmdEndLabel(cmd); } void MeshPipeline::cleanup(VkDevice device) { m_pipeline.reset(); m_depthPrepassPipeline.reset(); m_selectionPrepassPipeline.reset(); if (m_pipelineLayout != VK_NULL_HANDLE && device != VK_NULL_HANDLE) { vkDestroyPipelineLayout(device, m_pipelineLayout, nullptr); } m_pipelineLayout = VK_NULL_HANDLE; }