Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4358 Serge 1
/*
2
 * Mesa 3-D graphics library
3
 *
4
 * Copyright (C) 1999-2006  Brian Paul   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 xm_buffer.h
28
 * Framebuffer and renderbuffer-related functions.
29
 */
30
 
31
 
32
#include "glxheader.h"
33
#include "xmesaP.h"
34
#include "main/imports.h"
35
#include "main/formats.h"
36
#include "main/framebuffer.h"
37
#include "main/renderbuffer.h"
38
#include "swrast/s_renderbuffer.h"
39
 
40
 
41
#define XMESA_RENDERBUFFER 0x1234
42
 
43
 
44
#if defined(USE_XSHM)
45
static volatile int mesaXErrorFlag = 0;
46
 
47
/**
48
 * Catches potential Xlib errors.
49
 */
50
static int
51
mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
52
{
53
   (void) dpy;
54
   (void) event;
55
   mesaXErrorFlag = 1;
56
   return 0;
57
}
58
 
59
/**
60
 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
61
 * Return:  GL_TRUE if success, GL_FALSE if error
62
 */
63
static GLboolean
64
alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
65
{
66
   /*
67
    * We have to do a _lot_ of error checking here to be sure we can
68
    * really use the XSHM extension.  It seems different servers trigger
69
    * errors at different points if the extension won't work.  Therefore
70
    * we have to be very careful...
71
    */
72
   GC gc;
73
   int (*old_handler)(XMesaDisplay *, XErrorEvent *);
74
 
75
   if (width == 0 || height == 0) {
76
      /* this will be true the first time we're called on 'b' */
77
      return GL_FALSE;
78
   }
79
 
80
   b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
81
                                        b->xm_visual->visinfo->visual,
82
                                        b->xm_visual->visinfo->depth,
83
                                        ZPixmap, NULL, &b->shminfo,
84
                                        width, height);
85
   if (b->backxrb->ximage == NULL) {
86
      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
87
      b->shm = 0;
88
      return GL_FALSE;
89
   }
90
 
91
   b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
92
			     * b->backxrb->ximage->height, IPC_CREAT|0777);
93
   if (b->shminfo.shmid < 0) {
94
      _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
95
      XDestroyImage(b->backxrb->ximage);
96
      b->backxrb->ximage = NULL;
97
      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
98
      b->shm = 0;
99
      return GL_FALSE;
100
   }
101
 
102
   b->shminfo.shmaddr = b->backxrb->ximage->data
103
                      = (char*)shmat(b->shminfo.shmid, 0, 0);
104
   if (b->shminfo.shmaddr == (char *) -1) {
105
      _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
106
      XDestroyImage(b->backxrb->ximage);
107
      shmctl(b->shminfo.shmid, IPC_RMID, 0);
108
      b->backxrb->ximage = NULL;
109
      _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
110
      b->shm = 0;
111
      return GL_FALSE;
112
   }
113
 
114
   b->shminfo.readOnly = False;
115
   mesaXErrorFlag = 0;
116
   old_handler = XSetErrorHandler(mesaHandleXError);
117
   /* This may trigger the X protocol error we're ready to catch: */
118
   XShmAttach(b->xm_visual->display, &b->shminfo);
119
   XSync(b->xm_visual->display, False);
120
 
121
   if (mesaXErrorFlag) {
122
      /* we are on a remote display, this error is normal, don't print it */
123
      XFlush(b->xm_visual->display);
124
      mesaXErrorFlag = 0;
125
      XDestroyImage(b->backxrb->ximage);
126
      shmdt(b->shminfo.shmaddr);
127
      shmctl(b->shminfo.shmid, IPC_RMID, 0);
128
      b->backxrb->ximage = NULL;
129
      b->shm = 0;
130
      (void) XSetErrorHandler(old_handler);
131
      return GL_FALSE;
132
   }
133
 
134
   shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
135
 
136
   /* Finally, try an XShmPutImage to be really sure the extension works */
137
   gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
138
   XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
139
		 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
140
   XSync(b->xm_visual->display, False);
141
   XFreeGC(b->xm_visual->display, gc);
142
   (void) XSetErrorHandler(old_handler);
143
   if (mesaXErrorFlag) {
144
      XFlush(b->xm_visual->display);
145
      mesaXErrorFlag = 0;
146
      XDestroyImage(b->backxrb->ximage);
147
      shmdt(b->shminfo.shmaddr);
148
      shmctl(b->shminfo.shmid, IPC_RMID, 0);
149
      b->backxrb->ximage = NULL;
150
      b->shm = 0;
151
      return GL_FALSE;
152
   }
153
 
154
   return GL_TRUE;
155
}
156
#else
157
static GLboolean
158
alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
159
{
160
   /* Can't compile XSHM support */
161
   return GL_FALSE;
162
}
163
#endif
164
 
165
 
166
 
167
/**
168
 * Setup an off-screen pixmap or Ximage to use as the back buffer.
169
 * Input:  b - the X/Mesa buffer
170
 */
171
static void
172
alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
173
{
174
   if (b->db_mode == BACK_XIMAGE) {
175
      /* Deallocate the old backxrb->ximage, if any */
176
      if (b->backxrb->ximage) {
177
#if defined(USE_XSHM)
178
	 if (b->shm) {
179
	    XShmDetach(b->xm_visual->display, &b->shminfo);
180
	    XDestroyImage(b->backxrb->ximage);
181
	    shmdt(b->shminfo.shmaddr);
182
	 }
183
	 else
184
#endif
185
	   XMesaDestroyImage(b->backxrb->ximage);
186
	 b->backxrb->ximage = NULL;
187
      }
188
 
189
      if (width == 0 || height == 0)
190
         return;
191
 
192
      /* Allocate new back buffer */
193
      if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
194
	 /* Allocate a regular XImage for the back buffer. */
195
	 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
196
                                      b->xm_visual->visinfo->visual,
197
                                      GET_VISUAL_DEPTH(b->xm_visual),
198
				      ZPixmap, 0,   /* format, offset */
199
				      NULL,
200
                                      width, height,
201
				      8, 0);  /* pad, bytes_per_line */
202
	 if (!b->backxrb->ximage) {
203
	    _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
204
            return;
205
	 }
206
         b->backxrb->ximage->data = malloc(b->backxrb->ximage->height
207
                                        * b->backxrb->ximage->bytes_per_line);
208
         if (!b->backxrb->ximage->data) {
209
            _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
210
            XMesaDestroyImage(b->backxrb->ximage);
211
            b->backxrb->ximage = NULL;
212
         }
213
      }
214
      b->backxrb->pixmap = None;
215
   }
216
   else if (b->db_mode == BACK_PIXMAP) {
217
      /* Free the old back pixmap */
218
      if (b->backxrb->pixmap) {
219
         XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
220
         b->backxrb->pixmap = 0;
221
      }
222
 
223
      if (width > 0 && height > 0) {
224
         /* Allocate new back pixmap */
225
         b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
226
                                                b->frontxrb->drawable,
227
                                                width, height,
228
                                                GET_VISUAL_DEPTH(b->xm_visual));
229
      }
230
 
231
      b->backxrb->ximage = NULL;
232
      b->backxrb->drawable = b->backxrb->pixmap;
233
   }
234
}
235
 
236
 
237
static void
238
xmesa_delete_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
239
{
240
   /* XXX Note: the ximage or Pixmap attached to this renderbuffer
241
    * should probably get freed here, but that's currently done in
242
    * XMesaDestroyBuffer().
243
    */
244
   free(rb);
245
}
246
 
247
 
248
/**
249
 * Reallocate renderbuffer storage for front color buffer.
250
 * Called via gl_renderbuffer::AllocStorage()
251
 */
252
static GLboolean
253
xmesa_alloc_front_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
254
                          GLenum internalFormat, GLuint width, GLuint height)
255
{
256
   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
257
 
258
   /* just clear these to be sure we don't accidentally use them */
259
   xrb->origin2 = NULL;
260
   xrb->origin3 = NULL;
261
   xrb->origin4 = NULL;
262
 
263
   /* for the FLIP macro: */
264
   xrb->bottom = height - 1;
265
 
266
   rb->Width = width;
267
   rb->Height = height;
268
   rb->InternalFormat = internalFormat;
269
 
270
   return GL_TRUE;
271
}
272
 
273
 
274
/**
275
 * Reallocate renderbuffer storage for back color buffer.
276
 * Called via gl_renderbuffer::AllocStorage()
277
 */
278
static GLboolean
279
xmesa_alloc_back_storage(struct gl_context *ctx, struct gl_renderbuffer *rb,
280
                         GLenum internalFormat, GLuint width, GLuint height)
281
{
282
   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
283
 
284
   /* reallocate the back buffer XImage or Pixmap */
285
   assert(xrb->Parent);
286
   alloc_back_buffer(xrb->Parent, width, height);
287
 
288
   /* same as front buffer */
289
   /* XXX why is this here? */
290
   (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
291
 
292
   /* plus... */
293
   if (xrb->ximage) {
294
      /* Needed by PIXELADDR2 macro */
295
      xrb->width2 = xrb->ximage->bytes_per_line / 2;
296
      xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
297
 
298
      /* Needed by PIXELADDR3 macro */
299
      xrb->width3 = xrb->ximage->bytes_per_line;
300
      xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
301
 
302
      /* Needed by PIXELADDR4 macro */
303
      xrb->width4 = xrb->ximage->width;
304
      xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
305
   }
306
   else {
307
      /* out of memory or buffer size is 0 x 0 */
308
      xrb->width2 = xrb->width3 = xrb->width4 = 0;
309
      xrb->origin2 = NULL;
310
      xrb->origin3 = NULL;
311
      xrb->origin4 = NULL;
312
   }
313
 
314
   return GL_TRUE;
315
}
316
 
317
 
318
/**
319
 * Used for allocating front/back renderbuffers for an X window.
320
 */
321
struct xmesa_renderbuffer *
322
xmesa_new_renderbuffer(struct gl_context *ctx, GLuint name,
323
                       const struct xmesa_visual *xmvis,
324
                       GLboolean backBuffer)
325
{
326
   struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
327
   if (xrb) {
328
      GLuint name = 0;
329
      _mesa_init_renderbuffer(&xrb->Base.Base, name);
330
 
331
      xrb->Base.Base.Delete = xmesa_delete_renderbuffer;
332
      if (backBuffer)
333
         xrb->Base.Base.AllocStorage = xmesa_alloc_back_storage;
334
      else
335
         xrb->Base.Base.AllocStorage = xmesa_alloc_front_storage;
336
 
337
      xrb->Base.Base.InternalFormat = GL_RGBA;
338
      xrb->Base.Base._BaseFormat = GL_RGBA;
339
      xrb->Base.Base.ClassID = XMESA_RENDERBUFFER;
340
 
341
      switch (xmvis->undithered_pf) {
342
      case PF_8R8G8B:
343
         /* This will really only happen for pixmaps.  We'll access the
344
          * pixmap via a temporary XImage which will be 32bpp.
345
          */
346
         xrb->Base.Base.Format = MESA_FORMAT_XRGB8888;
347
         break;
348
      case PF_8A8R8G8B:
349
         xrb->Base.Base.Format = MESA_FORMAT_ARGB8888;
350
         break;
351
      case PF_8A8B8G8R:
352
         xrb->Base.Base.Format = MESA_FORMAT_RGBA8888_REV;
353
         break;
354
      case PF_5R6G5B:
355
         xrb->Base.Base.Format = MESA_FORMAT_RGB565;
356
         break;
357
      default:
358
         _mesa_warning(ctx, "Bad pixel format in xmesa_new_renderbuffer");
359
         xrb->Base.Base.Format = MESA_FORMAT_ARGB8888;
360
         break;
361
      }
362
 
363
      /* only need to set Red/Green/EtcBits fields for user-created RBs */
364
   }
365
   return xrb;
366
}
367
 
368
 
369
/**
370
 * Called via gl_framebuffer::Delete() method when this buffer
371
 * is _really_ being deleted.
372
 */
373
void
374
xmesa_delete_framebuffer(struct gl_framebuffer *fb)
375
{
376
   XMesaBuffer b = XMESA_BUFFER(fb);
377
 
378
   if (b->num_alloced > 0) {
379
      /* If no other buffer uses this X colormap then free the colors. */
380
      if (!xmesa_find_buffer(b->display, b->cmap, b)) {
381
         XFreeColors(b->display, b->cmap,
382
                     b->alloced_colors, b->num_alloced, 0);
383
      }
384
   }
385
 
386
   if (b->gc)
387
      XMesaFreeGC(b->display, b->gc);
388
   if (b->cleargc)
389
      XMesaFreeGC(b->display, b->cleargc);
390
   if (b->swapgc)
391
      XMesaFreeGC(b->display, b->swapgc);
392
 
393
   if (fb->Visual.doubleBufferMode) {
394
      /* free back ximage/pixmap/shmregion */
395
      if (b->backxrb->ximage) {
396
#if defined(USE_XSHM)
397
         if (b->shm) {
398
            XShmDetach( b->display, &b->shminfo );
399
            XDestroyImage( b->backxrb->ximage );
400
            shmdt( b->shminfo.shmaddr );
401
         }
402
         else
403
#endif
404
            XMesaDestroyImage( b->backxrb->ximage );
405
         b->backxrb->ximage = NULL;
406
      }
407
      if (b->backxrb->pixmap) {
408
         XMesaFreePixmap( b->display, b->backxrb->pixmap );
409
      }
410
   }
411
 
412
   _mesa_free_framebuffer_data(fb);
413
   free(fb);
414
}
415
 
416
 
417
/**
418
 * Called via ctx->Driver.MapRenderbuffer()
419
 */
420
void
421
xmesa_MapRenderbuffer(struct gl_context *ctx,
422
                      struct gl_renderbuffer *rb,
423
                      GLuint x, GLuint y, GLuint w, GLuint h,
424
                      GLbitfield mode,
425
                      GLubyte **mapOut, GLint *rowStrideOut)
426
{
427
   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
428
 
429
   if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) {
430
      XImage *ximage = xrb->ximage;
431
 
432
      assert(!xrb->map_mode); /* only a single mapping allowed */
433
 
434
      xrb->map_mode = mode;
435
      xrb->map_x = x;
436
      xrb->map_y = y;
437
      xrb->map_w = w;
438
      xrb->map_h = h;
439
 
440
      if (ximage) {
441
         int y2 = rb->Height - y - 1;
442
 
443
         *mapOut = (GLubyte *) ximage->data
444
            + y2 * ximage->bytes_per_line
445
            + x * ximage->bits_per_pixel / 8;
446
      }
447
      else {
448
         /* this must be a pixmap/window renderbuffer */
449
         int (*old_handler)(XMesaDisplay *, XErrorEvent *);
450
         int y2 = rb->Height - y - h;
451
 
452
         assert(xrb->pixmap);
453
 
454
         /* Install error handler for XGetImage() in case the the window
455
          * isn't mapped.  If we fail we'll create a temporary XImage.
456
          */
457
         mesaXErrorFlag = 0;
458
         old_handler = XSetErrorHandler(mesaHandleXError);
459
 
460
         /* read pixel data out of the pixmap/window into an XImage */
461
         ximage = XGetImage(xrb->Parent->display,
462
                            xrb->pixmap, x, y2, w, h,
463
                            AllPlanes, ZPixmap);
464
 
465
         XSetErrorHandler(old_handler);
466
 
467
         if (mesaXErrorFlag) {
468
            /* create new, temporary XImage */
469
            int bytes_per_line =
470
               _mesa_format_row_stride(xrb->Base.Base.Format,
471
                                       xrb->Base.Base.Width);
472
            char *image = malloc(bytes_per_line *
473
                                          xrb->Base.Base.Height);
474
            ximage = XCreateImage(xrb->Parent->display,
475
                                  xrb->Parent->xm_visual->visinfo->visual,
476
                                  xrb->Parent->xm_visual->visinfo->depth,
477
                                  ZPixmap, /* format */
478
                                  0, /* offset */
479
                                  image, /* data */
480
                                  xrb->Base.Base.Width,
481
                                  xrb->Base.Base.Height,
482
                                  8, /* pad */
483
                                  bytes_per_line);
484
         }
485
 
486
         if (!ximage) {
487
            *mapOut = NULL;
488
            *rowStrideOut = 0;
489
            return;
490
         }
491
 
492
         xrb->map_ximage = ximage;
493
 
494
         /* the first row of the OpenGL image is last row of the XImage */
495
         *mapOut = (GLubyte *) ximage->data
496
            + (h - 1) * ximage->bytes_per_line;
497
      }
498
 
499
      /* We return a negative stride here since XImage data is upside down
500
       * with respect to OpenGL images.
501
       */
502
      *rowStrideOut = -ximage->bytes_per_line;
503
      return;
504
   }
505
 
506
   /* otherwise, this is an ordinary malloc-based renderbuffer */
507
   _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode,
508
                                 mapOut, rowStrideOut);
509
}
510
 
511
 
512
/**
513
 * Called via ctx->Driver.UnmapRenderbuffer()
514
 */
515
void
516
xmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb)
517
{
518
   struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
519
 
520
   if (xrb->Base.Base.ClassID == XMESA_RENDERBUFFER) {
521
      XImage *ximage = xrb->ximage;
522
 
523
      if (!ximage) {
524
         /* this must be a pixmap/window renderbuffer */
525
         assert(xrb->pixmap);
526
         assert(xrb->map_ximage);
527
         if (xrb->map_ximage) {
528
            if (xrb->map_mode & GL_MAP_WRITE_BIT) {
529
               /* put modified ximage data back into the pixmap/window */
530
               int y2 = rb->Height - xrb->map_y - xrb->map_h;
531
               GC gc = XCreateGC(xrb->Parent->display, xrb->pixmap, 0, NULL);
532
 
533
               XPutImage(xrb->Parent->display,
534
                         xrb->pixmap,              /* dest */
535
                         gc,
536
                         xrb->map_ximage,          /* source */
537
                         0, 0,                     /* src x, y */
538
                         xrb->map_x, y2,           /* dest x, y */
539
                         xrb->map_w, xrb->map_h);  /* size */
540
 
541
               XFreeGC(xrb->Parent->display, gc);
542
            }
543
            XMesaDestroyImage(xrb->map_ximage);
544
            xrb->map_ximage = NULL;
545
         }
546
      }
547
 
548
      xrb->map_mode = 0x0;
549
 
550
      return;
551
   }
552
 
553
   /* otherwise, this is an ordinary malloc-based renderbuffer */
554
   _swrast_unmap_soft_renderbuffer(ctx, rb);
555
}
556