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