• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2016-2020 NVIDIA Corporation
2//
3// SPDX-License-Identifier: CC-BY-4.0
4
5include::{generated}/meta/{refprefix}VK_NV_viewport_swizzle.adoc[]
6
7=== Other Extension Metadata
8
9*Last Modified Date*::
10    2016-12-22
11*Interactions and External Dependencies*::
12  - This extension requires pname:multiViewport and pname:geometryShader
13    features to be useful.
14*Contributors*::
15  - Daniel Koch, NVIDIA
16  - Jeff Bolz, NVIDIA
17
18=== Description
19
20This extension provides a new per-viewport swizzle that can modify the
21position of primitives sent to each viewport.
22New viewport swizzle state is added for each viewport, and a new position
23vector is computed for each vertex by selecting from and optionally negating
24any of the four components of the original position vector.
25
26This new viewport swizzle is useful for a number of algorithms, including
27single-pass cube map rendering (broadcasting a primitive to multiple faces
28and reorienting the vertex position for each face) and voxel rasterization.
29The per-viewport component remapping and negation provided by the swizzle
30allows application code to re-orient three-dimensional geometry with a view
31along any of the *X*, *Y*, or *Z* axes.
32If a perspective projection and depth buffering is required, [eq]#1/W#
33buffering should be used, as described in the single-pass cube map rendering
34example in the "`Issues`" section below.
35
36include::{generated}/interfaces/VK_NV_viewport_swizzle.adoc[]
37
38=== Issues
39
401) Where does viewport swizzling occur in the pipeline?
41
42*RESOLVED*: Despite being associated with the viewport, viewport swizzling
43must happen prior to the viewport transform.
44In particular, it needs to be performed before clipping and perspective
45division.
46
47The viewport mask expansion (`apiext:VK_NV_viewport_array2`) and the
48viewport swizzle could potentially be performed before or after transform
49feedback, but feeding back several viewports worth of primitives with
50different swizzles does not seem particularly useful.
51This specification applies the viewport mask and swizzle after transform
52feedback, and makes primitive queries only count each primitive once.
53
542) Any interesting examples of how this extension,
55`apiext:VK_NV_viewport_array2`, and
56`apiext:VK_NV_geometry_shader_passthrough` can be used together in practice?
57
58*RESOLVED*: One interesting use case for this extension is for single-pass
59rendering to a cube map.
60In this example, the application would attach a cube map texture to a
61layered FBO where the six cube faces are treated as layers.
62Vertices are sent through the vertex shader without applying a projection
63matrix, where the code:gl_Position output is [eq]#(x,y,z,1)# and the center
64of the cube map is at [eq]#(0,0,0)#.
65With unextended Vulkan, one could have a conventional instanced geometry
66shader that looks something like the following:
67
68[source,c]
69----
70layout(invocations = 6) in;     // separate invocation per face
71layout(triangles) in;
72layout(triangle_strip) out;
73layout(max_vertices = 3) out;
74
75in Inputs {
76vec2 texcoord;
77vec3 normal;
78vec4 baseColor;
79} v[];
80
81    out Outputs {
82    vec2 texcoord;
83    vec3 normal;
84    vec4 baseColor;
85    };
86
87    void main()
88    {
89    int face = gl_InvocationID;  // which face am I?
90
91    // Project gl_Position for each vertex onto the cube map face.
92    vec4 positions[3];
93    for (int i = 0; i < 3; i++) {
94        positions[i] = rotate(gl_in[i].gl_Position, face);
95    }
96
97    // If the primitive does not project onto this face, we are done.
98    if (shouldCull(positions)) {
99        return;
100    }
101
102    // Otherwise, emit a copy of the input primitive to the
103    // appropriate face (using gl_Layer).
104    for (int i = 0; i < 3; i++) {
105        gl_Layer = face;
106        gl_Position = positions[i];
107        texcoord = v[i].texcoord;
108        normal = v[i].normal;
109        baseColor = v[i].baseColor;
110        EmitVertex();
111    }
112}
113----
114
115With passthrough geometry shaders, this can be done using a much simpler
116shader:
117
118[source,c]
119----
120layout(triangles) in;
121layout(passthrough) in Inputs {
122    vec2 texcoord;
123    vec3 normal;
124    vec4 baseColor;
125}
126layout(passthrough) in gl_PerVertex {
127    vec4 gl_Position;
128} gl_in[];
129layout(viewport_relative) out int gl_Layer;
130
131void main()
132{
133    // Figure out which faces the primitive projects onto and
134    // generate a corresponding viewport mask.
135    uint mask = 0;
136    for (int i = 0; i < 6; i++) {
137        if (!shouldCull(face)) {
138        mask |= 1U << i;
139        }
140    }
141    gl_ViewportMask = mask;
142    gl_Layer = 0;
143}
144----
145
146The application code is set up so that each of the six cube faces has a
147separate viewport (numbered 0 to 5).
148Each face also has a separate swizzle, programmed via the
149slink:VkPipelineViewportSwizzleStateCreateInfoNV pipeline state.
150The viewport swizzle feature performs the coordinate transformation handled
151by the code:rotate() function in the original shader.
152The code:viewport_relative layout qualifier says that the viewport number (0
153to 5) is added to the base code:gl_Layer value of 0 to determine which layer
154(cube face) the primitive should be sent to.
155
156Note that the use of the passed through input code:normal in this example
157suggests that the fragment shader in this example would perform an operation
158like per-fragment lighting.
159The viewport swizzle would transform the position to be face-relative, but
160code:normal would remain in the original coordinate system.
161It seems likely that the fragment shader in either version of the example
162would want to perform lighting in the original coordinate system.
163It would likely do this by reconstructing the position of the fragment in
164the original coordinate system using code:gl_FragCoord, a constant or
165uniform holding the size of the cube face, and the input
166code:gl_ViewportIndex (or code:gl_Layer), which identifies the cube face.
167Since the value of code:normal is in the original coordinate system, it
168would not need to be modified as part of this coordinate transformation.
169
170Note that while the code:rotate() operation in the regular geometry shader
171above could include an arbitrary post-rotation projection matrix, the
172viewport swizzle does not support arbitrary math.
173To get proper projection, [eq]#1/W# buffering should be used.
174To do this:
175
176  . Program the viewport swizzles to move the pre-projection [eq]#W# eye
177    coordinate (typically 1.0) into the [eq]#Z# coordinate of the swizzle
178    output and the eye coordinate component used for depth into the [eq]#W#
179    coordinate.
180    For example, the viewport corresponding to the [eq]#+Z# face might use a
181    swizzle of [eq]#(+X, -Y, +W, +Z)#.
182    The [eq]#Z# normalized device coordinate computed after swizzling would
183    then be [eq]#z'/w' = 1/Z~eye~#.
184  . On NVIDIA implementations supporting floating-point depth buffers with
185    values outside [eq]#[0,1]#, prevent unwanted near plane clipping by
186    enabling pname:depthClampEnable.
187    Ensure that the depth clamp does not mess up depth testing by
188    programming the depth range to very large values, such as
189    [eq]#pname:minDepthBounds=-z#, [eq]#pname:maxDepthBounds=+z#, where
190    [eq]#z = 2^127^#.
191    It should be possible to use IEEE infinity encodings also (`0xFF800000`
192    for `-INF`, `0x7F800000` for `+INF`).
193    Even when near/far clipping is disabled, primitives extending behind the
194    eye will still be clipped because one or more vertices will have a
195    negative [eq]#W# coordinate and fail [eq]#X#/[eq]#Y# clipping tests.
196+
197--
198On other implementations, scale [eq]#X#, [eq]#Y#, and [eq]#Z# eye
199coordinates so that vertices on the near plane have a post-swizzle [eq]#W#
200coordinate of 1.0.
201For example, if the near plane is at [eq]#Z~eye~ = 1/256#, scale [eq]#X#,
202[eq]#Y#, and [eq]#Z# by 256.
203--
204  . Adjust depth testing to reflect the fact that [eq]#1/W# values are large
205    near the eye and small away from the eye.
206    Clear the depth buffer to zero (infinitely far away) and use a depth
207    test of ename:VK_COMPARE_OP_GREATER instead of ename:VK_COMPARE_OP_LESS.
208
209
210=== Version History
211
212  * Revision 1, 2016-12-22 (Piers Daniell)
213  ** Internal revisions
214