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