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 38static void 39clamp_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 63static void 64set_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 83struct gl_viewport_inputs { 84 GLfloat X, Y; /**< position */ 85 GLfloat Width, Height; /**< size */ 86}; 87 88struct gl_depthrange_inputs { 89 GLdouble Near, Far; /**< Depth buffer range */ 90}; 91 92static void 93viewport(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 */ 126void 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 133void 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 */ 161void 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 172static void 173viewport_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 188void 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 197void 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 229static void 230viewport_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 255void 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 263void 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 271void 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 278void 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 286static void 287set_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 302void 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 */ 320void 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 348void 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 */ 362static ALWAYS_INLINE void 363depth_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 373void 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 383void 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 403void 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 */ 435void 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 444void 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 463void 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 */ 473void _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 ctx->SubpixelPrecisionBias[0] = 0; 494 ctx->SubpixelPrecisionBias[1] = 0; 495} 496 497 498static ALWAYS_INLINE void 499clip_control(struct gl_context *ctx, GLenum origin, GLenum depth, bool no_error) 500{ 501 if (ctx->Transform.ClipOrigin == origin && 502 ctx->Transform.ClipDepthMode == depth) 503 return; 504 505 if (!no_error && 506 origin != GL_LOWER_LEFT && origin != GL_UPPER_LEFT) { 507 _mesa_error(ctx, GL_INVALID_ENUM, "glClipControl"); 508 return; 509 } 510 511 if (!no_error && 512 depth != GL_NEGATIVE_ONE_TO_ONE && depth != GL_ZERO_TO_ONE) { 513 _mesa_error(ctx, GL_INVALID_ENUM, "glClipControl"); 514 return; 515 } 516 517 /* Affects transform state and the viewport transform */ 518 FLUSH_VERTICES(ctx, ctx->DriverFlags.NewClipControl ? 0 : 519 _NEW_TRANSFORM | _NEW_VIEWPORT); 520 ctx->NewDriverState |= ctx->DriverFlags.NewClipControl; 521 522 if (ctx->Transform.ClipOrigin != origin) { 523 ctx->Transform.ClipOrigin = origin; 524 525 /* Affects the winding order of the front face. */ 526 if (ctx->DriverFlags.NewPolygonState) 527 ctx->NewDriverState |= ctx->DriverFlags.NewPolygonState; 528 else 529 ctx->NewState |= _NEW_POLYGON; 530 531 if (ctx->Driver.FrontFace) 532 ctx->Driver.FrontFace(ctx, ctx->Polygon.FrontFace); 533 } 534 535 if (ctx->Transform.ClipDepthMode != depth) { 536 ctx->Transform.ClipDepthMode = depth; 537 538 if (ctx->Driver.DepthRange) 539 ctx->Driver.DepthRange(ctx); 540 } 541} 542 543 544void GLAPIENTRY 545_mesa_ClipControl_no_error(GLenum origin, GLenum depth) 546{ 547 GET_CURRENT_CONTEXT(ctx); 548 clip_control(ctx, origin, depth, true); 549} 550 551 552void GLAPIENTRY 553_mesa_ClipControl(GLenum origin, GLenum depth) 554{ 555 GET_CURRENT_CONTEXT(ctx); 556 557 if (MESA_VERBOSE & VERBOSE_API) 558 _mesa_debug(ctx, "glClipControl(%s, %s)\n", 559 _mesa_enum_to_string(origin), 560 _mesa_enum_to_string(depth)); 561 562 ASSERT_OUTSIDE_BEGIN_END(ctx); 563 564 if (!ctx->Extensions.ARB_clip_control) { 565 _mesa_error(ctx, GL_INVALID_OPERATION, "glClipControl"); 566 return; 567 } 568 569 clip_control(ctx, origin, depth, false); 570} 571 572/** 573 * Computes the scaling and the translation part of the 574 * viewport transform matrix of the \param i-th viewport 575 * and writes that into \param scale and \param translate. 576 */ 577void 578_mesa_get_viewport_xform(struct gl_context *ctx, unsigned i, 579 float scale[3], float translate[3]) 580{ 581 float x = ctx->ViewportArray[i].X; 582 float y = ctx->ViewportArray[i].Y; 583 float half_width = 0.5f * ctx->ViewportArray[i].Width; 584 float half_height = 0.5f * ctx->ViewportArray[i].Height; 585 double n = ctx->ViewportArray[i].Near; 586 double f = ctx->ViewportArray[i].Far; 587 588 scale[0] = half_width; 589 translate[0] = half_width + x; 590 if (ctx->Transform.ClipOrigin == GL_UPPER_LEFT) { 591 scale[1] = -half_height; 592 } else { 593 scale[1] = half_height; 594 } 595 translate[1] = half_height + y; 596 597 if (ctx->Transform.ClipDepthMode == GL_NEGATIVE_ONE_TO_ONE) { 598 scale[2] = 0.5 * (f - n); 599 translate[2] = 0.5 * (n + f); 600 } else { 601 scale[2] = f - n; 602 translate[2] = n; 603 } 604} 605 606 607static void 608subpixel_precision_bias(struct gl_context *ctx, GLuint xbits, GLuint ybits) 609{ 610 if (MESA_VERBOSE & VERBOSE_API) 611 _mesa_debug(ctx, "glSubpixelPrecisionBiasNV(%u, %u)\n", xbits, ybits); 612 613 ctx->SubpixelPrecisionBias[0] = xbits; 614 ctx->SubpixelPrecisionBias[1] = ybits; 615 616 FLUSH_VERTICES(ctx, 0); 617 ctx->NewDriverState |= 618 ctx->DriverFlags.NewNvConservativeRasterizationParams; 619} 620 621void GLAPIENTRY 622_mesa_SubpixelPrecisionBiasNV_no_error(GLuint xbits, GLuint ybits) 623{ 624 GET_CURRENT_CONTEXT(ctx); 625 626 if (MESA_VERBOSE & VERBOSE_API) 627 _mesa_debug(ctx, "glSubpixelPrecisionBiasNV(%u, %u)\n", xbits, ybits); 628 629 subpixel_precision_bias(ctx, xbits, ybits); 630} 631 632void GLAPIENTRY 633_mesa_SubpixelPrecisionBiasNV(GLuint xbits, GLuint ybits) 634{ 635 GET_CURRENT_CONTEXT(ctx); 636 637 if (MESA_VERBOSE & VERBOSE_API) 638 _mesa_debug(ctx, "glSubpixelPrecisionBiasNV(%u, %u)\n", xbits, ybits); 639 640 ASSERT_OUTSIDE_BEGIN_END(ctx); 641 642 if (!ctx->Extensions.NV_conservative_raster) { 643 _mesa_error(ctx, GL_INVALID_OPERATION, 644 "glSubpixelPrecisionBiasNV not supported"); 645 return; 646 } 647 648 if (xbits > ctx->Const.MaxSubpixelPrecisionBiasBits) { 649 _mesa_error(ctx, GL_INVALID_VALUE, "glSubpixelPrecisionBiasNV"); 650 return; 651 } 652 653 if (ybits > ctx->Const.MaxSubpixelPrecisionBiasBits) { 654 _mesa_error(ctx, GL_INVALID_VALUE, "glSubpixelPrecisionBiasNV"); 655 return; 656 } 657 658 subpixel_precision_bias(ctx, xbits, ybits); 659} 660