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