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 35struct 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 50static inline struct xmesa_st_framebuffer * 51xmesa_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 */ 60static bool 61xmesa_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 */ 89static void 90xmesa_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 */ 119bool 120xmesa_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 */ 202static bool 203xmesa_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 */ 263static bool 264xmesa_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 282static uint32_t xmesa_stfbi_ID = 0; 283 284struct st_framebuffer_iface * 285xmesa_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 321void 322xmesa_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 */ 341struct pipe_resource * 342xmesa_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 350void 351xmesa_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 379void 380xmesa_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 398struct pipe_resource* 399xmesa_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 410struct pipe_context* 411xmesa_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