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