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