/* * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION * SPDX-License-Identifier: Apache-2.0 */ #pragma once #include #include #include #include #include namespace nvvk { //-------------------------------------------------------------------------------------------------- /** # functions in nvvk - nvprintPipelineStats : prints stats of the pipeline using VK_KHR_pipeline_executable_properties (don't forget to enable extension and set VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) - dumpPipelineStats : dumps stats of the pipeline using VK_KHR_pipeline_executable_properties to a text file (don't forget to enable extension and set VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) - dumpPipelineBinCodes : dumps shader binaries using VK_KHR_pipeline_executable_properties to multiple binary files (don't forget to enable extension and set VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR) */ // nvprints stats to LOGLEVEL_STATS stream void nvprintPipelineStats(VkDevice device, VkPipeline pipeline, const char* name, bool verbose = false); // writes stats into single file void dumpPipelineStats(VkDevice device, VkPipeline pipeline, const char* fileName); // creates multiple files, one for each pipe executable and representation. // The baseFilename will get appended along the lines of ".some details.bin" void dumpPipelineInternals(VkDevice device, VkPipeline pipeline, const char* baseFileName); //-------------------------------------------------------------------------------------------------- /** \struct nvvk::GraphicsPipelineState Most graphic pipelines have similar states, therefore the helper `GraphicsPipelineStage` holds all the elements and initialize the structures with the proper default values, such as the primitive type, `PipelineColorBlendAttachmentState` with their mask, `DynamicState` for viewport and scissor, adjust depth test if enabled, line width to 1 pixel, for example. Example of usage : \code{.cpp} nvvk::GraphicsPipelineState pipelineState(); pipelineState.depthStencilState.setDepthTestEnable(true); pipelineState.rasterizationState.setCullMode(vk::CullModeFlagBits::eNone); pipelineState.addBindingDescription({0, sizeof(Vertex)}); pipelineState.addAttributeDescriptions ({ {0, 0, vk::Format::eR32G32B32Sfloat, static_cast(offsetof(Vertex, pos))}, {1, 0, vk::Format::eR32G32B32Sfloat, static_cast(offsetof(Vertex, nrm))}, {2, 0, vk::Format::eR32G32B32Sfloat, static_cast(offsetof(Vertex, col))}}); \endcode */ struct GraphicsPipelineState { // Initialize the state to common values: triangle list topology, depth test enabled, // dynamic viewport and scissor, one render target, blending disabled GraphicsPipelineState() { rasterizationState.flags = {}; rasterizationState.depthClampEnable = {}; rasterizationState.rasterizerDiscardEnable = {}; setValue(rasterizationState.polygonMode, VK_POLYGON_MODE_FILL); setValue(rasterizationState.cullMode, VK_CULL_MODE_BACK_BIT); setValue(rasterizationState.frontFace, VK_FRONT_FACE_COUNTER_CLOCKWISE); rasterizationState.depthBiasEnable = {}; rasterizationState.depthBiasConstantFactor = {}; rasterizationState.depthBiasClamp = {}; rasterizationState.depthBiasSlopeFactor = {}; rasterizationState.lineWidth = 1.f; inputAssemblyState.flags = {}; setValue(inputAssemblyState.topology, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); inputAssemblyState.primitiveRestartEnable = {}; colorBlendState.flags = {}; colorBlendState.logicOpEnable = {}; setValue(colorBlendState.logicOp, VK_LOGIC_OP_CLEAR); colorBlendState.attachmentCount = {}; colorBlendState.pAttachments = {}; for(int i = 0; i < 4; i++) { colorBlendState.blendConstants[i] = 0.f; } dynamicState.flags = {}; dynamicState.dynamicStateCount = {}; dynamicState.pDynamicStates = {}; vertexInputState.flags = {}; vertexInputState.vertexBindingDescriptionCount = {}; vertexInputState.pVertexBindingDescriptions = {}; vertexInputState.vertexAttributeDescriptionCount = {}; vertexInputState.pVertexAttributeDescriptions = {}; viewportState.flags = {}; viewportState.viewportCount = {}; viewportState.pViewports = {}; viewportState.scissorCount = {}; viewportState.pScissors = {}; depthStencilState.flags = {}; depthStencilState.depthTestEnable = VK_TRUE; depthStencilState.depthWriteEnable = VK_TRUE; setValue(depthStencilState.depthCompareOp, VK_COMPARE_OP_LESS_OR_EQUAL); depthStencilState.depthBoundsTestEnable = {}; depthStencilState.stencilTestEnable = {}; setValue(depthStencilState.front, VkStencilOpState()); setValue(depthStencilState.back, VkStencilOpState()); depthStencilState.minDepthBounds = {}; depthStencilState.maxDepthBounds = {}; setValue(multisampleState.rasterizationSamples, VK_SAMPLE_COUNT_1_BIT); } GraphicsPipelineState(const GraphicsPipelineState& src) = default; // Attach the pointer values of the structures to the internal arrays void update() { colorBlendState.attachmentCount = (uint32_t)blendAttachmentStates.size(); colorBlendState.pAttachments = blendAttachmentStates.data(); dynamicState.dynamicStateCount = (uint32_t)dynamicStateEnables.size(); dynamicState.pDynamicStates = dynamicStateEnables.data(); vertexInputState.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); vertexInputState.vertexBindingDescriptionCount = static_cast(bindingDescriptions.size()); vertexInputState.pVertexBindingDescriptions = bindingDescriptions.data(); vertexInputState.pVertexAttributeDescriptions = attributeDescriptions.data(); if(viewports.empty()) { viewportState.viewportCount = 1; viewportState.pViewports = nullptr; } else { viewportState.viewportCount = (uint32_t)viewports.size(); viewportState.pViewports = viewports.data(); } if(scissors.empty()) { viewportState.scissorCount = 1; viewportState.pScissors = nullptr; } else { viewportState.scissorCount = (uint32_t)scissors.size(); viewportState.pScissors = scissors.data(); } } static inline VkPipelineColorBlendAttachmentState makePipelineColorBlendAttachmentState( VkColorComponentFlags colorWriteMask_ = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, VkBool32 blendEnable_ = 0, VkBlendFactor srcColorBlendFactor_ = VK_BLEND_FACTOR_ZERO, VkBlendFactor dstColorBlendFactor_ = VK_BLEND_FACTOR_ZERO, VkBlendOp colorBlendOp_ = VK_BLEND_OP_ADD, VkBlendFactor srcAlphaBlendFactor_ = VK_BLEND_FACTOR_ZERO, VkBlendFactor dstAlphaBlendFactor_ = VK_BLEND_FACTOR_ZERO, VkBlendOp alphaBlendOp_ = VK_BLEND_OP_ADD) { VkPipelineColorBlendAttachmentState res; res.blendEnable = blendEnable_; res.srcColorBlendFactor = srcColorBlendFactor_; res.dstColorBlendFactor = dstColorBlendFactor_; res.colorBlendOp = colorBlendOp_; res.srcAlphaBlendFactor = srcAlphaBlendFactor_; res.dstAlphaBlendFactor = dstAlphaBlendFactor_; res.alphaBlendOp = alphaBlendOp_; res.colorWriteMask = colorWriteMask_; return res; } static inline VkVertexInputBindingDescription makeVertexInputBinding(uint32_t binding, uint32_t stride, VkVertexInputRate rate = VK_VERTEX_INPUT_RATE_VERTEX) { VkVertexInputBindingDescription vertexBinding; vertexBinding.binding = binding; vertexBinding.inputRate = rate; vertexBinding.stride = stride; return vertexBinding; } static inline VkVertexInputAttributeDescription makeVertexInputAttribute(uint32_t location, uint32_t binding, VkFormat format, uint32_t offset) { VkVertexInputAttributeDescription attrib; attrib.binding = binding; attrib.location = location; attrib.format = format; attrib.offset = offset; return attrib; } void clearBlendAttachmentStates() { blendAttachmentStates.clear(); } void setBlendAttachmentCount(uint32_t attachmentCount) { blendAttachmentStates.resize(attachmentCount); } void setBlendAttachmentState(uint32_t attachment, const VkPipelineColorBlendAttachmentState& blendState) { assert(attachment < blendAttachmentStates.size()); if(attachment <= blendAttachmentStates.size()) { blendAttachmentStates[attachment] = blendState; } } uint32_t addBlendAttachmentState(const VkPipelineColorBlendAttachmentState& blendState) { blendAttachmentStates.push_back(blendState); return (uint32_t)(blendAttachmentStates.size() - 1); } void clearDynamicStateEnables() { dynamicStateEnables.clear(); } void setDynamicStateEnablesCount(uint32_t dynamicStateCount) { dynamicStateEnables.resize(dynamicStateCount); } void setDynamicStateEnable(uint32_t state, VkDynamicState dynamicState) { assert(state < dynamicStateEnables.size()); if(state <= dynamicStateEnables.size()) { dynamicStateEnables[state] = dynamicState; } } uint32_t addDynamicStateEnable(VkDynamicState dynamicState) { dynamicStateEnables.push_back(dynamicState); return (uint32_t)(dynamicStateEnables.size() - 1); } void clearBindingDescriptions() { bindingDescriptions.clear(); } void setBindingDescriptionsCount(uint32_t bindingDescriptionCount) { bindingDescriptions.resize(bindingDescriptionCount); } void setBindingDescription(uint32_t binding, VkVertexInputBindingDescription bindingDescription) { assert(binding < bindingDescriptions.size()); if(binding <= bindingDescriptions.size()) { bindingDescriptions[binding] = bindingDescription; } } uint32_t addBindingDescription(const VkVertexInputBindingDescription& bindingDescription) { bindingDescriptions.push_back(bindingDescription); return (uint32_t)(bindingDescriptions.size() - 1); } void addBindingDescriptions(const std::vector& bindingDescriptions_) { bindingDescriptions.insert(bindingDescriptions.end(), bindingDescriptions_.begin(), bindingDescriptions_.end()); } void clearAttributeDescriptions() { attributeDescriptions.clear(); } void setAttributeDescriptionsCount(uint32_t attributeDescriptionCount) { attributeDescriptions.resize(attributeDescriptionCount); } void setAttributeDescription(uint32_t attribute, const VkVertexInputAttributeDescription &attributeDescription) { assert(attribute < attributeDescriptions.size()); if(attribute <= attributeDescriptions.size()) { attributeDescriptions[attribute] = attributeDescription; } } uint32_t addAttributeDescription(const VkVertexInputAttributeDescription &attributeDescription) { attributeDescriptions.push_back(attributeDescription); return (uint32_t)(attributeDescriptions.size() - 1); } void addAttributeDescriptions(const std::vector& attributeDescriptions_) { attributeDescriptions.insert(attributeDescriptions.end(), attributeDescriptions_.begin(), attributeDescriptions_.end()); } void clearViewports() { viewports.clear(); } void setViewportsCount(uint32_t viewportCount) { viewports.resize(viewportCount); } void setViewport(uint32_t attribute, VkViewport viewport) { assert(attribute < viewports.size()); if(attribute <= viewports.size()) { viewports[attribute] = viewport; } } uint32_t addViewport(VkViewport viewport) { viewports.push_back(viewport); return (uint32_t)(viewports.size() - 1); } void clearScissors() { scissors.clear(); } void setScissorsCount(uint32_t scissorCount) { scissors.resize(scissorCount); } void setScissor(uint32_t attribute, VkRect2D scissor) { assert(attribute < scissors.size()); if(attribute <= scissors.size()) { scissors[attribute] = scissor; } } uint32_t addScissor(VkRect2D scissor) { scissors.push_back(scissor); return (uint32_t)(scissors.size() - 1); } VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; VkPipelineRasterizationStateCreateInfo rasterizationState{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; VkPipelineMultisampleStateCreateInfo multisampleState{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; VkPipelineDepthStencilStateCreateInfo depthStencilState{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; VkPipelineViewportStateCreateInfo viewportState{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; VkPipelineDynamicStateCreateInfo dynamicState{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; VkPipelineColorBlendStateCreateInfo colorBlendState{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; VkPipelineVertexInputStateCreateInfo vertexInputState{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; private: std::vector blendAttachmentStates{makePipelineColorBlendAttachmentState()}; std::vector dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; std::vector bindingDescriptions; std::vector attributeDescriptions; std::vector viewports; std::vector scissors; // Helper to set objects for either C and C++ template void setValue(T& target, const U& val) { target = (T)(val); } }; //-------------------------------------------------------------------------------------------------- /** \struct nvvk::GraphicsPipelineGenerator The graphics pipeline generator takes a GraphicsPipelineState object and pipeline-specific information such as the render pass and pipeline layout to generate the final pipeline. Example of usage : \code{.cpp} nvvk::GraphicsPipelineState pipelineState(); ... nvvk::GraphicsPipelineGenerator pipelineGenerator(m_device, m_pipelineLayout, m_renderPass, pipelineState); pipelineGenerator.addShader(readFile("spv/vert_shader.vert.spv"), VkShaderStageFlagBits::eVertex); pipelineGenerator.addShader(readFile("spv/frag_shader.frag.spv"), VkShaderStageFlagBits::eFragment); m_pipeline = pipelineGenerator.createPipeline(); \endcode */ struct GraphicsPipelineGenerator { public: GraphicsPipelineGenerator(GraphicsPipelineState& pipelineState_) : pipelineState(pipelineState_) { init(); } GraphicsPipelineGenerator(const GraphicsPipelineGenerator& src) : createInfo(src.createInfo) , device(src.device) , pipelineCache(src.pipelineCache) , pipelineState(src.pipelineState) { init(); } GraphicsPipelineGenerator(VkDevice device_, const VkPipelineLayout& layout, const VkRenderPass& renderPass, GraphicsPipelineState& pipelineState_) : device(device_) , pipelineState(pipelineState_) { createInfo.layout = layout; createInfo.renderPass = renderPass; init(); } // For VK_KHR_dynamic_rendering using PipelineRenderingCreateInfo = VkPipelineRenderingCreateInfo; GraphicsPipelineGenerator(VkDevice device_, const VkPipelineLayout& layout, const PipelineRenderingCreateInfo& pipelineRenderingCreateInfo, GraphicsPipelineState& pipelineState_) : device(device_) , pipelineState(pipelineState_) { createInfo.layout = layout; setPipelineRenderingCreateInfo(pipelineRenderingCreateInfo); init(); } const GraphicsPipelineGenerator& operator=(const GraphicsPipelineGenerator& src) { device = src.device; pipelineState = src.pipelineState; createInfo = src.createInfo; pipelineCache = src.pipelineCache; init(); return *this; } void setDevice(VkDevice device_) { device = device_; } void setRenderPass(VkRenderPass renderPass) { createInfo.renderPass = renderPass; createInfo.pNext = nullptr; } void setPipelineRenderingCreateInfo(const PipelineRenderingCreateInfo& pipelineRenderingCreateInfo) { // Deep copy assert(pipelineRenderingCreateInfo.pNext == nullptr); // Update deep copy if needed. dynamicRenderingInfo = pipelineRenderingCreateInfo; if(dynamicRenderingInfo.colorAttachmentCount != 0) { dynamicRenderingColorFormats.assign(dynamicRenderingInfo.pColorAttachmentFormats, dynamicRenderingInfo.pColorAttachmentFormats + dynamicRenderingInfo.colorAttachmentCount); dynamicRenderingInfo.pColorAttachmentFormats = dynamicRenderingColorFormats.data(); } // Set VkGraphicsPipelineCreateInfo::pNext to point to deep copy of extension struct. // NB: Will have to change if more than 1 extension struct needs to be supported. createInfo.pNext = &dynamicRenderingInfo; } void setLayout(VkPipelineLayout layout) { createInfo.layout = layout; } ~GraphicsPipelineGenerator() { destroyShaderModules(); } VkPipelineShaderStageCreateInfo& addShader(const std::string& code, VkShaderStageFlagBits stage, const char* entryPoint = "main") { std::vector v; std::copy(code.begin(), code.end(), std::back_inserter(v)); return addShader(v, stage, entryPoint); } template VkPipelineShaderStageCreateInfo& addShader(const std::vector& code, VkShaderStageFlagBits stage, const char* entryPoint = "main") { VkShaderModuleCreateInfo createInfo{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; createInfo.codeSize = sizeof(T) * code.size(); createInfo.pCode = reinterpret_cast(code.data()); VkShaderModule shaderModule; vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule); temporaryModules.push_back(shaderModule); return addShader(shaderModule, stage, entryPoint); } VkPipelineShaderStageCreateInfo& addShader(VkShaderModule shaderModule, VkShaderStageFlagBits stage, const char* entryPoint = "main") { VkPipelineShaderStageCreateInfo shaderStage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO}; shaderStage.stage = (VkShaderStageFlagBits)stage; shaderStage.module = shaderModule; shaderStage.pName = entryPoint; shaderStages.push_back(shaderStage); return shaderStages.back(); } void clearShaders() { shaderStages.clear(); destroyShaderModules(); } VkShaderModule getShaderModule(size_t index) const { if(index < shaderStages.size()) return shaderStages[index].module; return VK_NULL_HANDLE; } VkPipeline createPipeline(const VkPipelineCache& cache) { update(); VkPipeline pipeline; vkCreateGraphicsPipelines(device, cache, 1, (VkGraphicsPipelineCreateInfo*)&createInfo, nullptr, &pipeline); return pipeline; } VkPipeline createPipeline() { return createPipeline(pipelineCache); } void destroyShaderModules() { for(const auto& shaderModule : temporaryModules) { vkDestroyShaderModule(device, shaderModule, nullptr); } temporaryModules.clear(); } void update() { createInfo.stageCount = static_cast(shaderStages.size()); createInfo.pStages = shaderStages.data(); pipelineState.update(); } VkGraphicsPipelineCreateInfo createInfo{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; private: VkDevice device; VkPipelineCache pipelineCache{}; std::vector shaderStages; std::vector temporaryModules; std::vector dynamicRenderingColorFormats; GraphicsPipelineState& pipelineState; PipelineRenderingCreateInfo dynamicRenderingInfo{VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; void init() { createInfo.pRasterizationState = &pipelineState.rasterizationState; createInfo.pInputAssemblyState = &pipelineState.inputAssemblyState; createInfo.pColorBlendState = &pipelineState.colorBlendState; createInfo.pMultisampleState = &pipelineState.multisampleState; createInfo.pViewportState = &pipelineState.viewportState; createInfo.pDepthStencilState = &pipelineState.depthStencilState; createInfo.pDynamicState = &pipelineState.dynamicState; createInfo.pVertexInputState = &pipelineState.vertexInputState; } // Helper to set objects for either C and C++ template void setValue(T& target, const U& val) { target = (T)(val); } }; //-------------------------------------------------------------------------------------------------- /** \class nvvk::GraphicsPipelineGeneratorCombined In some cases the application may have each state associated to a single pipeline. For convenience, nvvk::GraphicsPipelineGeneratorCombined combines both the state and generator into a single object. Example of usage : \code{.cpp} nvvk::GraphicsPipelineGeneratorCombined pipelineGenerator(m_device, m_pipelineLayout, m_renderPass); pipelineGenerator.depthStencilState.setDepthTestEnable(true); pipelineGenerator.rasterizationState.setCullMode(vk::CullModeFlagBits::eNone); pipelineGenerator.addBindingDescription({0, sizeof(Vertex)}); pipelineGenerator.addAttributeDescriptions ({ {0, 0, vk::Format::eR32G32B32Sfloat, static_cast(offsetof(Vertex, pos))}, {1, 0, vk::Format::eR32G32B32Sfloat, static_cast(offsetof(Vertex, nrm))}, {2, 0, vk::Format::eR32G32B32Sfloat, static_cast(offsetof(Vertex, col))}}); pipelineGenerator.addShader(readFile("spv/vert_shader.vert.spv"), VkShaderStageFlagBits::eVertex); pipelineGenerator.addShader(readFile("spv/frag_shader.frag.spv"), VkShaderStageFlagBits::eFragment); m_pipeline = pipelineGenerator.createPipeline(); \endcode */ struct GraphicsPipelineGeneratorCombined : public GraphicsPipelineState, public GraphicsPipelineGenerator { GraphicsPipelineGeneratorCombined(VkDevice device_, const VkPipelineLayout& layout, const VkRenderPass& renderPass) : GraphicsPipelineState() , GraphicsPipelineGenerator(device_, layout, renderPass, *this) { } }; } // namespace nvvk