intel_buffers.c revision fb6bff3712d71cfe131fbf70154d326cdf39e7c8
1/**************************************************************************
2 *
3 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * 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
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "intel_context.h"
29#include "intel_buffers.h"
30#include "intel_fbo.h"
31#include "intel_batchbuffer.h"
32#include "main/framebuffer.h"
33
34
35/**
36 * XXX move this into a new dri/common/cliprects.c file.
37 */
38GLboolean
39intel_intersect_cliprects(drm_clip_rect_t * dst,
40                          const drm_clip_rect_t * a,
41                          const drm_clip_rect_t * b)
42{
43   GLint bx = b->x1;
44   GLint by = b->y1;
45   GLint bw = b->x2 - bx;
46   GLint bh = b->y2 - by;
47
48   if (bx < a->x1)
49      bw -= a->x1 - bx, bx = a->x1;
50   if (by < a->y1)
51      bh -= a->y1 - by, by = a->y1;
52   if (bx + bw > a->x2)
53      bw = a->x2 - bx;
54   if (by + bh > a->y2)
55      bh = a->y2 - by;
56   if (bw <= 0)
57      return GL_FALSE;
58   if (bh <= 0)
59      return GL_FALSE;
60
61   dst->x1 = bx;
62   dst->y1 = by;
63   dst->x2 = bx + bw;
64   dst->y2 = by + bh;
65
66   return GL_TRUE;
67}
68
69/**
70 * Return pointer to current color drawing region, or NULL.
71 */
72struct intel_region *
73intel_drawbuf_region(struct intel_context *intel)
74{
75   struct intel_renderbuffer *irbColor =
76      intel_renderbuffer(intel->ctx.DrawBuffer->_ColorDrawBuffers[0]);
77   if (irbColor)
78      return irbColor->region;
79   else
80      return NULL;
81}
82
83/**
84 * Return pointer to current color reading region, or NULL.
85 */
86struct intel_region *
87intel_readbuf_region(struct intel_context *intel)
88{
89   struct intel_renderbuffer *irb
90      = intel_renderbuffer(intel->ctx.ReadBuffer->_ColorReadBuffer);
91   if (irb)
92      return irb->region;
93   else
94      return NULL;
95}
96
97void
98intel_get_cliprects(struct intel_context *intel,
99		    struct drm_clip_rect **cliprects,
100		    unsigned int *num_cliprects,
101		    int *x_off, int *y_off)
102{
103   __DRIdrawablePrivate *dPriv = intel->driDrawable;
104
105   if (intel->constant_cliprect) {
106      /* FBO or DRI2 rendering, which can just use the fb's size. */
107      intel->fboRect.x1 = 0;
108      intel->fboRect.y1 = 0;
109      intel->fboRect.x2 = intel->ctx.DrawBuffer->Width;
110      intel->fboRect.y2 = intel->ctx.DrawBuffer->Height;
111
112      *cliprects = &intel->fboRect;
113      *num_cliprects = 1;
114      *x_off = 0;
115      *y_off = 0;
116   } else if (intel->front_cliprects || dPriv->numBackClipRects == 0) {
117      /* use the front clip rects */
118      *cliprects = dPriv->pClipRects;
119      *num_cliprects = dPriv->numClipRects;
120      *x_off = dPriv->x;
121      *y_off = dPriv->y;
122   }
123   else {
124      /* use the back clip rects */
125      *num_cliprects = dPriv->numBackClipRects;
126      *cliprects = dPriv->pBackClipRects;
127      *x_off = dPriv->backX;
128      *y_off = dPriv->backY;
129   }
130}
131
132
133/**
134 * Check if we're about to draw into the front color buffer.
135 * If so, set the intel->front_buffer_dirty field to true.
136 */
137void
138intel_check_front_buffer_rendering(struct intel_context *intel)
139{
140   const struct gl_framebuffer *fb = intel->ctx.DrawBuffer;
141   if (fb->Name == 0) {
142      /* drawing to window system buffer */
143      if (fb->_NumColorDrawBuffers > 0) {
144         if (fb->_ColorDrawBufferIndexes[0] == BUFFER_FRONT_LEFT) {
145	    intel->front_buffer_dirty = GL_TRUE;
146	 }
147      }
148   }
149}
150
151
152/**
153 * Update the hardware state for drawing into a window or framebuffer object.
154 *
155 * Called by glDrawBuffer, glBindFramebufferEXT, MakeCurrent, and other
156 * places within the driver.
157 *
158 * Basically, this needs to be called any time the current framebuffer
159 * changes, the renderbuffers change, or we need to draw into different
160 * color buffers.
161 */
162void
163intel_draw_buffer(GLcontext * ctx, struct gl_framebuffer *fb)
164{
165   struct intel_context *intel = intel_context(ctx);
166   struct intel_region *colorRegions[MAX_DRAW_BUFFERS], *depthRegion = NULL;
167   struct intel_renderbuffer *irbDepth = NULL, *irbStencil = NULL;
168
169   if (!fb) {
170      /* this can happen during the initial context initialization */
171      return;
172   }
173
174   /* Do this here, not core Mesa, since this function is called from
175    * many places within the driver.
176    */
177   if (ctx->NewState & _NEW_BUFFERS) {
178      /* this updates the DrawBuffer->_NumColorDrawBuffers fields, etc */
179      _mesa_update_framebuffer(ctx);
180      /* this updates the DrawBuffer's Width/Height if it's a FBO */
181      _mesa_update_draw_buffer_bounds(ctx);
182   }
183
184   if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
185      /* this may occur when we're called by glBindFrameBuffer() during
186       * the process of someone setting up renderbuffers, etc.
187       */
188      /*_mesa_debug(ctx, "DrawBuffer: incomplete user FBO\n");*/
189      return;
190   }
191
192   /* How many color buffers are we drawing into?
193    *
194    * If there are zero buffers or the buffer is too big, don't configure any
195    * regions for hardware drawing.  We'll fallback to software below.  Not
196    * having regions set makes some of the software fallback paths faster.
197    */
198   if ((fb->Width > ctx->Const.MaxRenderbufferSize)
199       || (fb->Height > ctx->Const.MaxRenderbufferSize)
200       || (fb->_NumColorDrawBuffers == 0)) {
201      /* writing to 0  */
202      colorRegions[0] = NULL;
203      intel->constant_cliprect = GL_TRUE;
204   }
205   else if (fb->_NumColorDrawBuffers > 1) {
206       int i;
207       struct intel_renderbuffer *irb;
208
209       for (i = 0; i < fb->_NumColorDrawBuffers; i++) {
210           irb = intel_renderbuffer(fb->_ColorDrawBuffers[i]);
211           colorRegions[i] = irb ? irb->region : NULL;
212       }
213       intel->constant_cliprect = GL_TRUE;
214   }
215   else {
216      /* Get the intel_renderbuffer for the single colorbuffer we're drawing
217       * into, and set up cliprects if it's a DRI1 window front buffer.
218       */
219      if (fb->Name == 0) {
220	 intel->constant_cliprect = intel->driScreen->dri2.enabled;
221	 /* drawing to window system buffer */
222	 if (fb->_ColorDrawBufferIndexes[0] == BUFFER_FRONT_LEFT) {
223	    if (!intel->constant_cliprect && !intel->front_cliprects)
224	       intel_batchbuffer_flush(intel->batch);
225	    intel->front_cliprects = GL_TRUE;
226	    colorRegions[0] = intel_get_rb_region(fb, BUFFER_FRONT_LEFT);
227	 }
228	 else {
229	    if (!intel->constant_cliprect && intel->front_cliprects)
230	       intel_batchbuffer_flush(intel->batch);
231	    intel->front_cliprects = GL_FALSE;
232	    colorRegions[0] = intel_get_rb_region(fb, BUFFER_BACK_LEFT);
233	 }
234      }
235      else {
236	 /* drawing to user-created FBO */
237	 struct intel_renderbuffer *irb;
238	 irb = intel_renderbuffer(fb->_ColorDrawBuffers[0]);
239	 colorRegions[0] = (irb && irb->region) ? irb->region : NULL;
240	 intel->constant_cliprect = GL_TRUE;
241      }
242   }
243
244   if (!colorRegions[0]) {
245      FALLBACK(intel, INTEL_FALLBACK_DRAW_BUFFER, GL_TRUE);
246   }
247   else {
248      FALLBACK(intel, INTEL_FALLBACK_DRAW_BUFFER, GL_FALSE);
249   }
250
251   /***
252    *** Get depth buffer region and check if we need a software fallback.
253    *** Note that the depth buffer is usually a DEPTH_STENCIL buffer.
254    ***/
255   if (fb->_DepthBuffer && fb->_DepthBuffer->Wrapped) {
256      irbDepth = intel_renderbuffer(fb->_DepthBuffer->Wrapped);
257      if (irbDepth && irbDepth->region) {
258         FALLBACK(intel, INTEL_FALLBACK_DEPTH_BUFFER, GL_FALSE);
259         depthRegion = irbDepth->region;
260      }
261      else {
262         FALLBACK(intel, INTEL_FALLBACK_DEPTH_BUFFER, GL_TRUE);
263         depthRegion = NULL;
264      }
265   }
266   else {
267      /* not using depth buffer */
268      FALLBACK(intel, INTEL_FALLBACK_DEPTH_BUFFER, GL_FALSE);
269      depthRegion = NULL;
270   }
271
272   /***
273    *** Stencil buffer
274    *** This can only be hardware accelerated if we're using a
275    *** combined DEPTH_STENCIL buffer.
276    ***/
277   if (fb->_StencilBuffer && fb->_StencilBuffer->Wrapped) {
278      irbStencil = intel_renderbuffer(fb->_StencilBuffer->Wrapped);
279      if (irbStencil && irbStencil->region) {
280         ASSERT(irbStencil->Base.Format == MESA_FORMAT_S8_Z24);
281         FALLBACK(intel, INTEL_FALLBACK_STENCIL_BUFFER, GL_FALSE);
282      }
283      else {
284         FALLBACK(intel, INTEL_FALLBACK_STENCIL_BUFFER, GL_TRUE);
285      }
286   }
287   else {
288      /* XXX FBO: instead of FALSE, pass ctx->Stencil._Enabled ??? */
289      FALLBACK(intel, INTEL_FALLBACK_STENCIL_BUFFER, GL_FALSE);
290   }
291
292   /*
293    * Update depth and stencil test state
294    */
295   if (ctx->Driver.Enable) {
296      ctx->Driver.Enable(ctx, GL_DEPTH_TEST,
297                         (ctx->Depth.Test && fb->Visual.depthBits > 0));
298      ctx->Driver.Enable(ctx, GL_STENCIL_TEST,
299                         (ctx->Stencil.Enabled && fb->Visual.stencilBits > 0));
300   }
301   else {
302      /* Mesa's Stencil._Enabled field is updated when
303       * _NEW_BUFFERS | _NEW_STENCIL, but i965 code assumes that the value
304       * only changes with _NEW_STENCIL (which seems sensible).  So flag it
305       * here since this is the _NEW_BUFFERS path.
306       */
307      ctx->NewState |= (_NEW_DEPTH | _NEW_STENCIL);
308   }
309
310   intel->vtbl.set_draw_region(intel, colorRegions, depthRegion,
311                               fb->_NumColorDrawBuffers);
312
313   /* update viewport since it depends on window size */
314#ifdef I915
315   intelCalcViewport(ctx);
316#else
317   ctx->NewState |= _NEW_VIEWPORT;
318#endif
319   /* Set state we know depends on drawable parameters:
320    */
321   if (ctx->Driver.Scissor)
322      ctx->Driver.Scissor(ctx, ctx->Scissor.X, ctx->Scissor.Y,
323			  ctx->Scissor.Width, ctx->Scissor.Height);
324   intel->NewGLState |= _NEW_SCISSOR;
325
326   if (ctx->Driver.DepthRange)
327      ctx->Driver.DepthRange(ctx,
328			     ctx->Viewport.Near,
329			     ctx->Viewport.Far);
330
331   /* Update culling direction which changes depending on the
332    * orientation of the buffer:
333    */
334   if (ctx->Driver.FrontFace)
335      ctx->Driver.FrontFace(ctx, ctx->Polygon.FrontFace);
336   else
337      ctx->NewState |= _NEW_POLYGON;
338}
339
340
341static void
342intelDrawBuffer(GLcontext * ctx, GLenum mode)
343{
344   if ((ctx->DrawBuffer != NULL) && (ctx->DrawBuffer->Name == 0)) {
345      struct intel_context *const intel = intel_context(ctx);
346      const GLboolean was_front_buffer_rendering =
347	intel->is_front_buffer_rendering;
348
349      intel->is_front_buffer_rendering = (mode == GL_FRONT_LEFT)
350	|| (mode == GL_FRONT);
351
352      /* If we weren't front-buffer rendering before but we are now, make sure
353       * that the front-buffer has actually been allocated.
354       */
355      if (!was_front_buffer_rendering && intel->is_front_buffer_rendering) {
356	 intel_update_renderbuffers(intel->driContext,
357				    intel->driContext->driDrawablePriv);
358      }
359   }
360
361   intel_draw_buffer(ctx, ctx->DrawBuffer);
362}
363
364
365static void
366intelReadBuffer(GLcontext * ctx, GLenum mode)
367{
368   if ((ctx->DrawBuffer != NULL) && (ctx->DrawBuffer->Name == 0)) {
369      struct intel_context *const intel = intel_context(ctx);
370      const GLboolean was_front_buffer_reading =
371	intel->is_front_buffer_reading;
372
373      intel->is_front_buffer_reading = (mode == GL_FRONT_LEFT)
374	|| (mode == GL_FRONT);
375
376      /* If we weren't front-buffer reading before but we are now, make sure
377       * that the front-buffer has actually been allocated.
378       */
379      if (!was_front_buffer_reading && intel->is_front_buffer_reading) {
380	 intel_update_renderbuffers(intel->driContext,
381				    intel->driContext->driDrawablePriv);
382      }
383   }
384
385   if (ctx->ReadBuffer == ctx->DrawBuffer) {
386      /* This will update FBO completeness status.
387       * A framebuffer will be incomplete if the GL_READ_BUFFER setting
388       * refers to a missing renderbuffer.  Calling glReadBuffer can set
389       * that straight and can make the drawing buffer complete.
390       */
391      intel_draw_buffer(ctx, ctx->DrawBuffer);
392   }
393   /* Generally, functions which read pixels (glReadPixels, glCopyPixels, etc)
394    * reference ctx->ReadBuffer and do appropriate state checks.
395    */
396}
397
398
399void
400intelInitBufferFuncs(struct dd_function_table *functions)
401{
402   functions->DrawBuffer = intelDrawBuffer;
403   functions->ReadBuffer = intelReadBuffer;
404}
405