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 (at) 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 * 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 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 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 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 templ.nr_samples = xstfb->stvis.samples; 138 139 for (i = 0; i < ST_ATTACHMENT_COUNT; i++) { 140 enum pipe_format format; 141 unsigned bind; 142 143 /* the texture already exists or not requested */ 144 if (xstfb->textures[i] || !(mask & (1 << i))) { 145 /* remember the texture */ 146 if (xstfb->textures[i]) 147 mask |= (1 << i); 148 continue; 149 } 150 151 switch (i) { 152 case ST_ATTACHMENT_FRONT_LEFT: 153 case ST_ATTACHMENT_BACK_LEFT: 154 case ST_ATTACHMENT_FRONT_RIGHT: 155 case ST_ATTACHMENT_BACK_RIGHT: 156 format = xstfb->stvis.color_format; 157 bind = PIPE_BIND_DISPLAY_TARGET | 158 PIPE_BIND_RENDER_TARGET; 159 break; 160 case ST_ATTACHMENT_DEPTH_STENCIL: 161 format = xstfb->stvis.depth_stencil_format; 162 bind = PIPE_BIND_DEPTH_STENCIL; 163 break; 164 default: 165 format = PIPE_FORMAT_NONE; 166 break; 167 } 168 169 if (format != PIPE_FORMAT_NONE) { 170 templ.format = format; 171 templ.bind = bind; 172 173 xstfb->textures[i] = 174 xstfb->screen->resource_create(xstfb->screen, &templ); 175 if (!xstfb->textures[i]) 176 return FALSE; 177 } 178 } 179 180 xstfb->texture_width = width; 181 xstfb->texture_height = height; 182 xstfb->texture_mask = mask; 183 184 return TRUE; 185 } 186 187 188 /** 189 * Check that a framebuffer's attachments match the window's size. 190 * 191 * Called via st_framebuffer_iface::validate() 192 * 193 * \param statts array of framebuffer attachments 194 * \param count number of framebuffer attachments in statts[] 195 * \param out returns resources for each of the attachments 196 */ 197 static boolean 198 xmesa_st_framebuffer_validate(struct st_context_iface *stctx, 199 struct st_framebuffer_iface *stfbi, 200 const enum st_attachment_type *statts, 201 unsigned count, 202 struct pipe_resource **out) 203 { 204 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 205 unsigned statt_mask, new_mask, i; 206 boolean resized; 207 boolean ret; 208 209 /* build mask of ST_ATTACHMENT bits */ 210 statt_mask = 0x0; 211 for (i = 0; i < count; i++) 212 statt_mask |= 1 << statts[i]; 213 214 /* record newly allocated textures */ 215 new_mask = statt_mask & ~xstfb->texture_mask; 216 217 /* If xmesa_strict_invalidate is not set, we will not yet have 218 * called XGetGeometry(). Do so here: 219 */ 220 if (!xmesa_strict_invalidate) 221 xmesa_check_buffer_size(xstfb->buffer); 222 223 resized = (xstfb->buffer->width != xstfb->texture_width || 224 xstfb->buffer->height != xstfb->texture_height); 225 226 /* revalidate textures */ 227 if (resized || new_mask) { 228 ret = xmesa_st_framebuffer_validate_textures(stfbi, 229 xstfb->buffer->width, xstfb->buffer->height, statt_mask); 230 if (!ret) 231 return ret; 232 233 if (!resized) { 234 enum st_attachment_type back, front; 235 236 back = ST_ATTACHMENT_BACK_LEFT; 237 front = ST_ATTACHMENT_FRONT_LEFT; 238 /* copy the contents if front is newly allocated and back is not */ 239 if ((statt_mask & (1 << back)) && 240 (new_mask & (1 << front)) && 241 !(new_mask & (1 << back))) { 242 xmesa_st_framebuffer_copy_textures(stfbi, back, front, 243 0, 0, xstfb->texture_width, xstfb->texture_height); 244 } 245 } 246 } 247 248 for (i = 0; i < count; i++) 249 pipe_resource_reference(&out[i], xstfb->textures[statts[i]]); 250 251 return TRUE; 252 } 253 254 255 /** 256 * Called via st_framebuffer_iface::flush_front() 257 */ 258 static boolean 259 xmesa_st_framebuffer_flush_front(struct st_context_iface *stctx, 260 struct st_framebuffer_iface *stfbi, 261 enum st_attachment_type statt) 262 { 263 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 264 boolean ret; 265 266 ret = xmesa_st_framebuffer_display(stfbi, statt); 267 268 if (ret && xmesa_strict_invalidate) 269 xmesa_check_buffer_size(xstfb->buffer); 270 271 return ret; 272 } 273 274 static uint32_t xmesa_stfbi_ID = 0; 275 276 struct st_framebuffer_iface * 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 stfbi->ID = p_atomic_inc_return(&xmesa_stfbi_ID); 305 stfbi->state_manager = xmdpy->smapi; 306 p_atomic_set(&stfbi->stamp, 1); 307 stfbi->st_manager_private = (void *) xstfb; 308 309 return stfbi; 310 } 311 312 313 void 314 xmesa_destroy_st_framebuffer(struct st_framebuffer_iface *stfbi) 315 { 316 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 317 int i; 318 319 pipe_resource_reference(&xstfb->display_resource, NULL); 320 321 for (i = 0; i < ST_ATTACHMENT_COUNT; i++) 322 pipe_resource_reference(&xstfb->textures[i], NULL); 323 324 free(xstfb); 325 free(stfbi); 326 } 327 328 329 /** 330 * Return the pipe_surface which corresponds to the given 331 * framebuffer attachment. 332 */ 333 struct pipe_resource * 334 xmesa_get_framebuffer_resource(struct st_framebuffer_iface *stfbi, 335 enum st_attachment_type att) 336 { 337 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 338 return xstfb->textures[att]; 339 } 340 341 342 void 343 xmesa_swap_st_framebuffer(struct st_framebuffer_iface *stfbi) 344 { 345 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 346 boolean ret; 347 348 ret = xmesa_st_framebuffer_display(stfbi, ST_ATTACHMENT_BACK_LEFT); 349 if (ret) { 350 struct pipe_resource **front, **back, *tmp; 351 352 front = &xstfb->textures[ST_ATTACHMENT_FRONT_LEFT]; 353 back = &xstfb->textures[ST_ATTACHMENT_BACK_LEFT]; 354 /* swap textures only if the front texture has been allocated */ 355 if (*front) { 356 tmp = *front; 357 *front = *back; 358 *back = tmp; 359 360 /* the current context should validate the buffer after swapping */ 361 if (!xmesa_strict_invalidate) 362 xmesa_notify_invalid_buffer(xstfb->buffer); 363 } 364 365 if (xmesa_strict_invalidate) 366 xmesa_check_buffer_size(xstfb->buffer); 367 } 368 } 369 370 371 void 372 xmesa_copy_st_framebuffer(struct st_framebuffer_iface *stfbi, 373 enum st_attachment_type src, 374 enum st_attachment_type dst, 375 int x, int y, int w, int h) 376 { 377 xmesa_st_framebuffer_copy_textures(stfbi, src, dst, x, y, w, h); 378 if (dst == ST_ATTACHMENT_FRONT_LEFT) 379 xmesa_st_framebuffer_display(stfbi, dst); 380 } 381 382 383 struct pipe_resource* 384 xmesa_get_attachment(struct st_framebuffer_iface *stfbi, 385 enum st_attachment_type st_attachment) 386 { 387 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 388 struct pipe_resource *res; 389 390 res = xstfb->textures[st_attachment]; 391 return res; 392 } 393 394 395 struct pipe_context* 396 xmesa_get_context(struct st_framebuffer_iface *stfbi) 397 { 398 struct pipe_context *pipe; 399 struct xmesa_st_framebuffer *xstfb = xmesa_st_framebuffer(stfbi); 400 401 pipe = xstfb->display->pipe; 402 if (!pipe) { 403 pipe = xstfb->screen->context_create(xstfb->screen, NULL, 0); 404 if (!pipe) 405 return NULL; 406 xstfb->display->pipe = pipe; 407 } 408 return pipe; 409 } 410