Fixed-Function Vertex Post-Processing
After pre-rasterization shader stages, the following fixed-function operations are applied to vertices of the resulting primitives:
- Transform feedback (see Transform Feedback)
- Viewport swizzle (see Viewport Swizzle)
- Flat shading (see Flat Shading).
- Primitive clipping, including application-defined half-spaces (see Primitive Clipping).
- Shader output attribute clipping (see Clipping Shader Outputs).
- Clip space W scaling (see Controlling Viewport W Scaling).
- Perspective division on clip coordinates (see Coordinate Transformations).
- Viewport mapping, including depth range scaling (see Controlling the Viewport).
- Front face determination for polygon primitives (see Basic Polygon Rasterization).
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:
- If neither geometry or tessellation
shading is active, vertices within a primitive are appended according
to the winding order described by the primitive topology defined by the
VkPipelineInputAssemblyStateCreateInfo:
topology
used to execute the drawing command. - If geometry shading is active, vertices within a primitive
are appended according to the winding order described by the
primitive topology defined by the
OutputPoints
,OutputLineStrip
, orOutputTriangleStrip
execution mode. - If tessellation shading is active but geometry shading is not, vertices within a primitive are appended according to the winding order defined by triangle tessellation, quad tessellation, and isoline tessellation.
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:
- When the
pipeline’s
provoking vertex mode is
VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT
, the primitive’s provoking vertex must be the first vertex written. - When the
pipeline’s
provoking vertex mode is
VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT
, the primitive’s provoking vertex must be the last vertex written.
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.
Viewport Swizzle
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.
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.
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.
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:
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
.
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.
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.
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
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.
Both VkPipelineViewportWScalingStateCreateInfoNV and
vkCmdSetViewportWScalingNV use VkViewportWScalingNV
to set the
viewport transformation parameters.
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
then the vertex’s normalized device coordinates are
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
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
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
, orPointCoord
will be similarly transformed by the implementation. - The (x,y) components of the
offset
operand of theInterpolateAtOffset
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
.
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.
Both VkPipelineViewportStateCreateInfo and vkCmdSetViewport use
VkViewport
to set the viewport transformation parameters.