Vireo  0.0
Vireo 3D Rendering Hardware Interface
Graphics Pipelines

The graphics pipeline is the sequence of operations that take the vertices and textures of your meshes all the way to the pixels in the render targets.

The different stages of the pipeline operations are roughly the same in Vulkan and DirectX but sometime with different names :

Configuration of a graphic pipeline

Creation of a graphic pipeline involve a lot of parameters. Most notable parameters are the format of the attachment (an attachment or render target is an image where the shaders will write), the color blending for each attachment, the format of the vertices, and the shaders.

The GraphicPipelineConfiguration structure is used to specify all the creation parameter.

Resources parameters

For vireo::Vireo::createVertexLayout each field of the vertex data structure must be described with the following parameters :

  • binding : Binding name which this attribute takes its data from for the HLSL style binding. For the Vulkan style the binding number will be calculated from the index of the field.
  • format : Type for data
  • offset : Byte offset of this attribute relative to the start of an element in the data structure.

For example, if the vertex structure is, for each vertex :

struct Vertex {
glm::vec3 position;
glm::vec3 normal;
glm::vec2 uv;
glm::vec3 tangent;
};

The input layout description will be the following array :

const std::vector<vireo::VertexAttributeDesc> vertexAttributes{
{"POSITION", vireo::AttributeFormat::R32G32B32_FLOAT, offsetof(Vertex, position) }, // Binding #0
{"NORMAL", vireo::AttributeFormat::R32G32B32_FLOAT, offsetof(Vertex, normal)}, // Binding #1
{"UV", vireo::AttributeFormat::R32G32_FLOAT, offsetof(Vertex, uv)}, // Binding #2
{"TANGENT", vireo::AttributeFormat::R32G32B32_FLOAT, offsetof(Vertex, tangent)}, // Binding #3
};

and the corresponding structure in the shader in the Slang shader language (must be in the same order) :

struct VertexInput {
float3 position : POSITION;
float3 normal : NORMAL;
float2 uv : UV;
float3 tangent : TANGENT;
};

Shader modules list

  • vertexShader, fragmentShader : the shader modules. Must have at least a vertex shader.

Color attachment parameters

Color blending parameters

  • colorBlendDesc : an array (with the same number of entries as colorRenderFormats) of vireo::ColorBlendDesc for each color attachment
  • logicOpEnable : controls whether to apply Logical Operations.
  • logicOp : which logical operation to apply.

For each value of colorBlendDesc :

  • blendEnable : Controls whether blending is enabled for the corresponding color attachment. If blending is not enabled, the source fragment’s color for that attachment is passed through unmodified.
  • srcColorBlendFactor : Selects which blend factor is used to determine the source factors (Sr,Sg,Sb).
  • dstColorBlendFactor : Selects which blend factor is used to determine the destination factors (Dr,Dg,Db).
  • colorBlendOp : Selects which blend operation is used to calculate the RGB values to write to the color attachment.
  • srcAlphaBlendFactor : Selects which blend factor is used to determine the source factor Sa.
  • dstAlphaBlendFactor : Selects which blend factor is used to determine the destination factor Da.
  • alphaBlendOp : Selects which blend operation is used to calculate the alpha values to write to the color attachment.
  • colorWriteMask : Is a bitmask specifying which of the R, G, B, and/or A components are enabled for writing

Multisampling parameters

  • msaa : the number of samples used in rasterization
  • alphaToCoverageEnable : Controls whether a temporary coverage value is generated based on the alpha component of the fragment’s first color output

Rasterization parameters

Depth testing parameters

  • depthStencilImageFormat : format of the depth and stencil attachment
  • depthTestEnable : controls whether depth testing is enabled.
  • depthWriteEnable : controls whether depth writes are enabled .
  • depthCompareOp : the comparison operator to use
  • depthBiasEnable : Controls whether to bias fragment depth values.
  • depthBiasConstantFactor : A scalar factor controlling the constant depth value added to each fragment.
  • depthBiasClamp : The maximum (or minimum) depth bias of a fragment.
  • depthBiasSlopeFactor : A scalar factor applied to a fragment’s slope in depth bias calculations.

Stencil testing parameters

  • stencilTestEnable : Controls whether stencil testing is enabled
  • frontStencilOpState : value controlling the corresponding parameters of the stencil test.
  • backStencilOpState : value controlling the corresponding parameters of the stencil test.

The stencil reference value is set in the render pass with vireo::CommandList::setStencilReference.

Creating a graphic pipeline

After populating the GraphicPipelineConfiguration structure the pipeline creation with vireo::Vireo::createGraphicPipeline is straightforward :

...
// The pipeline parameters structure
vireo::GraphicPipelineConfiguration pipelineConfig {
.colorRenderFormats = {vireo::ImageFormat::R8G8B8A8_SRGB},
.colorBlendDesc = {{}}
};
// The vertex structure
struct Vertex {
glm::vec3 pos;
glm::vec3 color;
};
// The vertex structure descriptions
const std::vector<vireo::VertexAttributeDesc> vertexAttributes{
{"POSITION", vireo::AttributeFormat::R32G32B32_FLOAT, offsetof(Vertex, pos)},
{"COLOR", vireo::AttributeFormat::R32G32B32_FLOAT, offsetof(Vertex, color)}
};
...
// Create the pipeline resources and shader modules
pipelineConfig.resources = vireo->createPipelineResources();
pipelineConfig.vertexInputLayout = vireo->createVertexLayout(sizeof(Vertex), vertexAttributes);
pipelineConfig.vertexShader = vireo->createShaderModule("shaders/triangle_color.vert");
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/triangle_color.frag");
// Create the pipeline
defaultPipeline = vireo->createGraphicPipeline(pipelineConfig);

Since the GraphicPipelineConfiguration structure is only used at creation time, you can use it for multiple pipelines creation :

// Pipeline #1
pipelineConfig.colorRenderFormats.push_back(vireo::ImageFormat::R16G16_SFLOAT);
pipelineConfig.resources = resources;
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/smaa_edge_detect.frag");
smaaEdgePipeline = vireo->createGraphicPipeline(pipelineConfig);
// Pipeline #2
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/smaa_blend_weigth.frag");
smaaBlendWeightPipeline = vireo->createGraphicPipeline(pipelineConfig);
// Pipeline #3
pipelineConfig.colorRenderFormats[0] = renderFormat;
pipelineConfig.resources = smaaResources;
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/smaa_neighborhood_blend.frag");
smaaBlendPipeline = vireo->createGraphicPipeline(pipelineConfig);
// Pipeline #4
pipelineConfig.resources = resources;
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/fxaa.frag");
fxaaPipeline = vireo->createGraphicPipeline(pipelineConfig);
// Pipeline #5
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/voronoi.frag");
effectPipeline = vireo->createGraphicPipeline(pipelineConfig);
// Pipeline #6
pipelineConfig.fragmentShader = vireo->createShaderModule("shaders/gamma_correction.frag");
gammaCorrectionPipeline = vireo->createGraphicPipeline(pipelineConfig);

Drawing

To execute the shaders, bind the compute pipeline with vireo::CommandList::bindPipeline and its descriptor sets with vireo::CommandList::bindDescriptors, then issue draw calls with vireo::CommandList::draw or vireo::CommandList::drawIndexed :

// Set the descriptor sets list
frame.commandList->setDescriptors({frame.descriptorSet});
// Bind the graphic pipeline
frame.commandList->bindPipeline(pipeline);
// Bind the descriptor sets
frame.commandList->bindDescriptors(pipeline, {frame.descriptorSet});
// Draw a triangle
frame.commandList->draw(triangleVertices.size());

If you need to select specific data in the descriptor set you can use dynamic uniform buffer descriptor set :

cmdList->bindDescriptor(pipeline,
frame.materialsDescriptorSet, SET_MATERIALS,
frame.materialUniform->getInstanceSizeAligned() * Scene::MATERIAL_GRID);
cmdList->bindDescriptor(pipeline,
frame.modelsDescriptorSet, SET_MODELS,
frame.modelUniform->getInstanceSizeAligned() * Scene::MODEL_TRANSPARENT);
scene.drawCube(cmdList);

Or push constants :

pushConstants.modelIndex = Scene::MODEL_TRANSPARENT;
pushConstants.materialIndex = Scene::MATERIAL_GRID;
cmdList->pushConstants(pipelineConfig.resources, pushConstantsDesc, &pushConstants);
scene.drawCube(cmdList);