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