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