1 /* 2 INTEL CONFIDENTIAL 3 Copyright 2009 Intel Corporation All Rights Reserved. 4 The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material contains trade secrets and proprietary and confidential information of Intel or its suppliers and licensors. The Material is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intels prior express written permission. 5 6 No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. 7 */ 8 9 /** 10 * SECTION:mixsurfacepool 11 * @short_description: MI-X Video Surface Pool 12 * 13 * A data object which stores and manipulates a pool of video surfaces. 14 */ 15 16 #include "mixvideolog.h" 17 #include "mixsurfacepool.h" 18 #include "mixvideoframe_private.h" 19 20 #define MIX_LOCK(lock) g_mutex_lock(lock); 21 #define MIX_UNLOCK(lock) g_mutex_unlock(lock); 22 23 #define SAFE_FREE(p) if(p) { g_free(p); p = NULL; } 24 25 static GType _mix_surfacepool_type = 0; 26 static MixParamsClass *parent_class = NULL; 27 28 #define _do_init { _mix_surfacepool_type = g_define_type_id; } 29 30 gboolean mix_surfacepool_copy(MixParams * target, const MixParams * src); 31 MixParams *mix_surfacepool_dup(const MixParams * obj); 32 gboolean mix_surfacepool_equal(MixParams * first, MixParams * second); 33 static void mix_surfacepool_finalize(MixParams * obj); 34 35 G_DEFINE_TYPE_WITH_CODE (MixSurfacePool, mix_surfacepool, MIX_TYPE_PARAMS, 36 _do_init); 37 38 static void mix_surfacepool_init(MixSurfacePool * self) { 39 /* initialize properties here */ 40 self->free_list = NULL; 41 self->in_use_list = NULL; 42 self->free_list_max_size = 0; 43 self->free_list_cur_size = 0; 44 self->high_water_mark = 0; 45 46 self->reserved1 = NULL; 47 self->reserved2 = NULL; 48 self->reserved3 = NULL; 49 self->reserved4 = NULL; 50 51 // TODO: relocate this mutex allocation -we can't communicate failure in ctor. 52 // Note that g_thread_init() has already been called by mix_video_init() 53 self->objectlock = g_mutex_new(); 54 55 } 56 57 static void mix_surfacepool_class_init(MixSurfacePoolClass * klass) { 58 MixParamsClass *mixparams_class = MIX_PARAMS_CLASS(klass); 59 60 /* setup static parent class */ 61 parent_class = (MixParamsClass *) g_type_class_peek_parent(klass); 62 63 mixparams_class->finalize = mix_surfacepool_finalize; 64 mixparams_class->copy = (MixParamsCopyFunction) mix_surfacepool_copy; 65 mixparams_class->dup = (MixParamsDupFunction) mix_surfacepool_dup; 66 mixparams_class->equal = (MixParamsEqualFunction) mix_surfacepool_equal; 67 } 68 69 MixSurfacePool * 70 mix_surfacepool_new(void) { 71 MixSurfacePool *ret = (MixSurfacePool *) g_type_create_instance( 72 MIX_TYPE_SURFACEPOOL); 73 return ret; 74 } 75 76 void mix_surfacepool_finalize(MixParams * obj) { 77 /* clean up here. */ 78 79 MixSurfacePool *self = MIX_SURFACEPOOL(obj); 80 81 if (self->objectlock) { 82 g_mutex_free(self->objectlock); 83 self->objectlock = NULL; 84 } 85 86 /* Chain up parent */ 87 if (parent_class->finalize) { 88 parent_class->finalize(obj); 89 } 90 } 91 92 MixSurfacePool * 93 mix_surfacepool_ref(MixSurfacePool * mix) { 94 return (MixSurfacePool *) mix_params_ref(MIX_PARAMS(mix)); 95 } 96 97 /** 98 * mix_surfacepool_dup: 99 * @obj: a #MixSurfacePool object 100 * @returns: a newly allocated duplicate of the object. 101 * 102 * Copy duplicate of the object. 103 */ 104 MixParams * 105 mix_surfacepool_dup(const MixParams * obj) { 106 MixParams *ret = NULL; 107 108 if (MIX_IS_SURFACEPOOL(obj)) { 109 110 MIX_LOCK(MIX_SURFACEPOOL(obj)->objectlock); 111 112 MixSurfacePool *duplicate = mix_surfacepool_new(); 113 if (mix_surfacepool_copy(MIX_PARAMS(duplicate), MIX_PARAMS(obj))) { 114 ret = MIX_PARAMS(duplicate); 115 } else { 116 mix_surfacepool_unref(duplicate); 117 } 118 119 MIX_UNLOCK(MIX_SURFACEPOOL(obj)->objectlock); 120 121 } 122 return ret; 123 } 124 125 /** 126 * mix_surfacepool_copy: 127 * @target: copy to target 128 * @src: copy from src 129 * @returns: boolean indicates if copy is successful. 130 * 131 * Copy instance data from @src to @target. 132 */ 133 gboolean mix_surfacepool_copy(MixParams * target, const MixParams * src) { 134 MixSurfacePool *this_target, *this_src; 135 136 if (MIX_IS_SURFACEPOOL(target) && MIX_IS_SURFACEPOOL(src)) { 137 138 MIX_LOCK(MIX_SURFACEPOOL(src)->objectlock); 139 MIX_LOCK(MIX_SURFACEPOOL(target)->objectlock); 140 141 // Cast the base object to this child object 142 this_target = MIX_SURFACEPOOL(target); 143 this_src = MIX_SURFACEPOOL(src); 144 145 // Free the existing properties 146 147 // Duplicate string 148 this_target->free_list = this_src->free_list; 149 this_target->in_use_list = this_src->in_use_list; 150 this_target->free_list_max_size = this_src->free_list_max_size; 151 this_target->free_list_cur_size = this_src->free_list_cur_size; 152 this_target->high_water_mark = this_src->high_water_mark; 153 154 MIX_UNLOCK(MIX_SURFACEPOOL(src)->objectlock); 155 MIX_UNLOCK(MIX_SURFACEPOOL(target)->objectlock); 156 157 // Now chainup base class 158 if (parent_class->copy) { 159 return parent_class->copy(MIX_PARAMS_CAST(target), MIX_PARAMS_CAST( 160 src)); 161 } else { 162 return TRUE; 163 } 164 } 165 return FALSE; 166 } 167 168 /** 169 * mix_surfacepool_equal: 170 * @first: first object to compare 171 * @second: seond object to compare 172 * @returns: boolean indicates if instance are equal. 173 * 174 * Copy instance data from @src to @target. 175 */ 176 gboolean mix_surfacepool_equal(MixParams * first, MixParams * second) { 177 gboolean ret = FALSE; 178 MixSurfacePool *this_first, *this_second; 179 180 if (MIX_IS_SURFACEPOOL(first) && MIX_IS_SURFACEPOOL(second)) { 181 // Deep compare 182 // Cast the base object to this child object 183 184 MIX_LOCK(MIX_SURFACEPOOL(first)->objectlock); 185 MIX_LOCK(MIX_SURFACEPOOL(second)->objectlock); 186 187 this_first = MIX_SURFACEPOOL(first); 188 this_second = MIX_SURFACEPOOL(second); 189 190 /* TODO: add comparison for other properties */ 191 if (this_first->free_list == this_second->free_list 192 && this_first->in_use_list == this_second->in_use_list 193 && this_first->free_list_max_size 194 == this_second->free_list_max_size 195 && this_first->free_list_cur_size 196 == this_second->free_list_cur_size 197 && this_first->high_water_mark == this_second->high_water_mark) { 198 // members within this scope equal. chaining up. 199 MixParamsClass *klass = MIX_PARAMS_CLASS(parent_class); 200 if (klass->equal) 201 ret = klass->equal(first, second); 202 else 203 ret = TRUE; 204 } 205 206 MIX_LOCK(MIX_SURFACEPOOL(first)->objectlock); 207 MIX_LOCK(MIX_SURFACEPOOL(second)->objectlock); 208 209 } 210 211 return ret; 212 } 213 214 /* Class Methods */ 215 216 /** 217 * mix_surfacepool_initialize: 218 * @returns: MIX_RESULT_SUCCESS if successful in creating the surface pool 219 * 220 * Use this method to create a new surface pool, consisting of a GSList of 221 * frame objects that represents a pool of surfaces. 222 */ 223 MIX_RESULT mix_surfacepool_initialize(MixSurfacePool * obj, 224 VASurfaceID *surfaces, guint num_surfaces) { 225 226 LOG_V( "Begin\n"); 227 228 if (obj == NULL || surfaces == NULL) { 229 230 LOG_E( 231 "Error NULL ptrs, obj %x, surfaces %x\n", (guint) obj, 232 (guint) surfaces); 233 234 return MIX_RESULT_NULL_PTR; 235 } 236 237 MIX_LOCK(obj->objectlock); 238 239 if ((obj->free_list != NULL) || (obj->in_use_list != NULL)) { 240 //surface pool is in use; return error; need proper cleanup 241 //TODO need cleanup here? 242 243 MIX_UNLOCK(obj->objectlock); 244 245 return MIX_RESULT_ALREADY_INIT; 246 } 247 248 if (num_surfaces == 0) { 249 obj->free_list = NULL; 250 251 obj->in_use_list = NULL; 252 253 obj->free_list_max_size = num_surfaces; 254 255 obj->free_list_cur_size = num_surfaces; 256 257 obj->high_water_mark = 0; 258 259 MIX_UNLOCK(obj->objectlock); 260 261 return MIX_RESULT_SUCCESS; 262 } 263 264 // Initialize the free pool with frame objects 265 266 gint i = 0; 267 MixVideoFrame *frame = NULL; 268 269 for (; i < num_surfaces; i++) { 270 271 //Create a frame object for each surface ID 272 frame = mix_videoframe_new(); 273 274 if (frame == NULL) { 275 //TODO need to log an error here and do cleanup 276 277 MIX_UNLOCK(obj->objectlock); 278 279 return MIX_RESULT_NO_MEMORY; 280 } 281 282 // Set the frame ID to the surface ID 283 mix_videoframe_set_frame_id(frame, surfaces[i]); 284 // Set the ci frame index to the surface ID 285 mix_videoframe_set_ci_frame_idx (frame, i); 286 // Leave timestamp for each frame object as zero 287 // Set the pool reference in the private data of the frame object 288 mix_videoframe_set_pool(frame, obj); 289 290 //Add each frame object to the pool list 291 obj->free_list = g_slist_append(obj->free_list, frame); 292 293 } 294 295 obj->in_use_list = NULL; 296 297 obj->free_list_max_size = num_surfaces; 298 299 obj->free_list_cur_size = num_surfaces; 300 301 obj->high_water_mark = 0; 302 303 MIX_UNLOCK(obj->objectlock); 304 305 LOG_V( "End\n"); 306 307 return MIX_RESULT_SUCCESS; 308 } 309 310 /** 311 * mix_surfacepool_put: 312 * @returns: SUCCESS or FAILURE 313 * 314 * Use this method to return a surface to the free pool 315 */ 316 MIX_RESULT mix_surfacepool_put(MixSurfacePool * obj, MixVideoFrame * frame) { 317 318 LOG_V( "Begin\n"); 319 if (obj == NULL || frame == NULL) 320 return MIX_RESULT_NULL_PTR; 321 322 LOG_V( "Frame id: %d\n", frame->frame_id); 323 MIX_LOCK(obj->objectlock); 324 325 if (obj->in_use_list == NULL) { 326 //in use list cannot be empty if a frame is in use 327 //TODO need better error code for this 328 329 MIX_UNLOCK(obj->objectlock); 330 331 return MIX_RESULT_FAIL; 332 } 333 334 GSList *element = g_slist_find(obj->in_use_list, frame); 335 if (element == NULL) { 336 //Integrity error; frame not found in in use list 337 //TODO need better error code and handling for this 338 339 MIX_UNLOCK(obj->objectlock); 340 341 return MIX_RESULT_FAIL; 342 } else { 343 //Remove this element from the in_use_list 344 obj->in_use_list = g_slist_remove_link(obj->in_use_list, element); 345 346 //Concat the element to the free_list and reset the timestamp of the frame 347 //Note that the surface ID stays valid 348 mix_videoframe_set_timestamp(frame, 0); 349 obj->free_list = g_slist_concat(obj->free_list, element); 350 351 //increment the free list count 352 obj->free_list_cur_size++; 353 } 354 355 //Note that we do nothing with the ref count for this. We want it to 356 //stay at 1, which is what triggered it to be added back to the free list. 357 358 MIX_UNLOCK(obj->objectlock); 359 360 LOG_V( "End\n"); 361 return MIX_RESULT_SUCCESS; 362 } 363 364 /** 365 * mix_surfacepool_get: 366 * @returns: SUCCESS or FAILURE 367 * 368 * Use this method to get a surface from the free pool 369 */ 370 MIX_RESULT mix_surfacepool_get(MixSurfacePool * obj, MixVideoFrame ** frame) { 371 372 LOG_V( "Begin\n"); 373 374 if (obj == NULL || frame == NULL) 375 return MIX_RESULT_NULL_PTR; 376 377 MIX_LOCK(obj->objectlock); 378 379 #if 0 380 if (obj->free_list == NULL) { 381 #else 382 if (obj->free_list_cur_size <= 1) { //Keep one surface free at all times for VBLANK bug 383 #endif 384 //We are out of surfaces 385 //TODO need to log this as well 386 387 MIX_UNLOCK(obj->objectlock); 388 389 LOG_E( "out of surfaces\n"); 390 391 return MIX_RESULT_NO_MEMORY; 392 } 393 394 //Remove a frame from the free pool 395 396 //We just remove the one at the head, since it's convenient 397 GSList *element = obj->free_list; 398 obj->free_list = g_slist_remove_link(obj->free_list, element); 399 if (element == NULL) { 400 //Unexpected behavior 401 //TODO need better error code and handling for this 402 403 MIX_UNLOCK(obj->objectlock); 404 405 LOG_E( "Element is null\n"); 406 407 return MIX_RESULT_FAIL; 408 } else { 409 //Concat the element to the in_use_list 410 obj->in_use_list = g_slist_concat(obj->in_use_list, element); 411 412 //TODO replace with proper logging 413 414 LOG_I( "frame refcount%d\n", 415 MIX_PARAMS(element->data)->refcount); 416 417 //Set the out frame pointer 418 *frame = (MixVideoFrame *) element->data; 419 420 LOG_V( "Frame id: %d\n", (*frame)->frame_id); 421 422 //decrement the free list count 423 obj->free_list_cur_size--; 424 425 //Check the high water mark for surface use 426 guint size = g_slist_length(obj->in_use_list); 427 if (size > obj->high_water_mark) 428 obj->high_water_mark = size; 429 //TODO Log this high water mark 430 } 431 432 //Increment the reference count for the frame 433 mix_videoframe_ref(*frame); 434 435 MIX_UNLOCK(obj->objectlock); 436 437 LOG_V( "End\n"); 438 439 return MIX_RESULT_SUCCESS; 440 } 441 442 443 gint mixframe_compare_index (MixVideoFrame * a, MixVideoFrame * b) 444 { 445 if (a == NULL || b == NULL) 446 return -1; 447 if (a->ci_frame_idx == b->ci_frame_idx) 448 return 0; 449 else 450 return -1; 451 } 452 453 /** 454 * mix_surfacepool_get: 455 * @returns: SUCCESS or FAILURE 456 * 457 * Use this method to get a surface from the free pool according to the CI frame idx 458 */ 459 460 MIX_RESULT mix_surfacepool_get_frame_with_ci_frameidx (MixSurfacePool * obj, MixVideoFrame ** frame, MixVideoFrame *in_frame) { 461 462 LOG_V( "Begin\n"); 463 464 if (obj == NULL || frame == NULL) 465 return MIX_RESULT_NULL_PTR; 466 467 MIX_LOCK(obj->objectlock); 468 469 if (obj->free_list == NULL) { 470 //We are out of surfaces 471 //TODO need to log this as well 472 473 MIX_UNLOCK(obj->objectlock); 474 475 LOG_E( "out of surfaces\n"); 476 477 return MIX_RESULT_NO_MEMORY; 478 } 479 480 //Remove a frame from the free pool 481 482 //We just remove the one at the head, since it's convenient 483 GSList *element = g_slist_find_custom (obj->free_list, in_frame, (GCompareFunc) mixframe_compare_index); 484 obj->free_list = g_slist_remove_link(obj->free_list, element); 485 if (element == NULL) { 486 //Unexpected behavior 487 //TODO need better error code and handling for this 488 489 MIX_UNLOCK(obj->objectlock); 490 491 LOG_E( "Element is null\n"); 492 493 return MIX_RESULT_FAIL; 494 } else { 495 //Concat the element to the in_use_list 496 obj->in_use_list = g_slist_concat(obj->in_use_list, element); 497 498 //TODO replace with proper logging 499 500 LOG_I( "frame refcount%d\n", 501 MIX_PARAMS(element->data)->refcount); 502 503 //Set the out frame pointer 504 *frame = (MixVideoFrame *) element->data; 505 506 //Check the high water mark for surface use 507 guint size = g_slist_length(obj->in_use_list); 508 if (size > obj->high_water_mark) 509 obj->high_water_mark = size; 510 //TODO Log this high water mark 511 } 512 513 //Increment the reference count for the frame 514 mix_videoframe_ref(*frame); 515 516 MIX_UNLOCK(obj->objectlock); 517 518 LOG_V( "End\n"); 519 520 return MIX_RESULT_SUCCESS; 521 } 522 /** 523 * mix_surfacepool_check_available: 524 * @returns: SUCCESS or FAILURE 525 * 526 * Use this method to check availability of getting a surface from the free pool 527 */ 528 MIX_RESULT mix_surfacepool_check_available(MixSurfacePool * obj) { 529 530 LOG_V( "Begin\n"); 531 532 if (obj == NULL) 533 return MIX_RESULT_NULL_PTR; 534 535 MIX_LOCK(obj->objectlock); 536 537 #if 0 538 if (obj->free_list == NULL) { 539 #else 540 if (obj->free_list_cur_size <= 1) { //Keep one surface free at all times for VBLANK bug 541 #endif 542 //We are out of surfaces 543 544 MIX_UNLOCK(obj->objectlock); 545 546 LOG_W( 547 "Returning MIX_RESULT_POOLEMPTY because out of surfaces\n"); 548 549 return MIX_RESULT_POOLEMPTY; 550 } else { 551 //Pool is not empty 552 553 MIX_UNLOCK(obj->objectlock); 554 555 LOG_I( 556 "Returning MIX_RESULT_SUCCESS because surfaces are available\n"); 557 558 return MIX_RESULT_SUCCESS; 559 } 560 561 } 562 563 /** 564 * mix_surfacepool_deinitialize: 565 * @returns: SUCCESS or FAILURE 566 * 567 * Use this method to teardown a surface pool 568 */ 569 MIX_RESULT mix_surfacepool_deinitialize(MixSurfacePool * obj) { 570 if (obj == NULL) 571 return MIX_RESULT_NULL_PTR; 572 573 MIX_LOCK(obj->objectlock); 574 575 if ((obj->in_use_list != NULL) || (g_slist_length(obj->free_list) 576 != obj->free_list_max_size)) { 577 //TODO better error code 578 //We have outstanding frame objects in use and they need to be 579 //freed before we can deinitialize. 580 581 MIX_UNLOCK(obj->objectlock); 582 583 return MIX_RESULT_FAIL; 584 } 585 586 //Now remove frame objects from the list 587 588 MixVideoFrame *frame = NULL; 589 590 while (obj->free_list != NULL) { 591 //Get the frame object from the head of the list 592 frame = obj->free_list->data; 593 //frame = g_slist_nth_data(obj->free_list, 0); 594 595 //Release it 596 mix_videoframe_unref(frame); 597 598 //Delete the head node of the list and store the new head 599 obj->free_list = g_slist_delete_link(obj->free_list, obj->free_list); 600 601 //Repeat until empty 602 } 603 604 obj->free_list_max_size = 0; 605 obj->free_list_cur_size = 0; 606 607 //May want to log this information for tuning 608 obj->high_water_mark = 0; 609 610 MIX_UNLOCK(obj->objectlock); 611 612 return MIX_RESULT_SUCCESS; 613 } 614 615 #define MIX_SURFACEPOOL_SETTER_CHECK_INPUT(obj) \ 616 if(!obj) return MIX_RESULT_NULL_PTR; \ 617 if(!MIX_IS_SURFACEPOOL(obj)) return MIX_RESULT_FAIL; \ 618 619 #define MIX_SURFACEPOOL_GETTER_CHECK_INPUT(obj, prop) \ 620 if(!obj || !prop) return MIX_RESULT_NULL_PTR; \ 621 if(!MIX_IS_SURFACEPOOL(obj)) return MIX_RESULT_FAIL; \ 622 623 624 MIX_RESULT 625 mix_surfacepool_dumpframe(MixVideoFrame *frame) 626 { 627 LOG_I( "\tFrame %x, id %lu, refcount %d, ts %lu\n", (guint)frame, 628 frame->frame_id, MIX_PARAMS(frame)->refcount, (gulong) frame->timestamp); 629 630 return MIX_RESULT_SUCCESS; 631 } 632 633 MIX_RESULT 634 mix_surfacepool_dumpprint (MixSurfacePool * obj) 635 { 636 //TODO replace this with proper logging later 637 638 LOG_I( "SURFACE POOL DUMP:\n"); 639 LOG_I( "Free list size is %d\n", obj->free_list_cur_size); 640 LOG_I( "In use list size is %d\n", g_slist_length(obj->in_use_list)); 641 LOG_I( "High water mark is %lu\n", obj->high_water_mark); 642 643 //Walk the free list and report the contents 644 LOG_I( "Free list contents:\n"); 645 g_slist_foreach(obj->free_list, (GFunc) mix_surfacepool_dumpframe, NULL); 646 647 //Walk the in_use list and report the contents 648 LOG_I( "In Use list contents:\n"); 649 g_slist_foreach(obj->in_use_list, (GFunc) mix_surfacepool_dumpframe, NULL); 650 651 return MIX_RESULT_SUCCESS; 652 } 653