vg_manager.c revision 63ab2509bf324812d9632c12528677724bdb8775
1/* 2 * Mesa 3-D graphics library 3 * Version: 7.9 4 * 5 * Copyright 2009 VMware, Inc. All Rights Reserved. 6 * Copyright (C) 2010 LunarG Inc. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the "Software"), 10 * to deal in the Software without restriction, including without limitation 11 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 * and/or sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included 16 * in all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 * DEALINGS IN THE SOFTWARE. 25 * 26 * Authors: 27 * Chia-I Wu <olv@lunarg.com> 28 */ 29 30#include "state_tracker/st_api.h" 31 32#include "pipe/p_context.h" 33#include "pipe/p_screen.h" 34#include "util/u_memory.h" 35#include "util/u_inlines.h" 36#include "util/u_format.h" 37#include "util/u_sampler.h" 38 39#include "vg_api.h" 40#include "vg_manager.h" 41#include "vg_context.h" 42#include "image.h" 43#include "mask.h" 44#include "api.h" 45 46static struct pipe_resource * 47create_texture(struct pipe_context *pipe, enum pipe_format format, 48 VGint width, VGint height) 49{ 50 struct pipe_resource templ; 51 52 memset(&templ, 0, sizeof(templ)); 53 54 if (format != PIPE_FORMAT_NONE) { 55 templ.format = format; 56 } 57 else { 58 templ.format = PIPE_FORMAT_B8G8R8A8_UNORM; 59 } 60 61 templ.target = PIPE_TEXTURE_2D; 62 templ.width0 = width; 63 templ.height0 = height; 64 templ.depth0 = 1; 65 templ.last_level = 0; 66 67 if (util_format_get_component_bits(format, UTIL_FORMAT_COLORSPACE_ZS, 1)) { 68 templ.bind = PIPE_BIND_DEPTH_STENCIL; 69 } else { 70 templ.bind = (PIPE_BIND_DISPLAY_TARGET | 71 PIPE_BIND_RENDER_TARGET | 72 PIPE_BIND_SAMPLER_VIEW); 73 } 74 75 return pipe->screen->resource_create(pipe->screen, &templ); 76} 77 78static struct pipe_sampler_view * 79create_tex_and_view(struct pipe_context *pipe, enum pipe_format format, 80 VGint width, VGint height) 81{ 82 struct pipe_resource *texture; 83 struct pipe_sampler_view view_templ; 84 struct pipe_sampler_view *view; 85 86 texture = create_texture(pipe, format, width, height); 87 88 if (!texture) 89 return NULL; 90 91 u_sampler_view_default_template(&view_templ, texture, texture->format); 92 view = pipe->create_sampler_view(pipe, texture, &view_templ); 93 /* want the texture to go away if the view is freed */ 94 pipe_resource_reference(&texture, NULL); 95 96 return view; 97} 98 99static void 100setup_new_alpha_mask(struct vg_context *ctx, struct st_framebuffer *stfb) 101{ 102 struct pipe_context *pipe = ctx->pipe; 103 struct pipe_sampler_view *old_sampler_view = stfb->alpha_mask_view; 104 105 /* 106 we use PIPE_FORMAT_B8G8R8A8_UNORM because we want to render to 107 this texture and use it as a sampler, so while this wastes some 108 space it makes both of those a lot simpler 109 */ 110 stfb->alpha_mask_view = create_tex_and_view(pipe, 111 PIPE_FORMAT_B8G8R8A8_UNORM, stfb->width, stfb->height); 112 113 if (!stfb->alpha_mask_view) { 114 if (old_sampler_view) 115 pipe_sampler_view_reference(&old_sampler_view, NULL); 116 return; 117 } 118 119 /* XXX could this call be avoided? */ 120 vg_validate_state(ctx); 121 122 /* alpha mask starts with 1.f alpha */ 123 mask_fill(0, 0, stfb->width, stfb->height, 1.f); 124 125 /* if we had an old surface copy it over */ 126 if (old_sampler_view) { 127 struct pipe_surface *surface = pipe->screen->get_tex_surface( 128 pipe->screen, 129 stfb->alpha_mask_view->texture, 130 0, 0, 0, 131 PIPE_BIND_RENDER_TARGET | 132 PIPE_BIND_BLIT_DESTINATION); 133 struct pipe_surface *old_surface = pipe->screen->get_tex_surface( 134 pipe->screen, 135 old_sampler_view->texture, 136 0, 0, 0, 137 PIPE_BIND_BLIT_SOURCE); 138 pipe->surface_copy(pipe, 139 surface, 140 0, 0, 141 old_surface, 142 0, 0, 143 MIN2(old_surface->width, surface->width), 144 MIN2(old_surface->height, surface->height)); 145 if (surface) 146 pipe_surface_reference(&surface, NULL); 147 if (old_surface) 148 pipe_surface_reference(&old_surface, NULL); 149 } 150 151 /* Free the old texture 152 */ 153 if (old_sampler_view) 154 pipe_sampler_view_reference(&old_sampler_view, NULL); 155} 156 157static boolean 158vg_context_update_depth_stencil_rb(struct vg_context * ctx, 159 uint width, uint height) 160{ 161 struct st_renderbuffer *dsrb = ctx->draw_buffer->dsrb; 162 struct pipe_context *pipe = ctx->pipe; 163 unsigned surface_usage; 164 165 if ((dsrb->width == width && dsrb->height == height) && dsrb->texture) 166 return FALSE; 167 168 /* unreference existing ones */ 169 pipe_surface_reference(&dsrb->surface, NULL); 170 pipe_resource_reference(&dsrb->texture, NULL); 171 dsrb->width = dsrb->height = 0; 172 173 /* Probably need dedicated flags for surface usage too: 174 */ 175 surface_usage = (PIPE_BIND_RENDER_TARGET | 176 PIPE_BIND_BLIT_SOURCE | 177 PIPE_BIND_BLIT_DESTINATION); 178 179 dsrb->texture = create_texture(pipe, dsrb->format, width, height); 180 if (!dsrb->texture) 181 return TRUE; 182 183 dsrb->surface = pipe->screen->get_tex_surface(pipe->screen, 184 dsrb->texture, 185 0, 0, 0, 186 surface_usage); 187 if (!dsrb->surface) { 188 pipe_resource_reference(&dsrb->texture, NULL); 189 return TRUE; 190 } 191 192 dsrb->width = width; 193 dsrb->height = height; 194 195 assert(dsrb->surface->width == width); 196 assert(dsrb->surface->height == height); 197 198 return TRUE; 199} 200 201static boolean 202vg_context_update_color_rb(struct vg_context *ctx, struct pipe_resource *pt) 203{ 204 struct st_renderbuffer *strb = ctx->draw_buffer->strb; 205 struct pipe_screen *screen = ctx->pipe->screen; 206 207 if (strb->texture == pt) { 208 pipe_resource_reference(&pt, NULL); 209 return FALSE; 210 } 211 212 /* unreference existing ones */ 213 pipe_surface_reference(&strb->surface, NULL); 214 pipe_resource_reference(&strb->texture, NULL); 215 strb->width = strb->height = 0; 216 217 strb->texture = pt; 218 strb->surface = screen->get_tex_surface(screen, strb->texture, 0, 0, 0, 219 PIPE_BIND_RENDER_TARGET | 220 PIPE_BIND_BLIT_SOURCE | 221 PIPE_BIND_BLIT_DESTINATION); 222 if (!strb->surface) { 223 pipe_resource_reference(&strb->texture, NULL); 224 return TRUE; 225 } 226 227 strb->width = pt->width0; 228 strb->height = pt->height0; 229 230 return TRUE; 231} 232 233static void 234vg_context_update_draw_buffer(struct vg_context *ctx, struct pipe_resource *pt) 235{ 236 struct st_framebuffer *stfb = ctx->draw_buffer; 237 boolean new_cbuf, new_zsbuf, new_size; 238 239 new_cbuf = vg_context_update_color_rb(ctx, pt); 240 new_zsbuf = 241 vg_context_update_depth_stencil_rb(ctx, pt->width0, pt->height0); 242 243 new_size = (stfb->width != pt->width0 || stfb->height != pt->height0); 244 stfb->width = pt->width0; 245 stfb->height = pt->height0; 246 247 if (new_cbuf || new_zsbuf || new_size) { 248 struct pipe_framebuffer_state *state = &ctx->state.g3d.fb; 249 250 memset(state, 0, sizeof(struct pipe_framebuffer_state)); 251 state->width = stfb->width; 252 state->height = stfb->height; 253 state->nr_cbufs = 1; 254 state->cbufs[0] = stfb->strb->surface; 255 state->zsbuf = stfb->dsrb->surface; 256 257 cso_set_framebuffer(ctx->cso_context, state); 258 } 259 260 if (new_zsbuf || new_size) { 261 ctx->state.dirty |= VIEWPORT_DIRTY; 262 ctx->state.dirty |= DEPTH_STENCIL_DIRTY;/*to reset the scissors*/ 263 264 ctx->pipe->clear(ctx->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 0.0, 0); 265 266 /* we need all the other state already set */ 267 268 setup_new_alpha_mask(ctx, stfb); 269 270 pipe_sampler_view_reference( &stfb->blend_texture_view, NULL); 271 stfb->blend_texture_view = create_tex_and_view(ctx->pipe, 272 PIPE_FORMAT_B8G8R8A8_UNORM, stfb->width, stfb->height); 273 } 274} 275 276/** 277 * Flush the front buffer if the current context renders to the front buffer. 278 */ 279void 280vg_manager_flush_frontbuffer(struct vg_context *ctx) 281{ 282 struct st_framebuffer *stfb = ctx->draw_buffer; 283 284 if (!stfb) 285 return; 286 287 switch (stfb->strb_att) { 288 case ST_ATTACHMENT_FRONT_LEFT: 289 case ST_ATTACHMENT_FRONT_RIGHT: 290 stfb->iface->flush_front(stfb->iface, stfb->strb_att); 291 break; 292 default: 293 break; 294 } 295} 296 297/** 298 * Re-validate the framebuffer. 299 */ 300void 301vg_manager_validate_framebuffer(struct vg_context *ctx) 302{ 303 struct st_framebuffer *stfb = ctx->draw_buffer; 304 struct pipe_resource *pt; 305 306 /* no binding surface */ 307 if (!stfb) 308 return; 309 310 if (!p_atomic_read(&ctx->draw_buffer_invalid)) 311 return; 312 313 /* validate the fb */ 314 if (!stfb->iface->validate(stfb->iface, &stfb->strb_att, 1, &pt) || !pt) 315 return; 316 317 /* 318 * unset draw_buffer_invalid first because vg_context_update_draw_buffer 319 * will cause the framebuffer to be validated again because of a call to 320 * vg_validate_state 321 */ 322 p_atomic_set(&ctx->draw_buffer_invalid, FALSE); 323 vg_context_update_draw_buffer(ctx, pt); 324} 325 326 327static void 328vg_context_notify_invalid_framebuffer(struct st_context_iface *stctxi, 329 struct st_framebuffer_iface *stfbi) 330{ 331 struct vg_context *ctx = (struct vg_context *) stctxi; 332 p_atomic_set(&ctx->draw_buffer_invalid, TRUE); 333} 334 335static void 336vg_context_flush(struct st_context_iface *stctxi, unsigned flags, 337 struct pipe_fence_handle **fence) 338{ 339 struct vg_context *ctx = (struct vg_context *) stctxi; 340 ctx->pipe->flush(ctx->pipe, flags, fence); 341 if (flags & PIPE_FLUSH_RENDER_CACHE) 342 vg_manager_flush_frontbuffer(ctx); 343} 344 345static void 346vg_context_destroy(struct st_context_iface *stctxi) 347{ 348 struct vg_context *ctx = (struct vg_context *) stctxi; 349 vg_destroy_context(ctx); 350} 351 352static struct st_context_iface * 353vg_api_create_context(struct st_api *stapi, struct st_manager *smapi, 354 const struct st_visual *visual, 355 struct st_context_iface *shared_stctxi) 356{ 357 struct vg_context *shared_ctx = (struct vg_context *) shared_stctxi; 358 struct vg_context *ctx; 359 struct pipe_context *pipe; 360 361 pipe = smapi->screen->context_create(smapi->screen, NULL); 362 if (!pipe) 363 return NULL; 364 ctx = vg_create_context(pipe, NULL, shared_ctx); 365 if (!ctx) { 366 pipe->destroy(pipe); 367 return NULL; 368 } 369 370 ctx->iface.destroy = vg_context_destroy; 371 372 ctx->iface.notify_invalid_framebuffer = 373 vg_context_notify_invalid_framebuffer; 374 ctx->iface.flush = vg_context_flush; 375 376 ctx->iface.teximage = NULL; 377 ctx->iface.copy = NULL; 378 379 ctx->iface.st_context_private = (void *) smapi; 380 381 return &ctx->iface; 382} 383 384static struct st_renderbuffer * 385create_renderbuffer(enum pipe_format format) 386{ 387 struct st_renderbuffer *strb; 388 389 strb = CALLOC_STRUCT(st_renderbuffer); 390 if (strb) 391 strb->format = format; 392 393 return strb; 394} 395 396static void 397destroy_renderbuffer(struct st_renderbuffer *strb) 398{ 399 pipe_surface_reference(&strb->surface, NULL); 400 pipe_resource_reference(&strb->texture, NULL); 401 free(strb); 402} 403 404/** 405 * Decide the buffer to render to. 406 */ 407static enum st_attachment_type 408choose_attachment(struct st_framebuffer_iface *stfbi) 409{ 410 enum st_attachment_type statt; 411 412 statt = stfbi->visual->render_buffer; 413 if (statt != ST_ATTACHMENT_INVALID) { 414 /* use the buffer given by the visual, unless it is unavailable */ 415 if (!st_visual_have_buffers(stfbi->visual, 1 << statt)) { 416 switch (statt) { 417 case ST_ATTACHMENT_BACK_LEFT: 418 statt = ST_ATTACHMENT_FRONT_LEFT; 419 break; 420 case ST_ATTACHMENT_BACK_RIGHT: 421 statt = ST_ATTACHMENT_FRONT_RIGHT; 422 break; 423 default: 424 break; 425 } 426 427 if (!st_visual_have_buffers(stfbi->visual, 1 << statt)) 428 statt = ST_ATTACHMENT_INVALID; 429 } 430 } 431 432 return statt; 433} 434 435/** 436 * Bind the context to the given framebuffers. 437 */ 438static boolean 439vg_context_bind_framebuffers(struct st_context_iface *stctxi, 440 struct st_framebuffer_iface *stdrawi, 441 struct st_framebuffer_iface *streadi) 442{ 443 struct vg_context *ctx = (struct vg_context *) stctxi; 444 struct st_framebuffer *stfb; 445 enum st_attachment_type strb_att; 446 447 /* the draw and read framebuffers must be the same */ 448 if (stdrawi != streadi) 449 return FALSE; 450 451 p_atomic_set(&ctx->draw_buffer_invalid, TRUE); 452 453 strb_att = (stdrawi) ? choose_attachment(stdrawi) : ST_ATTACHMENT_INVALID; 454 455 if (ctx->draw_buffer) { 456 stfb = ctx->draw_buffer; 457 458 /* free the existing fb */ 459 if (!stdrawi || 460 stfb->strb_att != strb_att || 461 stfb->strb->format != stdrawi->visual->color_format || 462 stfb->dsrb->format != stdrawi->visual->depth_stencil_format) { 463 destroy_renderbuffer(stfb->strb); 464 destroy_renderbuffer(stfb->dsrb); 465 free(stfb); 466 467 ctx->draw_buffer = NULL; 468 } 469 } 470 471 if (!stdrawi) 472 return TRUE; 473 474 if (strb_att == ST_ATTACHMENT_INVALID) 475 return FALSE; 476 477 /* create a new fb */ 478 if (!ctx->draw_buffer) { 479 stfb = CALLOC_STRUCT(st_framebuffer); 480 if (!stfb) 481 return FALSE; 482 483 stfb->strb = create_renderbuffer(stdrawi->visual->color_format); 484 if (!stfb->strb) { 485 free(stfb); 486 return FALSE; 487 } 488 489 stfb->dsrb = create_renderbuffer(stdrawi->visual->depth_stencil_format); 490 if (!stfb->dsrb) { 491 free(stfb->strb); 492 free(stfb); 493 return FALSE; 494 } 495 496 stfb->width = 0; 497 stfb->height = 0; 498 stfb->strb_att = strb_att; 499 500 ctx->draw_buffer = stfb; 501 } 502 503 ctx->draw_buffer->iface = stdrawi; 504 505 return TRUE; 506} 507 508static boolean 509vg_api_make_current(struct st_api *stapi, struct st_context_iface *stctxi, 510 struct st_framebuffer_iface *stdrawi, 511 struct st_framebuffer_iface *streadi) 512{ 513 struct vg_context *ctx = (struct vg_context *) stctxi; 514 515 if (stctxi) 516 vg_context_bind_framebuffers(stctxi, stdrawi, streadi); 517 vg_set_current_context(ctx); 518 519 return TRUE; 520} 521 522static struct st_context_iface * 523vg_api_get_current(struct st_api *stapi) 524{ 525 struct vg_context *ctx = vg_current_context(); 526 527 return (ctx) ? &ctx->iface : NULL; 528} 529 530static boolean 531vg_api_is_visual_supported(struct st_api *stapi, 532 const struct st_visual *visual) 533{ 534 /* the impl requires a depth/stencil buffer */ 535 return util_format_is_depth_and_stencil(visual->depth_stencil_format); 536} 537 538static st_proc_t 539vg_api_get_proc_address(struct st_api *stapi, const char *procname) 540{ 541 return api_get_proc_address(procname); 542} 543 544static void 545vg_api_destroy(struct st_api *stapi) 546{ 547} 548 549static const struct st_api vg_api = { 550 vg_api_destroy, 551 vg_api_get_proc_address, 552 vg_api_is_visual_supported, 553 vg_api_create_context, 554 vg_api_make_current, 555 vg_api_get_current, 556}; 557 558const struct st_api * 559vg_api_get(void) 560{ 561 return &vg_api; 562} 563