Name ARB_viewport_array Name Strings GL_ARB_viewport_array Contributors Graham Sellers, AMD Mark Young, AMD Nick Haemel, AMD Bill Licea-Kane, AMD Jeff Bolz, NVIDIA Daniel Koch, TransGaming Pat Brown, NVIDIA Bruce Merry, ARM Ian Stewart, NVIDIA Contact Graham Sellers, AMD (graham.sellers 'at' amd.com) Notice Copyright (c) 2010-2013 The Khronos Group Inc. Copyright terms at http://www.khronos.org/registry/speccopyright.html Specification Update Policy Khronos-approved extension specifications are updated in response to issues and bugs prioritized by the Khronos OpenGL Working Group. For extensions which have been promoted to a core Specification, fixes will first appear in the latest version of that core Specification, and will eventually be backported to the extension document. This policy is described in more detail at https://www.khronos.org/registry/OpenGL/docs/update_policy.php Status Complete. Approved by the ARB on June 9, 2010. Approved by the Khronos Board of Promoters on July 23, 2010. Version Last Modified Date: 06/25/2012 Author Revision: 18 Number ARB Extension #100 Dependencies OpenGL 1.0 is required. OpenGL 3.2 or the EXT_geometry_shader4 or ARB_geometry_shader4 extensions are required. This extension is written against the OpenGL 3.2 (Compatibility) Specification. This extension is written against the OpenGL Shading Language Specification version 1.50.09. Overview OpenGL is modeled on a pipeline of operations. The final stage in this pipeline before rasterization is the viewport transformation. This stage transforms vertices from view space into window coordinates and allows the application to specify a rectangular region of screen space into which OpenGL should draw primitives. Unextended OpenGL implementations provide a single viewport per context. In order to draw primitives into multiple viewports, the OpenGL viewport may be changed between several draw calls. With the advent of Geometry Shaders, it has become possible for an application to amplify geometry and produce multiple output primitives for each primitive input to the Geometry Shader. It is possible to direct these primitives to render into a selected render target. However, all render targets share the same, global OpenGL viewport. This extension enhances OpenGL by providing a mechanism to expose multiple viewports. Each viewport is specified as a rectangle. The destination viewport may be selected per-primitive by the geometry shader. This allows the Geometry Shader to produce different versions of primitives destined for separate viewport rectangles on the same surface. Additionally, when combined with multiple framebuffer attachments, it allows a different viewport rectangle to be selected for each. This extension also exposes a separate scissor rectangle for each viewport. Finally, the viewport bounds are now floating point quantities allowing fractional pixel offsets to be applied during the viewport transform. IP Status No known IP claims. New Procedures and Functions void ViewportArrayv(uint first, sizei count, const float * v); void ViewportIndexedf(uint index, float x, float y, float w, float h); void ViewportIndexedfv(uint index, const float * v); void ScissorArrayv(uint first, sizei count, const int * v); void ScissorIndexed(uint index, int left, int bottom, sizei width, sizei height); void ScissorIndexedv(uint index, const int * v); void DepthRangeArrayv(uint first, sizei count, const clampd * v); void DepthRangeIndexed(uint index, clampd n, clampd f); void GetFloati_v(enum target, uint index, float *data); void GetDoublei_v(enum target, uint index, double *data); void GetIntegerIndexedvEXT(enum target, uint index, int * v); void EnableIndexedEXT(enum target, uint index); void DisableIndexedEXT(enum target, uint index); boolean IsEnabledIndexedEXT(enum target, uint index); Note that GetIntegerIndexedvEXT, EnableIndexedEXT, DisableIndexedEXT and IsEnabledIndexedEXT are introduced by other OpenGL extensions such as EXT_draw_buffers2. If this extension is implemented against an earlier version of OpenGL that does not support GetIntegeri_v and so on, the 'Indexed' versions of these functions may be used in their place. New Tokens Accepted by the parameter of GetBooleanv, GetIntegerv, GetFloatv, GetDoublev and GetInteger64v: MAX_VIEWPORTS 0x825B VIEWPORT_SUBPIXEL_BITS 0x825C VIEWPORT_BOUNDS_RANGE 0x825D LAYER_PROVOKING_VERTEX 0x825E VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F Accepted by the parameter of GetIntegeri_v: SCISSOR_BOX 0x0C10 Accepted by the parameter of GetFloati_v: VIEWPORT 0x0BA2 Accepted by the parameter of GetDoublei_v: DEPTH_RANGE 0x0B70 Accepted by the parameter of Enablei, Disablei, and IsEnabledi: SCISSOR_TEST 0x0C11 Returned in the parameter from a Get query with a of LAYER_PROVOKING_VERTEX or VIEWPORT_INDEX_PROVOKING_VERTEX: FIRST_VERTEX_CONVENTION 0x8E4D LAST_VERTEX_CONVENTION 0x8E4E PROVOKING_VERTEX 0x8E4F UNDEFINED_VERTEX 0x8260 Additions to Chapter 2 of the OpenGL 3.2 (Compatibility) Specification (OpenGL Operation) Modifications to Section 2.15.4, Geometry Shader Execution Environment Add a paragraph after the description of gl_Layer, page 124. The built-in special variable gl_ViewportIndex is used to direct rendering to one of several viewports and is discussed in the section entitled "Layer and Viewport Selection", below. Rename the the "Layered Rendering" subsection to "Layer and Viewport Selection" and append the following: Geometry shaders may also select the destination viewport for each output primitive. The destination viewport for a primitive may be selected in the geometry shader by writing to the built-in output variable gl_ViewportIndex. This functionality allows a geometry shader to direct its output to a different viewport for each primitive, or to draw multiple versions of a primitive into several different viewports. The specific vertex of a primitive that is used to select the rendering layer or viewport index is implementation-dependent and thus portable applications will assign the same layer and viewport index for all vertices in a primitive. The vertex conventions followed for gl_Layer and gl_ViewportIndex may be determined by calling GetIntegerv with the symbolic constants LAYER_PROVOKING_VERTEX and VIEWPORT_INDEX_PROVOKING_VERTEX, respectively. For either value, if the value returned is PROVOKING_VERTEX, then vertex selection follows the convention specified by ProvokingVertex (see section 2.21). If the value returned is FIRST_VERTEX_CONVENTION, selection is always taken from the first vertex of a primitive. If the value returned is LAST_VERTEX_CONVENTION, the selection is always taken from the last vertex of a primitive. If the value returned is UNDEFINED_VERTEX, the selection is not guaranteed to be taken from any specific vertex in the primitive. The vertex considered the provoking vertex for particular primitive types is given in Table 2.15. Modify section 2.16.1 "Controlling the Viewport", page 126. Change the first paragraph of section 2.16.1 to read The viewport transformation is determined by the selected viewport's width and height in pixels, p_x and p_y, respectively, and its center (o_x,o_y) (also in pixels) ... { leave equations intact } Multiple viewports are available and are numbered zero through the value of MAX_VIEWPORTS minus one. If a geometry shader is active and writes to gl_ViewportIndex, the viewport transformation uses the viewport corresponding to the value assigned to gl_ViewportIndex taken from an implementation-dependent primitive vertex. If the value of the viewport index is outside the range zero to the value of MAX_VIEWPORTS minus one, the results of the viewport transformation are undefined. If no geometry shader is active, or if the active geometry shader does not write to gl_ViewportIndex, the viewport numbered zero is used by the viewport transformation. A single vertex may be used in more than one individual primitive, in primitives such as TRIANGLE_STRIP. In this case, the viewport transformation is applied separately for each primitive. The factor and offset applied to Z_d for each viewport encoded by n and f are set using void DepthRangeArrayv(uint first, sizei count, const clampd * v); void DepthRangeIndexed(uint index, clampd n, clampd f); void DepthRange(clampd n, clampd f); DepthRangeArrayv is used to specify the depth range for multiple viewports simultaneously. specifies the index of the first viewport to modify and specifies the number of viewports. If ( + ) is greater than the value of MAX_VIEWPORTS then an INVALID_VALUE error will be generated. Viewports whose indices lie outside the range [, + ) are not modified. The parameter contains the address of an array of clampd types specifying near (n) and far (f) for each viewport in that order. DepthRangeIndexed specifies the depth range for a single viewport and is equivalent (assuming no errors are generated) to: clampd v[] = { n, f }; DepthRangeArrayv(index, 1, v); DepthRange sets the depth range for all viewports to the same values and is equivalent (assuming no errors are generated) to: for (uint i = 0; i < MAX_VIEWPORTS; i++) DepthRangeIndexed(i, n, f); Z_w is represented as either ... Replace the end of section 2.16.1, starting from "Viewport transformation parameters are specified using..." Viewport transformation parameters are specified using void ViewportArrayv(uint first, sizei count, const float * v); void Viewport(int x, int y, sizei w, sizei h); void ViewportIndexedf(uint index, float x, float y, float w, float h); void ViewportIndexedfv(uint index, const float * v); ViewportArrayv specifies parameters for multiple viewports simultaneously. specifies the index of the first viewport to modify and specifies the number of viewports. If ( + ) is greater than the value of MAX_VIEWPORTS then an INVALID_VALUE error will be generated. Viewports whose indices lie outside the range [, + ) are not modified. contains the address of an array of floating point values specifying the left (x), bottom (y), width (w) and height (h) of each viewport, in that order. and give the location of the viewport's lower left corner and and give the viewport's width and height, respectively. ViewportIndexedf and ViewportIndexedfv specify parameters for a single viewport and are equivalent (assuming no errors are generated) to: float v[4] = { x, y, w, h }; ViewportArrayv(index, 1, v); and ViewportArrayv(index, 1, v); respectively. Viewport sets the parameters for all viewports to the same values and is equivalent (assuming no errors are generated) to: for (uint i = 0; i < MAX_VIEWPORTS; i++) ViewportIndexedf(i, 1, (float)x, (float)y, (float)w, (float)h); The viewport parameters shown in the above equations are found from these values as o_x = x + w /2, o_y = y + h / 2, p_x = w, p = h. The location of the viewport's bottom-left corner, given by (x,y), are clamped to be within the implementation-dependent viewport bounds range. The viewport bounds range [min, max] tuple may be determined by calling GetFloatv with the symbolic constant VIEWPORT_BOUNDS_RANGE (see section 6.1). Viewport width and height are clamped to implementation-dependent maximums when specified. The maximum width and height may be found by calling GetFloatv with the symbolic constant MAX_VIEWPORT_DIMS. The maximum viewport dimensions must be greater than or equal to the larger of the visible dimensions of the display being rendered to (if a display exists), and the largest renderbuffer image which can be successfully created and attached to a framebuffer object (see chapter 4). INVALID_VALUE is generated if either w or h is negative. The state required to implement the viewport transformations is four floating-point values and two clamped floating-point values for each viewport. In the initial state, w and h for each viewport are set to the width and height, respectively, of the window into which the GL is to do its rendering. If the default framebuffer is bound but no default framebuffer is associated with the GL context (see chapter 4), then w and h are initially set to zero. o_x and o_y are set to w/2 and h/2, respectively. n and f are set to 0.0 and 1.0, respectively. The precision with which the GL interprets the floating point viewport bounds is implementation-dependent and may be determined by querying the implementation-defined constant VIEWPORT_SUBPIXEL_BITS. Additions to Chapter 3 of the OpenGL 3.2 (Compatibility) Specification (Rasterization) None. Additions to Chapter 4 of the OpenGL 3.2 (Compatibility) Specification (Per- Fragment Operations and the Framebuffer) Replace section 4.1.2 "Scissor Test", page 284. The scissor test determines if (xw, yw) lies within the scissor rectangle defined by four values for each viewport. These values are set with void ScissorArrayv(uint first, sizei count, const int * v); void ScissorIndexed(uint index, int left, int bottom, sizei width, sizei height); void ScissorIndexedv(uint index, int * v); void Scissor(int left, int bottom, sizei width, sizei height); ScissorArrayv defines a set of scissor rectangles that are each applied to the corresponding viewport (see section 2.16.1 "Controlling the Viewport"). specifies the index of the first scissor rectangle to modify, and specifies the number of scissor rectangles. If ( + ) is greater than the value of MAX_VIEWPORTS, then an INVALID_VALUE error is generated. contains the address of an array of integers containing the left, bottom, width and height of the scissor rectangles, in that order. If left <= x_w < left + width and bottom <= y_w < bottom + height for the selected scissor rectangle, then the scissor test passes. Otherwise, the test fails and the fragment is discarded. For points, lines, and polygons, the scissor rectangle for a primitive is selected in the same manner as the viewport (see section 2.16.1). For pixel rectangles and bitmaps, the scissor rectangle numbered zero is used for the scissor test. The scissor test is enabled or disabled for all viewports using Enable or Disable with the symbolic constant SCISSOR_TEST. The test is enabled or disabled for a specific viewport using Enablei or Disablei with the constant SCISSOR_TEST and the index of the selected viewport. When disabled, it is as if the scissor test always passes. The value of the scissor test enable for viewport can be queried by calling IsEnabledi with SCISSOR_TEST and . The value of the scissor test enable for viewport zero may also be queried by calling IsEnabled with the same symbolic constant, but no parameter. If either width or height is less than zero for any scissor rectangle, then an INVALID_VALUE error is generated. If the viewport index specified to Enablei, Disablei or IsEnabledi is greater or equal to the value of MAX_VIEWPORTS, then an INVALID_VALUE error is generated. The state required consists of four integer values per viewport, and a bit indicating whether the test is enabled or disabled for each viewport. In the initial state, left = bottom = 0, and width and height are determined by the size of the window into which the GL is to do its rendering for all viewports. If the default framebuffer is bound but no default framebuffer is associated with the GL context (see chapter 4), then with and height are initially set to zero. Initially, the scissor test is disabled for all viewports. ScissorIndexed and ScissorIndexedv specify the scissor rectangle for a single viewport and are equivalent (assuming no errors are generated) to: int v[] = { left, bottom, width, height }; ScissorArrayv(index, 1, v); and ScissorArrayv(index, 1, v); respectively. Scissor sets the scissor rectangle for all viewports to the same values and is equivalent (assuming no errors are generated) to: for (uint i = 0; i < MAX_VIEWPORTS; i++) { ScissorIndexed(i, left, bottom, width, height); } Calling Enable or Disable with the symbolic constant SCISSOR_TEST is equivalent, assuming no errors, to: for (uint i = 0; i < MAX_VIEWPORTS; i++) { Enablei(SCISSOR_TEST, i); /* or */ Disablei(SCISSOR_TEST, i); } Additions to Chapter 5 of the OpenGL 3.2 (Compatibility) Specification (Special Functions) None. Additions to Chapter 6 of the OpenGL 3.2 (Compatibility) Specification (State and State Requests) Modifications to Section 6.1.1 Simple Queries Add to the list of indexed query functions: void GetFloati_v(enum target, uint index, float *data); void GetDoublei_v(enum target, uint index, float *data); Additions to the OpenGL Shading Language Version 1.50.09 Specification Add a new Section 3.3.x, GL_ARB_viewport_array Extension (p. 13) 3.3.x GL_ARB_viewport_array Extension To use the GL_ARB_viewport_array extension in a shader it must be enabled using the #extension directive. The shading language preprocessor #define GL_ARB_viewport_array will be defined to 1 if the GL_ARB_viewport_array extension is supported. Additions to Section 7.1 "Vertex and Geometry Shader Special Variables" Add a paragraph after the paragraph describing gl_Layer, starting "The built-in output variable gl_Layer is available only in the geometry language, and provides the number of the layer of textures attached to a FBO to direct rendering to.": The built-in output variable gl_ViewportIndex is available only in the geometry language, and provides the index of the viewport to which the next primitive emitted from the geometry shader should be drawn. Primitives generated by the geometry shader will undergo viewport transformation and scissor testing using the viewport transformation and scissor rectangle selected by the value of gl_ViewportIndex. The viewport index used will come from one of the vertices in the primitive being shaded. Which vertex the viewport index comes from is implementation-dependent, so it is best to use the same viewport index for all vertices of the primitive. If a geometry shader does not assign a value to gl_ViewportIndex, viewport transform and scissor rectangle zero will be used. If a geometry shader assigns a value to gl_ViewportIndex and there is a path through the shader that does not set gl_ViewportIndex, then the value of gl_ViewportIndex is undefined for executions of the shader that take that path. See section 2.15.4, under "Geometry Shader Outputs" for more information. Add to the list of geometry shader built-in variables on p. 69: out int gl_ViewportIndex; // may be written to Modify the description of EmitVertex() in Section 8.10, "Geometry Shader Functions", page 104: The function EmitVertex() specifies that a vertex is completed. A vertex is added to the current output primitive using the current values of the geometry shader's output variables, including gl_PointSize, gl_ClipDistance, gl_Layer, gl_Position, gl_PrimitiveID and gl_ViewportIndex. The values of all these... Add to the list of built in constants available to geometry shaders in Section 7.4: const int gl_MaxViewports = 16; Additions to the AGL/GLX/WGL Specifications None. GLX Protocol TBD. Errors INVALID_VALUE is generated by ViewportArrayv if + is greater than or equal to the value of MAX_VIEWPORTS, or if any viewport's width or height is less than 0. INVALID_VALUE is generated by ScissorArrayv if + is greater than or equal to the value of MAX_VIEWPORTS, or if any scissor rectangle's width or height is less than zero. INVALID_VALUE is generated by DepthRangeArrayv if + is greater than or equal to the vaue of MAX_VIEWPORTS. INVALID_VALUE is generated by Enablei, Disablei and IsEnabledi if is greater than or equal to the value of MAX_VIEWPORTS. New State Table 6.13 (p. 405) Get Value Type Get Command Initial Value Description Sec Attribute ------------------------ ---------------- ------------ ------------- ------------------------ ----- --------- VIEWPORT 16* x 4 x R GetFloati_v See 2.11.1 Viewport origin & extent 2.11.1 viewport DEPTH_RANGE 16* x 2 x R[0,1] GetDoublei_v See 2.16.1 Depth range near & far 2.16.1 viewport NOTE: The changes are that VIEWPORT and DEPTH_RANGE are extended to accommodate 16* copies and now consist of floating-point and double-precision values, respectively. Table 6.26 (p. 418) Get Value Type Get Command Initial Value Description Sec Attribute ------------------------ ---------- ------------- ------------- ------------------- ----- --------- SCISSOR_TEST 16* x B IsEnabledi FALSE Scissoring enabled 4.1.2 scissor/enable SCISSOR_BOX 16* x 4 x Z GetIntegeri_v See 4.1.2 Scissor box 4.1.2 scissor NOTE: The only change is that SCISSOR_TEST and SCISSOR_BOX are extended to accommodate 16* copies. New Implementation Dependent State Get Value Type Get Command Minimum Value Description Sec. --------- ---- ----------- ------------- ------------------- ----- MAX_VIEWPORT_DIMS (NOTE 1) 2 x Z+ GetFloatv See 2.16.1 Maximum viewport dimensions 2.16.1 MAX_VIEWPORTS Z+ GetIntegerv 16 Maximum number of 2.16.1 active viewports VIEWPORT_SUBPIXEL_BITS Z+ GetIntegerv 0 Number of bits of sub-pixel 2.16.1 precision for viewport bounds VIEWPORT_BOUNDS_RANGE 2 x R GetFloatv (NOTE 2) Viewport bounds range [min,max] 2.16.1 LAYER_PROVOKING_VERTEX Z_4 GetIntegerv -- (NOTE 3) vertex convention followed by 2.15.4 the gl_Layer GLSL variable VIEWPORT_INDEX_PROVOKING_VERTEX Z_4 GetIntegerv -- (NOTE 3) vertex convention followed by 2.15.4 the gl_ViewportIndex GLSL variable NOTE 1: The recommended get command is changed from GetIntegerv to GetFloatv. NOTE 2: range for viewport bounds: * On GL3-capable hardware the VIEWPORT_BOUNDS_RANGE should be at least [-16384, 16383]. * On GL4-capable hardware the VIEWPORT_BOUNDS_RANGE should be at least [-32768, 32767]. NOTE 3: Valid values are: FIRST_VERTEX_CONVENTION, LAST_VERTEX_CONVENTION, PROVOKING_VERTEX, UNDEFINED_VERTEX. Interactions with NV_depth_buffer_float If NV_depth_buffer_float is supported, add the following commands: void DepthRangeArraydvNV(uint first, sizei count, const double * v); void DepthRangeIndexeddNV(uint index, double n, double f); These functions are equivalent to the corresponding DepthRange* functions, except the the parameters are clamped to [0, 1] when using DepthRange*, but not when using DepthRange*dNV. When and are applied to , they are clamped to the range appropriate given the depth buffer's representation. Interactions with ARB_provoking_vertex, EXT_provoking_vertex, and OpenGL 3.2 or later If none of ARB_provoking_vertex, EXT_provoking_vertex or OpenGL 3.2 or later are supported, ignore all references to ProvokingVertex and PROVOKING_VERTEX. This extension will continue to require support for the LAYER_PROVOKING_VERTEX and VIEWPORT_INDEX_PROVOKING_VERTEX queries, but only FIRST_VERTEX_CONVENTION, LAST_VERTEX_CONVENTION, or UNDEFINED_VERTEX will be enumerated. Dependencies on compatibitliy profile contexts Pixel rectangle primitives and bitmaps are available only in compatibility profile contexts. In the core profile, references to pixel rectangles and bitmaps are removed from the description of scissor rectangles in section 4.1.2. Interactions with NV_geometry_program4 If NV_geometry_program4 is supported and the "ARB_viewport_array" program option is specified, geometry result variable "result.viewport" can be used to specify the viewport array index to use for primitive viewport transformations and scissoring. (add the following rule to the NV_geometry_program4 grammar) ::= ... | "viewport" (add the following to Table X.3, Geometry Program Result Variable Bindings) Binding Components Description ----------------------------- ---------- ---------------------------- result.viewport (v,*,*,*) viewport array index (add the following to Section 2.X.2, Program Grammar) If a result variable binding matches "result.viewport", updates to the "x" component of the result variable provide a single integer that serves as a viewport index specifier for viewport arrays. The index must be written as an integer value; writing a floating-point value will produce undefined results. If a value outside the range [0, MAX_VIEWPORTS-1] is given, the behavior is to proceed as if viewport index 0 was selected. If the "ARB_viewport_array" program option is not specified, the "result.viewport" binding is unavailable. (add the following to Section 2.X.6.Y, Geometry Program Options) + Viewport Array (ARB_viewport_array) If a geometry program specifies the "ARB_viewport_array" option, the result binding "result.viewport" will be available to specify the viewport index to use for primitive viewport transformations and scissoring as described in section 2.X.2. Issues 1) The name glViewportArray infers dynamic behavior and that the GL may use values that present in the array at draw time. Would it be more consistent to call this glViewportiv or glViewportv? UNRESOLVED: For now, we'll leave it as glViewportArray. 2) Should we provide a mechanism to write gl_ViewportIndex in the vertex shader? This would allow an application to assign gl_ViewportIndex based on the value of a uniform, or from data read through an attribute, for example. RESOLVED: No. While it may be possible, there is no compelling use case, and gl_Layer whose precedent we follow here, is not writable in the vertex shader. 3) Does legacy glViewport update just the first viewport, or all of them? RESOLVED: glViewport is equivalent to calling glViewportArray with an array containing a single viewport once for each supported viewport. It therefore defines all viewports in a single call. This is also true for the legacy glScissor, glDepthRange, glEnable and glDisable functions. 4) When EXT_provoking_vertex is supported, is the provoking vertex convention honored when selecting which vertex the gl_ViewportIndex property is to use? RESOLVED: It is desirable that the provoking vertex convention should be honored when selecting the vertex which gl_ViewportIndex is obtained from (and similarly for gl_Layer). Other APIs require that these properties should be taken from the "leading vertex", and this for maximum content portability, it is desireable to be able to configure the pipeline in the same way. However, there exists hardware which would otherwise be able to support these features which does not have the capability to configure which vertex this is selected from (even though it may be doing so in a content-portable way). The LAYER_PROVOKING_VERTEX and VIEWPORT_INDEX_PROVOKING_VERTEX queries have been added to allow applications to determine if the provoking vertex convention is being followed, and if not, which convention is being used (if any). Note that applications which are creating new content are advised that writing the same gl_ViewportIndex and gl_Layer to all the vertices of a primitive is the only portable solution. 5) Why glViewportIndex rather than glEnablei, and so on? DISCUSSION: This extension follows the precedent of extensions such as EXT_draw_buffers2, which introduced glEnableIndexed. These 'indexed' functions since have been promoted to core OpenGL as glEnablei. If this extension is used on an implementation supporting the glEnablei style indexed functions, those may be used instead of, or in conjunction with the glXXXXIndexed style indexed functions. 6) What happens if the viewport bounds lie on exact half-pixel coordinates? For example, on a multi-sample surface, which samples should be considered 'inside' the viewport? DISCUSSION: The viewport transformation includes clipping. Assuming this clipping has similar precision to the viewport transform itself, then the resulting clipped primitives should cut through partial pixels, lighting only some of the samples within the pixel. FEEDBACK FROM PAT: This discussion is technically incorrect -- the viewport transformation technically does *NOT* include any clipping. However, for geometric primitives, the viewport transformation is applied to vertices post-clipping (despite the fact that it precedes clipping in the spec), so there is some clipping in the vicinity of the viewport transformation. "Guardband clipping" is an alternate implementation, producing nearly equivalent results to those specified by OpenGL. When using guardband clipping, primitives are not clipped tightly to the view volume in X/Y: -w <= x <= w -w <= y <= w Instead, looser (or no) clipping is applied, for example: -8w <= x <= 8w -8w <= y <= 8w Since primitives are clipped far less aggressively, something has to be done to produce results similar to those with aggressive clipping. To do this, such implementations will enable per-pixel scissoring to the viewport rectangle. There are several areas of difference that implementations using guardband clipping need to deal with (or ignore): * line- and point-mode polygons: The OpenGL spec says that lines should be drawn along the edges of polygons clipped to the frustum. If you don't clip tightly, you can't draw those edges. (NOTE: The behavior specified by OpenGL ends up being somewhat shitty. Let's say you have a line-mode primitive clipped by both the left and right side of the frustum, which implies that you should have vertical edges on the left and right side of the viewport. With integer viewport coordinates, both edges will be exactly between pixel centers. In practice, implementations' tiebreaking rules will have either the left or right edge light up pixels outside the viewport. If the viewport is the full window, this means that one of those lines won't be visible. * wide points and lines: According to the OpenGL spec, line or point primitives on or near the edge of the viewport should technically extend outside the viewport. For example, a four-pixel point on the left edge of the viewport should light up eight pixels (2x4) outside the left edge of the viewport. The scissoring used for guardband clipping will discard those pixels. In my opinion, the scissored results are preferable to those called for by the spec. Of course, with the OpenGL spec behavior, there are no visible artifacts if: (a) the viewport covers the entire window or (b) the application scissors manually itself. Fractional viewports make things more complicated, particularly if the implementation doesn't scissor at a per-sample granularity. In this case, tight view volume clipping will result in primitives that are fully contained within the fractional viewport (to the limits of clipping math, at least). Guardband clipping will have primitives that extend beyond the viewport and probably cover full pixels at the boundary of the viewport. (This discussion assumes that a guardband implementation with fractional viewports extends its viewport clip to pass on pixels containing any fraction of the floating-point viewport.) Direct3D 11 specifies that rasterization along the one-pixel edges of fractional viewports to be undefined. If implementations want defined behavior with fractional viewports, they can program a slightly wider viewport and scissor away the pixels along the edge of the expanded viewport. My recommendation is as follows: (1) Edit the clipping section of the spec to explicitly permit implementations to clip to larger view volume extents in (x,y) and instead scissor to the viewport rectangle. Note that this scissor rectangle needs to either be separate from the API-level scissor rectangle, or intersected with it. This scissoring would always have to be enabled, regardless of the SCISSOR enabled. (2) Edit the viewport section of the spec to briefly discuss the implications of fractional viewports on the newly permitted scissoring. 7) What is the VIEWPORT_SUBPIXEL_BITS implementation defined value for? This allows an application to query the precision of the viewport transform. More specifically, if VIEWPORT_SUBPIXEL_BITS is zero, then this indicates that the viewport bounds are likely implemented using integers in hardware. If there are more bits (such as fixed point) then this value will be non-zero. If the implementation truely has floating point viewport bounds, it may report a sufficiently high value to indicate this. 8) What happened to glGetIntegerv(GL_VIEWPORT, v)? It still works. You can query floating point state with an integer query. You'll get a rounded version of the state. You can also query indexed state with a non-indexed query - you'll get the state for index 0. Thus glGetIntegerv(GL_VIEWPORT, v) is the same as glGetIntegeri_v(GL_VIEWPORT, 0, v), which is legal. Revision History Rev. Date Author Changes ---- -------- -------- ----------------------------------------- 18 06/25/2012 Jon Leech Fixed GetIntegerIndexedivEXT -> GetIntegerIndexedvEXT typo (Bug 6694). 17 07/25/2010 Jon Leech Fix typo in ViewportArrayv pseudocode (Bug 6682). 16 07/19/2010 Jon Leech Add GetDoublei_v entry point and change state for DEPTH_RANGE to be indexed and queryable with this command (Bug 6495). Reflow a few paragraphs and sync language with 4.1 API spec. 15 06/16/2010 istewart Add interaction with NV_geometry_program4. 14 05/26/2010 Jon Leech Fix minor typos, remove tabs, make language more consistent with GL core spec in some places, and reflow paragraphs following changes. 13 05/18/2010 gsellers Rename to ARB_viewport_array. ARBify. Remove suffixes for Core 4.1. 12 05/17/2010 gsellers Error is not generated for viewport bounds outside VIEWPORT_BOUNDS_RANGE. Incoporate feedback from pbrown. 11 05/11/2010 gsellers Incorporate feedback from bmerry. 10 05/10/2010 dgkoch allow UNDEFINED_VERTEX_EXT for compatibility 9 05/10/2010 dgkoch add VIEWPORT_BOUNDS_RANGE and clarify the valid values for the viewport location. added queries to determing layer and viewport index provoking vertex convention. updated issue 4. 8 05/06/2010 gsellers Remove error if viewport > MAX_VIEWPORT_DIMS. Fix typo in definition of glScissorIndexedv. Update description of ViewportArrayv to accept an array of floats, rather than an array of integers. 7 04/29/2010 gsellers Updates and clarifications. 6 04/15/2010 gsellers Add interaction with NV_depth_range. Change viewport bounds to floating point values. Add viewport subpixel precision query. Chage function names to ...Indexed. Add issues 6 and 7. 5 01/07/2010 gsellers Change from AMD to EXT Change function prototypes Add glViewporti{_v}. Add glScissorArray, glScissori{_v}. Add glDepthRangeArrayv, glDepthRangei. 4 07/16/2009 gsellers Document EXT_provoking_vertex interaction. Change 'leading vertex' to 'provoking vertex'. Clarify interaction with glViewport. Add multiple scissor rectangles. 3 07/14/2009 gsellers Updates from nickh and wwlk 2 07/08/2009 gsellers Updates from myoung 1 07/06/2009 gsellers Initial draft