1 /*
2  * Mesa 3-D graphics library
3  *
4  * Copyright (C) 2010 LunarG Inc.
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 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
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Chia-I Wu <olv@lunarg.com>
26  */
27 
28 #include "xm_api.h"
29 #include "xm_st.h"
30 
31 #include "util/u_inlines.h"
32 #include "util/u_atomic.h"
33 
34 struct xmesa_st_framebuffer {
35    XMesaDisplay display;
36    XMesaBuffer buffer;
37    struct pipe_screen *screen;
38 
39    struct st_visual stvis;
40    enum pipe_texture_target target;
41 
42    unsigned texture_width, texture_height, texture_mask;
43    struct pipe_resource *textures[ST_ATTACHMENT_COUNT];
44 
45    struct pipe_resource *display_resource;
46 };
47 
48 
49 static inline struct xmesa_st_framebuffer *
xmesa_st_framebuffer(struct st_framebuffer_iface * stfbi)50 xmesa_st_framebuffer(struct st_framebuffer_iface *stfbi)
51 {
52    return (struct xmesa_st_framebuffer *) stfbi->st_manager_private;
53 }
54 
55 
56 /**
57  * Display (present) an attachment to the xlib_drawable of the framebuffer.
58  */
59 static boolean
xmesa_st_framebuffer_display(struct st_framebuffer_iface * stfbi,enum st_attachment_type statt)60 xmesa_st_framebuffer_display(struct st_framebuffer_iface *stfbi,
61                              enum st_attachment_type statt)
62 {
63    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
64    struct pipe_resource *ptex = xstfb->textures[statt];
65    struct pipe_resource *pres;
66 
67    if (!ptex)
68       return TRUE;
69 
70    pres = xstfb->display_resource;
71    /* (re)allocate the surface for the texture to be displayed */
72    if (!pres || pres != ptex) {
73       pipe_resource_reference(&xstfb->display_resource, ptex);
74       pres = xstfb->display_resource;
75    }
76 
77    xstfb->screen->flush_frontbuffer(xstfb->screen, pres, 0, 0, &xstfb->buffer->ws, NULL);
78    return TRUE;
79 }
80 
81 
82 /**
83  * Copy the contents between the attachments.
84  */
85 static void
xmesa_st_framebuffer_copy_textures(struct st_framebuffer_iface * stfbi,enum st_attachment_type src_statt,enum st_attachment_type dst_statt,unsigned x,unsigned y,unsigned width,unsigned height)86 xmesa_st_framebuffer_copy_textures(struct st_framebuffer_iface *stfbi,
87                                    enum st_attachment_type src_statt,
88                                    enum st_attachment_type dst_statt,
89                                    unsigned x, unsigned y,
90                                    unsigned width, unsigned height)
91 {
92    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
93    struct pipe_resource *src_ptex = xstfb->textures[src_statt];
94    struct pipe_resource *dst_ptex = xstfb->textures[dst_statt];
95    struct pipe_box src_box;
96    struct pipe_context *pipe;
97 
98    if (!src_ptex || !dst_ptex)
99       return;
100 
101    pipe = xmesa_get_context(stfbi);
102 
103    u_box_2d(x, y, width, height, &src_box);
104 
105    if (src_ptex && dst_ptex)
106       pipe->resource_copy_region(pipe, dst_ptex, 0, x, y, 0,
107                                  src_ptex, 0, &src_box);
108 }
109 
110 
111 /**
112  * Remove outdated textures and create the requested ones.
113  * This is a helper used during framebuffer validation.
114  */
115 boolean
xmesa_st_framebuffer_validate_textures(struct st_framebuffer_iface * stfbi,unsigned width,unsigned height,unsigned mask)116 xmesa_st_framebuffer_validate_textures(struct st_framebuffer_iface *stfbi,
117                                        unsigned width, unsigned height,
118                                        unsigned mask)
119 {
120    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
121    struct pipe_resource templ;
122    enum st_attachment_type i;
123 
124    /* remove outdated textures */
125    if (xstfb->texture_width != width || xstfb->texture_height != height) {
126       for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
127          pipe_resource_reference(&xstfb->textures[i], NULL);
128    }
129 
130    memset(&templ, 0, sizeof(templ));
131    templ.target = xstfb->target;
132    templ.width0 = width;
133    templ.height0 = height;
134    templ.depth0 = 1;
135    templ.array_size = 1;
136    templ.last_level = 0;
137 
138    for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
139       enum pipe_format format;
140       unsigned bind;
141 
142       /* the texture already exists or not requested */
143       if (xstfb->textures[i] || !(mask & (1 << i))) {
144          /* remember the texture */
145          if (xstfb->textures[i])
146             mask |= (1 << i);
147          continue;
148       }
149 
150       switch (i) {
151       case ST_ATTACHMENT_FRONT_LEFT:
152       case ST_ATTACHMENT_BACK_LEFT:
153       case ST_ATTACHMENT_FRONT_RIGHT:
154       case ST_ATTACHMENT_BACK_RIGHT:
155          format = xstfb->stvis.color_format;
156          bind = PIPE_BIND_DISPLAY_TARGET |
157                      PIPE_BIND_RENDER_TARGET;
158          break;
159       case ST_ATTACHMENT_DEPTH_STENCIL:
160          format = xstfb->stvis.depth_stencil_format;
161          bind = PIPE_BIND_DEPTH_STENCIL;
162          break;
163       default:
164          format = PIPE_FORMAT_NONE;
165          break;
166       }
167 
168       if (format != PIPE_FORMAT_NONE) {
169          templ.format = format;
170          templ.bind = bind;
171 
172          xstfb->textures[i] =
173             xstfb->screen->resource_create(xstfb->screen, &templ);
174          if (!xstfb->textures[i])
175             return FALSE;
176       }
177    }
178 
179    xstfb->texture_width = width;
180    xstfb->texture_height = height;
181    xstfb->texture_mask = mask;
182 
183    return TRUE;
184 }
185 
186 
187 /**
188  * Check that a framebuffer's attachments match the window's size.
189  *
190  * Called via st_framebuffer_iface::validate()
191  *
192  * \param statts  array of framebuffer attachments
193  * \param count  number of framebuffer attachments in statts[]
194  * \param out  returns resources for each of the attachments
195  */
196 static boolean
xmesa_st_framebuffer_validate(struct st_context_iface * stctx,struct st_framebuffer_iface * stfbi,const enum st_attachment_type * statts,unsigned count,struct pipe_resource ** out)197 xmesa_st_framebuffer_validate(struct st_context_iface *stctx,
198                               struct st_framebuffer_iface *stfbi,
199                               const enum st_attachment_type *statts,
200                               unsigned count,
201                               struct pipe_resource **out)
202 {
203    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
204    unsigned statt_mask, new_mask, i;
205    boolean resized;
206    boolean ret;
207 
208    /* build mask of ST_ATTACHMENT bits */
209    statt_mask = 0x0;
210    for (i = 0; i < count; i++)
211       statt_mask |= 1 << statts[i];
212 
213    /* record newly allocated textures */
214    new_mask = statt_mask & ~xstfb->texture_mask;
215 
216    /* If xmesa_strict_invalidate is not set, we will not yet have
217     * called XGetGeometry().  Do so here:
218     */
219    if (!xmesa_strict_invalidate)
220       xmesa_check_buffer_size(xstfb->buffer);
221 
222    resized = (xstfb->buffer->width != xstfb->texture_width ||
223               xstfb->buffer->height != xstfb->texture_height);
224 
225    /* revalidate textures */
226    if (resized || new_mask) {
227       ret = xmesa_st_framebuffer_validate_textures(stfbi,
228                   xstfb->buffer->width, xstfb->buffer->height, statt_mask);
229       if (!ret)
230          return ret;
231 
232       if (!resized) {
233          enum st_attachment_type back, front;
234 
235          back = ST_ATTACHMENT_BACK_LEFT;
236          front = ST_ATTACHMENT_FRONT_LEFT;
237          /* copy the contents if front is newly allocated and back is not */
238          if ((statt_mask & (1 << back)) &&
239              (new_mask & (1 << front)) &&
240              !(new_mask & (1 << back))) {
241             xmesa_st_framebuffer_copy_textures(stfbi, back, front,
242                   0, 0, xstfb->texture_width, xstfb->texture_height);
243          }
244       }
245    }
246 
247    for (i = 0; i < count; i++) {
248       out[i] = NULL;
249       pipe_resource_reference(&out[i], xstfb->textures[statts[i]]);
250    }
251 
252    return TRUE;
253 }
254 
255 
256 /**
257  * Called via st_framebuffer_iface::flush_front()
258  */
259 static boolean
xmesa_st_framebuffer_flush_front(struct st_context_iface * stctx,struct st_framebuffer_iface * stfbi,enum st_attachment_type statt)260 xmesa_st_framebuffer_flush_front(struct st_context_iface *stctx,
261                                  struct st_framebuffer_iface *stfbi,
262                                  enum st_attachment_type statt)
263 {
264    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
265    boolean ret;
266 
267    ret = xmesa_st_framebuffer_display(stfbi, statt);
268 
269    if (ret && xmesa_strict_invalidate)
270       xmesa_check_buffer_size(xstfb->buffer);
271 
272    return ret;
273 }
274 
275 
276 struct st_framebuffer_iface *
xmesa_create_st_framebuffer(XMesaDisplay xmdpy,XMesaBuffer b)277 xmesa_create_st_framebuffer(XMesaDisplay xmdpy, XMesaBuffer b)
278 {
279    struct st_framebuffer_iface *stfbi;
280    struct xmesa_st_framebuffer *xstfb;
281 
282    assert(xmdpy->display == b->xm_visual->display);
283 
284    stfbi = CALLOC_STRUCT(st_framebuffer_iface);
285    xstfb = CALLOC_STRUCT(xmesa_st_framebuffer);
286    if (!stfbi || !xstfb) {
287       free(stfbi);
288       free(xstfb);
289       return NULL;
290    }
291 
292    xstfb->display = xmdpy;
293    xstfb->buffer = b;
294    xstfb->screen = xmdpy->screen;
295    xstfb->stvis = b->xm_visual->stvis;
296    if (xstfb->screen->get_param(xstfb->screen, PIPE_CAP_NPOT_TEXTURES))
297       xstfb->target = PIPE_TEXTURE_2D;
298    else
299       xstfb->target = PIPE_TEXTURE_RECT;
300 
301    stfbi->visual = &xstfb->stvis;
302    stfbi->flush_front = xmesa_st_framebuffer_flush_front;
303    stfbi->validate = xmesa_st_framebuffer_validate;
304    p_atomic_set(&stfbi->stamp, 1);
305    stfbi->st_manager_private = (void *) xstfb;
306 
307    return stfbi;
308 }
309 
310 
311 void
xmesa_destroy_st_framebuffer(struct st_framebuffer_iface * stfbi)312 xmesa_destroy_st_framebuffer(struct st_framebuffer_iface *stfbi)
313 {
314    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
315    int i;
316 
317    pipe_resource_reference(&xstfb->display_resource, NULL);
318 
319    for (i = 0; i < ST_ATTACHMENT_COUNT; i++)
320       pipe_resource_reference(&xstfb->textures[i], NULL);
321 
322    free(xstfb);
323    free(stfbi);
324 }
325 
326 
327 /**
328  * Return the pipe_surface which corresponds to the given
329  * framebuffer attachment.
330  */
331 struct pipe_resource *
xmesa_get_framebuffer_resource(struct st_framebuffer_iface * stfbi,enum st_attachment_type att)332 xmesa_get_framebuffer_resource(struct st_framebuffer_iface *stfbi,
333                                enum st_attachment_type att)
334 {
335    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
336    return xstfb->textures[att];
337 }
338 
339 
340 void
xmesa_swap_st_framebuffer(struct st_framebuffer_iface * stfbi)341 xmesa_swap_st_framebuffer(struct st_framebuffer_iface *stfbi)
342 {
343    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
344    boolean ret;
345 
346    ret = xmesa_st_framebuffer_display(stfbi, ST_ATTACHMENT_BACK_LEFT);
347    if (ret) {
348       struct pipe_resource **front, **back, *tmp;
349 
350       front = &xstfb->textures[ST_ATTACHMENT_FRONT_LEFT];
351       back = &xstfb->textures[ST_ATTACHMENT_BACK_LEFT];
352       /* swap textures only if the front texture has been allocated */
353       if (*front) {
354          tmp = *front;
355          *front = *back;
356          *back = tmp;
357 
358          /* the current context should validate the buffer after swapping */
359          if (!xmesa_strict_invalidate)
360             xmesa_notify_invalid_buffer(xstfb->buffer);
361       }
362 
363       if (xmesa_strict_invalidate)
364 	 xmesa_check_buffer_size(xstfb->buffer);
365    }
366 }
367 
368 
369 void
xmesa_copy_st_framebuffer(struct st_framebuffer_iface * stfbi,enum st_attachment_type src,enum st_attachment_type dst,int x,int y,int w,int h)370 xmesa_copy_st_framebuffer(struct st_framebuffer_iface *stfbi,
371                           enum st_attachment_type src,
372                           enum st_attachment_type dst,
373                           int x, int y, int w, int h)
374 {
375    xmesa_st_framebuffer_copy_textures(stfbi, src, dst, x, y, w, h);
376    if (dst == ST_ATTACHMENT_FRONT_LEFT)
377       xmesa_st_framebuffer_display(stfbi, dst);
378 }
379 
380 
381 struct pipe_resource*
xmesa_get_attachment(struct st_framebuffer_iface * stfbi,enum st_attachment_type st_attachment)382 xmesa_get_attachment(struct st_framebuffer_iface *stfbi,
383                      enum st_attachment_type st_attachment)
384 {
385    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
386    struct pipe_resource *res;
387 
388    res = xstfb->textures[st_attachment];
389    return res;
390 }
391 
392 
393 struct pipe_context*
xmesa_get_context(struct st_framebuffer_iface * stfbi)394 xmesa_get_context(struct st_framebuffer_iface *stfbi)
395 {
396    struct pipe_context *pipe;
397    struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi);
398 
399    pipe = xstfb->display->pipe;
400    if (!pipe) {
401       pipe = xstfb->screen->context_create(xstfb->screen, NULL, 0);
402       if (!pipe)
403          return NULL;
404       xstfb->display->pipe = pipe;
405    }
406    return pipe;
407 }
408