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
39set_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
77struct gl_viewport_inputs {
78   GLfloat X, Y;                /**< position */
79   GLfloat Width, Height;       /**< size */
80};
81
82struct 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 */
93void 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 */
142void
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
156void 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
194static void
195ViewportIndexedf(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
222void 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
229void GLAPIENTRY
230_mesa_ViewportIndexedfv(GLuint index, const GLfloat *v)
231{
232   ViewportIndexedf(index, v[0], v[1], v[2], v[3], "glViewportIndexedfv");
233}
234
235static void
236set_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
248void
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 */
266void 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
296void 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 */
310void 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
335void 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 */
367void 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
386void 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 */
396void _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
418extern 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 */
475void
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