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