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 clamp_viewport(struct gl_context *ctx, GLfloat *x, GLfloat *y, 40 GLfloat *width, GLfloat *height) 41 { 42 /* clamp width and height to the implementation dependent range */ 43 *width = MIN2(*width, (GLfloat) ctx->Const.MaxViewportWidth); 44 *height = MIN2(*height, (GLfloat) ctx->Const.MaxViewportHeight); 45 46 /* The GL_ARB_viewport_array spec says: 47 * 48 * "The location of the viewport's bottom-left corner, given by (x,y), 49 * are clamped to be within the implementation-dependent viewport 50 * bounds range. The viewport bounds range [min, max] tuple may be 51 * determined by calling GetFloatv with the symbolic constant 52 * VIEWPORT_BOUNDS_RANGE (see section 6.1)." 53 */ 54 if (_mesa_has_ARB_viewport_array(ctx) || 55 _mesa_has_OES_viewport_array(ctx)) { 56 *x = CLAMP(*x, 57 ctx->Const.ViewportBounds.Min, ctx->Const.ViewportBounds.Max); 58 *y = CLAMP(*y, 59 ctx->Const.ViewportBounds.Min, ctx->Const.ViewportBounds.Max); 60 } 61 } 62 63 static void 64 set_viewport_no_notify(struct gl_context *ctx, unsigned idx, 65 GLfloat x, GLfloat y, 66 GLfloat width, GLfloat height) 67 { 68 if (ctx->ViewportArray[idx].X == x && 69 ctx->ViewportArray[idx].Width == width && 70 ctx->ViewportArray[idx].Y == y && 71 ctx->ViewportArray[idx].Height == height) 72 return; 73 74 FLUSH_VERTICES(ctx, ctx->DriverFlags.NewViewport ? 0 : _NEW_VIEWPORT); 75 ctx->NewDriverState |= ctx->DriverFlags.NewViewport; 76 77 ctx->ViewportArray[idx].X = x; 78 ctx->ViewportArray[idx].Width = width; 79 ctx->ViewportArray[idx].Y = y; 80 ctx->ViewportArray[idx].Height = height; 81 } 82 83 struct gl_viewport_inputs { 84 GLfloat X, Y; /**< position */ 85 GLfloat Width, Height; /**< size */ 86 }; 87 88 struct gl_depthrange_inputs { 89 GLdouble Near, Far; /**< Depth buffer range */ 90 }; 91 92 static void 93 viewport(struct gl_context *ctx, GLint x, GLint y, GLsizei width, 94 GLsizei height) 95 { 96 struct gl_viewport_inputs input = { x, y, width, height }; 97 98 /* Clamp the viewport to the implementation dependent values. */ 99 clamp_viewport(ctx, &input.X, &input.Y, &input.Width, &input.Height); 100 101 /* The GL_ARB_viewport_array spec says: 102 * 103 * "Viewport sets the parameters for all viewports to the same values 104 * and is equivalent (assuming no errors are generated) to: 105 * 106 * for (uint i = 0; i < MAX_VIEWPORTS; i++) 107 * ViewportIndexedf(i, 1, (float)x, (float)y, (float)w, (float)h);" 108 * 109 * Set all of the viewports supported by the implementation, but only 110 * signal the driver once at the end. 111 */ 112 for (unsigned i = 0; i < ctx->Const.MaxViewports; i++) 113 set_viewport_no_notify(ctx, i, input.X, input.Y, input.Width, input.Height); 114 115 if (ctx->Driver.Viewport) 116 ctx->Driver.Viewport(ctx); 117 } 118 119 /** 120 * Set the viewport. 121 * \sa Called via glViewport() or display list execution. 122 * 123 * Flushes the vertices and calls _mesa_set_viewport() with the given 124 * parameters. 125 */ 126 void GLAPIENTRY 127 _mesa_Viewport_no_error(GLint x, GLint y, GLsizei width, GLsizei height) 128 { 129 GET_CURRENT_CONTEXT(ctx); 130 viewport(ctx, x, y, width, height); 131 } 132 133 void GLAPIENTRY 134 _mesa_Viewport(GLint x, GLint y, GLsizei width, GLsizei height) 135 { 136 GET_CURRENT_CONTEXT(ctx); 137 138 if (MESA_VERBOSE & VERBOSE_API) 139 _mesa_debug(ctx, "glViewport %d %d %d %d\n", x, y, width, height); 140 141 if (width < 0 || height < 0) { 142 _mesa_error(ctx, GL_INVALID_VALUE, 143 "glViewport(%d, %d, %d, %d)", x, y, width, height); 144 return; 145 } 146 147 viewport(ctx, x, y, width, height); 148 } 149 150 151 /** 152 * Set new viewport parameters and update derived state. 153 * Usually called from _mesa_Viewport(). 154 * 155 * \param ctx GL context. 156 * \param idx Index of the viewport to be updated. 157 * \param x, y coordinates of the lower left corner of the viewport rectangle. 158 * \param width width of the viewport rectangle. 159 * \param height height of the viewport rectangle. 160 */ 161 void 162 _mesa_set_viewport(struct gl_context *ctx, unsigned idx, GLfloat x, GLfloat y, 163 GLfloat width, GLfloat height) 164 { 165 clamp_viewport(ctx, &x, &y, &width, &height); 166 set_viewport_no_notify(ctx, idx, x, y, width, height); 167 168 if (ctx->Driver.Viewport) 169 ctx->Driver.Viewport(ctx); 170 } 171 172 static void 173 viewport_array(struct gl_context *ctx, GLuint first, GLsizei count, 174 struct gl_viewport_inputs *inputs) 175 { 176 for (GLsizei i = 0; i < count; i++) { 177 clamp_viewport(ctx, &inputs[i].X, &inputs[i].Y, 178 &inputs[i].Width, &inputs[i].Height); 179 180 set_viewport_no_notify(ctx, i + first, inputs[i].X, inputs[i].Y, 181 inputs[i].Width, inputs[i].Height); 182 } 183 184 if (ctx->Driver.Viewport) 185 ctx->Driver.Viewport(ctx); 186 } 187 188 void GLAPIENTRY 189 _mesa_ViewportArrayv_no_error(GLuint first, GLsizei count, const GLfloat *v) 190 { 191 GET_CURRENT_CONTEXT(ctx); 192 193 struct gl_viewport_inputs *p = (struct gl_viewport_inputs *)v; 194 viewport_array(ctx, first, count, p); 195 } 196 197 void GLAPIENTRY 198 _mesa_ViewportArrayv(GLuint first, GLsizei count, const GLfloat *v) 199 { 200 int i; 201 struct gl_viewport_inputs *p = (struct gl_viewport_inputs *)v; 202 GET_CURRENT_CONTEXT(ctx); 203 204 if (MESA_VERBOSE & VERBOSE_API) 205 _mesa_debug(ctx, "glViewportArrayv %d %d\n", first, count); 206 207 if ((first + count) > ctx->Const.MaxViewports) { 208 _mesa_error(ctx, GL_INVALID_VALUE, 209 "glViewportArrayv: first (%d) + count (%d) > MaxViewports " 210 "(%d)", 211 first, count, ctx->Const.MaxViewports); 212 return; 213 } 214 215 /* Verify width & height */ 216 for (i = 0; i < count; i++) { 217 if (p[i].Width < 0 || p[i].Height < 0) { 218 _mesa_error(ctx, GL_INVALID_VALUE, 219 "glViewportArrayv: index (%d) width or height < 0 " 220 "(%f, %f)", 221 i + first, p[i].Width, p[i].Height); 222 return; 223 } 224 } 225 226 viewport_array(ctx, first, count, p); 227 } 228 229 static void 230 viewport_indexed_err(struct gl_context *ctx, GLuint index, GLfloat x, GLfloat y, 231 GLfloat w, GLfloat h, const char *function) 232 { 233 if (MESA_VERBOSE & VERBOSE_API) 234 _mesa_debug(ctx, "%s(%d, %f, %f, %f, %f)\n", 235 function, index, x, y, w, h); 236 237 if (index >= ctx->Const.MaxViewports) { 238 _mesa_error(ctx, GL_INVALID_VALUE, 239 "%s: index (%d) >= MaxViewports (%d)", 240 function, index, ctx->Const.MaxViewports); 241 return; 242 } 243 244 /* Verify width & height */ 245 if (w < 0 || h < 0) { 246 _mesa_error(ctx, GL_INVALID_VALUE, 247 "%s: index (%d) width or height < 0 (%f, %f)", 248 function, index, w, h); 249 return; 250 } 251 252 _mesa_set_viewport(ctx, index, x, y, w, h); 253 } 254 255 void GLAPIENTRY 256 _mesa_ViewportIndexedf_no_error(GLuint index, GLfloat x, GLfloat y, 257 GLfloat w, GLfloat h) 258 { 259 GET_CURRENT_CONTEXT(ctx); 260 _mesa_set_viewport(ctx, index, x, y, w, h); 261 } 262 263 void GLAPIENTRY 264 _mesa_ViewportIndexedf(GLuint index, GLfloat x, GLfloat y, 265 GLfloat w, GLfloat h) 266 { 267 GET_CURRENT_CONTEXT(ctx); 268 viewport_indexed_err(ctx, index, x, y, w, h, "glViewportIndexedf"); 269 } 270 271 void GLAPIENTRY 272 _mesa_ViewportIndexedfv_no_error(GLuint index, const GLfloat *v) 273 { 274 GET_CURRENT_CONTEXT(ctx); 275 _mesa_set_viewport(ctx, index, v[0], v[1], v[2], v[3]); 276 } 277 278 void GLAPIENTRY 279 _mesa_ViewportIndexedfv(GLuint index, const GLfloat *v) 280 { 281 GET_CURRENT_CONTEXT(ctx); 282 viewport_indexed_err(ctx, index, v[0], v[1], v[2], v[3], 283 "glViewportIndexedfv"); 284 } 285 286 static void 287 set_depth_range_no_notify(struct gl_context *ctx, unsigned idx, 288 GLclampd nearval, GLclampd farval) 289 { 290 if (ctx->ViewportArray[idx].Near == nearval && 291 ctx->ViewportArray[idx].Far == farval) 292 return; 293 294 /* The depth range is needed by program state constants. */ 295 FLUSH_VERTICES(ctx, _NEW_VIEWPORT); 296 ctx->NewDriverState |= ctx->DriverFlags.NewViewport; 297 298 ctx->ViewportArray[idx].Near = CLAMP(nearval, 0.0, 1.0); 299 ctx->ViewportArray[idx].Far = CLAMP(farval, 0.0, 1.0); 300 } 301 302 void 303 _mesa_set_depth_range(struct gl_context *ctx, unsigned idx, 304 GLclampd nearval, GLclampd farval) 305 { 306 set_depth_range_no_notify(ctx, idx, nearval, farval); 307 308 if (ctx->Driver.DepthRange) 309 ctx->Driver.DepthRange(ctx); 310 } 311 312 /** 313 * Called by glDepthRange 314 * 315 * \param nearval specifies the Z buffer value which should correspond to 316 * the near clip plane 317 * \param farval specifies the Z buffer value which should correspond to 318 * the far clip plane 319 */ 320 void GLAPIENTRY 321 _mesa_DepthRange(GLclampd nearval, GLclampd farval) 322 { 323 unsigned i; 324 GET_CURRENT_CONTEXT(ctx); 325 326 if (MESA_VERBOSE&VERBOSE_API) 327 _mesa_debug(ctx, "glDepthRange %f %f\n", nearval, farval); 328 329 /* The GL_ARB_viewport_array spec says: 330 * 331 * "DepthRange sets the depth range for all viewports to the same 332 * values and is equivalent (assuming no errors are generated) to: 333 * 334 * for (uint i = 0; i < MAX_VIEWPORTS; i++) 335 * DepthRangeIndexed(i, n, f);" 336 * 337 * Set the depth range for all of the viewports supported by the 338 * implementation, but only signal the driver once at the end. 339 */ 340 for (i = 0; i < ctx->Const.MaxViewports; i++) 341 set_depth_range_no_notify(ctx, i, nearval, farval); 342 343 if (ctx->Driver.DepthRange) { 344 ctx->Driver.DepthRange(ctx); 345 } 346 } 347 348 void GLAPIENTRY 349 _mesa_DepthRangef(GLclampf nearval, GLclampf farval) 350 { 351 _mesa_DepthRange(nearval, farval); 352 } 353 354 /** 355 * Update a range DepthRange values 356 * 357 * \param first starting array index 358 * \param count count of DepthRange items to update 359 * \param v pointer to memory containing 360 * GLclampd near and far clip-plane values 361 */ 362 static ALWAYS_INLINE void 363 depth_range_arrayv(struct gl_context *ctx, GLuint first, GLsizei count, 364 const struct gl_depthrange_inputs *const inputs) 365 { 366 for (GLsizei i = 0; i < count; i++) 367 set_depth_range_no_notify(ctx, i + first, inputs[i].Near, inputs[i].Far); 368 369 if (ctx->Driver.DepthRange) 370 ctx->Driver.DepthRange(ctx); 371 } 372 373 void GLAPIENTRY 374 _mesa_DepthRangeArrayv_no_error(GLuint first, GLsizei count, const GLclampd *v) 375 { 376 GET_CURRENT_CONTEXT(ctx); 377 378 const struct gl_depthrange_inputs *const p = 379 (struct gl_depthrange_inputs *)v; 380 depth_range_arrayv(ctx, first, count, p); 381 } 382 383 void GLAPIENTRY 384 _mesa_DepthRangeArrayv(GLuint first, GLsizei count, const GLclampd *v) 385 { 386 const struct gl_depthrange_inputs *const p = 387 (struct gl_depthrange_inputs *) v; 388 GET_CURRENT_CONTEXT(ctx); 389 390 if (MESA_VERBOSE & VERBOSE_API) 391 _mesa_debug(ctx, "glDepthRangeArrayv %d %d\n", first, count); 392 393 if ((first + count) > ctx->Const.MaxViewports) { 394 _mesa_error(ctx, GL_INVALID_VALUE, 395 "glDepthRangev: first (%d) + count (%d) >= MaxViewports (%d)", 396 first, count, ctx->Const.MaxViewports); 397 return; 398 } 399 400 depth_range_arrayv(ctx, first, count, p); 401 } 402 403 void GLAPIENTRY 404 _mesa_DepthRangeArrayfvOES(GLuint first, GLsizei count, const GLfloat *v) 405 { 406 int i; 407 GET_CURRENT_CONTEXT(ctx); 408 409 if (MESA_VERBOSE & VERBOSE_API) 410 _mesa_debug(ctx, "glDepthRangeArrayfv %d %d\n", first, count); 411 412 if ((first + count) > ctx->Const.MaxViewports) { 413 _mesa_error(ctx, GL_INVALID_VALUE, 414 "glDepthRangeArrayfv: first (%d) + count (%d) >= MaxViewports (%d)", 415 first, count, ctx->Const.MaxViewports); 416 return; 417 } 418 419 for (i = 0; i < count; i++) 420 set_depth_range_no_notify(ctx, i + first, v[i * 2], v[i * 2 + 1]); 421 422 if (ctx->Driver.DepthRange) 423 ctx->Driver.DepthRange(ctx); 424 } 425 426 /** 427 * Update a single DepthRange 428 * 429 * \param index array index to update 430 * \param nearval specifies the Z buffer value which should correspond to 431 * the near clip plane 432 * \param farval specifies the Z buffer value which should correspond to 433 * the far clip plane 434 */ 435 void GLAPIENTRY 436 _mesa_DepthRangeIndexed_no_error(GLuint index, GLclampd nearval, 437 GLclampd farval) 438 { 439 GET_CURRENT_CONTEXT(ctx); 440 _mesa_set_depth_range(ctx, index, nearval, farval); 441 } 442 443 444 void GLAPIENTRY 445 _mesa_DepthRangeIndexed(GLuint index, GLclampd nearval, GLclampd farval) 446 { 447 GET_CURRENT_CONTEXT(ctx); 448 449 if (MESA_VERBOSE & VERBOSE_API) 450 _mesa_debug(ctx, "glDepthRangeIndexed(%d, %f, %f)\n", 451 index, nearval, farval); 452 453 if (index >= ctx->Const.MaxViewports) { 454 _mesa_error(ctx, GL_INVALID_VALUE, 455 "glDepthRangeIndexed: index (%d) >= MaxViewports (%d)", 456 index, ctx->Const.MaxViewports); 457 return; 458 } 459 460 _mesa_set_depth_range(ctx, index, nearval, farval); 461 } 462 463 void GLAPIENTRY 464 _mesa_DepthRangeIndexedfOES(GLuint index, GLfloat nearval, GLfloat farval) 465 { 466 _mesa_DepthRangeIndexed(index, nearval, farval); 467 } 468 469 /** 470 * Initialize the context viewport attribute group. 471 * \param ctx the GL context. 472 */ 473 void _mesa_init_viewport(struct gl_context *ctx) 474 { 475 unsigned i; 476 477 ctx->Transform.ClipOrigin = GL_LOWER_LEFT; 478 ctx->Transform.ClipDepthMode = GL_NEGATIVE_ONE_TO_ONE; 479 480 /* Note: ctx->Const.MaxViewports may not have been set by the driver yet, 481 * so just initialize all of them. 482 */ 483 for (i = 0; i < MAX_VIEWPORTS; i++) { 484 /* Viewport group */ 485 ctx->ViewportArray[i].X = 0; 486 ctx->ViewportArray[i].Y = 0; 487 ctx->ViewportArray[i].Width = 0; 488 ctx->ViewportArray[i].Height = 0; 489 ctx->ViewportArray[i].Near = 0.0; 490 ctx->ViewportArray[i].Far = 1.0; 491 } 492 } 493 494 495 static ALWAYS_INLINE void 496 clip_control(struct gl_context *ctx, GLenum origin, GLenum depth, bool no_error) 497 { 498 if (ctx->Transform.ClipOrigin == origin && 499 ctx->Transform.ClipDepthMode == depth) 500 return; 501 502 if (!no_error && 503 origin != GL_LOWER_LEFT && origin != GL_UPPER_LEFT) { 504 _mesa_error(ctx, GL_INVALID_ENUM, "glClipControl"); 505 return; 506 } 507 508 if (!no_error && 509 depth != GL_NEGATIVE_ONE_TO_ONE && depth != GL_ZERO_TO_ONE) { 510 _mesa_error(ctx, GL_INVALID_ENUM, "glClipControl"); 511 return; 512 } 513 514 /* Affects transform state and the viewport transform */ 515 FLUSH_VERTICES(ctx, ctx->DriverFlags.NewClipControl ? 0 : 516 _NEW_TRANSFORM | _NEW_VIEWPORT); 517 ctx->NewDriverState |= ctx->DriverFlags.NewClipControl; 518 519 if (ctx->Transform.ClipOrigin != origin) { 520 ctx->Transform.ClipOrigin = origin; 521 522 /* Affects the winding order of the front face. */ 523 if (ctx->DriverFlags.NewPolygonState) 524 ctx->NewDriverState |= ctx->DriverFlags.NewPolygonState; 525 else 526 ctx->NewState |= _NEW_POLYGON; 527 528 if (ctx->Driver.FrontFace) 529 ctx->Driver.FrontFace(ctx, ctx->Polygon.FrontFace); 530 } 531 532 if (ctx->Transform.ClipDepthMode != depth) { 533 ctx->Transform.ClipDepthMode = depth; 534 535 if (ctx->Driver.DepthRange) 536 ctx->Driver.DepthRange(ctx); 537 } 538 } 539 540 541 void GLAPIENTRY 542 _mesa_ClipControl_no_error(GLenum origin, GLenum depth) 543 { 544 GET_CURRENT_CONTEXT(ctx); 545 clip_control(ctx, origin, depth, true); 546 } 547 548 549 void GLAPIENTRY 550 _mesa_ClipControl(GLenum origin, GLenum depth) 551 { 552 GET_CURRENT_CONTEXT(ctx); 553 554 if (MESA_VERBOSE & VERBOSE_API) 555 _mesa_debug(ctx, "glClipControl(%s, %s)\n", 556 _mesa_enum_to_string(origin), 557 _mesa_enum_to_string(depth)); 558 559 ASSERT_OUTSIDE_BEGIN_END(ctx); 560 561 if (!ctx->Extensions.ARB_clip_control) { 562 _mesa_error(ctx, GL_INVALID_OPERATION, "glClipControl"); 563 return; 564 } 565 566 clip_control(ctx, origin, depth, false); 567 } 568 569 /** 570 * Computes the scaling and the translation part of the 571 * viewport transform matrix of the \param i-th viewport 572 * and writes that into \param scale and \param translate. 573 */ 574 void 575 _mesa_get_viewport_xform(struct gl_context *ctx, unsigned i, 576 float scale[3], float translate[3]) 577 { 578 float x = ctx->ViewportArray[i].X; 579 float y = ctx->ViewportArray[i].Y; 580 float half_width = 0.5f * ctx->ViewportArray[i].Width; 581 float half_height = 0.5f * ctx->ViewportArray[i].Height; 582 double n = ctx->ViewportArray[i].Near; 583 double f = ctx->ViewportArray[i].Far; 584 585 scale[0] = half_width; 586 translate[0] = half_width + x; 587 if (ctx->Transform.ClipOrigin == GL_UPPER_LEFT) { 588 scale[1] = -half_height; 589 } else { 590 scale[1] = half_height; 591 } 592 translate[1] = half_height + y; 593 594 if (ctx->Transform.ClipDepthMode == GL_NEGATIVE_ONE_TO_ONE) { 595 scale[2] = 0.5 * (f - n); 596 translate[2] = 0.5 * (n + f); 597 } else { 598 scale[2] = f - n; 599 translate[2] = n; 600 } 601 } 602