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