/ 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