i915: Fix driver for the miptree x/y offset changes.
[mesa.git] / src / mesa / drivers / x11 / xm_buffer.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5.2
4 *
5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR 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/framebuffer.h"
36 #include "main/renderbuffer.h"
37
38
39 #if defined(USE_XSHM) && !defined(XFree86Server)
40 static volatile int mesaXErrorFlag = 0;
41
42 /**
43 * Catches potential Xlib errors.
44 */
45 static int
46 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
47 {
48 (void) dpy;
49 (void) event;
50 mesaXErrorFlag = 1;
51 return 0;
52 }
53
54 /**
55 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
56 * Return: GL_TRUE if success, GL_FALSE if error
57 */
58 static GLboolean
59 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
60 {
61 /*
62 * We have to do a _lot_ of error checking here to be sure we can
63 * really use the XSHM extension. It seems different servers trigger
64 * errors at different points if the extension won't work. Therefore
65 * we have to be very careful...
66 */
67 GC gc;
68 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
69
70 if (width == 0 || height == 0) {
71 /* this will be true the first time we're called on 'b' */
72 return GL_FALSE;
73 }
74
75 b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
76 b->xm_visual->visinfo->visual,
77 b->xm_visual->visinfo->depth,
78 ZPixmap, NULL, &b->shminfo,
79 width, height);
80 if (b->backxrb->ximage == NULL) {
81 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
82 b->shm = 0;
83 return GL_FALSE;
84 }
85
86 b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
87 * b->backxrb->ximage->height, IPC_CREAT|0777);
88 if (b->shminfo.shmid < 0) {
89 _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
90 XDestroyImage(b->backxrb->ximage);
91 b->backxrb->ximage = NULL;
92 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
93 b->shm = 0;
94 return GL_FALSE;
95 }
96
97 b->shminfo.shmaddr = b->backxrb->ximage->data
98 = (char*)shmat(b->shminfo.shmid, 0, 0);
99 if (b->shminfo.shmaddr == (char *) -1) {
100 _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
101 XDestroyImage(b->backxrb->ximage);
102 shmctl(b->shminfo.shmid, IPC_RMID, 0);
103 b->backxrb->ximage = NULL;
104 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
105 b->shm = 0;
106 return GL_FALSE;
107 }
108
109 b->shminfo.readOnly = False;
110 mesaXErrorFlag = 0;
111 old_handler = XSetErrorHandler(mesaHandleXError);
112 /* This may trigger the X protocol error we're ready to catch: */
113 XShmAttach(b->xm_visual->display, &b->shminfo);
114 XSync(b->xm_visual->display, False);
115
116 if (mesaXErrorFlag) {
117 /* we are on a remote display, this error is normal, don't print it */
118 XFlush(b->xm_visual->display);
119 mesaXErrorFlag = 0;
120 XDestroyImage(b->backxrb->ximage);
121 shmdt(b->shminfo.shmaddr);
122 shmctl(b->shminfo.shmid, IPC_RMID, 0);
123 b->backxrb->ximage = NULL;
124 b->shm = 0;
125 (void) XSetErrorHandler(old_handler);
126 return GL_FALSE;
127 }
128
129 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
130
131 /* Finally, try an XShmPutImage to be really sure the extension works */
132 gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
133 XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
134 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
135 XSync(b->xm_visual->display, False);
136 XFreeGC(b->xm_visual->display, gc);
137 (void) XSetErrorHandler(old_handler);
138 if (mesaXErrorFlag) {
139 XFlush(b->xm_visual->display);
140 mesaXErrorFlag = 0;
141 XDestroyImage(b->backxrb->ximage);
142 shmdt(b->shminfo.shmaddr);
143 shmctl(b->shminfo.shmid, IPC_RMID, 0);
144 b->backxrb->ximage = NULL;
145 b->shm = 0;
146 return GL_FALSE;
147 }
148
149 return GL_TRUE;
150 }
151 #else
152 static GLboolean
153 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
154 {
155 /* Can't compile XSHM support */
156 return GL_FALSE;
157 }
158 #endif
159
160
161
162 /**
163 * Setup an off-screen pixmap or Ximage to use as the back buffer.
164 * Input: b - the X/Mesa buffer
165 */
166 static void
167 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
168 {
169 if (b->db_mode == BACK_XIMAGE) {
170 /* Deallocate the old backxrb->ximage, if any */
171 if (b->backxrb->ximage) {
172 #if defined(USE_XSHM) && !defined(XFree86Server)
173 if (b->shm) {
174 XShmDetach(b->xm_visual->display, &b->shminfo);
175 XDestroyImage(b->backxrb->ximage);
176 shmdt(b->shminfo.shmaddr);
177 }
178 else
179 #endif
180 XMesaDestroyImage(b->backxrb->ximage);
181 b->backxrb->ximage = NULL;
182 }
183
184 if (width == 0 || height == 0)
185 return;
186
187 /* Allocate new back buffer */
188 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
189 /* Allocate a regular XImage for the back buffer. */
190 #ifdef XFree86Server
191 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
192 width, height, NULL);
193 #else
194 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
195 b->xm_visual->visinfo->visual,
196 GET_VISUAL_DEPTH(b->xm_visual),
197 ZPixmap, 0, /* format, offset */
198 NULL,
199 width, height,
200 8, 0); /* pad, bytes_per_line */
201 #endif
202 if (!b->backxrb->ximage) {
203 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
204 return;
205 }
206 b->backxrb->ximage->data = (char *) 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_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 _mesa_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(GLcontext *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->origin1 = NULL;
260 xrb->origin2 = NULL;
261 xrb->origin3 = NULL;
262 xrb->origin4 = NULL;
263
264 /* for the FLIP macro: */
265 xrb->bottom = height - 1;
266
267 rb->Width = width;
268 rb->Height = height;
269 rb->InternalFormat = internalFormat;
270
271 return GL_TRUE;
272 }
273
274
275 /**
276 * Reallocate renderbuffer storage for back color buffer.
277 * Called via gl_renderbuffer::AllocStorage()
278 */
279 static GLboolean
280 xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
281 GLenum internalFormat, GLuint width, GLuint height)
282 {
283 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
284
285 /* reallocate the back buffer XImage or Pixmap */
286 assert(xrb->Parent);
287 alloc_back_buffer(xrb->Parent, width, height);
288
289 /* same as front buffer */
290 /* XXX why is this here? */
291 (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
292
293 /* plus... */
294 if (xrb->ximage) {
295 /* Needed by PIXELADDR1 macro */
296 xrb->width1 = xrb->ximage->bytes_per_line;
297 xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1);
298
299 /* Needed by PIXELADDR2 macro */
300 xrb->width2 = xrb->ximage->bytes_per_line / 2;
301 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
302
303 /* Needed by PIXELADDR3 macro */
304 xrb->width3 = xrb->ximage->bytes_per_line;
305 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
306
307 /* Needed by PIXELADDR4 macro */
308 xrb->width4 = xrb->ximage->width;
309 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
310 }
311 else {
312 /* out of memory or buffer size is 0 x 0 */
313 xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0;
314 xrb->origin1 = NULL;
315 xrb->origin2 = NULL;
316 xrb->origin3 = NULL;
317 xrb->origin4 = NULL;
318 }
319
320 return GL_TRUE;
321 }
322
323
324 struct xmesa_renderbuffer *
325 xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
326 GLboolean backBuffer)
327 {
328 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
329 if (xrb) {
330 GLuint name = 0;
331 _mesa_init_renderbuffer(&xrb->Base, name);
332
333 xrb->Base.Delete = xmesa_delete_renderbuffer;
334 if (backBuffer)
335 xrb->Base.AllocStorage = xmesa_alloc_back_storage;
336 else
337 xrb->Base.AllocStorage = xmesa_alloc_front_storage;
338
339 if (visual->rgbMode) {
340 xrb->Base.InternalFormat = GL_RGBA;
341 xrb->Base._BaseFormat = GL_RGBA;
342 xrb->Base.DataType = GL_UNSIGNED_BYTE;
343 xrb->Base.RedBits = visual->redBits;
344 xrb->Base.GreenBits = visual->greenBits;
345 xrb->Base.BlueBits = visual->blueBits;
346 xrb->Base.AlphaBits = visual->alphaBits;
347 }
348 else {
349 xrb->Base.InternalFormat = GL_COLOR_INDEX;
350 xrb->Base._BaseFormat = GL_COLOR_INDEX;
351 xrb->Base.DataType = GL_UNSIGNED_INT;
352 xrb->Base.IndexBits = visual->indexBits;
353 }
354 /* only need to set Red/Green/EtcBits fields for user-created RBs */
355 }
356 return xrb;
357 }
358
359
360 /**
361 * Called via gl_framebuffer::Delete() method when this buffer
362 * is _really_ being deleted.
363 */
364 void
365 xmesa_delete_framebuffer(struct gl_framebuffer *fb)
366 {
367 XMesaBuffer b = XMESA_BUFFER(fb);
368
369 if (b->num_alloced > 0) {
370 /* If no other buffer uses this X colormap then free the colors. */
371 if (!xmesa_find_buffer(b->display, b->cmap, b)) {
372 #ifdef XFree86Server
373 int client = 0;
374 if (b->frontxrb->drawable)
375 client = CLIENT_ID(b->frontxrb->drawable->id);
376 (void)FreeColors(b->cmap, client,
377 b->num_alloced, b->alloced_colors, 0);
378 #else
379 XFreeColors(b->display, b->cmap,
380 b->alloced_colors, b->num_alloced, 0);
381 #endif
382 }
383 }
384
385 if (b->gc)
386 XMesaFreeGC(b->display, b->gc);
387 if (b->cleargc)
388 XMesaFreeGC(b->display, b->cleargc);
389 if (b->swapgc)
390 XMesaFreeGC(b->display, b->swapgc);
391
392 if (fb->Visual.doubleBufferMode) {
393 /* free back ximage/pixmap/shmregion */
394 if (b->backxrb->ximage) {
395 #if defined(USE_XSHM) && !defined(XFree86Server)
396 if (b->shm) {
397 XShmDetach( b->display, &b->shminfo );
398 XDestroyImage( b->backxrb->ximage );
399 shmdt( b->shminfo.shmaddr );
400 }
401 else
402 #endif
403 XMesaDestroyImage( b->backxrb->ximage );
404 b->backxrb->ximage = NULL;
405 }
406 if (b->backxrb->pixmap) {
407 XMesaFreePixmap( b->display, b->backxrb->pixmap );
408 if (b->xm_visual->hpcr_clear_flag) {
409 XMesaFreePixmap( b->display,
410 b->xm_visual->hpcr_clear_pixmap );
411 XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage );
412 }
413 }
414 }
415
416 if (b->rowimage) {
417 _mesa_free( b->rowimage->data );
418 b->rowimage->data = NULL;
419 XMesaDestroyImage( b->rowimage );
420 }
421
422 _mesa_free_framebuffer_data(fb);
423 _mesa_free(fb);
424 }