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