cso_context.c revision 386102c62a3315182ffbc6319351cb883234511a
1/************************************************************************** 2 * 3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 /** 29 * @file 30 * 31 * Wrap the cso cache & hash mechanisms in a simplified 32 * pipe-driver-specific interface. 33 * 34 * @author Zack Rusin <zack@tungstengraphics.com> 35 * @author Keith Whitwell <keith@tungstengraphics.com> 36 */ 37 38#include "pipe/p_state.h" 39#include "pipe/p_util.h" 40#include "pipe/p_inlines.h" 41#include "tgsi/util/tgsi_parse.h" 42 43#include "cso_cache/cso_context.h" 44#include "cso_cache/cso_cache.h" 45#include "cso_cache/cso_hash.h" 46 47struct cso_context { 48 struct pipe_context *pipe; 49 struct cso_cache *cache; 50 51 struct { 52 void *samplers[PIPE_MAX_SAMPLERS]; 53 unsigned nr_samplers; 54 } hw; 55 56 void *samplers[PIPE_MAX_SAMPLERS]; 57 unsigned nr_samplers; 58 59 void *samplers_saved[PIPE_MAX_SAMPLERS]; 60 unsigned nr_samplers_saved; 61 62 struct pipe_texture *textures[PIPE_MAX_SAMPLERS]; 63 uint nr_textures; 64 65 struct pipe_texture *textures_saved[PIPE_MAX_SAMPLERS]; 66 uint nr_textures_saved; 67 68 /** Current and saved state. 69 * The saved state is used as a 1-deep stack. 70 */ 71 void *blend, *blend_saved; 72 void *depth_stencil, *depth_stencil_saved; 73 void *rasterizer, *rasterizer_saved; 74 void *fragment_shader, *fragment_shader_saved; 75 void *vertex_shader, *vertex_shader_saved; 76 77 struct pipe_framebuffer_state fb, fb_saved; 78 struct pipe_viewport_state vp, vp_saved; 79 struct pipe_blend_color blend_color; 80}; 81 82 83struct cso_context *cso_create_context( struct pipe_context *pipe ) 84{ 85 struct cso_context *ctx = CALLOC_STRUCT(cso_context); 86 if (ctx == NULL) 87 goto out; 88 89 ctx->cache = cso_cache_create(); 90 if (ctx->cache == NULL) 91 goto out; 92 93 ctx->pipe = pipe; 94 95 /* Enable for testing: */ 96 if (0) cso_set_maximum_cache_size( ctx->cache, 4 ); 97 98 return ctx; 99 100out: 101 cso_destroy_context( ctx ); 102 return NULL; 103} 104 105 106/** 107 * Prior to context destruction, this function unbinds all state objects. 108 */ 109void cso_release_all( struct cso_context *ctx ) 110{ 111 unsigned i; 112 113 if (ctx->pipe) { 114 ctx->pipe->bind_blend_state( ctx->pipe, NULL ); 115 ctx->pipe->bind_rasterizer_state( ctx->pipe, NULL ); 116 ctx->pipe->bind_sampler_states( ctx->pipe, 0, NULL ); 117 ctx->pipe->bind_depth_stencil_alpha_state( ctx->pipe, NULL ); 118 ctx->pipe->bind_fs_state( ctx->pipe, NULL ); 119 ctx->pipe->bind_vs_state( ctx->pipe, NULL ); 120 } 121 122 for (i = 0; i < PIPE_MAX_SAMPLERS; i++) { 123 pipe_texture_reference(&ctx->textures[i], NULL); 124 pipe_texture_reference(&ctx->textures_saved[i], NULL); 125 } 126 127 if (ctx->cache) { 128 cso_cache_delete( ctx->cache ); 129 ctx->cache = NULL; 130 } 131} 132 133 134void cso_destroy_context( struct cso_context *ctx ) 135{ 136 if (ctx) { 137 //cso_release_all( ctx ); 138 FREE( ctx ); 139 } 140} 141 142 143/* Those function will either find the state of the given template 144 * in the cache or they will create a new state from the given 145 * template, insert it in the cache and return it. 146 */ 147 148/* 149 * If the driver returns 0 from the create method then they will assign 150 * the data member of the cso to be the template itself. 151 */ 152 153enum pipe_error cso_set_blend(struct cso_context *ctx, 154 const struct pipe_blend_state *templ) 155{ 156 unsigned hash_key = cso_construct_key((void*)templ, sizeof(struct pipe_blend_state)); 157 struct cso_hash_iter iter = cso_find_state_template(ctx->cache, 158 hash_key, CSO_BLEND, 159 (void*)templ); 160 void *handle; 161 162 if (cso_hash_iter_is_null(iter)) { 163 struct cso_blend *cso = MALLOC(sizeof(struct cso_blend)); 164 if (!cso) 165 return PIPE_ERROR_OUT_OF_MEMORY; 166 167 cso->state = *templ; 168 cso->data = ctx->pipe->create_blend_state(ctx->pipe, &cso->state); 169 cso->delete_state = (cso_state_callback)ctx->pipe->delete_blend_state; 170 cso->context = ctx->pipe; 171 172 iter = cso_insert_state(ctx->cache, hash_key, CSO_BLEND, cso); 173 if (cso_hash_iter_is_null(iter)) { 174 FREE(cso); 175 return PIPE_ERROR_OUT_OF_MEMORY; 176 } 177 178 handle = cso->data; 179 } 180 else { 181 handle = ((struct cso_blend *)cso_hash_iter_data(iter))->data; 182 } 183 184 if (ctx->blend != handle) { 185 ctx->blend = handle; 186 ctx->pipe->bind_blend_state(ctx->pipe, handle); 187 } 188 return PIPE_OK; 189} 190 191void cso_save_blend(struct cso_context *ctx) 192{ 193 assert(!ctx->blend_saved); 194 ctx->blend_saved = ctx->blend; 195} 196 197void cso_restore_blend(struct cso_context *ctx) 198{ 199 if (ctx->blend != ctx->blend_saved) { 200 ctx->blend = ctx->blend_saved; 201 ctx->pipe->bind_blend_state(ctx->pipe, ctx->blend_saved); 202 } 203 ctx->blend_saved = NULL; 204} 205 206 207 208enum pipe_error cso_single_sampler(struct cso_context *ctx, 209 unsigned idx, 210 const struct pipe_sampler_state *templ) 211{ 212 void *handle = NULL; 213 214 if (templ != NULL) { 215 unsigned hash_key = cso_construct_key((void*)templ, sizeof(struct pipe_sampler_state)); 216 struct cso_hash_iter iter = cso_find_state_template(ctx->cache, 217 hash_key, CSO_SAMPLER, 218 (void*)templ); 219 220 if (cso_hash_iter_is_null(iter)) { 221 struct cso_sampler *cso = MALLOC(sizeof(struct cso_sampler)); 222 if (!cso) 223 return PIPE_ERROR_OUT_OF_MEMORY; 224 225 cso->state = *templ; 226 cso->data = ctx->pipe->create_sampler_state(ctx->pipe, &cso->state); 227 cso->delete_state = (cso_state_callback)ctx->pipe->delete_sampler_state; 228 cso->context = ctx->pipe; 229 230 iter = cso_insert_state(ctx->cache, hash_key, CSO_SAMPLER, cso); 231 if (cso_hash_iter_is_null(iter)) { 232 FREE(cso); 233 return PIPE_ERROR_OUT_OF_MEMORY; 234 } 235 236 handle = cso->data; 237 } 238 else { 239 handle = ((struct cso_sampler *)cso_hash_iter_data(iter))->data; 240 } 241 } 242 243 ctx->samplers[idx] = handle; 244 return PIPE_OK; 245} 246 247void cso_single_sampler_done( struct cso_context *ctx ) 248{ 249 unsigned i; 250 251 /* find highest non-null sampler */ 252 for (i = PIPE_MAX_SAMPLERS; i > 0; i--) { 253 if (ctx->samplers[i - 1] != NULL) 254 break; 255 } 256 257 ctx->nr_samplers = i; 258 259 if (ctx->hw.nr_samplers != ctx->nr_samplers || 260 memcmp(ctx->hw.samplers, 261 ctx->samplers, 262 ctx->nr_samplers * sizeof(void *)) != 0) 263 { 264 memcpy(ctx->hw.samplers, ctx->samplers, ctx->nr_samplers * sizeof(void *)); 265 ctx->hw.nr_samplers = ctx->nr_samplers; 266 267 ctx->pipe->bind_sampler_states(ctx->pipe, ctx->nr_samplers, ctx->samplers); 268 } 269} 270 271/* 272 * If the function encouters any errors it will return the 273 * last one. Done to always try to set as many samplers 274 * as possible. 275 */ 276enum pipe_error cso_set_samplers( struct cso_context *ctx, 277 unsigned nr, 278 const struct pipe_sampler_state **templates ) 279{ 280 unsigned i; 281 enum pipe_error temp, error = PIPE_OK; 282 283 /* TODO: fastpath 284 */ 285 286 for (i = 0; i < nr; i++) { 287 temp = cso_single_sampler( ctx, i, templates[i] ); 288 if (temp != PIPE_OK) 289 error = temp; 290 } 291 292 for ( ; i < ctx->nr_samplers; i++) { 293 temp = cso_single_sampler( ctx, i, NULL ); 294 if (temp != PIPE_OK) 295 error = temp; 296 } 297 298 cso_single_sampler_done( ctx ); 299 300 return error; 301} 302 303void cso_save_samplers(struct cso_context *ctx) 304{ 305 ctx->nr_samplers_saved = ctx->nr_samplers; 306 memcpy(ctx->samplers_saved, ctx->samplers, sizeof(ctx->samplers)); 307} 308 309void cso_restore_samplers(struct cso_context *ctx) 310{ 311 ctx->nr_samplers = ctx->nr_samplers_saved; 312 memcpy(ctx->samplers, ctx->samplers_saved, sizeof(ctx->samplers)); 313 cso_single_sampler_done( ctx ); 314} 315 316 317enum pipe_error cso_set_sampler_textures( struct cso_context *ctx, 318 uint count, 319 struct pipe_texture **textures ) 320{ 321 uint i; 322 323 ctx->nr_textures = count; 324 325 for (i = 0; i < count; i++) 326 pipe_texture_reference(&ctx->textures[i], textures[i]); 327 for ( ; i < PIPE_MAX_SAMPLERS; i++) 328 pipe_texture_reference(&ctx->textures[i], NULL); 329 330 ctx->pipe->set_sampler_textures(ctx->pipe, count, textures); 331 332 return PIPE_OK; 333} 334 335void cso_save_sampler_textures( struct cso_context *ctx ) 336{ 337 uint i; 338 339 ctx->nr_textures_saved = ctx->nr_textures; 340 for (i = 0; i < ctx->nr_textures; i++) { 341 assert(!ctx->textures_saved[i]); 342 pipe_texture_reference(&ctx->textures_saved[i], ctx->textures[i]); 343 } 344} 345 346void cso_restore_sampler_textures( struct cso_context *ctx ) 347{ 348 uint i; 349 350 ctx->nr_textures = ctx->nr_textures_saved; 351 352 for (i = 0; i < ctx->nr_textures; i++) { 353 pipe_texture_reference(&ctx->textures[i], NULL); 354 ctx->textures[i] = ctx->textures_saved[i]; 355 ctx->textures_saved[i] = NULL; 356 } 357 for ( ; i < PIPE_MAX_SAMPLERS; i++) 358 pipe_texture_reference(&ctx->textures[i], NULL); 359 360 ctx->pipe->set_sampler_textures(ctx->pipe, ctx->nr_textures, ctx->textures); 361 362 ctx->nr_textures_saved = 0; 363} 364 365 366 367enum pipe_error cso_set_depth_stencil_alpha(struct cso_context *ctx, 368 const struct pipe_depth_stencil_alpha_state *templ) 369{ 370 unsigned hash_key = cso_construct_key((void*)templ, 371 sizeof(struct pipe_depth_stencil_alpha_state)); 372 struct cso_hash_iter iter = cso_find_state_template(ctx->cache, 373 hash_key, 374 CSO_DEPTH_STENCIL_ALPHA, 375 (void*)templ); 376 void *handle; 377 378 if (cso_hash_iter_is_null(iter)) { 379 struct cso_depth_stencil_alpha *cso = MALLOC(sizeof(struct cso_depth_stencil_alpha)); 380 if (!cso) 381 return PIPE_ERROR_OUT_OF_MEMORY; 382 383 cso->state = *templ; 384 cso->data = ctx->pipe->create_depth_stencil_alpha_state(ctx->pipe, &cso->state); 385 cso->delete_state = (cso_state_callback)ctx->pipe->delete_depth_stencil_alpha_state; 386 cso->context = ctx->pipe; 387 388 iter = cso_insert_state(ctx->cache, hash_key, CSO_DEPTH_STENCIL_ALPHA, cso); 389 if (cso_hash_iter_is_null(iter)) { 390 FREE(cso); 391 return PIPE_ERROR_OUT_OF_MEMORY; 392 } 393 394 handle = cso->data; 395 } 396 else { 397 handle = ((struct cso_depth_stencil_alpha *)cso_hash_iter_data(iter))->data; 398 } 399 400 if (ctx->depth_stencil != handle) { 401 ctx->depth_stencil = handle; 402 ctx->pipe->bind_depth_stencil_alpha_state(ctx->pipe, handle); 403 } 404 return PIPE_OK; 405} 406 407void cso_save_depth_stencil_alpha(struct cso_context *ctx) 408{ 409 assert(!ctx->depth_stencil_saved); 410 ctx->depth_stencil_saved = ctx->depth_stencil; 411} 412 413void cso_restore_depth_stencil_alpha(struct cso_context *ctx) 414{ 415 if (ctx->depth_stencil != ctx->depth_stencil_saved) { 416 ctx->depth_stencil = ctx->depth_stencil_saved; 417 ctx->pipe->bind_depth_stencil_alpha_state(ctx->pipe, ctx->depth_stencil_saved); 418 } 419 ctx->depth_stencil_saved = NULL; 420} 421 422 423 424enum pipe_error cso_set_rasterizer(struct cso_context *ctx, 425 const struct pipe_rasterizer_state *templ) 426{ 427 unsigned hash_key = cso_construct_key((void*)templ, 428 sizeof(struct pipe_rasterizer_state)); 429 struct cso_hash_iter iter = cso_find_state_template(ctx->cache, 430 hash_key, CSO_RASTERIZER, 431 (void*)templ); 432 void *handle = NULL; 433 434 if (cso_hash_iter_is_null(iter)) { 435 struct cso_rasterizer *cso = MALLOC(sizeof(struct cso_rasterizer)); 436 if (!cso) 437 return PIPE_ERROR_OUT_OF_MEMORY; 438 439 cso->state = *templ; 440 cso->data = ctx->pipe->create_rasterizer_state(ctx->pipe, &cso->state); 441 cso->delete_state = (cso_state_callback)ctx->pipe->delete_rasterizer_state; 442 cso->context = ctx->pipe; 443 444 iter = cso_insert_state(ctx->cache, hash_key, CSO_RASTERIZER, cso); 445 if (cso_hash_iter_is_null(iter)) { 446 FREE(cso); 447 return PIPE_ERROR_OUT_OF_MEMORY; 448 } 449 450 handle = cso->data; 451 } 452 else { 453 handle = ((struct cso_rasterizer *)cso_hash_iter_data(iter))->data; 454 } 455 456 if (ctx->rasterizer != handle) { 457 ctx->rasterizer = handle; 458 ctx->pipe->bind_rasterizer_state(ctx->pipe, handle); 459 } 460 return PIPE_OK; 461} 462 463void cso_save_rasterizer(struct cso_context *ctx) 464{ 465 assert(!ctx->rasterizer_saved); 466 ctx->rasterizer_saved = ctx->rasterizer; 467} 468 469void cso_restore_rasterizer(struct cso_context *ctx) 470{ 471 if (ctx->rasterizer != ctx->rasterizer_saved) { 472 ctx->rasterizer = ctx->rasterizer_saved; 473 ctx->pipe->bind_rasterizer_state(ctx->pipe, ctx->rasterizer_saved); 474 } 475 ctx->rasterizer_saved = NULL; 476} 477 478 479 480enum pipe_error cso_set_fragment_shader_handle(struct cso_context *ctx, 481 void *handle ) 482{ 483 if (ctx->fragment_shader != handle) { 484 ctx->fragment_shader = handle; 485 ctx->pipe->bind_fs_state(ctx->pipe, handle); 486 } 487 return PIPE_OK; 488} 489 490void cso_delete_fragment_shader(struct cso_context *ctx, void *handle ) 491{ 492 if (handle == ctx->fragment_shader) { 493 /* unbind before deleting */ 494 ctx->pipe->bind_fs_state(ctx->pipe, NULL); 495 ctx->fragment_shader = NULL; 496 } 497 ctx->pipe->delete_fs_state(ctx->pipe, handle); 498} 499 500/* Not really working: 501 */ 502#if 0 503enum pipe_error cso_set_fragment_shader(struct cso_context *ctx, 504 const struct pipe_shader_state *templ) 505{ 506 const struct tgsi_token *tokens = templ->tokens; 507 unsigned num_tokens = tgsi_num_tokens(tokens); 508 size_t tokens_size = num_tokens*sizeof(struct tgsi_token); 509 unsigned hash_key = cso_construct_key((void*)tokens, tokens_size); 510 struct cso_hash_iter iter = cso_find_state_template(ctx->cache, 511 hash_key, 512 CSO_FRAGMENT_SHADER, 513 (void*)tokens); 514 void *handle = NULL; 515 516 if (cso_hash_iter_is_null(iter)) { 517 struct cso_fragment_shader *cso = MALLOC(sizeof(struct cso_fragment_shader) + tokens_size); 518 struct tgsi_token *cso_tokens = (struct tgsi_token *)((char *)cso + sizeof(*cso)); 519 520 if (!cso) 521 return PIPE_ERROR_OUT_OF_MEMORY; 522 523 memcpy(cso_tokens, tokens, tokens_size); 524 cso->state.tokens = cso_tokens; 525 cso->data = ctx->pipe->create_fs_state(ctx->pipe, &cso->state); 526 cso->delete_state = (cso_state_callback)ctx->pipe->delete_fs_state; 527 cso->context = ctx->pipe; 528 529 iter = cso_insert_state(ctx->cache, hash_key, CSO_FRAGMENT_SHADER, cso); 530 if (cso_hash_iter_is_null(iter)) { 531 FREE(cso); 532 return PIPE_ERROR_OUT_OF_MEMORY; 533 } 534 535 handle = cso->data; 536 } 537 else { 538 handle = ((struct cso_fragment_shader *)cso_hash_iter_data(iter))->data; 539 } 540 541 return cso_set_fragment_shader_handle( ctx, handle ); 542} 543#endif 544 545void cso_save_fragment_shader(struct cso_context *ctx) 546{ 547 assert(!ctx->fragment_shader_saved); 548 ctx->fragment_shader_saved = ctx->fragment_shader; 549} 550 551void cso_restore_fragment_shader(struct cso_context *ctx) 552{ 553 if (ctx->fragment_shader_saved != ctx->fragment_shader) { 554 ctx->pipe->bind_fs_state(ctx->pipe, ctx->fragment_shader_saved); 555 ctx->fragment_shader = ctx->fragment_shader_saved; 556 } 557 ctx->fragment_shader_saved = NULL; 558} 559 560 561enum pipe_error cso_set_vertex_shader_handle(struct cso_context *ctx, 562 void *handle ) 563{ 564 if (ctx->vertex_shader != handle) { 565 ctx->vertex_shader = handle; 566 ctx->pipe->bind_vs_state(ctx->pipe, handle); 567 } 568 return PIPE_OK; 569} 570 571void cso_delete_vertex_shader(struct cso_context *ctx, void *handle ) 572{ 573 if (handle == ctx->vertex_shader) { 574 /* unbind before deleting */ 575 ctx->pipe->bind_vs_state(ctx->pipe, NULL); 576 ctx->vertex_shader = NULL; 577 } 578 ctx->pipe->delete_vs_state(ctx->pipe, handle); 579} 580 581 582/* Not really working: 583 */ 584#if 0 585enum pipe_error cso_set_vertex_shader(struct cso_context *ctx, 586 const struct pipe_shader_state *templ) 587{ 588 unsigned hash_key = cso_construct_key((void*)templ, 589 sizeof(struct pipe_shader_state)); 590 struct cso_hash_iter iter = cso_find_state_template(ctx->cache, 591 hash_key, CSO_VERTEX_SHADER, 592 (void*)templ); 593 void *handle = NULL; 594 595 if (cso_hash_iter_is_null(iter)) { 596 struct cso_vertex_shader *cso = MALLOC(sizeof(struct cso_vertex_shader)); 597 598 if (!cso) 599 return PIPE_ERROR_OUT_OF_MEMORY; 600 601 cso->state = *templ; 602 cso->data = ctx->pipe->create_vs_state(ctx->pipe, &cso->state); 603 cso->delete_state = (cso_state_callback)ctx->pipe->delete_vs_state; 604 cso->context = ctx->pipe; 605 606 iter = cso_insert_state(ctx->cache, hash_key, CSO_VERTEX_SHADER, cso); 607 if (cso_hash_iter_is_null(iter)) { 608 FREE(cso); 609 return PIPE_ERROR_OUT_OF_MEMORY; 610 } 611 612 handle = cso->data; 613 } 614 else { 615 handle = ((struct cso_vertex_shader *)cso_hash_iter_data(iter))->data; 616 } 617 618 return cso_set_vertex_shader_handle( ctx, handle ); 619} 620#endif 621 622 623 624void cso_save_vertex_shader(struct cso_context *ctx) 625{ 626 assert(!ctx->vertex_shader_saved); 627 ctx->vertex_shader_saved = ctx->vertex_shader; 628} 629 630void cso_restore_vertex_shader(struct cso_context *ctx) 631{ 632 if (ctx->vertex_shader_saved != ctx->vertex_shader) { 633 ctx->pipe->bind_vs_state(ctx->pipe, ctx->vertex_shader_saved); 634 ctx->vertex_shader = ctx->vertex_shader_saved; 635 } 636 ctx->vertex_shader_saved = NULL; 637} 638 639 640 641enum pipe_error cso_set_framebuffer(struct cso_context *ctx, 642 const struct pipe_framebuffer_state *fb) 643{ 644 /* XXX this memcmp() fails to detect buffer size changes */ 645 if (1/*memcmp(&ctx->fb, fb, sizeof(*fb))*/) { 646 ctx->fb = *fb; 647 ctx->pipe->set_framebuffer_state(ctx->pipe, fb); 648 } 649 return PIPE_OK; 650} 651 652void cso_save_framebuffer(struct cso_context *ctx) 653{ 654 ctx->fb_saved = ctx->fb; 655} 656 657void cso_restore_framebuffer(struct cso_context *ctx) 658{ 659 if (memcmp(&ctx->fb, &ctx->fb_saved, sizeof(ctx->fb))) { 660 ctx->fb = ctx->fb_saved; 661 ctx->pipe->set_framebuffer_state(ctx->pipe, &ctx->fb); 662 } 663} 664 665 666enum pipe_error cso_set_viewport(struct cso_context *ctx, 667 const struct pipe_viewport_state *vp) 668{ 669 if (memcmp(&ctx->vp, vp, sizeof(*vp))) { 670 ctx->vp = *vp; 671 ctx->pipe->set_viewport_state(ctx->pipe, vp); 672 } 673 return PIPE_OK; 674} 675 676void cso_save_viewport(struct cso_context *ctx) 677{ 678 ctx->vp_saved = ctx->vp; 679} 680 681 682void cso_restore_viewport(struct cso_context *ctx) 683{ 684 if (memcmp(&ctx->vp, &ctx->vp_saved, sizeof(ctx->vp))) { 685 ctx->vp = ctx->vp_saved; 686 ctx->pipe->set_viewport_state(ctx->pipe, &ctx->vp); 687 } 688} 689 690 691 692 693enum pipe_error cso_set_blend_color(struct cso_context *ctx, 694 const struct pipe_blend_color *bc) 695{ 696 if (memcmp(&ctx->blend_color, bc, sizeof(ctx->blend_color))) { 697 ctx->blend_color = *bc; 698 ctx->pipe->set_blend_color(ctx->pipe, bc); 699 } 700 return PIPE_OK; 701} 702