Home | History | Annotate | Download | only in main
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 2009  VMware, Inc.  All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included
     14  * in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 
     26 /**
     27  * \file viewport.c
     28  * glViewport and glDepthRange functions.
     29  */
     30 
     31 
     32 #include "context.h"
     33 #include "enums.h"
     34 #include "macros.h"
     35 #include "mtypes.h"
     36 #include "viewport.h"
     37 
     38 static void
     39 set_viewport_no_notify(struct gl_context *ctx, unsigned idx,
     40                        GLfloat x, GLfloat y,
     41                        GLfloat width, GLfloat height)
     42 {
     43    /* clamp width and height to the implementation dependent range */
     44    width  = MIN2(width, (GLfloat) ctx->Const.MaxViewportWidth);
     45    height = MIN2(height, (GLfloat) ctx->Const.MaxViewportHeight);
     46 
     47    /* The GL_ARB_viewport_array spec says:
     48     *
     49     *     "The location of the viewport's bottom-left corner, given by (x,y),
     50     *     are clamped to be within the implementation-dependent viewport
     51     *     bounds range.  The viewport bounds range [min, max] tuple may be
     52     *     determined by calling GetFloatv with the symbolic constant
     53     *     VIEWPORT_BOUNDS_RANGE (see section 6.1)."
     54     */
     55    if (ctx->Extensions.ARB_viewport_array ||
     56        (ctx->Extensions.OES_viewport_array &&
     57         _mesa_is_gles31(ctx))) {
     58       x = CLAMP(x,
     59                 ctx->Const.ViewportBounds.Min, ctx->Const.ViewportBounds.Max);
     60       y = CLAMP(y,
     61                 ctx->Const.ViewportBounds.Min, ctx->Const.ViewportBounds.Max);
     62    }
     63 
     64    if (ctx->ViewportArray[idx].X == x &&
     65        ctx->ViewportArray[idx].Width == width &&
     66        ctx->ViewportArray[idx].Y == y &&
     67        ctx->ViewportArray[idx].Height == height)
     68       return;
     69 
     70    ctx->ViewportArray[idx].X = x;
     71    ctx->ViewportArray[idx].Width = width;
     72    ctx->ViewportArray[idx].Y = y;
     73    ctx->ViewportArray[idx].Height = height;
     74    ctx->NewState |= _NEW_VIEWPORT;
     75 }
     76 
     77 struct gl_viewport_inputs {
     78    GLfloat X, Y;                /**< position */
     79    GLfloat Width, Height;       /**< size */
     80 };
     81 
     82 struct gl_depthrange_inputs {
     83    GLdouble Near, Far;          /**< Depth buffer range */
     84 };
     85 
     86 /**
     87  * Set the viewport.
     88  * \sa Called via glViewport() or display list execution.
     89  *
     90  * Flushes the vertices and calls _mesa_set_viewport() with the given
     91  * parameters.
     92  */
     93 void GLAPIENTRY
     94 _mesa_Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
     95 {
     96    unsigned i;
     97    GET_CURRENT_CONTEXT(ctx);
     98    FLUSH_VERTICES(ctx, 0);
     99 
    100    if (MESA_VERBOSE & VERBOSE_API)
    101       _mesa_debug(ctx, "glViewport %d %d %d %d\n", x, y, width, height);
    102 
    103    if (width < 0 || height < 0) {
    104       _mesa_error(ctx,  GL_INVALID_VALUE,
    105                    "glViewport(%d, %d, %d, %d)", x, y, width, height);
    106       return;
    107    }
    108 
    109    /* The GL_ARB_viewport_array spec says:
    110     *
    111     *     "Viewport sets the parameters for all viewports to the same values
    112     *     and is equivalent (assuming no errors are generated) to:
    113     *
    114     *     for (uint i = 0; i < MAX_VIEWPORTS; i++)
    115     *         ViewportIndexedf(i, 1, (float)x, (float)y, (float)w, (float)h);"
    116     *
    117     * Set all of the viewports supported by the implementation, but only
    118     * signal the driver once at the end.
    119     */
    120    for (i = 0; i < ctx->Const.MaxViewports; i++)
    121       set_viewport_no_notify(ctx, i, x, y, width, height);
    122 
    123    if (ctx->Driver.Viewport) {
    124       /* Many drivers will use this call to check for window size changes
    125        * and reallocate the z/stencil/accum/etc buffers if needed.
    126        */
    127       ctx->Driver.Viewport(ctx);
    128    }
    129 }
    130 
    131 
    132 /**
    133  * Set new viewport parameters and update derived state.
    134  * Usually called from _mesa_Viewport().
    135  *
    136  * \param ctx GL context.
    137  * \param idx    Index of the viewport to be updated.
    138  * \param x, y coordinates of the lower left corner of the viewport rectangle.
    139  * \param width width of the viewport rectangle.
    140  * \param height height of the viewport rectangle.
    141  */
    142 void
    143 _mesa_set_viewport(struct gl_context *ctx, unsigned idx, GLfloat x, GLfloat y,
    144                     GLfloat width, GLfloat height)
    145 {
    146    set_viewport_no_notify(ctx, idx, x, y, width, height);
    147 
    148    if (ctx->Driver.Viewport) {
    149       /* Many drivers will use this call to check for window size changes
    150        * and reallocate the z/stencil/accum/etc buffers if needed.
    151        */
    152       ctx->Driver.Viewport(ctx);
    153    }
    154 }
    155 
    156 void GLAPIENTRY
    157 _mesa_ViewportArrayv(GLuint first, GLsizei count, const GLfloat *v)
    158 {
    159    int i;
    160    const struct gl_viewport_inputs *const p = (struct gl_viewport_inputs *) v;
    161    GET_CURRENT_CONTEXT(ctx);
    162 
    163    if (MESA_VERBOSE & VERBOSE_API)
    164       _mesa_debug(ctx, "glViewportArrayv %d %d\n", first, count);
    165 
    166    if ((first + count) > ctx->Const.MaxViewports) {
    167       _mesa_error(ctx, GL_INVALID_VALUE,
    168                   "glViewportArrayv: first (%d) + count (%d) > MaxViewports "
    169                   "(%d)",
    170                   first, count, ctx->Const.MaxViewports);
    171       return;
    172    }
    173 
    174    /* Verify width & height */
    175    for (i = 0; i < count; i++) {
    176       if (p[i].Width < 0 || p[i].Height < 0) {
    177          _mesa_error(ctx, GL_INVALID_VALUE,
    178                      "glViewportArrayv: index (%d) width or height < 0 "
    179                      "(%f, %f)",
    180                      i + first, p[i].Width, p[i].Height);
    181          return;
    182       }
    183    }
    184 
    185    for (i = 0; i < count; i++)
    186       set_viewport_no_notify(ctx, i + first,
    187                              p[i].X, p[i].Y,
    188                              p[i].Width, p[i].Height);
    189 
    190    if (ctx->Driver.Viewport)
    191       ctx->Driver.Viewport(ctx);
    192 }
    193 
    194 static void
    195 ViewportIndexedf(GLuint index, GLfloat x, GLfloat y,
    196                  GLfloat w, GLfloat h, const char *function)
    197 {
    198    GET_CURRENT_CONTEXT(ctx);
    199 
    200    if (MESA_VERBOSE & VERBOSE_API)
    201       _mesa_debug(ctx, "%s(%d, %f, %f, %f, %f)\n",
    202                   function, index, x, y, w, h);
    203 
    204    if (index >= ctx->Const.MaxViewports) {
    205       _mesa_error(ctx, GL_INVALID_VALUE,
    206                   "%s: index (%d) >= MaxViewports (%d)",
    207                   function, index, ctx->Const.MaxViewports);
    208       return;
    209    }
    210 
    211    /* Verify width & height */
    212    if (w < 0 || h < 0) {
    213       _mesa_error(ctx, GL_INVALID_VALUE,
    214                   "%s: index (%d) width or height < 0 (%f, %f)",
    215                   function, index, w, h);
    216       return;
    217    }
    218 
    219    _mesa_set_viewport(ctx, index, x, y, w, h);
    220 }
    221 
    222 void GLAPIENTRY
    223 _mesa_ViewportIndexedf(GLuint index, GLfloat x, GLfloat y,
    224                        GLfloat w, GLfloat h)
    225 {
    226    ViewportIndexedf(index, x, y, w, h, "glViewportIndexedf");
    227 }
    228 
    229 void GLAPIENTRY
    230 _mesa_ViewportIndexedfv(GLuint index, const GLfloat *v)
    231 {
    232    ViewportIndexedf(index, v[0], v[1], v[2], v[3], "glViewportIndexedfv");
    233 }
    234 
    235 static void
    236 set_depth_range_no_notify(struct gl_context *ctx, unsigned idx,
    237                           GLclampd nearval, GLclampd farval)
    238 {
    239    if (ctx->ViewportArray[idx].Near == nearval &&
    240        ctx->ViewportArray[idx].Far == farval)
    241       return;
    242 
    243    ctx->ViewportArray[idx].Near = CLAMP(nearval, 0.0, 1.0);
    244    ctx->ViewportArray[idx].Far = CLAMP(farval, 0.0, 1.0);
    245    ctx->NewState |= _NEW_VIEWPORT;
    246 }
    247 
    248 void
    249 _mesa_set_depth_range(struct gl_context *ctx, unsigned idx,
    250                       GLclampd nearval, GLclampd farval)
    251 {
    252    set_depth_range_no_notify(ctx, idx, nearval, farval);
    253 
    254    if (ctx->Driver.DepthRange)
    255       ctx->Driver.DepthRange(ctx);
    256 }
    257 
    258 /**
    259  * Called by glDepthRange
    260  *
    261  * \param nearval  specifies the Z buffer value which should correspond to
    262  *                 the near clip plane
    263  * \param farval  specifies the Z buffer value which should correspond to
    264  *                the far clip plane
    265  */
    266 void GLAPIENTRY
    267 _mesa_DepthRange(GLclampd nearval, GLclampd farval)
    268 {
    269    unsigned i;
    270    GET_CURRENT_CONTEXT(ctx);
    271 
    272    FLUSH_VERTICES(ctx, 0);
    273 
    274    if (MESA_VERBOSE&VERBOSE_API)
    275       _mesa_debug(ctx, "glDepthRange %f %f\n", nearval, farval);
    276 
    277    /* The GL_ARB_viewport_array spec says:
    278     *
    279     *     "DepthRange sets the depth range for all viewports to the same
    280     *     values and is equivalent (assuming no errors are generated) to:
    281     *
    282     *     for (uint i = 0; i < MAX_VIEWPORTS; i++)
    283     *         DepthRangeIndexed(i, n, f);"
    284     *
    285     * Set the depth range for all of the viewports supported by the
    286     * implementation, but only signal the driver once at the end.
    287     */
    288    for (i = 0; i < ctx->Const.MaxViewports; i++)
    289       set_depth_range_no_notify(ctx, i, nearval, farval);
    290 
    291    if (ctx->Driver.DepthRange) {
    292       ctx->Driver.DepthRange(ctx);
    293    }
    294 }
    295 
    296 void GLAPIENTRY
    297 _mesa_DepthRangef(GLclampf nearval, GLclampf farval)
    298 {
    299    _mesa_DepthRange(nearval, farval);
    300 }
    301 
    302 /**
    303  * Update a range DepthRange values
    304  *
    305  * \param first   starting array index
    306  * \param count   count of DepthRange items to update
    307  * \param v       pointer to memory containing
    308  *                GLclampd near and far clip-plane values
    309  */
    310 void GLAPIENTRY
    311 _mesa_DepthRangeArrayv(GLuint first, GLsizei count, const GLclampd *v)
    312 {
    313    int i;
    314    const struct gl_depthrange_inputs *const p =
    315       (struct gl_depthrange_inputs *) v;
    316    GET_CURRENT_CONTEXT(ctx);
    317 
    318    if (MESA_VERBOSE & VERBOSE_API)
    319       _mesa_debug(ctx, "glDepthRangeArrayv %d %d\n", first, count);
    320 
    321    if ((first + count) > ctx->Const.MaxViewports) {
    322       _mesa_error(ctx, GL_INVALID_VALUE,
    323                   "glDepthRangev: first (%d) + count (%d) >= MaxViewports (%d)",
    324                   first, count, ctx->Const.MaxViewports);
    325       return;
    326    }
    327 
    328    for (i = 0; i < count; i++)
    329       set_depth_range_no_notify(ctx, i + first, p[i].Near, p[i].Far);
    330 
    331    if (ctx->Driver.DepthRange)
    332       ctx->Driver.DepthRange(ctx);
    333 }
    334 
    335 void GLAPIENTRY
    336 _mesa_DepthRangeArrayfvOES(GLuint first, GLsizei count, const GLfloat *v)
    337 {
    338    int i;
    339    GET_CURRENT_CONTEXT(ctx);
    340 
    341    if (MESA_VERBOSE & VERBOSE_API)
    342       _mesa_debug(ctx, "glDepthRangeArrayfv %d %d\n", first, count);
    343 
    344    if ((first + count) > ctx->Const.MaxViewports) {
    345       _mesa_error(ctx, GL_INVALID_VALUE,
    346                   "glDepthRangeArrayfv: first (%d) + count (%d) >= MaxViewports (%d)",
    347                   first, count, ctx->Const.MaxViewports);
    348       return;
    349    }
    350 
    351    for (i = 0; i < count; i++)
    352       set_depth_range_no_notify(ctx, i + first, v[i * 2], v[i * 2 + 1]);
    353 
    354    if (ctx->Driver.DepthRange)
    355       ctx->Driver.DepthRange(ctx);
    356 }
    357 
    358 /**
    359  * Update a single DepthRange
    360  *
    361  * \param index    array index to update
    362  * \param nearval  specifies the Z buffer value which should correspond to
    363  *                 the near clip plane
    364  * \param farval   specifies the Z buffer value which should correspond to
    365  *                 the far clip plane
    366  */
    367 void GLAPIENTRY
    368 _mesa_DepthRangeIndexed(GLuint index, GLclampd nearval, GLclampd farval)
    369 {
    370    GET_CURRENT_CONTEXT(ctx);
    371 
    372    if (MESA_VERBOSE & VERBOSE_API)
    373       _mesa_debug(ctx, "glDepthRangeIndexed(%d, %f, %f)\n",
    374                   index, nearval, farval);
    375 
    376    if (index >= ctx->Const.MaxViewports) {
    377       _mesa_error(ctx, GL_INVALID_VALUE,
    378                   "glDepthRangeIndexed: index (%d) >= MaxViewports (%d)",
    379                   index, ctx->Const.MaxViewports);
    380       return;
    381    }
    382 
    383    _mesa_set_depth_range(ctx, index, nearval, farval);
    384 }
    385 
    386 void GLAPIENTRY
    387 _mesa_DepthRangeIndexedfOES(GLuint index, GLfloat nearval, GLfloat farval)
    388 {
    389    _mesa_DepthRangeIndexed(index, nearval, farval);
    390 }
    391 
    392 /**
    393  * Initialize the context viewport attribute group.
    394  * \param ctx  the GL context.
    395  */
    396 void _mesa_init_viewport(struct gl_context *ctx)
    397 {
    398    unsigned i;
    399 
    400    ctx->Transform.ClipOrigin = GL_LOWER_LEFT;
    401    ctx->Transform.ClipDepthMode = GL_NEGATIVE_ONE_TO_ONE;
    402 
    403    /* Note: ctx->Const.MaxViewports may not have been set by the driver yet,
    404     * so just initialize all of them.
    405     */
    406    for (i = 0; i < MAX_VIEWPORTS; i++) {
    407       /* Viewport group */
    408       ctx->ViewportArray[i].X = 0;
    409       ctx->ViewportArray[i].Y = 0;
    410       ctx->ViewportArray[i].Width = 0;
    411       ctx->ViewportArray[i].Height = 0;
    412       ctx->ViewportArray[i].Near = 0.0;
    413       ctx->ViewportArray[i].Far = 1.0;
    414    }
    415 }
    416 
    417 
    418 extern void GLAPIENTRY
    419 _mesa_ClipControl(GLenum origin, GLenum depth)
    420 {
    421    GET_CURRENT_CONTEXT(ctx);
    422 
    423    if (MESA_VERBOSE&VERBOSE_API)
    424       _mesa_debug(ctx, "glClipControl(%s, %s)\n",
    425 	          _mesa_enum_to_string(origin),
    426                   _mesa_enum_to_string(depth));
    427 
    428    ASSERT_OUTSIDE_BEGIN_END(ctx);
    429 
    430    if (!ctx->Extensions.ARB_clip_control) {
    431       _mesa_error(ctx, GL_INVALID_OPERATION, "glClipControl");
    432       return;
    433    }
    434 
    435    if (origin != GL_LOWER_LEFT && origin != GL_UPPER_LEFT) {
    436       _mesa_error(ctx, GL_INVALID_ENUM, "glClipControl");
    437       return;
    438    }
    439 
    440    if (depth != GL_NEGATIVE_ONE_TO_ONE && depth != GL_ZERO_TO_ONE) {
    441       _mesa_error(ctx, GL_INVALID_ENUM, "glClipControl");
    442       return;
    443    }
    444 
    445    if (ctx->Transform.ClipOrigin == origin &&
    446        ctx->Transform.ClipDepthMode == depth)
    447       return;
    448 
    449    /* Affects transform state and the viewport transform */
    450    FLUSH_VERTICES(ctx, _NEW_TRANSFORM | _NEW_VIEWPORT);
    451 
    452    if (ctx->Transform.ClipOrigin != origin) {
    453       ctx->Transform.ClipOrigin = origin;
    454 
    455       /* Affects the winding order of the front face. */
    456       ctx->NewState |= _NEW_POLYGON;
    457 
    458       if (ctx->Driver.FrontFace)
    459          ctx->Driver.FrontFace(ctx, ctx->Polygon.FrontFace);
    460    }
    461 
    462    if (ctx->Transform.ClipDepthMode != depth) {
    463       ctx->Transform.ClipDepthMode = depth;
    464 
    465       if (ctx->Driver.DepthRange)
    466          ctx->Driver.DepthRange(ctx);
    467    }
    468 }
    469 
    470 /**
    471  * Computes the scaling and the translation part of the
    472  * viewport transform matrix of the \param i-th viewport
    473  * and writes that into \param scale and \param translate.
    474  */
    475 void
    476 _mesa_get_viewport_xform(struct gl_context *ctx, unsigned i,
    477                          float scale[3], float translate[3])
    478 {
    479    float x = ctx->ViewportArray[i].X;
    480    float y = ctx->ViewportArray[i].Y;
    481    float half_width = 0.5f * ctx->ViewportArray[i].Width;
    482    float half_height = 0.5f * ctx->ViewportArray[i].Height;
    483    double n = ctx->ViewportArray[i].Near;
    484    double f = ctx->ViewportArray[i].Far;
    485 
    486    scale[0] = half_width;
    487    translate[0] = half_width + x;
    488    if (ctx->Transform.ClipOrigin == GL_UPPER_LEFT) {
    489       scale[1] = -half_height;
    490    } else {
    491       scale[1] = half_height;
    492    }
    493    translate[1] = half_height + y;
    494 
    495    if (ctx->Transform.ClipDepthMode == GL_NEGATIVE_ONE_TO_ONE) {
    496       scale[2] = 0.5 * (f - n);
    497       translate[2] = 0.5 * (n + f);
    498    } else {
    499       scale[2] = f - n;
    500       translate[2] = n;
    501    }
    502 }
    503