Implicit surface rendering via ray tracing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

617 lines
23 KiB

/*
* 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 <cassert>
#include <iterator>
#include <string>
#include <vector>
#include <vulkan/vulkan_core.h>
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<uint32_t>(offsetof(Vertex, pos))},
{1, 0, vk::Format::eR32G32B32Sfloat, static_cast<uint32_t>(offsetof(Vertex, nrm))},
{2, 0, vk::Format::eR32G32B32Sfloat, static_cast<uint32_t>(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<uint32_t>(attributeDescriptions.size());
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(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<VkVertexInputBindingDescription>& 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<VkVertexInputAttributeDescription>& 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<VkPipelineColorBlendAttachmentState> blendAttachmentStates{makePipelineColorBlendAttachmentState()};
std::vector<VkDynamicState> dynamicStateEnables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
std::vector<VkViewport> viewports;
std::vector<VkRect2D> scissors;
// Helper to set objects for either C and C++
template <class T, class U>
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<char> v;
std::copy(code.begin(), code.end(), std::back_inserter(v));
return addShader(v, stage, entryPoint);
}
template <typename T>
VkPipelineShaderStageCreateInfo& addShader(const std::vector<T>& 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<const uint32_t*>(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<uint32_t>(shaderStages.size());
createInfo.pStages = shaderStages.data();
pipelineState.update();
}
VkGraphicsPipelineCreateInfo createInfo{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
private:
VkDevice device;
VkPipelineCache pipelineCache{};
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
std::vector<VkShaderModule> temporaryModules;
std::vector<VkFormat> 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 <class T, class U>
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<uint32_t>(offsetof(Vertex, pos))},
{1, 0, vk::Format::eR32G32B32Sfloat, static_cast<uint32_t>(offsetof(Vertex, nrm))},
{2, 0, vk::Format::eR32G32B32Sfloat, static_cast<uint32_t>(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