viewport.c revision af69d88d
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 "macros.h"
34#include "mtypes.h"
35#include "viewport.h"
36
37static void
38set_viewport_no_notify(struct gl_context *ctx, unsigned idx,
39                       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 (ctx->Extensions.ARB_viewport_array) {
55      x = CLAMP(x,
56                ctx->Const.ViewportBounds.Min, ctx->Const.ViewportBounds.Max);
57      y = CLAMP(y,
58                ctx->Const.ViewportBounds.Min, ctx->Const.ViewportBounds.Max);
59   }
60
61   ctx->ViewportArray[idx].X = x;
62   ctx->ViewportArray[idx].Width = width;
63   ctx->ViewportArray[idx].Y = y;
64   ctx->ViewportArray[idx].Height = height;
65   ctx->NewState |= _NEW_VIEWPORT;
66
67#if 1
68   /* XXX remove this someday.  Currently the DRI drivers rely on
69    * the WindowMap matrix being up to date in the driver's Viewport
70    * and DepthRange functions.
71    */
72   _math_matrix_viewport(&ctx->ViewportArray[idx]._WindowMap,
73                         ctx->ViewportArray[idx].X,
74                         ctx->ViewportArray[idx].Y,
75                         ctx->ViewportArray[idx].Width,
76                         ctx->ViewportArray[idx].Height,
77                         ctx->ViewportArray[idx].Near,
78                         ctx->ViewportArray[idx].Far,
79                         ctx->DrawBuffer->_DepthMaxF);
80#endif
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
92/**
93 * Set the viewport.
94 * \sa Called via glViewport() or display list execution.
95 *
96 * Flushes the vertices and calls _mesa_set_viewport() with the given
97 * parameters.
98 */
99void GLAPIENTRY
100_mesa_Viewport(GLint x, GLint y, GLsizei width, GLsizei height)
101{
102   unsigned i;
103   GET_CURRENT_CONTEXT(ctx);
104   FLUSH_VERTICES(ctx, 0);
105
106   if (MESA_VERBOSE & VERBOSE_API)
107      _mesa_debug(ctx, "glViewport %d %d %d %d\n", x, y, width, height);
108
109   if (width < 0 || height < 0) {
110      _mesa_error(ctx,  GL_INVALID_VALUE,
111                   "glViewport(%d, %d, %d, %d)", x, y, width, height);
112      return;
113   }
114
115   /* The GL_ARB_viewport_array spec says:
116    *
117    *     "Viewport sets the parameters for all viewports to the same values
118    *     and is equivalent (assuming no errors are generated) to:
119    *
120    *     for (uint i = 0; i < MAX_VIEWPORTS; i++)
121    *         ViewportIndexedf(i, 1, (float)x, (float)y, (float)w, (float)h);"
122    *
123    * Set all of the viewports supported by the implementation, but only
124    * signal the driver once at the end.
125    */
126   for (i = 0; i < ctx->Const.MaxViewports; i++)
127      set_viewport_no_notify(ctx, i, x, y, width, height);
128
129   if (ctx->Driver.Viewport) {
130      /* Many drivers will use this call to check for window size changes
131       * and reallocate the z/stencil/accum/etc buffers if needed.
132       */
133      ctx->Driver.Viewport(ctx);
134   }
135}
136
137
138/**
139 * Set new viewport parameters and update derived state (the _WindowMap
140 * matrix).  Usually called from _mesa_Viewport().
141 *
142 * \param ctx GL context.
143 * \param idx    Index of the viewport to be updated.
144 * \param x, y coordinates of the lower left corner of the viewport rectangle.
145 * \param width width of the viewport rectangle.
146 * \param height height of the viewport rectangle.
147 */
148void
149_mesa_set_viewport(struct gl_context *ctx, unsigned idx, GLfloat x, GLfloat y,
150                    GLfloat width, GLfloat height)
151{
152   set_viewport_no_notify(ctx, idx, x, y, width, height);
153
154   if (ctx->Driver.Viewport) {
155      /* Many drivers will use this call to check for window size changes
156       * and reallocate the z/stencil/accum/etc buffers if needed.
157       */
158      ctx->Driver.Viewport(ctx);
159   }
160}
161
162void GLAPIENTRY
163_mesa_ViewportArrayv(GLuint first, GLsizei count, const GLfloat *v)
164{
165   int i;
166   const struct gl_viewport_inputs *const p = (struct gl_viewport_inputs *) v;
167   GET_CURRENT_CONTEXT(ctx);
168
169   if (MESA_VERBOSE & VERBOSE_API)
170      _mesa_debug(ctx, "glViewportArrayv %d %d\n", first, count);
171
172   if ((first + count) > ctx->Const.MaxViewports) {
173      _mesa_error(ctx, GL_INVALID_VALUE,
174                  "glViewportArrayv: first (%d) + count (%d) > MaxViewports "
175                  "(%d)",
176                  first, count, ctx->Const.MaxViewports);
177      return;
178   }
179
180   /* Verify width & height */
181   for (i = 0; i < count; i++) {
182      if (p[i].Width < 0 || p[i].Height < 0) {
183         _mesa_error(ctx, GL_INVALID_VALUE,
184                     "glViewportArrayv: index (%d) width or height < 0 "
185                     "(%f, %f)",
186                     i + first, p[i].Width, p[i].Height);
187         return;
188      }
189   }
190
191   for (i = 0; i < count; i++)
192      set_viewport_no_notify(ctx, i + first,
193                             p[i].X, p[i].Y,
194                             p[i].Width, p[i].Height);
195
196   if (ctx->Driver.Viewport)
197      ctx->Driver.Viewport(ctx);
198}
199
200static void
201ViewportIndexedf(GLuint index, GLfloat x, GLfloat y,
202                 GLfloat w, GLfloat h, const char *function)
203{
204   GET_CURRENT_CONTEXT(ctx);
205
206   if (MESA_VERBOSE & VERBOSE_API)
207      _mesa_debug(ctx, "%s(%d, %f, %f, %f, %f)\n",
208                  function, index, x, y, w, h);
209
210   if (index >= ctx->Const.MaxViewports) {
211      _mesa_error(ctx, GL_INVALID_VALUE,
212                  "%s: index (%d) >= MaxViewports (%d)",
213                  function, index, ctx->Const.MaxViewports);
214      return;
215   }
216
217   /* Verify width & height */
218   if (w < 0 || h < 0) {
219      _mesa_error(ctx, GL_INVALID_VALUE,
220                  "%s: index (%d) width or height < 0 (%f, %f)",
221                  function, index, w, h);
222      return;
223   }
224
225   _mesa_set_viewport(ctx, index, x, y, w, h);
226}
227
228void GLAPIENTRY
229_mesa_ViewportIndexedf(GLuint index, GLfloat x, GLfloat y,
230                       GLfloat w, GLfloat h)
231{
232   ViewportIndexedf(index, x, y, w, h, "glViewportIndexedf");
233}
234
235void GLAPIENTRY
236_mesa_ViewportIndexedfv(GLuint index, const GLfloat *v)
237{
238   ViewportIndexedf(index, v[0], v[1], v[2], v[3], "glViewportIndexedfv");
239}
240
241static void
242set_depth_range_no_notify(struct gl_context *ctx, unsigned idx,
243                          GLclampd nearval, GLclampd farval)
244{
245   if (ctx->ViewportArray[idx].Near == nearval &&
246       ctx->ViewportArray[idx].Far == farval)
247      return;
248
249   ctx->ViewportArray[idx].Near = CLAMP(nearval, 0.0, 1.0);
250   ctx->ViewportArray[idx].Far = CLAMP(farval, 0.0, 1.0);
251   ctx->NewState |= _NEW_VIEWPORT;
252
253#if 1
254   /* XXX remove this someday.  Currently the DRI drivers rely on
255    * the WindowMap matrix being up to date in the driver's Viewport
256    * and DepthRange functions.
257    */
258   _math_matrix_viewport(&ctx->ViewportArray[idx]._WindowMap,
259                         ctx->ViewportArray[idx].X,
260                         ctx->ViewportArray[idx].Y,
261                         ctx->ViewportArray[idx].Width,
262                         ctx->ViewportArray[idx].Height,
263                         ctx->ViewportArray[idx].Near,
264                         ctx->ViewportArray[idx].Far,
265                         ctx->DrawBuffer->_DepthMaxF);
266#endif
267}
268
269void
270_mesa_set_depth_range(struct gl_context *ctx, unsigned idx,
271                      GLclampd nearval, GLclampd farval)
272{
273   set_depth_range_no_notify(ctx, idx, nearval, farval);
274
275   if (ctx->Driver.DepthRange)
276      ctx->Driver.DepthRange(ctx);
277}
278
279/**
280 * Called by glDepthRange
281 *
282 * \param nearval  specifies the Z buffer value which should correspond to
283 *                 the near clip plane
284 * \param farval  specifies the Z buffer value which should correspond to
285 *                the far clip plane
286 */
287void GLAPIENTRY
288_mesa_DepthRange(GLclampd nearval, GLclampd farval)
289{
290   unsigned i;
291   GET_CURRENT_CONTEXT(ctx);
292
293   FLUSH_VERTICES(ctx, 0);
294
295   if (MESA_VERBOSE&VERBOSE_API)
296      _mesa_debug(ctx, "glDepthRange %f %f\n", nearval, farval);
297
298   /* The GL_ARB_viewport_array spec says:
299    *
300    *     "DepthRange sets the depth range for all viewports to the same
301    *     values and is equivalent (assuming no errors are generated) to:
302    *
303    *     for (uint i = 0; i < MAX_VIEWPORTS; i++)
304    *         DepthRangeIndexed(i, n, f);"
305    *
306    * Set the depth range for all of the viewports supported by the
307    * implementation, but only signal the driver once at the end.
308    */
309   for (i = 0; i < ctx->Const.MaxViewports; i++)
310      set_depth_range_no_notify(ctx, i, nearval, farval);
311
312   if (ctx->Driver.DepthRange) {
313      ctx->Driver.DepthRange(ctx);
314   }
315}
316
317void GLAPIENTRY
318_mesa_DepthRangef(GLclampf nearval, GLclampf farval)
319{
320   _mesa_DepthRange(nearval, farval);
321}
322
323/**
324 * Update a range DepthRange values
325 *
326 * \param first   starting array index
327 * \param count   count of DepthRange items to update
328 * \param v       pointer to memory containing
329 *                GLclampd near and far clip-plane values
330 */
331void GLAPIENTRY
332_mesa_DepthRangeArrayv(GLuint first, GLsizei count, const GLclampd *v)
333{
334   int i;
335   const struct gl_depthrange_inputs *const p =
336      (struct gl_depthrange_inputs *) v;
337   GET_CURRENT_CONTEXT(ctx);
338
339   if (MESA_VERBOSE & VERBOSE_API)
340      _mesa_debug(ctx, "glDepthRangeArrayv %d %d\n", first, count);
341
342   if ((first + count) > ctx->Const.MaxViewports) {
343      _mesa_error(ctx, GL_INVALID_VALUE,
344                  "glDepthRangev: first (%d) + count (%d) >= MaxViewports (%d)",
345                  first, count, ctx->Const.MaxViewports);
346      return;
347   }
348
349   for (i = 0; i < count; i++)
350      set_depth_range_no_notify(ctx, i + first, p[i].Near, p[i].Far);
351
352   if (ctx->Driver.DepthRange)
353      ctx->Driver.DepthRange(ctx);
354}
355
356/**
357 * Update a single DepthRange
358 *
359 * \param index    array index to update
360 * \param nearval  specifies the Z buffer value which should correspond to
361 *                 the near clip plane
362 * \param farval   specifies the Z buffer value which should correspond to
363 *                 the far clip plane
364 */
365void GLAPIENTRY
366_mesa_DepthRangeIndexed(GLuint index, GLclampd nearval, GLclampd farval)
367{
368   GET_CURRENT_CONTEXT(ctx);
369
370   if (MESA_VERBOSE & VERBOSE_API)
371      _mesa_debug(ctx, "glDepthRangeIndexed(%d, %f, %f)\n",
372                  index, nearval, farval);
373
374   if (index >= ctx->Const.MaxViewports) {
375      _mesa_error(ctx, GL_INVALID_VALUE,
376                  "glDepthRangeIndexed: index (%d) >= MaxViewports (%d)",
377                  index, ctx->Const.MaxViewports);
378      return;
379   }
380
381   _mesa_set_depth_range(ctx, index, nearval, farval);
382}
383
384/**
385 * Initialize the context viewport attribute group.
386 * \param ctx  the GL context.
387 */
388void _mesa_init_viewport(struct gl_context *ctx)
389{
390   GLfloat depthMax = 65535.0F; /* sorf of arbitrary */
391   unsigned i;
392
393   /* Note: ctx->Const.MaxViewports may not have been set by the driver yet,
394    * so just initialize all of them.
395    */
396   for (i = 0; i < MAX_VIEWPORTS; i++) {
397      /* Viewport group */
398      ctx->ViewportArray[i].X = 0;
399      ctx->ViewportArray[i].Y = 0;
400      ctx->ViewportArray[i].Width = 0;
401      ctx->ViewportArray[i].Height = 0;
402      ctx->ViewportArray[i].Near = 0.0;
403      ctx->ViewportArray[i].Far = 1.0;
404      _math_matrix_ctr(&ctx->ViewportArray[i]._WindowMap);
405
406      _math_matrix_viewport(&ctx->ViewportArray[i]._WindowMap, 0, 0, 0, 0,
407                            0.0F, 1.0F, depthMax);
408   }
409}
410
411
412/**
413 * Free the context viewport attribute group data.
414 * \param ctx  the GL context.
415 */
416void _mesa_free_viewport_data(struct gl_context *ctx)
417{
418   unsigned i;
419
420   for (i = 0; i < MAX_VIEWPORTS; i++)
421      _math_matrix_dtr(&ctx->ViewportArray[i]._WindowMap);
422}
423
424