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 Intel’s 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
mix_surfacepool_init(MixSurfacePool * self)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
mix_surfacepool_class_init(MixSurfacePoolClass * klass)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 *
mix_surfacepool_new(void)70 mix_surfacepool_new(void) {
71 MixSurfacePool *ret = (MixSurfacePool *) g_type_create_instance(
72 MIX_TYPE_SURFACEPOOL);
73 return ret;
74 }
75
mix_surfacepool_finalize(MixParams * obj)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 *
mix_surfacepool_ref(MixSurfacePool * mix)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 *
mix_surfacepool_dup(const MixParams * obj)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 */
mix_surfacepool_copy(MixParams * target,const MixParams * src)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 */
mix_surfacepool_equal(MixParams * first,MixParams * second)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 */
mix_surfacepool_initialize(MixSurfacePool * obj,VASurfaceID * surfaces,guint num_surfaces)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 */
mix_surfacepool_put(MixSurfacePool * obj,MixVideoFrame * frame)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 */
mix_surfacepool_get(MixSurfacePool * obj,MixVideoFrame ** frame)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