1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#define VCOS_VERIFY_BKPTS 1 // TODO remove
29#define VCOS_LOG_CATEGORY (&log_cat)
30
31#include "interface/khronos/common/khrn_client.h"
32#include "interface/vcos/vcos.h"
33#ifdef KHRN_FRUIT_DIRECT
34#include "middleware/khronos/egl/egl_server.h"
35#include "middleware/khronos/ext/egl_khr_image.h"
36#include "middleware/khronos/common/khrn_umem.h"
37#endif
38
39#include "interface/khronos/wf/wfc_client_stream.h"
40#include "interface/khronos/wf/wfc_server_api.h"
41
42//#define WFC_FULL_LOGGING
43#ifdef WFC_FULL_LOGGING
44#define WFC_LOG_LEVEL VCOS_LOG_TRACE
45#else
46#define WFC_LOG_LEVEL VCOS_LOG_WARN
47#endif
48
49//==============================================================================
50
51//!@name Stream data block pool sizes
52//!@{
53#define WFC_STREAM_BLOCK_SIZE (WFC_MAX_STREAMS_PER_CLIENT / 8)
54#define WFC_STREAM_MAX_EXTENSIONS 7
55#define WFC_STREAM_MAX_STREAMS (WFC_STREAM_BLOCK_SIZE * (WFC_STREAM_MAX_EXTENSIONS + 1))
56//!@}
57
58//!@name Global lock to protect global data (stream data list, next stream ID)
59//!@{
60#define GLOBAL_LOCK() do {vcos_once(&wfc_stream_initialise_once, wfc_stream_initialise); vcos_mutex_lock(&wfc_stream_global_lock);} while (0)
61#define GLOBAL_UNLOCK() do {vcos_mutex_unlock(&wfc_stream_global_lock);} while (0)
62//!@}
63
64//!@name Stream-specific mutex. Global lock must already be held when acquiring this lock.
65//!@{
66#define STREAM_LOCK(stream_ptr) do {vcos_mutex_lock(&stream_ptr->mutex);} while (0)
67#define STREAM_UNLOCK(stream_ptr) do {vcos_mutex_unlock(&stream_ptr->mutex);} while (0)
68//!@}
69
70//! Period in milliseconds to wait for an existing stream handle to be released
71//! when creating a new one.
72#define WFC_STREAM_RETRY_DELAY_MS 1
73//! Number of attempts allowed to create a stream with a given handle.
74#define WFC_STREAM_RETRIES 50
75
76//==============================================================================
77
78//! Top-level stream type
79typedef struct WFC_STREAM_tag
80{
81 //! Handle; may be assigned by window manager.
82 WFCNativeStreamType handle;
83
84 //! Number of times this stream has been registered in the process. Creation implies registration
85 uint32_t registrations;
86
87 //! Flag to indicate entry is no longer in use and imminently due for destruction.
88 bool to_be_deleted;
89
90 //! Mutex, for thread safety.
91 VCOS_MUTEX_T mutex;
92
93 //! Configuration info.
94 WFC_STREAM_INFO_T info;
95
96 //!@brief Image providers to which this stream sends data; recorded so we do
97 //! not destroy this stream if it is still associated with a source or mask.
98 uint32_t num_of_sources_or_masks;
99 //! Record if this stream holds the output from an off-screen context.
100 bool used_for_off_screen;
101
102 //! Thread for handling server-side request to change source and/or destination rectangles
103 VCOS_THREAD_T rect_req_thread_data;
104 //! Flag for when thread must terminate
105 bool rect_req_terminate;
106 //! Callback function notifying calling module
107 WFC_STREAM_REQ_RECT_CALLBACK_T req_rect_callback;
108 //! Argument to callback function
109 void *req_rect_cb_args;
110
111 //! Pointer to next stream
112 struct WFC_STREAM_tag *next;
113 //! Pointer to previous stream
114 struct WFC_STREAM_tag *prev;
115} WFC_STREAM_T;
116
117//==============================================================================
118
119//! Blockpool containing all created streams.
120static VCOS_BLOCKPOOL_T wfc_stream_blockpool;
121//! Next stream handle, allocated by wfc_stream_get_next().
122static WFCNativeStreamType wfc_stream_next_handle = (1 << 31);
123
124static VCOS_LOG_CAT_T log_cat = VCOS_LOG_INIT("wfc_client_stream", WFC_LOG_LEVEL);
125
126//! Ensure lock and blockpool are only initialised once
127static VCOS_ONCE_T wfc_stream_initialise_once;
128//! The global (process-wide) lock
129static VCOS_MUTEX_T wfc_stream_global_lock;
130//! Pointer to the first stream data block
131static WFC_STREAM_T *wfc_stream_head;
132
133//==============================================================================
134//!@name Static functions
135//!@{
136static void wfc_stream_initialise(void);
137static WFC_STREAM_T *wfc_stream_global_lock_and_find_stream_ptr(WFCNativeStreamType stream);
138static WFC_STREAM_T *wfc_stream_create_stream_ptr(WFCNativeStreamType stream, bool allow_duplicate);
139static WFC_STREAM_T *wfc_stream_find_stream_ptr(WFCNativeStreamType stream);
140static void wfc_stream_destroy_if_ready(WFC_STREAM_T *stream_ptr);
141static void *wfc_stream_rect_req_thread(void *arg);
142static void wfc_client_stream_post_sem(void *cb_data);
143//!@}
144//==============================================================================
145//!@name Public functions
146//!@{
147
148WFCNativeStreamType wfc_stream_get_next(void)
149// In cases where the caller doesn't want to assign a stream number, provide
150// one for it.
151{
152 GLOBAL_LOCK();
153
154 WFCNativeStreamType next_stream = wfc_stream_next_handle;
155 wfc_stream_next_handle++;
156
157 GLOBAL_UNLOCK();
158
159 return next_stream;
160}
161
162//------------------------------------------------------------------------------
163
164uint32_t wfc_stream_create(WFCNativeStreamType stream, uint32_t flags)
165// Create a stream, using the given stream handle (typically assigned by the
166// window manager). Return zero if OK.
167{
168 WFC_STREAM_T *stream_ptr;
169 uint32_t result = 0;
170
171 vcos_log_info("%s: stream 0x%x flags 0x%x", VCOS_FUNCTION, stream, flags);
172
173 // Create stream
174 stream_ptr = wfc_stream_create_stream_ptr(stream, false);
175 if(stream_ptr == NULL)
176 {
177 vcos_log_error("%s: unable to create data block for stream 0x%x", VCOS_FUNCTION, stream);
178 return VCOS_ENOMEM;
179 }
180
181 uint64_t pid = vcos_process_id_current();
182 uint32_t pid_lo = (uint32_t) pid;
183 uint32_t pid_hi = (uint32_t) (pid >> 32);
184 int stream_in_use_retries = WFC_STREAM_RETRIES;
185 WFC_STREAM_INFO_T info;
186
187 memset(&info, 0, sizeof(info));
188 info.size = sizeof(info);
189 info.flags = flags;
190
191 do
192 {
193 stream_ptr->handle = wfc_server_stream_create_info(stream, &info, pid_lo, pid_hi);
194 vcos_log_trace("%s: server create returned 0x%x", VCOS_FUNCTION, stream_ptr->handle);
195
196 // If a handle is re-used rapidly, it may still be in use in the server temporarily
197 // Retry after a short delay
198 if (stream_ptr->handle == WFC_INVALID_HANDLE)
199 vcos_sleep(WFC_STREAM_RETRY_DELAY_MS);
200 }
201 while (stream_ptr->handle == WFC_INVALID_HANDLE && stream_in_use_retries-- > 0);
202
203 if (stream_ptr->handle == WFC_INVALID_HANDLE)
204 {
205 // Even after the retries, stream handle was still in use. Fail.
206 vcos_log_error("%s: stream 0x%x already exists in server", VCOS_FUNCTION, stream);
207 result = VCOS_EEXIST;
208 wfc_stream_destroy_if_ready(stream_ptr);
209 }
210 else
211 {
212 vcos_assert(stream_ptr->handle == stream);
213
214 stream_ptr->registrations++;
215 stream_ptr->info.flags = flags;
216 STREAM_UNLOCK(stream_ptr);
217 }
218
219 return result;
220}
221
222//------------------------------------------------------------------------------
223
224WFCNativeStreamType wfc_stream_create_assign_id(uint32_t flags)
225// Create a stream, and automatically assign it a new stream number, which is returned
226{
227 WFCNativeStreamType stream = wfc_stream_get_next();
228 uint32_t failure = wfc_stream_create(stream, flags);
229
230 if (failure == VCOS_EEXIST)
231 {
232 // If a duplicate stream exists, give it one more go with a new ID
233 stream = wfc_stream_get_next();
234 failure = wfc_stream_create(stream, flags);
235 }
236
237 if(failure) {return WFC_INVALID_HANDLE;}
238 else {return stream;}
239}
240
241//------------------------------------------------------------------------------
242
243uint32_t wfc_stream_create_req_rect
244 (WFCNativeStreamType stream, uint32_t flags,
245 WFC_STREAM_REQ_RECT_CALLBACK_T callback, void *cb_args)
246// Create a stream, using the given stream handle, which will notify the calling
247// module when the server requests a change in source and/or destination rectangle,
248// using the supplied callback. Return zero if OK.
249{
250 vcos_log_info("wfc_stream_create_req_rect: stream %X", stream);
251
252 uint32_t failure;
253
254 failure = wfc_stream_create(stream, flags | WFC_STREAM_FLAGS_REQ_RECT);
255 if (failure)
256 return failure;
257
258 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
259 // Stream just created, so ought to be found
260 vcos_assert(stream_ptr);
261
262 // There's no point creating this type of stream if you don't supply a callback
263 // to update the src/dest rects via WF-C.
264 vcos_assert(callback != NULL);
265
266 stream_ptr->req_rect_callback = callback;
267 stream_ptr->req_rect_cb_args = cb_args;
268
269 // Create thread for handling server-side request to change source
270 // and/or destination rectangles. One per stream (if enabled).
271 VCOS_STATUS_T status = vcos_thread_create(&stream_ptr->rect_req_thread_data, "wfc_stream_rect_req_thread",
272 NULL, wfc_stream_rect_req_thread, (void *) stream);
273 vcos_demand(status == VCOS_SUCCESS);
274
275 STREAM_UNLOCK(stream_ptr);
276
277 return 0;
278}
279
280//------------------------------------------------------------------------------
281
282bool wfc_stream_register_source_or_mask(WFCNativeStreamType stream, bool add_source_or_mask)
283// Indicate that a source or mask is now associated with this stream, or should
284// now be removed from such an association.
285{
286 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
287
288 if (!stream_ptr)
289 return false;
290
291 vcos_log_trace("%s: stream 0x%x %d->%d", VCOS_FUNCTION, stream,
292 stream_ptr->num_of_sources_or_masks,
293 add_source_or_mask ? stream_ptr->num_of_sources_or_masks + 1 : stream_ptr->num_of_sources_or_masks - 1);
294
295 if(add_source_or_mask)
296 {
297 stream_ptr->num_of_sources_or_masks++;
298 STREAM_UNLOCK(stream_ptr);
299 }
300 else
301 {
302 if(vcos_verify(stream_ptr->num_of_sources_or_masks > 0))
303 {stream_ptr->num_of_sources_or_masks--;}
304
305 // Stream is unlocked by destroy_if_ready
306 wfc_stream_destroy_if_ready(stream_ptr);
307 }
308
309 return true;
310}
311
312//------------------------------------------------------------------------------
313
314void wfc_stream_await_buffer(WFCNativeStreamType stream)
315// Suspend until buffer is available on the server.
316{
317 vcos_log_trace("%s: stream 0x%x", VCOS_FUNCTION, stream);
318
319 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
320 if (!stream_ptr)
321 return;
322
323 if(vcos_verify(stream_ptr->info.flags & WFC_STREAM_FLAGS_BUF_AVAIL))
324 {
325 VCOS_SEMAPHORE_T image_available_sem;
326 VCOS_STATUS_T status;
327
328 // Long running operation, so keep VC alive until it completes.
329 wfc_server_use_keep_alive();
330
331 status = vcos_semaphore_create(&image_available_sem, "WFC image available", 0);
332 vcos_assert(status == VCOS_SUCCESS); // For all relevant platforms
333 vcos_unused(status);
334
335 wfc_server_stream_on_image_available(stream, wfc_client_stream_post_sem, &image_available_sem);
336
337 vcos_log_trace("%s: pre async sem wait: stream: %X", VCOS_FUNCTION, stream);
338 vcos_semaphore_wait(&image_available_sem);
339 vcos_log_trace("%s: post async sem wait: stream: %X", VCOS_FUNCTION, stream);
340
341 vcos_semaphore_delete(&image_available_sem);
342 wfc_server_release_keep_alive();
343 }
344
345 STREAM_UNLOCK(stream_ptr);
346
347}
348
349//------------------------------------------------------------------------------
350
351void wfc_stream_destroy(WFCNativeStreamType stream)
352// Destroy a stream - unless it is still in use, in which case, mark it for
353// destruction once all users have finished with it.
354{
355 vcos_log_info("%s: stream: %X", VCOS_FUNCTION, stream);
356
357 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
358
359 if (stream_ptr)
360 {
361 /* If stream is still in use (e.g. it's attached to at least one source/mask
362 * which is associated with at least one element) then destruction is delayed
363 * until it's no longer in use. */
364 if (stream_ptr->registrations> 0)
365 {
366 stream_ptr->registrations--;
367 vcos_log_trace("%s: stream: %X ready to destroy?", VCOS_FUNCTION, stream);
368 }
369 else
370 {
371 vcos_log_error("%s: stream: %X destroyed when unregistered", VCOS_FUNCTION, stream);
372 }
373
374 // Stream is unlocked by destroy_if_ready
375 wfc_stream_destroy_if_ready(stream_ptr);
376 }
377 else
378 {
379 vcos_log_warn("%s: stream %X doesn't exist", VCOS_FUNCTION, stream);
380 }
381
382}
383
384//------------------------------------------------------------------------------
385//!@name
386//! Off-screen composition functions
387//!@{
388//------------------------------------------------------------------------------
389
390uint32_t wfc_stream_create_for_context
391 (WFCNativeStreamType stream, uint32_t width, uint32_t height)
392// Create a stream for an off-screen context to output to, with the default number of buffers.
393{
394 return wfc_stream_create_for_context_nbufs(stream, width, height, 0);
395}
396
397uint32_t wfc_stream_create_for_context_nbufs
398 (WFCNativeStreamType stream, uint32_t width, uint32_t height, uint32_t nbufs)
399// Create a stream for an off-screen context to output to, with a specific number of buffers.
400{
401 WFC_STREAM_T *stream_ptr;
402 bool stream_created = false;
403
404 if(!vcos_verify(stream != WFC_INVALID_HANDLE))
405 {return 1;}
406
407 stream_ptr = wfc_stream_find_stream_ptr(stream);
408 if (stream_ptr)
409 {
410 uint32_t flags = stream_ptr->info.flags;
411
412 // Stream already exists, check flags match expected
413 STREAM_UNLOCK(stream_ptr);
414
415 if (flags != WFC_STREAM_FLAGS_NONE)
416 {
417 vcos_log_error("%s: stream flags mismatch (expected 0x%x, got 0x%x)", VCOS_FUNCTION, WFC_STREAM_FLAGS_NONE, flags);
418 return 1;
419 }
420 }
421 else
422 {
423 // Create stream
424 if (wfc_stream_create(stream, WFC_STREAM_FLAGS_NONE) != 0)
425 return 1;
426 stream_created = true;
427 }
428
429 // Allocate buffers on the server.
430 if (!wfc_server_stream_allocate_images(stream, width, height, nbufs))
431 {
432 // Failed to allocate buffers
433 vcos_log_warn("%s: failed to allocate %u buffers for stream %X size %ux%u", VCOS_FUNCTION, nbufs, stream, width, height);
434 if (stream_created)
435 wfc_stream_destroy(stream);
436 return 1;
437 }
438
439 return 0;
440}
441
442//------------------------------------------------------------------------------
443
444bool wfc_stream_used_for_off_screen(WFCNativeStreamType stream)
445// Returns true if this stream exists, and is in use as the output of an
446// off-screen context.
447{
448 bool used_for_off_screen;
449
450 vcos_log_trace("%s: stream 0x%x", VCOS_FUNCTION, stream);
451
452 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
453 if (!stream_ptr)
454 return false;
455
456 used_for_off_screen = stream_ptr->used_for_off_screen;
457
458 STREAM_UNLOCK(stream_ptr);
459
460 return used_for_off_screen;
461
462}
463
464//------------------------------------------------------------------------------
465
466void wfc_stream_register_off_screen(WFCNativeStreamType stream, bool used_for_off_screen)
467// Called on behalf of an off-screen context, to either set or clear the stream's
468// flag indicating that it's being used as output for that context.
469{
470 if(stream == WFC_INVALID_HANDLE)
471 {return;}
472
473 vcos_log_trace("%s: stream 0x%x", VCOS_FUNCTION, stream);
474
475 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
476 if (!stream_ptr)
477 return;
478
479 stream_ptr->used_for_off_screen = used_for_off_screen;
480
481 if (used_for_off_screen)
482 STREAM_UNLOCK(stream_ptr);
483 else
484 {
485 // Stream is unlocked by destroy_if_ready
486 wfc_stream_destroy_if_ready(stream_ptr);
487 }
488}
489
490//!@} // Off-screen composition functions
491//!@} // Public functions
492//==============================================================================
493
494/** Initialise logging and global mutex */
495static void wfc_stream_initialise(void)
496{
497 VCOS_STATUS_T status;
498
499 vcos_log_set_level(&log_cat, WFC_LOG_LEVEL);
500 vcos_log_register("wfc_client_stream", &log_cat);
501
502 vcos_log_trace("%s", VCOS_FUNCTION);
503
504 status = vcos_mutex_create(&wfc_stream_global_lock, "WFC stream global lock");
505 vcos_assert(status == VCOS_SUCCESS);
506
507 status = vcos_blockpool_create_on_heap(&wfc_stream_blockpool,
508 WFC_STREAM_BLOCK_SIZE, sizeof(WFC_STREAM_T),
509 VCOS_BLOCKPOOL_ALIGN_DEFAULT, VCOS_BLOCKPOOL_FLAG_NONE,
510 "wfc stream pool");
511 vcos_assert(status == VCOS_SUCCESS);
512
513 status = vcos_blockpool_extend(&wfc_stream_blockpool,
514 WFC_STREAM_MAX_EXTENSIONS, WFC_STREAM_BLOCK_SIZE);
515 vcos_assert(status == VCOS_SUCCESS);
516}
517
518//------------------------------------------------------------------------------
519
520/** Take the global lock and then search for the stream data for a given handle.
521 * The global lock is not released on return and the stream is not locked.
522 *
523 * @param stream The stream handle.
524 * @return The pointer to the stream structure, or NULL if not found.
525 */
526static WFC_STREAM_T *wfc_stream_global_lock_and_find_stream_ptr(WFCNativeStreamType stream)
527{
528 WFC_STREAM_T *stream_ptr;
529
530 GLOBAL_LOCK();
531
532 stream_ptr = wfc_stream_head;
533 while (stream_ptr && stream_ptr->handle != stream)
534 stream_ptr = stream_ptr->next;
535
536 return stream_ptr;
537}
538
539//------------------------------------------------------------------------------
540
541/** Create a stream structure corresponding to the specified stream handle. If
542 * the stream structure already exists or there is an error allocating it, the
543 * function returns NULL. On success, the stream pointer is left locked.
544 *
545 * @param stream The stream handle.
546 * @param allow_duplicate True to allow an existing entry
547 * @return The pointer to the new stream structure, or NULL on error.
548 */
549static WFC_STREAM_T *wfc_stream_create_stream_ptr(WFCNativeStreamType stream, bool allow_duplicate)
550{
551 WFC_STREAM_T *stream_ptr = wfc_stream_global_lock_and_find_stream_ptr(stream);
552
553 vcos_log_trace("%s: stream handle 0x%x", VCOS_FUNCTION, stream);
554
555 if (stream_ptr && !stream_ptr->to_be_deleted)
556 {
557 if (!allow_duplicate)
558 {
559 vcos_log_error("%s: attempt to create duplicate of stream handle 0x%x", VCOS_FUNCTION, stream);
560 // Stream already exists, return NULL
561 stream_ptr = NULL;
562 }
563 else
564 {
565 vcos_log_trace("%s: duplicate of stream handle 0x%x created", VCOS_FUNCTION, stream);
566
567 STREAM_LOCK(stream_ptr);
568 }
569 }
570 else
571 {
572 if (stream_ptr)
573 {
574 vcos_log_trace("%s: recycling data block for stream handle 0x%x", VCOS_FUNCTION, stream);
575
576 // Recycle existing entry
577 stream_ptr->to_be_deleted = false;
578
579 STREAM_LOCK(stream_ptr);
580 }
581 else
582 {
583 vcos_log_trace("%s: allocating block for stream handle 0x%x", VCOS_FUNCTION, stream);
584
585 // Create new block and insert it into the list
586 stream_ptr = vcos_blockpool_calloc(&wfc_stream_blockpool);
587
588 if (stream_ptr)
589 {
590 VCOS_STATUS_T status;
591
592 status = vcos_mutex_create(&stream_ptr->mutex, "WFC_STREAM_T mutex");
593 if (vcos_verify(status == VCOS_SUCCESS))
594 {
595 STREAM_LOCK(stream_ptr);
596
597 // First stream in this process, connect
598 if (!wfc_stream_head)
599 wfc_server_connect();
600
601 stream_ptr->handle = stream;
602 stream_ptr->info.size = sizeof(stream_ptr->info);
603
604 // Insert data into list
605 stream_ptr->next = wfc_stream_head;
606 if (wfc_stream_head)
607 wfc_stream_head->prev = stream_ptr;
608 wfc_stream_head = stream_ptr;
609 }
610 else
611 {
612 vcos_log_error("%s: unable to create mutex for stream handle 0x%x", VCOS_FUNCTION, stream);
613 vcos_blockpool_free(stream_ptr);
614 stream_ptr = NULL;
615 }
616 }
617 else
618 {
619 vcos_log_error("%s: unable to allocate data for stream handle 0x%x", VCOS_FUNCTION, stream);
620 }
621 }
622 }
623
624 GLOBAL_UNLOCK();
625
626 return stream_ptr;
627}
628
629//------------------------------------------------------------------------------
630
631/** Destroys a stream structure identified by stream handle. If the stream is not
632 * found or the stream has not been marked for deletion, the operation has no
633 * effect.
634 *
635 * @param stream The stream handle.
636 */
637static void wfc_stream_destroy_stream_ptr(WFCNativeStreamType stream)
638{
639 WFC_STREAM_T *stream_ptr = wfc_stream_global_lock_and_find_stream_ptr(stream);
640
641 vcos_log_trace("%s: stream handle 0x%x", VCOS_FUNCTION, stream);
642
643 if (stream_ptr)
644 {
645 if (stream_ptr->to_be_deleted)
646 {
647 STREAM_LOCK(stream_ptr);
648
649 vcos_log_trace("%s: unlinking from list", VCOS_FUNCTION);
650
651 if (stream_ptr->next)
652 stream_ptr->next->prev = stream_ptr->prev;
653 if (stream_ptr->prev)
654 stream_ptr->prev->next = stream_ptr->next;
655 else
656 wfc_stream_head = stream_ptr->next;
657
658 // No streams left in this process, disconnect
659 if (wfc_stream_head == NULL)
660 wfc_server_disconnect();
661 }
662 else
663 {
664 vcos_log_trace("%s: stream 0x%x recycled before destruction", VCOS_FUNCTION, stream);
665 stream_ptr = NULL;
666 }
667 }
668 else
669 {
670 vcos_log_error("%s: stream 0x%x not found", VCOS_FUNCTION, stream);
671 }
672
673 GLOBAL_UNLOCK();
674
675 if (stream_ptr)
676 {
677 // Stream data block no longer in list, can safely destroy it
678 STREAM_UNLOCK(stream_ptr);
679
680 // Wait for rectangle request thread to complete
681 if(stream_ptr->info.flags & WFC_STREAM_FLAGS_REQ_RECT)
682 vcos_thread_join(&stream_ptr->rect_req_thread_data, NULL);
683
684 // Destroy mutex
685 vcos_mutex_delete(&stream_ptr->mutex);
686
687 // Delete
688 vcos_blockpool_free(stream_ptr);
689 }
690}
691
692//------------------------------------------------------------------------------
693
694/** Return a pointer to the stream structure corresponding to the specified stream
695 * handle. On success, the stream pointer is locked.
696 *
697 * @param stream The stream handle.
698 * @return The pointer to the stream structure, or NULL on error.
699 */
700static WFC_STREAM_T *wfc_stream_find_stream_ptr(WFCNativeStreamType stream)
701{
702 WFC_STREAM_T *stream_ptr = wfc_stream_global_lock_and_find_stream_ptr(stream);
703
704 if (stream_ptr && !stream_ptr->to_be_deleted)
705 STREAM_LOCK(stream_ptr);
706
707 GLOBAL_UNLOCK();
708
709 return stream_ptr;
710}
711
712//------------------------------------------------------------------------------
713
714/** Destroy the stream if it is no longer in use. The stream must be locked on
715 * entry and shall be unlocked (or destroyed along with the rest of the stream)
716 * on exit.
717 *
718 * @param stream_ptr The locked stream data pointer.
719 */
720static void wfc_stream_destroy_if_ready(WFC_STREAM_T *stream_ptr)
721{
722 WFCNativeStreamType stream;
723 uint64_t pid = vcos_process_id_current();
724 uint32_t pid_lo = (uint32_t)pid;
725 uint32_t pid_hi = (uint32_t)(pid >> 32);
726
727 if (stream_ptr == NULL)
728 {
729 vcos_log_error("%s: stream_ptr is NULL", VCOS_FUNCTION);
730 return;
731 }
732
733 if(stream_ptr->num_of_sources_or_masks > 0
734 || stream_ptr->used_for_off_screen
735 || stream_ptr->registrations > 0)
736 {
737 vcos_log_trace("%s: stream: %X not ready: reg:%u srcs:%u o/s:%d", VCOS_FUNCTION,
738 stream_ptr->handle, stream_ptr->registrations,
739 stream_ptr->num_of_sources_or_masks, stream_ptr->used_for_off_screen);
740 STREAM_UNLOCK(stream_ptr);
741 return;
742 }
743
744 stream = stream_ptr->handle;
745
746 vcos_log_info("%s: stream: %X to be destroyed", VCOS_FUNCTION, stream);
747
748 // Prevent stream from being found, although it can be recycled.
749 stream_ptr->to_be_deleted = true;
750
751 // Delete server-side stream
752 wfc_server_stream_destroy(stream, pid_lo, pid_hi);
753
754 STREAM_UNLOCK(stream_ptr);
755
756 wfc_stream_destroy_stream_ptr(stream);
757}
758
759//------------------------------------------------------------------------------
760
761//! Convert from dispmanx source rectangle type (int * 2^16) to WF-C type (float).
762#define WFC_DISPMANX_TO_SRC_VAL(value) (((WFCfloat) (value)) / 65536.0)
763
764static void *wfc_stream_rect_req_thread(void *arg)
765//!@brief Thread for handling server-side request to change source and/or destination
766//! rectangles. One per stream (if enabled).
767{
768 WFCNativeStreamType stream = (WFCNativeStreamType) arg;
769
770 WFC_STREAM_REQ_RECT_CALLBACK_T callback;
771 void *cb_args;
772 VCOS_SEMAPHORE_T rect_req_sem;
773 VCOS_STATUS_T status;
774
775 int32_t vc_rects[8];
776 WFCint dest_rect[WFC_RECT_SIZE];
777 WFCfloat src_rect[WFC_RECT_SIZE];
778
779 vcos_log_info("wfc_stream_rect_req_thread: START: stream: %X", stream);
780
781 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
782 if (!stream_ptr)
783 return NULL;
784
785 // Get local pointers to stream parameters
786 callback = stream_ptr->req_rect_callback;
787 cb_args = stream_ptr->req_rect_cb_args;
788
789 STREAM_UNLOCK(stream_ptr);
790
791 status = vcos_semaphore_create(&rect_req_sem, "WFC rect req", 0);
792 vcos_assert(status == VCOS_SUCCESS); // On all relevant platforms
793
794 while (status == VCOS_SUCCESS)
795 {
796 wfc_server_stream_on_rects_change(stream, wfc_client_stream_post_sem, &rect_req_sem);
797
798 // Await new values from server
799 vcos_semaphore_wait(&rect_req_sem);
800
801 status = wfc_server_stream_get_rects(stream, vc_rects);
802 if (status == VCOS_SUCCESS)
803 {
804 // Convert from VC/dispmanx to WF-C types.
805 vcos_static_assert(sizeof(dest_rect) == (4 * sizeof(int32_t)));
806 memcpy(dest_rect, vc_rects, sizeof(dest_rect)); // Types are equivalent
807
808 src_rect[WFC_RECT_X] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[4]);
809 src_rect[WFC_RECT_Y] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[5]);
810 src_rect[WFC_RECT_WIDTH] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[6]);
811 src_rect[WFC_RECT_HEIGHT] = WFC_DISPMANX_TO_SRC_VAL(vc_rects[7]);
812
813 callback(cb_args, dest_rect, src_rect);
814 }
815 }
816
817 vcos_semaphore_delete(&rect_req_sem);
818
819 vcos_log_info("wfc_stream_rect_req_thread: END: stream: %X", stream);
820
821 return NULL;
822}
823
824//------------------------------------------------------------------------------
825
826static void wfc_client_stream_post_sem(void *cb_data)
827{
828 VCOS_SEMAPHORE_T *sem = (VCOS_SEMAPHORE_T *)cb_data;
829
830 vcos_log_trace("%s: sem %p", VCOS_FUNCTION, sem);
831 vcos_assert(sem != NULL);
832 vcos_semaphore_post(sem);
833}
834
835//==============================================================================
836#ifdef KHRN_FRUIT_DIRECT
837static KHRN_UMEM_HANDLE_T get_ustorage(EGLImageKHR im, KHRN_DEPS_T *deps)
838{
839 KHRN_UMEM_HANDLE_T ret = KHRN_UMEM_HANDLE_INVALID;
840 EGL_SERVER_STATE_T *state = EGL_GET_SERVER_STATE();
841 EGL_IMAGE_T *eglimage_;
842 KHRN_IMAGE_T *image;
843
844 KHRN_MEM_HANDLE_T eglimage =
845 khrn_map_lookup(&state->eglimages, (uint32_t) im);
846
847 if (eglimage == KHRN_MEM_HANDLE_INVALID) {
848 vcos_log_error("Bad image %p", im);
849 goto end;
850 }
851
852 eglimage_ = khrn_mem_lock(eglimage);
853 vcos_assert(eglimage_->external.src != KHRN_UMEM_HANDLE_INVALID);
854 image = khrn_mem_lock(eglimage_->external.src);
855
856 /* FIXME: We probably don't need this. It doesn't make any sense */
857 khrn_deps_quick_write(deps, &image->interlock);
858
859 ret = image->ustorage;
860
861 khrn_mem_unlock(eglimage_->external.src);
862 khrn_mem_unlock(eglimage);
863
864end:
865 return ret;
866}
867
868void wfc_stream_signal_eglimage_data(WFCNativeStreamType stream, EGLImageKHR im)
869{
870 wfc_stream_signal_eglimage_data_protected(stream, im, 0);
871}
872
873#ifdef ANDROID
874void wfc_stream_signal_eglimage_data_protected(WFCNativeStreamType stream, EGLImageKHR im, uint32_t is_protected)
875{
876 EGL_SERVER_STATE_T *state = EGL_GET_SERVER_STATE();
877 KHRN_DEPS_T deps;
878 KHRN_MEM_HANDLE_T eglimage;
879 EGL_IMAGE_T *eglimage_;
880 KHRN_IMAGE_T *image_;
881
882 CLIENT_LOCK();
883
884 khrn_deps_init(&deps);
885
886 eglimage = khrn_map_lookup(&state->eglimages, (uint32_t)im);
887 eglimage_ = khrn_mem_lock(eglimage);
888 image_ = khrn_mem_lock(eglimage_->external.src);
889
890 vcos_assert(image_->ustorage != KHRN_UMEM_HANDLE_INVALID);
891 khrn_umem_acquire(&deps, image_->ustorage);
892
893 image_->flags &= ~IMAGE_FLAG_PROTECTED;
894 if (is_protected)
895 image_->flags |= IMAGE_FLAG_PROTECTED;
896
897 khrn_signal_image_data((uint32_t)stream, image_->ustorage, image_->width, image_->height, image_->stride, image_->offset,
898 image_->format, image_->flags, eglimage_->flip_y ? true : false);
899
900 khrn_mem_unlock(eglimage_->external.src);
901 khrn_mem_unlock(eglimage);
902
903 CLIENT_UNLOCK();
904}
905#else
906void wfc_stream_signal_eglimage_data_protected(WFCNativeStreamType stream, EGLImageKHR im, uint32_t is_protected)
907{
908 EGL_SERVER_STATE_T *state = EGL_GET_SERVER_STATE();
909 KHRN_DEPS_T deps;
910 KHRN_MEM_HANDLE_T eglimage;
911 EGL_IMAGE_T *eglimage_;
912 KHRN_IMAGE_T *image_;
913 WFC_STREAM_IMAGE_T stream_image;
914
915 CLIENT_LOCK();
916
917 khrn_deps_init(&deps);
918
919 eglimage = khrn_map_lookup(&state->eglimages, (uint32_t)im);
920 eglimage_ = khrn_mem_lock(eglimage);
921 image_ = khrn_mem_lock(eglimage_->external.src);
922
923 vcos_assert(image_->ustorage != KHRN_UMEM_HANDLE_INVALID);
924 khrn_umem_acquire(&deps, image_->ustorage);
925
926 /* The EGL protection flag is passed through the KHRN_IMAGE_T flags field */
927 image_->flags &= ~IMAGE_FLAG_PROTECTED;
928 if (is_protected)
929 image_->flags |= IMAGE_FLAG_PROTECTED;
930
931 memset(&stream_image, 0, sizeof(stream_image));
932 stream_image.length = sizeof(stream_image);
933 stream_image.type = WFC_STREAM_IMAGE_TYPE_EGL;
934
935 stream_image.handle = image_->ustorage;
936 stream_image.width = image_->width;
937 stream_image.height = image_->height;
938 stream_image.format = image_->format;
939 stream_image.pitch = image_->stride;
940 stream_image.offset = image_->offset;
941 stream_image.flags = image_->flags;
942 stream_image.flip = eglimage_->flip_y ? WFC_STREAM_IMAGE_FLIP_VERT : WFC_STREAM_IMAGE_FLIP_NONE;
943
944 khrn_mem_unlock(eglimage_->external.src);
945 khrn_mem_unlock(eglimage);
946
947 CLIENT_UNLOCK();
948
949 wfc_server_stream_signal_image(stream, &stream_image);
950}
951#endif
952
953void wfc_stream_release_eglimage_data(WFCNativeStreamType stream,
954 EGLImageKHR im)
955{
956 KHRN_DEPS_T deps;
957 KHRN_UMEM_HANDLE_T ustorage;
958
959 CLIENT_LOCK();
960 ustorage = get_ustorage(im, &deps);
961 khrn_umem_release(&deps, ustorage);
962 CLIENT_UNLOCK();
963}
964#endif
965
966void wfc_stream_signal_mm_image_data(WFCNativeStreamType stream, uint32_t im)
967{
968 wfc_server_stream_signal_mm_image_data(stream, im);
969}
970
971void wfc_stream_signal_raw_pixels(WFCNativeStreamType stream, uint32_t handle,
972 uint32_t format, uint32_t w, uint32_t h, uint32_t pitch, uint32_t vpitch)
973{
974 wfc_server_stream_signal_raw_pixels(stream, handle, format, w, h, pitch, vpitch);
975}
976
977void wfc_stream_signal_image(WFCNativeStreamType stream,
978 const WFC_STREAM_IMAGE_T *image)
979{
980 wfc_server_stream_signal_image(stream, image);
981}
982
983void wfc_stream_register(WFCNativeStreamType stream) {
984 uint64_t pid = vcos_process_id_current();
985 uint32_t pid_lo = (uint32_t)pid;
986 uint32_t pid_hi = (uint32_t)(pid >> 32);
987
988 if (wfc_server_connect() == VCOS_SUCCESS)
989 {
990 WFC_STREAM_INFO_T info;
991 uint32_t status;
992
993 info.size = sizeof(info);
994 status = wfc_server_stream_get_info(stream, &info);
995
996 if (status == VCOS_SUCCESS)
997 {
998 WFC_STREAM_T *stream_ptr = wfc_stream_create_stream_ptr(stream, true);
999
1000 if (stream_ptr)
1001 {
1002 stream_ptr->registrations++;
1003 memcpy(&stream_ptr->info, &info, info.size);
1004 STREAM_UNLOCK(stream_ptr);
1005 }
1006
1007 wfc_server_stream_register(stream, pid_lo, pid_hi);
1008 }
1009 else
1010 {
1011 vcos_log_error("%s: get stream info failed: %u", VCOS_FUNCTION, status);
1012 }
1013 }
1014}
1015
1016void wfc_stream_unregister(WFCNativeStreamType stream) {
1017 uint64_t pid = vcos_process_id_current();
1018 uint32_t pid_lo = (uint32_t)pid;
1019 uint32_t pid_hi = (uint32_t)(pid >> 32);
1020 WFC_STREAM_T *stream_ptr = wfc_stream_find_stream_ptr(stream);
1021
1022 if (vcos_verify(stream_ptr != NULL))
1023 {
1024 wfc_server_stream_unregister(stream, pid_lo, pid_hi);
1025
1026 if (stream_ptr->registrations > 0)
1027 {
1028 stream_ptr->registrations--;
1029 vcos_log_trace("%s: stream %X", VCOS_FUNCTION, stream);
1030 }
1031 else
1032 {
1033 vcos_log_error("%s: stream %X already fully unregistered", VCOS_FUNCTION, stream);
1034 }
1035
1036 wfc_stream_destroy_if_ready(stream_ptr);
1037 }
1038
1039 wfc_server_disconnect();
1040}
1041
1042//==============================================================================
1043
1044