1 /*
2 * Copyright © 2009 Intel Corporation
3 * Copyright © 1998 Keith Packard
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * 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 OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 *
24 * Authors:
25 * Zhigang Gong <zhigang.gong@gmail.com>
26 *
27 */
28
29 #include <stdlib.h>
30
31 #include "glamor_priv.h"
32
33 void
glamor_destroy_fbo(glamor_screen_private * glamor_priv,glamor_pixmap_fbo * fbo)34 glamor_destroy_fbo(glamor_screen_private *glamor_priv,
35 glamor_pixmap_fbo *fbo)
36 {
37 glamor_make_current(glamor_priv);
38
39 if (fbo->fb)
40 glDeleteFramebuffers(1, &fbo->fb);
41 if (fbo->tex)
42 glDeleteTextures(1, &fbo->tex);
43
44 free(fbo);
45 }
46
47 static int
glamor_pixmap_ensure_fb(glamor_screen_private * glamor_priv,glamor_pixmap_fbo * fbo)48 glamor_pixmap_ensure_fb(glamor_screen_private *glamor_priv,
49 glamor_pixmap_fbo *fbo)
50 {
51 int status, err = 0;
52
53 glamor_make_current(glamor_priv);
54
55 if (fbo->fb == 0)
56 glGenFramebuffers(1, &fbo->fb);
57 assert(fbo->tex != 0);
58 glBindFramebuffer(GL_FRAMEBUFFER, fbo->fb);
59 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
60 GL_TEXTURE_2D, fbo->tex, 0);
61 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
62 if (status != GL_FRAMEBUFFER_COMPLETE) {
63 const char *str;
64
65 switch (status) {
66 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
67 str = "incomplete attachment";
68 break;
69 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
70 str = "incomplete/missing attachment";
71 break;
72 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
73 str = "incomplete draw buffer";
74 break;
75 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
76 str = "incomplete read buffer";
77 break;
78 case GL_FRAMEBUFFER_UNSUPPORTED:
79 str = "unsupported";
80 break;
81 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
82 str = "incomplete multiple";
83 break;
84 default:
85 str = "unknown error";
86 break;
87 }
88
89 glamor_fallback("glamor: Failed to create fbo, %s\n", str);
90 err = -1;
91 }
92
93 return err;
94 }
95
96 glamor_pixmap_fbo *
glamor_create_fbo_from_tex(glamor_screen_private * glamor_priv,int w,int h,GLenum format,GLint tex,int flag)97 glamor_create_fbo_from_tex(glamor_screen_private *glamor_priv,
98 int w, int h, GLenum format, GLint tex, int flag)
99 {
100 glamor_pixmap_fbo *fbo;
101
102 fbo = calloc(1, sizeof(*fbo));
103 if (fbo == NULL)
104 return NULL;
105
106 fbo->tex = tex;
107 fbo->width = w;
108 fbo->height = h;
109 fbo->format = format;
110
111 if (flag != GLAMOR_CREATE_FBO_NO_FBO) {
112 if (glamor_pixmap_ensure_fb(glamor_priv, fbo) != 0) {
113 glamor_destroy_fbo(glamor_priv, fbo);
114 fbo = NULL;
115 }
116 }
117
118 return fbo;
119 }
120
121 static int
_glamor_create_tex(glamor_screen_private * glamor_priv,int w,int h,GLenum format)122 _glamor_create_tex(glamor_screen_private *glamor_priv,
123 int w, int h, GLenum format)
124 {
125 unsigned int tex;
126 GLenum iformat = format;
127
128 if (format == GL_RGB10_A2)
129 format = GL_RGBA;
130 glamor_make_current(glamor_priv);
131 glGenTextures(1, &tex);
132 glBindTexture(GL_TEXTURE_2D, tex);
133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
135 if (format == glamor_priv->one_channel_format && format == GL_RED)
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
137 glamor_priv->suppress_gl_out_of_memory_logging = true;
138 glTexImage2D(GL_TEXTURE_2D, 0, iformat, w, h, 0,
139 format, GL_UNSIGNED_BYTE, NULL);
140 glamor_priv->suppress_gl_out_of_memory_logging = false;
141
142 if (glGetError() == GL_OUT_OF_MEMORY) {
143 if (!glamor_priv->logged_any_fbo_allocation_failure) {
144 LogMessageVerb(X_WARNING, 0, "glamor: Failed to allocate %dx%d "
145 "FBO due to GL_OUT_OF_MEMORY.\n", w, h);
146 LogMessageVerb(X_WARNING, 0,
147 "glamor: Expect reduced performance.\n");
148 glamor_priv->logged_any_fbo_allocation_failure = true;
149 }
150 glDeleteTextures(1, &tex);
151 return 0;
152 }
153
154 return tex;
155 }
156
157 glamor_pixmap_fbo *
glamor_create_fbo(glamor_screen_private * glamor_priv,int w,int h,GLenum format,int flag)158 glamor_create_fbo(glamor_screen_private *glamor_priv,
159 int w, int h, GLenum format, int flag)
160 {
161 GLint tex = _glamor_create_tex(glamor_priv, w, h, format);
162
163 if (!tex) /* Texture creation failed due to GL_OUT_OF_MEMORY */
164 return NULL;
165
166 return glamor_create_fbo_from_tex(glamor_priv, w, h, format, tex, flag);
167 }
168
169 /**
170 * Create storage for the w * h region, using FBOs of the GL's maximum
171 * supported size.
172 */
173 glamor_pixmap_fbo *
glamor_create_fbo_array(glamor_screen_private * glamor_priv,int w,int h,GLenum format,int flag,int block_w,int block_h,glamor_pixmap_private * priv)174 glamor_create_fbo_array(glamor_screen_private *glamor_priv,
175 int w, int h, GLenum format, int flag,
176 int block_w, int block_h,
177 glamor_pixmap_private *priv)
178 {
179 int block_wcnt;
180 int block_hcnt;
181 glamor_pixmap_fbo **fbo_array;
182 BoxPtr box_array;
183 int i, j;
184
185 priv->block_w = block_w;
186 priv->block_h = block_h;
187
188 block_wcnt = (w + block_w - 1) / block_w;
189 block_hcnt = (h + block_h - 1) / block_h;
190
191 box_array = calloc(block_wcnt * block_hcnt, sizeof(box_array[0]));
192 if (box_array == NULL)
193 return NULL;
194
195 fbo_array = calloc(block_wcnt * block_hcnt, sizeof(glamor_pixmap_fbo *));
196 if (fbo_array == NULL) {
197 free(box_array);
198 return FALSE;
199 }
200 for (i = 0; i < block_hcnt; i++) {
201 int block_y1, block_y2;
202 int fbo_w, fbo_h;
203
204 block_y1 = i * block_h;
205 block_y2 = (block_y1 + block_h) > h ? h : (block_y1 + block_h);
206 fbo_h = block_y2 - block_y1;
207
208 for (j = 0; j < block_wcnt; j++) {
209 box_array[i * block_wcnt + j].x1 = j * block_w;
210 box_array[i * block_wcnt + j].y1 = block_y1;
211 box_array[i * block_wcnt + j].x2 =
212 (j + 1) * block_w > w ? w : (j + 1) * block_w;
213 box_array[i * block_wcnt + j].y2 = block_y2;
214 fbo_w =
215 box_array[i * block_wcnt + j].x2 - box_array[i * block_wcnt +
216 j].x1;
217 fbo_array[i * block_wcnt + j] = glamor_create_fbo(glamor_priv,
218 fbo_w, fbo_h,
219 format,
220 GLAMOR_CREATE_PIXMAP_FIXUP);
221 if (fbo_array[i * block_wcnt + j] == NULL)
222 goto cleanup;
223 }
224 }
225
226 priv->box = box_array[0];
227 priv->box_array = box_array;
228 priv->fbo_array = fbo_array;
229 priv->block_wcnt = block_wcnt;
230 priv->block_hcnt = block_hcnt;
231 return fbo_array[0];
232
233 cleanup:
234 for (i = 0; i < block_wcnt * block_hcnt; i++)
235 if (fbo_array[i])
236 glamor_destroy_fbo(glamor_priv, fbo_array[i]);
237 free(box_array);
238 free(fbo_array);
239 return NULL;
240 }
241
242 void
glamor_pixmap_clear_fbo(glamor_screen_private * glamor_priv,glamor_pixmap_fbo * fbo)243 glamor_pixmap_clear_fbo(glamor_screen_private *glamor_priv, glamor_pixmap_fbo *fbo)
244 {
245 glamor_make_current(glamor_priv);
246
247 assert(fbo->fb != 0 && fbo->tex != 0);
248
249 glamor_set_destination_pixmap_fbo(glamor_priv, fbo, 0, 0, fbo->width, fbo->height);
250 glClearColor(0.0, 0.0, 0.0, 0.0);
251 glClear(GL_COLOR_BUFFER_BIT);
252 }
253
254 glamor_pixmap_fbo *
glamor_pixmap_detach_fbo(glamor_pixmap_private * pixmap_priv)255 glamor_pixmap_detach_fbo(glamor_pixmap_private *pixmap_priv)
256 {
257 glamor_pixmap_fbo *fbo;
258
259 if (pixmap_priv == NULL)
260 return NULL;
261
262 fbo = pixmap_priv->fbo;
263 if (fbo == NULL)
264 return NULL;
265
266 pixmap_priv->fbo = NULL;
267 return fbo;
268 }
269
270 /* The pixmap must not be attached to another fbo. */
271 void
glamor_pixmap_attach_fbo(PixmapPtr pixmap,glamor_pixmap_fbo * fbo)272 glamor_pixmap_attach_fbo(PixmapPtr pixmap, glamor_pixmap_fbo *fbo)
273 {
274 glamor_pixmap_private *pixmap_priv;
275
276 pixmap_priv = glamor_get_pixmap_private(pixmap);
277
278 if (pixmap_priv->fbo)
279 return;
280
281 pixmap_priv->fbo = fbo;
282
283 switch (pixmap_priv->type) {
284 case GLAMOR_TEXTURE_ONLY:
285 case GLAMOR_TEXTURE_DRM:
286 pixmap_priv->gl_fbo = GLAMOR_FBO_NORMAL;
287 pixmap->devPrivate.ptr = NULL;
288 default:
289 break;
290 }
291 }
292
293 void
glamor_pixmap_destroy_fbo(PixmapPtr pixmap)294 glamor_pixmap_destroy_fbo(PixmapPtr pixmap)
295 {
296 ScreenPtr screen = pixmap->drawable.pScreen;
297 glamor_screen_private *glamor_priv = glamor_get_screen_private(screen);
298 glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap);
299 glamor_pixmap_fbo *fbo;
300
301 if (glamor_pixmap_priv_is_large(priv)) {
302 int i;
303
304 for (i = 0; i < priv->block_wcnt * priv->block_hcnt; i++)
305 glamor_destroy_fbo(glamor_priv, priv->fbo_array[i]);
306 free(priv->fbo_array);
307 priv->fbo_array = NULL;
308 }
309 else {
310 fbo = glamor_pixmap_detach_fbo(priv);
311 if (fbo)
312 glamor_destroy_fbo(glamor_priv, fbo);
313 }
314 }
315
316 Bool
glamor_pixmap_ensure_fbo(PixmapPtr pixmap,GLenum format,int flag)317 glamor_pixmap_ensure_fbo(PixmapPtr pixmap, GLenum format, int flag)
318 {
319 glamor_screen_private *glamor_priv;
320 glamor_pixmap_private *pixmap_priv;
321 glamor_pixmap_fbo *fbo;
322
323 glamor_priv = glamor_get_screen_private(pixmap->drawable.pScreen);
324 pixmap_priv = glamor_get_pixmap_private(pixmap);
325 if (pixmap_priv->fbo == NULL) {
326
327 fbo = glamor_create_fbo(glamor_priv, pixmap->drawable.width,
328 pixmap->drawable.height, format, flag);
329 if (fbo == NULL)
330 return FALSE;
331
332 glamor_pixmap_attach_fbo(pixmap, fbo);
333 }
334 else {
335 /* We do have a fbo, but it may lack of fb or tex. */
336 if (!pixmap_priv->fbo->tex)
337 pixmap_priv->fbo->tex =
338 _glamor_create_tex(glamor_priv, pixmap->drawable.width,
339 pixmap->drawable.height, format);
340
341 if (flag != GLAMOR_CREATE_FBO_NO_FBO && pixmap_priv->fbo->fb == 0)
342 if (glamor_pixmap_ensure_fb(glamor_priv, pixmap_priv->fbo) != 0)
343 return FALSE;
344 }
345
346 return TRUE;
347 }
348
349 _X_EXPORT void
glamor_pixmap_exchange_fbos(PixmapPtr front,PixmapPtr back)350 glamor_pixmap_exchange_fbos(PixmapPtr front, PixmapPtr back)
351 {
352 glamor_pixmap_private *front_priv, *back_priv;
353 glamor_pixmap_fbo *temp_fbo;
354
355 front_priv = glamor_get_pixmap_private(front);
356 back_priv = glamor_get_pixmap_private(back);
357 temp_fbo = front_priv->fbo;
358 front_priv->fbo = back_priv->fbo;
359 back_priv->fbo = temp_fbo;
360 }
361