Big re-org of buffer size management.
[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 "GL/xmesa.h"
34 #include "xmesaP.h"
35 #include "imports.h"
36 #include "renderbuffer.h"
37
38
39 #ifndef 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 #endif
54
55
56 /**
57 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
58 * Return: GL_TRUE if success, GL_FALSE if error
59 */
60 #ifndef XFree86Server
61 static GLboolean
62 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
63 {
64 #ifdef USE_XSHM
65 /*
66 * We have to do a _lot_ of error checking here to be sure we can
67 * really use the XSHM extension. It seems different servers trigger
68 * errors at different points if the extension won't work. Therefore
69 * we have to be very careful...
70 */
71 GC gc;
72 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
73
74 if (width == 0 || height == 0) {
75 /* this will be true the first time we're called on 'b' */
76 return GL_FALSE;
77 }
78
79 b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
80 b->xm_visual->visinfo->visual,
81 b->xm_visual->visinfo->depth,
82 ZPixmap, NULL, &b->shminfo,
83 width, height);
84 if (b->backxrb->ximage == NULL) {
85 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
86 b->shm = 0;
87 return GL_FALSE;
88 }
89
90 b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
91 * b->backxrb->ximage->height, IPC_CREAT|0777);
92 if (b->shminfo.shmid < 0) {
93 _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
94 XDestroyImage(b->backxrb->ximage);
95 b->backxrb->ximage = NULL;
96 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
97 b->shm = 0;
98 return GL_FALSE;
99 }
100
101 b->shminfo.shmaddr = b->backxrb->ximage->data
102 = (char*)shmat(b->shminfo.shmid, 0, 0);
103 if (b->shminfo.shmaddr == (char *) -1) {
104 _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
105 XDestroyImage(b->backxrb->ximage);
106 shmctl(b->shminfo.shmid, IPC_RMID, 0);
107 b->backxrb->ximage = NULL;
108 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
109 b->shm = 0;
110 return GL_FALSE;
111 }
112
113 b->shminfo.readOnly = False;
114 mesaXErrorFlag = 0;
115 old_handler = XSetErrorHandler(mesaHandleXError);
116 /* This may trigger the X protocol error we're ready to catch: */
117 XShmAttach(b->xm_visual->display, &b->shminfo);
118 XSync(b->xm_visual->display, False);
119
120 if (mesaXErrorFlag) {
121 /* we are on a remote display, this error is normal, don't print it */
122 XFlush(b->xm_visual->display);
123 mesaXErrorFlag = 0;
124 XDestroyImage(b->backxrb->ximage);
125 shmdt(b->shminfo.shmaddr);
126 shmctl(b->shminfo.shmid, IPC_RMID, 0);
127 b->backxrb->ximage = NULL;
128 b->shm = 0;
129 (void) XSetErrorHandler(old_handler);
130 return GL_FALSE;
131 }
132
133 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
134
135 /* Finally, try an XShmPutImage to be really sure the extension works */
136 gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
137 XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
138 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
139 XSync(b->xm_visual->display, False);
140 XFreeGC(b->xm_visual->display, gc);
141 (void) XSetErrorHandler(old_handler);
142 if (mesaXErrorFlag) {
143 XFlush(b->xm_visual->display);
144 mesaXErrorFlag = 0;
145 XDestroyImage(b->backxrb->ximage);
146 shmdt(b->shminfo.shmaddr);
147 shmctl(b->shminfo.shmid, IPC_RMID, 0);
148 b->backxrb->ximage = NULL;
149 b->shm = 0;
150 return GL_FALSE;
151 }
152
153 return GL_TRUE;
154 #else
155 /* Can't compile XSHM support */
156 return GL_FALSE;
157 #endif
158 }
159 #endif
160
161
162
163 /**
164 * Setup an off-screen pixmap or Ximage to use as the back buffer.
165 * Input: b - the X/Mesa buffer
166 */
167 static void
168 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
169 {
170 if (width == 0 || height == 0)
171 return;
172
173 if (b->db_mode == BACK_XIMAGE) {
174 /* Deallocate the old backxrb->ximage, if any */
175 if (b->backxrb->ximage) {
176 #if defined(USE_XSHM) && !defined(XFree86Server)
177 if (b->shm) {
178 XShmDetach(b->xm_visual->display, &b->shminfo);
179 XDestroyImage(b->backxrb->ximage);
180 shmdt(b->shminfo.shmaddr);
181 }
182 else
183 #endif
184 XMesaDestroyImage(b->backxrb->ximage);
185 b->backxrb->ximage = NULL;
186 }
187
188 /* Allocate new back buffer */
189 #ifdef XFree86Server
190 /* Allocate a regular XImage for the back buffer. */
191 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
192 width, height, NULL);
193 {
194 #else
195 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
196 /* Allocate a regular XImage for the back buffer. */
197 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
198 b->xm_visual->visinfo->visual,
199 GET_VISUAL_DEPTH(b->xm_visual),
200 ZPixmap, 0, /* format, offset */
201 NULL,
202 width, height,
203 8, 0); /* pad, bytes_per_line */
204 #endif
205 if (!b->backxrb->ximage) {
206 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
207 return;
208 }
209 b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height
210 * b->backxrb->ximage->bytes_per_line);
211 if (!b->backxrb->ximage->data) {
212 _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
213 XMesaDestroyImage(b->backxrb->ximage);
214 b->backxrb->ximage = NULL;
215 }
216 else {
217 /* this call just updates the width/origin fields in the xrb */
218 b->backxrb->Base.AllocStorage(NULL, &b->backxrb->Base,
219 b->backxrb->Base.InternalFormat,
220 b->backxrb->ximage->width,
221 b->backxrb->ximage->height);
222 }
223 }
224 b->backxrb->pixmap = None;
225 }
226 else if (b->db_mode == BACK_PIXMAP) {
227 if (!width)
228 width = 1;
229 if (!height)
230 height = 1;
231
232 /* Free the old back pixmap */
233 if (b->backxrb->pixmap) {
234 XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
235 }
236 /* Allocate new back pixmap */
237 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
238 b->frontxrb->drawable,
239 width, height,
240 GET_VISUAL_DEPTH(b->xm_visual));
241 b->backxrb->ximage = NULL;
242 }
243 }
244
245
246 static void
247 xmesa_delete_renderbuffer(struct gl_renderbuffer *rb)
248 {
249 /* XXX Note: the ximage or Pixmap attached to this renderbuffer
250 * should probably get freed here, but that's currently done in
251 * XMesaDestroyBuffer().
252 */
253 _mesa_free(rb);
254 }
255
256
257 /**
258 * Reallocate renderbuffer storage for front color buffer.
259 */
260 static GLboolean
261 xmesa_alloc_front_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
262 GLenum internalFormat, GLuint width, GLuint height)
263 {
264 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
265
266 /* just clear these to be sure we don't accidentally use them */
267 xrb->origin1 = NULL;
268 xrb->origin2 = NULL;
269 xrb->origin4 = NULL;
270
271 /* for the FLIP macro: */
272 xrb->bottom = height - 1;
273
274 rb->Width = width;
275 rb->Height = height;
276 rb->InternalFormat = internalFormat;
277
278 return GL_TRUE;
279 }
280
281
282 /**
283 * Reallocate renderbuffer storage for back color buffer.
284 */
285 static GLboolean
286 xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
287 GLenum internalFormat, GLuint width, GLuint height)
288 {
289 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
290
291 /* reallocate the back buffer XImage or Pixmap */
292 assert(xrb->Parent);
293 alloc_back_buffer(xrb->Parent, width, height);
294
295 /* same as front buffer */
296 /* XXX why is this here? */
297 (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
298
299 /* plus... */
300 if (xrb->ximage) {
301 /* Needed by PIXELADDR1 macro */
302 xrb->width1 = xrb->ximage->bytes_per_line;
303 xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1);
304
305 /* Needed by PIXELADDR2 macro */
306 xrb->width2 = xrb->ximage->bytes_per_line / 2;
307 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
308
309 /* Needed by PIXELADDR3 macro */
310 xrb->width3 = xrb->ximage->bytes_per_line;
311 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
312
313 /* Needed by PIXELADDR4 macro */
314 xrb->width4 = xrb->ximage->width;
315 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
316 }
317 else {
318 /* this assertion will fail if we happend to run out of memory */
319 /*assert(xrb->pixmap);*/
320 }
321
322 return GL_TRUE;
323 }
324
325
326 struct xmesa_renderbuffer *
327 xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
328 GLboolean backBuffer)
329 {
330 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
331 if (xrb) {
332 GLuint name = 0;
333 _mesa_init_renderbuffer(&xrb->Base, name);
334
335 xrb->Base.Delete = xmesa_delete_renderbuffer;
336 if (backBuffer)
337 xrb->Base.AllocStorage = xmesa_alloc_back_storage;
338 else
339 xrb->Base.AllocStorage = xmesa_alloc_front_storage;
340
341 if (visual->rgbMode) {
342 xrb->Base.InternalFormat = GL_RGBA;
343 xrb->Base._BaseFormat = GL_RGBA;
344 xrb->Base.DataType = GL_UNSIGNED_BYTE;
345 xrb->Base.RedBits = visual->redBits;
346 xrb->Base.GreenBits = visual->greenBits;
347 xrb->Base.BlueBits = visual->blueBits;
348 xrb->Base.AlphaBits = visual->alphaBits;
349 }
350 else {
351 xrb->Base.InternalFormat = GL_COLOR_INDEX;
352 xrb->Base._BaseFormat = GL_COLOR_INDEX;
353 xrb->Base.DataType = GL_UNSIGNED_INT;
354 xrb->Base.IndexBits = visual->indexBits;
355 }
356 /* only need to set Red/Green/EtcBits fields for user-created RBs */
357 }
358 return xrb;
359 }
360
361
362
363