Fixed-Function Vertex Post-Processing

After pre-rasterization shader stages, the following fixed-function operations are applied to vertices of the resulting primitives:

Next, rasterization is performed on primitives as described in chapter Rasterization.

Transform Feedback

Before any other fixed-function vertex post-processing, vertex outputs from the last shader in the pre-rasterization shader stage can be written out to one or more transform feedback buffers bound to the command buffer. To capture vertex outputs the last pre-rasterization shader stage shader must be declared with the Xfb execution mode. Outputs decorated with XfbBuffer will be written out to the corresponding transform feedback buffers bound to the command buffer when transform feedback is active. Transform feedback buffers are bound to the command buffer by using vkCmdBindTransformFeedbackBuffersEXT. Transform feedback is made active by calling vkCmdBeginTransformFeedbackEXT and made inactive by calling vkCmdEndTransformFeedbackEXT. After vertex data is written it is possible to use vkCmdDrawIndirectByteCountEXT to start a new draw where the vertexCount is derived from the number of bytes written by a previous transform feedback.

When an individual point, line, or triangle primitive reaches the transform feedback stage while transform feedback is active, the values of the specified output variables are assembled into primitives and appended to the bound transform feedback buffers. After activating transform feedback, the values of the first assembled primitive are written at the starting offsets of the bound transform feedback buffers, and subsequent primitives are appended to the buffer. If the optional pCounterBuffers and pCounterBufferOffsets parameters are specified, the starting points within the transform feedback buffers are adjusted so data is appended to the previously written values indicated by the value stored by the implementation in the counter buffer.

For multi-vertex primitives, all values for a given vertex are written before writing values for any other vertex. When transformFeedbackPreservesProvokingVertex is not enabled, implementations may write out any vertex within the primitive first, but all subsequent vertices for that primitive must be written out in a consistent winding order defined as follows:

When transformFeedbackPreservesProvokingVertex is enabled, then in addition to writing vertices with a consistent winding order, the vertex order must preserve the provoking vertex of each primitive:

If transformFeedbackPreservesTriangleFanProvokingVertex is VK_FALSE, neither geometry nor tessellation shading is active, and the primitive topology is VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, then the first vertex written from each primitive is implementation-defined even when transformFeedbackPreservesProvokingVertex is enabled.

When capturing vertices, the stride associated with each transform feedback buffer, as indicated by the XfbStride decoration, indicates the number of bytes of storage reserved for each vertex in the transform feedback buffer. For every vertex captured, each output attribute with a Offset decoration will be written to the storage reserved for the vertex at the associated transform feedback buffer. When writing output variables that are arrays or structures, individual array elements or structure members are written tightly packed in order. For vector types, individual components are written in order. For matrix types, outputs are written as an array of column vectors.

If any component of an output with an assigned transform feedback offset was not written to by its shader, the value recorded for that component is undefined:. All components of an output variable must be written at an offset aligned to the size of the component. The size of each component of an output variable must be at least 32-bits. When capturing a vertex, any portion of the reserved storage not associated with an output variable with an assigned transform feedback offset will be unmodified.

When transform feedback is inactive, no vertices are recorded. If there is a valid counter buffer handle and counter buffer offset in the pCounterBuffers and pCounterBufferOffsets arrays, writes to the corresponding transform feedback buffer will start at the byte offset represented by the value stored in the counter buffer location.

Individual lines or triangles of a strip or fan primitive will be extracted and recorded separately. Incomplete primitives are not recorded.

When using a geometry shader that emits vertices to multiple vertex streams, a primitive will be assembled and output for each stream when there are enough vertices emitted for the output primitive type. All outputs assigned to a given transform feedback buffer are required to come from a single vertex stream.

The sizes of the transform feedback buffers are defined by the vkCmdBindTransformFeedbackBuffersEXT pSizes parameter for each of the bound buffers, or the size of the bound buffer, whichever is the lesser. If there is less space remaining in any of the transform feedback buffers than the size of all of the vertex data for that primitive based on the XfbStride for that XfbBuffer then no vertex data of that primitive is recorded in any transform feedback buffer, and the value for the number of primitives written in the corresponding VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT query for all transform feedback buffers is no longer incremented.

Any outputs made to a XfbBuffer that is not bound to a transform feedback buffer is ignored.

vkCmdBindTransformFeedbackBuffersEXTBind transform feedback buffers to a command buffer
vkCmdBeginTransformFeedbackEXTMake transform feedback active in the command buffer
vkCmdEndTransformFeedbackEXTMake transform feedback inactive in the command buffer

Viewport Swizzle

VkPipelineViewportSwizzleStateCreateInfoNVStructure specifying swizzle applied to primitive clip coordinates
VkPipelineViewportSwizzleStateCreateFlagsNVReserved for future use

The VkPipelineViewportSwizzleStateCreateInfoNV state is set by adding this structure to the pNext chain of a VkPipelineViewportStateCreateInfo structure and setting the graphics pipeline state with vkCreateGraphicsPipelines.

vkCmdSetViewportSwizzleNVSpecify the viewport swizzle state dynamically for a command buffer

Each viewport specified from 0 to viewportCount - 1 has its x,y,z,w swizzle state set to the corresponding x, y, z and w in the VkViewportSwizzleNV structure. Each component is of type VkViewportCoordinateSwizzleNV, which determines the type of swizzle for that component. The value of x computes the new x component of the position as:

if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV) x' = x;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV) x' = -x;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV) x' = y;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV) x' = -y;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV) x' = z;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV) x' = -z;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV) x' = w;
if (x == VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV) x' = -w;

Similar selections are performed for the y, z, and w coordinates. This swizzling is applied before clipping and perspective divide. If the swizzle for an active viewport index is not specified, the swizzle for x is VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV, y is VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV, z is VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV and w is VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV.

Viewport swizzle parameters are specified by setting the pNext pointer of VkGraphicsPipelineCreateInfo to point to a VkPipelineViewportSwizzleStateCreateInfoNV structure. VkPipelineViewportSwizzleStateCreateInfoNV uses VkViewportSwizzleNV to set the viewport swizzle parameters.

VkViewportSwizzleNVStructure specifying a viewport swizzle
VkViewportCoordinateSwizzleNVSpecify how a viewport coordinate is swizzled

Flat Shading

Flat shading a vertex output attribute means to assign all vertices of the primitive the same value for that output. The output values assigned are those of the provoking vertex of the primitive. Flat shading is applied to those vertex attributes that match fragment input attributes which are decorated as Flat.

If neither mesh, geometry nor tessellation shading is active, the provoking vertex is determined by the primitive topology defined by VkPipelineInputAssemblyStateCreateInfo:topology used to execute the drawing command.

If a shader using MeshNV Execution Model is active, the provoking vertex is determined by the primitive topology defined by the OutputPoints, OutputLinesNV, or OutputTrianglesNV execution mode.

If a shader using MeshEXT Execution Model is active, the provoking vertex is determined by the primitive topology defined by the OutputPoints, OutputLinesEXT, or OutputTrianglesEXT execution mode.

If geometry shading is active, the provoking vertex is determined by the primitive topology defined by the OutputPoints, OutputLineStrip, or OutputTriangleStrip execution mode.

If tessellation shading is active but geometry shading is not, the provoking vertex may be any of the vertices in each primitive.

VkPipelineRasterizationProvokingVertexStateCreateInfoEXTStructure specifying provoking vertex mode used by a graphics pipeline
VkProvokingVertexModeEXTSpecify which vertex in a primitive is the provoking vertex
vkCmdSetProvokingVertexModeEXTSpecify the provoking vertex mode dynamically for a command buffer

Primitive Clipping

Primitives are culled against the cull volume and then clipped to the clip volume. In clip coordinates, the view volume is defined by:

w_cx_cw_c w_cy_cw_c z_mz_cw_c\begin{array}{c} -w\_c \leq x\_c \leq w\_c \\\ -w\_c \leq y\_c \leq w\_c \\\ z\_m \leq z\_c \leq w\_c \end{array}

where if VkPipelineViewportDepthClipControlCreateInfoEXT::negativeOneToOne is VK_TRUE zm is equal to -wc otherwise zm is equal to zero.

This view volume can be further restricted by as many as VkPhysicalDeviceLimits::maxClipDistances application-defined half-spaces.

The cull volume is the intersection of up to VkPhysicalDeviceLimits::maxCullDistances application-defined half-spaces (if no application-defined cull half-spaces are enabled, culling against the cull volume is skipped).

A shader must write a single cull distance for each enabled cull half-space to elements of the CullDistance array. If the cull distance for any enabled cull half-space is negative for all of the vertices of the primitive under consideration, the primitive is discarded. Otherwise the primitive is clipped against the clip volume as defined below.

The clip volume is the intersection of up to VkPhysicalDeviceLimits::maxClipDistances application-defined half-spaces with the view volume (if no application-defined clip half-spaces are enabled, the clip volume is the view volume).

A shader must write a single clip distance for each enabled clip half-space to elements of the ClipDistance array. Clip half-space i is then given by the set of points satisfying the inequality

  • ci(P) ≥ 0

where ci(P) is the clip distance i at point P. For point primitives, ci(P) is simply the clip distance for the vertex in question. For line and triangle primitives, per-vertex clip distances are interpolated using a weighted mean, with weights derived according to the algorithms described in sections Basic Line Segment Rasterization and Basic Polygon Rasterization, using the perspective interpolation equations.

The number of application-defined clip and cull half-spaces that are enabled is determined by the explicit size of the built-in arrays ClipDistance and CullDistance, respectively, declared as an output in the interface of the entry point of the final shader stage before clipping.

If VkPipelineRasterizationDepthClipStateCreateInfoEXT is present in the graphics pipeline state then depth clipping is disabled if VkPipelineRasterizationDepthClipStateCreateInfoEXT::depthClipEnable is VK_FALSE. Otherwise, if VkPipelineRasterizationDepthClipStateCreateInfoEXT is not present, depth clipping is disabled when VkPipelineRasterizationStateCreateInfo::depthClampEnable is VK_TRUE.

vkCmdSetDepthClampEnableEXTSpecify dynamically whether depth clamping is enabled in the command buffer
vkCmdSetDepthClipEnableEXTSpecify dynamically whether depth clipping is enabled in the command buffer

When depth clipping is disabled, the plane equation

  • zm ≤ zc ≤ wc

(see the clip volume definition above) is ignored by view volume clipping (effectively, there is no near or far plane clipping).

If the primitive under consideration is a point or line segment, then clipping passes it unchanged if its vertices lie entirely within the clip volume.

VkPointClippingBehaviorEnum specifying the point clipping behavior

If either of a line segment’s vertices lie outside of the clip volume, the line segment may be clipped, with new vertex coordinates computed for each vertex that lies outside the clip volume. A clipped line segment endpoint lies on both the original line segment and the boundary of the clip volume.

This clipping produces a value, 0 ≤ t ≤ 1, for each clipped vertex. If the coordinates of a clipped vertex are P and the unclipped line segment’s vertex coordinates are P1 and P2, then t satisfies the following equation

  • P = t P1 + (1-t) P2.

t is used to clip vertex output attributes as described in Clipping Shader Outputs.

If the primitive is a polygon, it passes unchanged if every one of its edges lies entirely inside the clip volume, and is either clipped or discarded otherwise. If the edges of the polygon intersect the boundary of the clip volume, the intersecting edges are reconnected by new edges that lie along the boundary of the clip volume - in some cases requiring the introduction of new vertices into a polygon.

If a polygon intersects an edge of the clip volume’s boundary, the clipped polygon must include a point on this boundary edge.

Primitives rendered with application-defined half-spaces must satisfy a complementarity criterion. Suppose a series of primitives is drawn where each vertex i has a single specified clip distance di (or a number of similarly specified clip distances, if multiple half-spaces are enabled). Next, suppose that the same series of primitives are drawn again with each such clip distance replaced by -di (and the graphics pipeline is otherwise the same). In this case, primitives must not be missing any pixels, and pixels must not be drawn twice in regions where those primitives are cut by the clip planes.

VkPipelineViewportDepthClipControlCreateInfoEXTStructure specifying parameters of a newly created pipeline depth clip control state
vkCmdSetDepthClipNegativeOneToOneEXTSpecify the negative one to one depth clip mode dynamically for a command buffer

Clipping Shader Outputs

Next, vertex output attributes are clipped. The output values associated with a vertex that lies within the clip volume are unaffected by clipping. If a primitive is clipped, however, the output values assigned to vertices produced by clipping are clipped.

Let the output values assigned to the two vertices P1 and P2 of an unclipped edge be c1 and c2. The value of t (see Primitive Clipping) for a clipped point P is used to obtain the output value associated with P as

  • c = t c1 + (1-t) c2.

(Multiplying an output value by a scalar means multiplying each of x, y, z, and w by the scalar.)

Since this computation is performed in clip space before division by wc, clipped output values are perspective-correct.

Polygon clipping creates a clipped vertex along an edge of the clip volume’s boundary. This situation is handled by noting that polygon clipping proceeds by clipping against one half-space at a time. Output value clipping is done in the same way, so that clipped points always occur at the intersection of polygon edges (possibly already clipped) with the clip volume’s boundary.

For vertex output attributes whose matching fragment input attributes are decorated with NoPerspective, the value of t used to obtain the output value associated with P will be adjusted to produce results that vary linearly in framebuffer space.

Output attributes of integer or unsigned integer type must always be flat shaded. Flat shaded attributes are constant over the primitive being rasterized (see Basic Line Segment Rasterization and Basic Polygon Rasterization), and no interpolation is performed. The output value c is taken from either c1 or c2, since flat shading has already occurred and the two values are identical.

Controlling Viewport W Scaling

If viewport W scaling is enabled, the W component of the clip coordinate is modified by the provided coefficients from the corresponding viewport as follows.

  • wc' = xcoeff xc + ycoeff yc + wc
VkPipelineViewportWScalingStateCreateInfoNVStructure specifying parameters of a newly created pipeline viewport W scaling state

The VkPipelineViewportWScalingStateCreateInfoNV state is set by adding this structure to the pNext chain of a VkPipelineViewportStateCreateInfo structure and setting the graphics pipeline state with vkCreateGraphicsPipelines.

vkCmdSetViewportWScalingEnableNVSpecify the viewport W scaling enable state dynamically for a command buffer
vkCmdSetViewportWScalingNVSet the viewport W scaling dynamically for a command buffer

Both VkPipelineViewportWScalingStateCreateInfoNV and vkCmdSetViewportWScalingNV use VkViewportWScalingNV to set the viewport transformation parameters.

VkViewportWScalingNVStructure specifying a viewport

Coordinate Transformations

Clip coordinates for a vertex result from shader execution, which yields a vertex coordinate Position.

Perspective division on clip coordinates yields normalized device coordinates, followed by a viewport transformation (see Controlling the Viewport) to convert these coordinates into framebuffer coordinates.

If a vertex in clip coordinates has a position given by

(x_c y_c z_c w_c)\left(\begin{array}{c} x\_c \\\ y\_c \\\ z\_c \\\ w\_c \end{array}\right)

then the vertex’s normalized device coordinates are

(x_d y_d z_d)=(x_cw_c y_cw_c z_cw_c)\left( \begin{array}{c} x\_d \\\ y\_d \\\ z\_d \end{array} \right) = \left( \begin{array}{c} \frac{x\_c}{w\_c} \\\ \frac{y\_c}{w\_c} \\\ \frac{z\_c}{w\_c} \end{array} \right)

Render Pass Transform

A render pass transform can be enabled for render pass instances. The clip coordinates (xc, yc) that result from vertex shader execution are transformed by a rotation of 0, 90, 180, or 270 degrees in the XY plane, centered at the origin.

When Render pass transform is enabled, the transform applies to all primitives for all subpasses of the render pass. The transformed vertex in clip coordinates has a position given by

(x_c_trans y_c_trans z_c_trans)=(x_ccosθy_csinθ x_csinθ+y_ccosθ z_c)\left( \begin{array}{c} x\_{c\_{trans}} \\\ y\_{c\_{trans}} \\\ z\_{c\_{trans}} \end{array} \right) = \left( \begin{array}{c} x\_{c} \cos \theta - y\_{c} \sin \theta \\\ x\_{c} \sin \theta + y\_{c} \cos \theta \\\ z\_c \end{array} \right)

where

  • θ is 0 degrees for VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
  • θ is 90 degrees for VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR
  • θ is 180 degrees for VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR
  • θ is 270 degrees for VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR

The transformed vertex’s normalized device coordinates are

(x_d y_d z_d)=(x_c_transw_c y_c_transw_c z_c_transw_c)\left( \begin{array}{c} x\_d \\\ y\_d \\\ z\_d \end{array} \right) = \left( \begin{array}{c} \frac{x\_{c\_{trans}}}{w\_c} \\\ \frac{y\_{c\_{trans}}}{w\_c} \\\ \frac{z\_{c\_{trans}}}{w\_c} \end{array} \right)

When render pass transform is enabled for a render pass instance, the following additional features are enabled:

  • Each VkViewport specified by either VkPipelineViewportStateCreateInfo::pViewports or vkCmdSetViewport will have its width/height (px, py) and its center (ox, oy) similarly transformed by the implementation.
  • Each scissor specified by VkPipelineViewportStateCreateInfo::pScissors or vkCmdSetScissor will have its (offsetx, offsety) and (extentx, extenty) similarly transformed by the implementation.
  • The renderArea specified in VkCommandBufferInheritanceRenderPassTransformInfoQCOM and VkRenderPassBeginInfo will be similarly transformed by the implementation.
  • The (x, y) components of shader variables with built-in decorations FragCoord, SamplePosition, or PointCoord will be similarly transformed by the implementation.
  • The (x,y) components of the offset operand of the InterpolateAtOffset extended instruction will be similarly transformed by the implementation.
  • The values returned by SPIR-V derivative instructions OpDPdx, OpDPdy, OpDPdxCourse, OpDPdyCourse, OpDPdxFine, OpDPdyFine will be similarly transformed by the implementation.

The net result of the above, is that applications can act as if rendering to a framebuffer oriented with the VkSurfaceCapabilitiesKHR::currentTransform. In other words, applications can act as if the presentation engine will be performing the transformation of the swapchain image after rendering and prior to presentation to the user. In fact, the transformation of the various items cited above are being handled by the implementation as the rendering takes place.

Controlling the Viewport

The viewport transformation is determined by the selected viewport’s width and height in pixels, px and py, respectively, and its center (ox, oy) (also in pixels), as well as its depth range min and max determining a depth range scale value pz and a depth range bias value oz (defined below). The vertex’s framebuffer coordinates (xf, yf, zf) are given by

  • xf = (px / 2) xd + ox
  • yf = (py / 2) yd + oy
  • zf = pz × zd + oz

Multiple viewports are available, numbered zero up to VkPhysicalDeviceLimits::maxViewports minus one. The number of viewports used by a pipeline is controlled by the viewportCount member of the VkPipelineViewportStateCreateInfo structure used in pipeline creation.

xf and yf have limited precision, where the number of fractional bits retained is specified by VkPhysicalDeviceLimits::subPixelPrecisionBits. When rasterizing line segments, the number of fractional bits is specified by VkPhysicalDeviceLineRasterizationPropertiesKHR::lineSubPixelPrecisionBits.

VkPipelineViewportStateCreateInfoStructure specifying parameters of a newly created pipeline viewport state
vkCmdSetViewportWithCountSet the viewport count and viewports dynamically for a command buffer
vkCmdSetScissorWithCountSet the scissor count and scissor rectangular bounds dynamically for a command buffer
VkPipelineViewportStateCreateFlagsReserved for future use

A pre-rasterization shader stage can direct each primitive to zero or more viewports. The destination viewports for a primitive are selected by the last active pre-rasterization shader stage that has an output variable decorated with ViewportIndex (selecting a single viewport) or ViewportMaskNV (selecting multiple viewports). The viewport transform uses the viewport corresponding to either the value assigned to ViewportIndex or one of the bits set in ViewportMaskNV, and taken from an implementation-dependent vertex of each primitive. If ViewportIndex or any of the bits in ViewportMaskNV are outside the range zero to viewportCount minus one for a primitive, or if the last active pre-rasterization shader stage did not assign a value to either ViewportIndex or ViewportMaskNV for all vertices of a primitive due to flow control, the values resulting from the viewport transformation of the vertices of such primitives are undefined:. If the last pre-rasterization shader stage does not have an output decorated with ViewportIndex or ViewportMaskNV, the viewport numbered zero is used by the viewport transformation.

A single vertex can be used in more than one individual primitive, in primitives such as VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP. In this case, the viewport transformation is applied separately for each primitive.

vkCmdSetViewportSet the viewport dynamically for a command buffer

Both VkPipelineViewportStateCreateInfo and vkCmdSetViewport use VkViewport to set the viewport transformation parameters.

VkViewportStructure specifying a viewport